Engine capabilities
Fan-out, Map and Gather
A minimal Horus showcase of the declarative map construct. A producer stage splits a collection into batches, the map block fans a stage out over every batch, and a gather stage collects the results.
What this workflow does
This workflow demonstrates the declarative map: construct of horus-runtime.
A producer stage splits a small input collection into batches. A map: block
fans one stage out over every batch. The clones run concurrently. A gather stage
collects the N results back into a single folder.
There is no science here. Every stage shells out to echo, wc, or tr, or it
runs a stdlib-only script. The workflow runs anywhere in a few seconds. Read it
as a template for a real pipeline, such as a batched ligand library.
The problem it solves
A screen over a large collection is embarrassingly parallel. The items do not depend on each other. A serial stage still processes them one at a time.
To run them in parallel by hand, you write a loop, a job array, and a merge script. You then track which clone failed and which output belongs to which batch. That code is not part of your science, and you rewrite it per pipeline.
The map: block moves that work into the engine. You declare one stage and the
collection it runs over. Horus creates one clone per element and gives each clone
its own working directory. Horus places the clone outputs under a single
<stage>.gathered/<i>/ tree.
The fan-in target is stable. The gather stage reads one folder and does not need to know the clone count. The clone count follows the data, so a bigger input adds clones without an edit to the graph.
Pipeline
split (local) examples/items.json (8 strings) ──► batches/ (4 files)
│ chunks the JSON list into batch_0..batch_3.txt, 2 items each
score[00..03] (map, 4x concurrent clones over batches/)
│ each clone: word-counts + uppercases its one batch file
│ ──► scored/count.txt, scored/upper.txt (fan-in target: score.gathered/<i>/)
gather (local) score.gathered/ (4 subfolders) ──► results/summary/
│ sums the per-batch word counts, concatenates the uppercased text
Inputs and outputs
Input
examples/items.json: a JSON array of 8 short strings.
Outputs land in workflow_results/:
score.gathered/<i>/scored/count.txt: the word count for batch<i>. The bundled example gives2for every clone.score.gathered/<i>/scored/upper.txt: the batch text, uppercased.results/summary/summary.json:{"total_words": 8, "batches": [...]}for the bundled example.results/summary/combined_upper.txt: every batch text, concatenated in batch order.
Run the workflow
Install uv if you do not have it:
curl -LsSf https://astral.sh/uv/install.sh | sh
Then install the horus-runtime and run the workflow:
cd workflows/engine-showcases/w01-fanout-map-gather
uv sync
horus run workflow.yaml
You can also install the runtime with pip:
pip install horus-runtime
To change the fan-out width, edit the --batch-size argument of the split
stage in workflow.yaml. The batch count sets the clone count. To use your own
data, replace the array in examples/items.json.
References
Run this workflow
The workflow is open source. Clone the pantheon repository and run it with the horus-runtime engine. To run it on managed compute without a cluster of your own, join the Temple Compute OS waitlist.