VirusAntibodyModel breaks the examples CI job on every mesa PR (create_agents dna broadcast bug)
- Dominant language
- Python
- Stars
- 252
- Forks
- 279
- Avg merge
- 8d 9h
- Merged PRs (30d)
- 2
Description
mesa/mesa-examples (the bug lives in the example; it just surfaces via mesa/mesa's CI, which checks out mesa-examples fresh on every PR)
## Summary
The `examples` job in mesa/mesa's CI (checks out `mesa-examples` and runs `test_examples.py` against the current build) fails consistently on `test_model_steps[VirusAntibodyModel]` — on every PR, regardless of what the PR actually changes. It's not flaky and it's not caused by any particular PR's diff.
## Error
```
ValueError: sequence of length 3 does not match the number of agents (20)
```
Raised from `mesa/agent.py`, inside `Agent.create_agents()` → `_resolve_per_agent_values()`. Seen across independent runs with only the random values changing:
```
value = [0, 7, 5], n = 20, strict = True
value = [9, 0, 9], n = 20, strict = True
value = [6, 8, 4], n = 20, strict = True
```
## Root cause
`create_agents(model, n, *args, **kwargs)` treats any `list` / `tuple` / `ndarray` / `pandas.Series` argument as "one value per agent" and requires it to have length `n`, raising `ValueError` otherwise. This is documented, intentional mesa-core behavior (see the Warning in `Agent.create_agents`'s docstring) — it is not a mesa-core bug.
In `examples/virus_antibody/agents.py`, `VirusAgent.generate_dna()` returns a fixed 3-element list:
```python
def generate_dna(self, dna=None):
if dna is None:
return [self.random.randint(0, 9) for _ in range(3)]
...
```
This is the model's "founder" virus DNA (later mutated per-lineage via `duplicate()`). When the model bulk-creates the initial virus population with something like `VirusAgent.create_agents(self, num_viruses, ..., dna=)`, `_resolve_per_agent_values` sees a length-3 list against `n = num_viruses` (20 in the test harness) and raises. Since the initial virus count is essentially never 3, this fails deterministically on every run.
> Note: I confirmed `generate_dna()`'s shape directly in `agents.py`, but couldn't pull the current `model.py` source to see the exact `create_agents(...)` call (GitHub's diff view for that file didn't come through in my fetch). The call site above is inferred with high confidence from the matching value shapes in the CI logs — worth a quick confirm against the actual line.
## Suggested fix
If all initial viruses are meant to share one founder strain, broadcast it explicitly so its length matches `n`:
```python
initial_dna = [self.random.randint(0, 9) for _ in range(3)]
...
VirusAgent.create_agents(
self,
num_viruses,
self.space,
mutation_rate,
duplication_rate,
position=virus_positions,
dna=[initial_dna] * num_viruses, # broadcast, not per-agent
)
```
If each initial virus should instead start with its own random DNA, generate one sub-list per agent so the outer list's length already equals `n`:
```python
dna=[[self.random.randint(0, 9) for _ in range(3)] for _ in range(num_viruses)]
```
Contributor guide
Research direction
Start with examples/virus_antibody/agents.py to confirm generate_dna() and inspect the corresponding create_agents call in the model file. Run test_examples.py, especially test_model_steps[VirusAntibodyModel], then determine whether initial viruses should share one DNA value or receive independent DNA values. Done means the VirusAntibodyModel example passes consistently in the examples CI job.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- backend, testing-qa
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 74/100