Food Foraging[chaos]
The Roomba of the previous section had an easy time of it in one respect: the dirt was everywhere. Wherever it went there was something to clean, so moving sensibly was the whole of the problem. Foraging is harder, because food is scarce. An animal that eats can spend most of its life nowhere near its next meal, and the difficulty shifts from what to do when you arrive to how to get there at all.
That shift raises a question worth asking carefully. If we want to help a forager, what should we let it smell?
The intuitive answer is: as much as possible. Give it receptors for the food’s scent, and the more finely it can discriminate concentrations the better it should do. The runs below say otherwise, and the reason turns out to matter more than the result.
The task
The agent is the same inchworm from the previous sections, stretching its head into a new patch and then pulling its tail after it. It forages on a torus, so there are no walls and no corners to get trapped in, and the surface is seeded with patches of cake. Each patch holds three servings worth one energy apiece, so a patch rewards a worm that stays put and finishes what it started.
Fitness is OptimalEnergy: what fraction of all the energy in the arena did this worm actually eat, within a fixed budget of steps?
Every design trains on four separately generated food layouts at once, five scenarios each, so nothing can be won by memorising a single arena.
Two senses come as standard.
A worm always knows whether it’s standing on cake, and in the FindFood designs it can also smell its own trail from the last sixteen steps, which is what lets it avoid re-walking ground it has just covered.
Some designs also get a noise_rate, which binds a Blip element that fires at random with that probability.
A Blip is the only source of randomness in an otherwise deterministic program, and it’s how a worm can wander at all rather than marching in a straight line for ever.
When food is everywhere, search is free
Start with a small dense arena: fifteen cells square, a tenth of it cake, twenty-two patches and sixty-six energy on the table, five hundred steps to eat it.
Fifty thousand generations solve this outright. Across twelve replays the best genotype averages 95% of the available energy, and in the run above it takes everything. No sense of smell is involved, and none is needed. At this density a worm that ambles about eating whatever it touches will trip over food often enough that finding it was never the bottleneck.
When food is scarce, search is the whole problem
Now stretch the arena to thirty cells square and thin the cake to three per cent. Twenty-seven patches, eighty-one energy, a thousand steps.
Watch the path rather than the score. The worm has found something like a systematic sweep, using its own fading trail to keep from doubling back, and on this layout it does well: sixty-six of eighty-one energy. Averaged over twelve replays it manages 56%.
A sweep is a reasonable answer to a search problem and an expensive one. The worm spends most of its thousand steps walking over empty ground, and it can’t do better without knowing where the food is before it arrives.
Two ways to build a nose
Each cake patch can be made to emit a chemical that diffuses across the torus and decays, giving every square a concentration that falls off with distance from food. What the worm gets to read off that field is a design choice, and there are two quite different answers.
The first is a level. One or more thresholds, each binding a receptor that fires when the local concentration exceeds it. This tells the worm how strong the smell is where it stands, which is a fact about proximity.
The second is a direction. A single bit that fires when the worm is heading toward the strongest-smelling neighbouring square. This says nothing about how close the food is, only which way is warmer.
Both are cheap: a level costs one receptor element per threshold, a direction costs one element altogether. The obvious expectation is that levels are the more informative of the two, since a graded reading plainly carries more bits than a single bit does.
Levels don’t help
The scent-threshold design gets receptors at concentrations 0.1 and 0.25, and matches the blind baseline in every other respect. Its best genotype collects 44% of the arena’s energy, against the blind worm’s 56%. Adding the sense made it worse, after twice as many generations of search.
A more sensitive pair of thresholds, 0.01 and 0.1, does better and reaches 61%. That’s a gain of five points over smelling nothing at all, and it took 203,600 generations to find, roughly nine times the baseline’s search. For a sense of smell, this is a thin return.
Direction does
The gradient-aligned design replaces the thresholds with the single heading bit.
This worm averages 73% of the available energy, and it does so on four hundred steps rather than a thousand. Less than half the time, and it eats more than the blind sweeper and far more than either level-reading version. The behaviour looks different too: rather than sweeping the arena, it makes for the nearest smell and works the patch.
A cleaner test
The comparison so far is untidy, because those designs differ in step budget as well as in sense.
A later pair, in refined/food.py, isolates the variable properly.
Both members run on a thirty-square torus at two per cent food, both get three hundred steps, both get the same 5% noise rate and the same instruction set.
One reads direction only, with no thresholds at all; the other reads three concentration levels and no direction.
The level-reading one is even handed the larger memory, five slots against two.
Direction averages 79% of the arena’s energy; levels average 57%. The design with more sensory thresholds and more memory collects about three quarters of what the design with one directional bit manages.
A caution about the fitness column
Here are all the runs together. The last two columns are worth comparing carefully, because they disagree.
| Design | Smell | Arena | Steps | Gens | Fitness | Energy eaten |
|---|---|---|---|---|---|---|
find_food |
none | 15x15, 10% | 500 | 50,000 | 1.000 | 0.95 |
find_food_big |
none | 30x30, 3% | 1000 | 23,700 | 0.198 | 0.56 |
find_food_big_scent |
levels 0.1, 0.25 |
30x30, 3% | 1000 | 46,200 | 0.023 | 0.44 |
find_food_big_scent-sensitive |
levels 0.01, 0.1 |
30x30, 3% | 1000 | 203,600 | 0.164 | 0.61 |
find_food_big_gradient-min-diff |
direction | 30x30, 3% | 400 | 100,000 | 0.273 | 0.73 |
find_cake |
direction | 30x30, 2% | 1000 | 8,630 | 0.752 | 0.95 |
find_cake-fast |
direction | 30x30, 2% | 300 | 89,210 | 0.124 | 0.79 |
find_cake_levels |
levels 2, 3, 5 |
30x30, 2% | 300 | 100,000 | 0.002 | 0.57 |
OptimalEnergy scales steeply, rewarding the last few per cent of the arena far more than the first half.
That’s useful for driving a search and thoroughly misleading as a measure of how good a forager is.
Read the fitness column alone and find_cake_levels looks like a total failure at 0.002.
It isn’t.
It eats 57% of the cake, roughly what the blind sweeper manages on a comparable arena.
A worm with only concentration levels is a competent forager; it just isn’t a good one, and the fitness scale turns that gap into a chasm.
So the honest version of the result is smaller than the fitness numbers suggest, and it points the same way. Direction is worth about twenty points of energy over levels, and levels are worth close to nothing over no smell at all.
What a nose is for
A concentration tells the worm how much scent is at its own position, and one position isn’t enough to act on. To get from “the smell here is 0.14” to a decision about which way to go, the worm has to sample a second position, remember the first, and compare them. Every part of that has to exist before any of it pays: the movement, the held value, the comparison wired between them. A threshold receptor by itself is a fact with no immediate use.
A direction bit hands over the comparison already done. Diffusion has spread the information across the arena, the receptor differences the field between neighbouring squares, and what reaches the gene network isn’t a quantity but an instruction: keep going, or turn. One bit, usable the moment it appears, and a genome that wires it to “don’t turn” has already improved.
Real cells do something close to this, comparing concentrations as they move rather than measuring them absolutely, and steering on the difference. What these runs press isn’t that more sensory information is better. It’s that information a small program can act on immediately beats information it would first have to do arithmetic on.
TODO: the claim about real chemotaxis needs a citation, and checking rather than asserting.
E. coli’s run-and-tumble is the obvious case, and this project already has a run_or_tumble design that may make the point directly.