Migration · Infrastructure · Policy · Economy

What happens to the world when you change the rules?

FlowWorld is a simulator of 320 real cities with a vectorized three sector economy, an autonomous infrastructure planner that picks its own build sites, and a machine learning optimizer that hunts for the best mix of policy levers under randomized shocks. I ran eight contrasting scenarios for two hundred timesteps each, then one shock augmented optimizer search across forty eight trials. What follows is what came back, including the results that fell apart when I went looking for the mechanism behind them.

8scenarios analyzed
200timesteps each
320cities × 1,800 edges
+341%best optimizer growth
FlowWorld overview

The question

“If you change borders, transit links, housing rules, and infrastructure spending, what happens to where people live, the global economy, and which cities boom or collapse over time?”

FlowWorld isn't trying to predict reality. It's a policy laboratory. You twist a knob, press play, and a specific number comes back in under a minute. Because the engine is deterministic, the same seed and the same city data give you the same trajectory every time, which matters more than it sounds: a disagreement about a result can be settled by re-running it instead of by arguing.

The appeal is that a simulation forces you to commit. Most “what if we opened the borders” arguments are thought experiments where nobody has to name a number. Here you set border friction to 0.05, you press play, and the model hands you a figure for global GDP, a list of cities that grew, and a list of cities that emptied out. You can attack the assumptions all you like. What you can't do is stay vague.

The eight scenarios weren't picked to produce a tidy story. They're presets straight out of the project's own library, chosen to spread across the design space of borders, infrastructure, housing, migration and prices. The numbers came out how they came out. Several came out badly for conclusions I'd expected to draw, and I've said so where that happened rather than quietly dropping them, because how a result falls apart usually teaches you more than the result would have.

How the project started

Before any simulation code existed, I wrote out what I wanted to be able to ask of it. Those four questions are still legible in the codebase, because nearly every feature exists to answer one of them.

The original mission

Build a simulator that can honestly answer what the world looks like under different border policies, where people end up moving when cities run out of room, how resources and trade routes shape the economy over time, and which infrastructure decisions actually pay off under a real budget constraint.

Everything else follows from that. The migration model is there because of borders and crowding. The three sector economy is there because of resources and trade. The autonomous planner and its budget cap are there because of the infrastructure question. If you're ever looking at a feature and wondering why it exists, the answer is almost always that one of those four questions needed it.

The four research questions

Each one is deliberately built around a tradeoff rather than a right answer:

Growth against crowding. Investment now against investment later. Prices against rents. Aggregate output against distribution. The project assumed from the start that good policy is a negotiation rather than a maximum, and the scoring function the optimizer uses later reflects that directly.

The honesty pledge

One early commitment was a hard line against fantasy modeling, written down roughly like this:

100 percent real-world accuracy is not achievable in complex social and economic systems. The goal is high fidelity calibrated modeling with quantified uncertainty, not perfect prediction.

A lot of policy modeling goes wrong at the point where the modeler forgets their assumptions were assumptions, so the simulator was built to treat itself as a calibrated estimator rather than an oracle. You can see that in the interface. Every run shows a confidence level in the top bar, and the right panel carries a factor coverage disclosure listing every economic factor the model knows about alongside how complete the underlying data actually is.

That machinery earns its keep later in this report. The project's automated threshold registry flags the optimizer's own best scoring configuration as failing, which is the single most important caveat in the whole study and would have been easy to miss without it.

The eight design principles

Eight constraints, written down as non-negotiable. They're less a feature list than a definition of what counts as a valid version of the project:

How it got built

Three rough phases, each with a different goal. None of them are visible to someone dropping into the app today, but the order they happened in explains a lot about how the simulator behaves.

First: set the rules of the game

Before writing simulation code I wrote down what would count as a finished feature, what would count as a valid experiment, and how decisions would get reviewed. A governance charter, a quality checklist, a template for documenting assumptions, and a schema for describing an experiment well enough that someone else could reproduce it.

It's tedious work, and it's the reason the project didn't turn into a research toy nobody could trust. Once the simulator started producing data, every result had somewhere to live, a bar it had to clear, and a paper trail behind it. The failure mode this avoids is the one where somebody asks whether a number came from the current engine or the early prototype and nobody can say.

Second: build a data layer that doesn't lie

The middle phase was about making the cities real. The loader reads a global city gazetteer, and it supports two of them: the SimpleMaps World Cities CSV and the GeoNames dumps such as cities5000.txt. Those are separate datasets with separate licenses, and the code tracks which one each record came from. If neither is installed the simulator falls back to synthetic placements, and it flags every city it invents.

Getting that data in wasn't a matter of importing a CSV. It needed ingestion quality protocols, a license and jurisdiction matrix documenting each source's legal boundaries, validation rules to catch missing or anomalous records, and provenance audits tracing each field back to where it came from. With those running, the simulator can say which of its cities are real and which are filler, and can report a per city confidence figure. That's the “city accuracy” field in the place inspector.

One caveat matters for reproducing anything here: the gazetteer is an optional download and isn't committed to the repository. The runs in this report used real city data, which is why Yunfu and Bahawalpur show up by name, but a fresh clone won't regenerate them without fetching the dataset first.

Third: make the research tool look alive

The most ambitious phase was about making the simulator worth watching. The aspiration was explicit: something closer to Cities: Skylines than to a plotting library, but with real planetary data and a real economic engine underneath. That meant a lot of frontend work on the deck.gl rendering layer, the timeline scrubber, the camera and the flow animations, plus a lot of new simulation output in the form of per timestep flow records, per leg throughput, named bottlenecks and replay bundles.

The same phase expanded the economy. Three abstract sectors became a supply chain twin with named commodities (oil, raw materials, food and agriculture, manufactured goods, technology and IP), modal transport across rail, pipeline, shipping, road and air, corridor level throughput tracking, and automatic bottleneck detection. The commodity master list in the right panel is the visible surface of all that.

Current state

Implementation is done, the runs work, the optimizer works, and the visualization is fully operational. What's outstanding is formal research signoff. Five gates covering city schema accuracy, resource accuracy, connectivity, supply chain economics and visualization integrity are drafted but unsigned, largely because nobody had assembled the evidence for them in one place. This report is a first pass at that bundle, and on the evidence below at least two of those gates should stay unsigned for now.

Architecture, briefly

Two services run locally. A Python backend on port 8010 does the math, a web frontend on port 5173 draws the map and the dashboards, and they talk over a WebSocket so the simulation can stream timesteps into the browser as it computes them.

Backend

Python, FastAPI for the API, NumPy for the math. Every per city calculation runs as an array operation across all 320 cities at once rather than as a Python loop, which is what makes a 200 step run take about a minute instead of being unusable. A separate optimizer module wraps the simulator in a cross entropy search loop. World generation reads real city data if a gazetteer is installed and falls back to synthetic placements if not. Runs persist in three formats: structured data for replay, raw rows for analysis, and a readable play by play log.

FastAPI 0.115 · NumPy 2.1 · Pydantic 2.9 · Uvicorn 0.30

Frontend

React 18 with TypeScript, served by Vite. The map is deck.gl over a MapLibre GL basemap. MapLibre is the community fork of the last open source release of Mapbox GL JS, split off in December 2020 when Mapbox moved to a proprietary license, and developed independently since. Each timestep arrives over the socket as JSON carrying per city state, top migration arcs and global metrics, and each renders as its own toggleable layer.

React 18.3 · deck.gl 9.0 · MapLibre 4.7 · Recharts 2.12

ML optimizer

A from scratch cross entropy search over fourteen parameters: the twelve exposed as sliders in the UI, plus the planner's build interval and build strength. Each candidate runs a full simulation augmented with three shocks drawn at random from resource, infrastructure and migration, then gets scored on final GDP, growth, volatility, concentration, overpopulation and budget overrun. The top thirty percent of each iteration become the elites and refit the sampling distribution for the next round.

POST /api/optimize

Data layer

Storage follows a lakehouse layout separating raw inputs, curated tables and ready to use features. Every run produces a timestamped summary, a replay bundle and a decision audit trail. Twenty four scenario presets ship with the project, and every result here can be regenerated from those presets and seed 42, given the same city gazetteer. That gazetteer is an optional download rather than part of the repository.

Measured performance

Those are wall clock figures recorded by the runner on a Windows laptop CPU, not estimates. The bottleneck is the simulation math rather than the socket. The browser is never the thing waiting.

What the interface looks like

Three panels. The map fills the middle, the left sidebar holds the scenario library, twelve economy sliders and the layer toggles, and the right panel carries the inspector plus the live dashboards. In Analyst mode the inspector has five tabs: Node, Leg, Chain, Policy and Trust.

Mature sim overview
The default view, about 170 timesteps in. Cyan and blue arcs are the top migration flows for this frame. The bright yellow arc near East Asia is a rail link the autonomous planner has just built. The right panel lists the largest active corridors by throughput, with Tianjin to Beijing leading at roughly 211,690 tons per day.
Money revenue mode
Money mode, revenue overlay. Corridors are recolored by whether moving goods along them pays: green where throughput times unit price clears the transport cost, amber where it doesn't. Line width still encodes throughput, so this is the quickest way to separate routes that are economically alive from routes that merely exist.
Population heatmap
The population heatmap layer. Hotspots are East Asia, South Asia, West Africa and Western Europe, which is also roughly where the model concentrates its migration pressure.
City-scale zoom
Zoomed into northwest Spain. The labels (Santiago de Compostela, Lugo, A Coruña) come from the installed city gazetteer, at their real coordinates. The geography isn't abstract even where the economics is.
Analyst mode
Analyst mode with the Open Borders preset applied. All twelve sliders are exposed: border friction, connectivity, agglomeration, housing elasticity, migration propensity, softmax temperature, three price indices, R&D efficiency, infrastructure budget and link base cost. The right panel switches to the five tab inspector with SLO dashboards and the commodity master list.
The same world, recolored six different ways

The Node metric dropdown changes only what the city dots are colored by. The underlying simulation is identical in all six, which makes this useful for asking where wages are high versus where housing is expensive without perturbing anything.

Wage
Wage
GDP pc
GDP per capita
Housing
Housing cost
Resources
Resource output
Goods
Goods output
IP
IP / technology output

How a single timestep works

This is the part the README doesn't cover. Every timestep the simulator does the same five things, in order, across every city at once. The findings make a lot more sense once these are clear, including the two findings that turn out not to mean what they look like they mean.

Step 1: every city gets an attractiveness score

When you're deciding where to move you weigh a handful of things: what you can earn, how pleasant the place is, what a roof costs, and how much of your industry is nearby. The simulator does the same thing explicitly. Each city gets a “destination utility” every step, built from wages, productivity, amenity, an agglomeration bonus for being large, minus housing cost, plus proximity bonuses for resources, factories, ports, universities and talent.

The agglomeration bonus is the load bearing part. A city gets extra attractiveness purely because a lot of people already live there, and that single mechanic drives most of what's interesting in the model. It's why Tokyo keeps growing and why Pittsburgh shrank for fifty years, and it's a feedback loop: bigger cities attract more people, who make them bigger. Without a counterweight, and housing cost is the only one on offer here, the whole world collapses into a single megacity.

Step 2: residents pick where to go

Each city's residents then decide whether to move and where to. A softmax handles that: the probability you pick city X is X's score divided by the summed scores of everywhere you might go, with a temperature knob controlling how sharply you optimize. Low temperature and everyone piles into the single best option. High temperature and choices get noisy.

Two things happen here that shape everything downstream. First, a destination's score depends not just on how good it is but on how hard it is to reach, which is where friction enters: distance friction, the connectivity multiplier, and border friction, an extra penalty when the destination sits in a different group from where you live. Crank border friction up and people stop crossing groups even when the other side is plainly better. Crank it down and the borders effectively dissolve.

Why migration volume can't be a result

The number of movers each step is migration_propensity × population. Nothing else enters it. And because migration conserves population, with total world population shifting by 0.0007% across a 200 step run, the total volume of movement in any scenario is just the propensity slider multiplied by a constant.

I checked this against all eight runs, and predicted volume matched actual volume to a ratio of 1.0000 in every one. So the tempting headline, that migration varies 7.5× across the panel, is really 0.06 ÷ 0.008. It's the ratio of two slider positions wearing the costume of a discovery. Finding 01 is written with that in mind.

Step 3: cities update based on who arrived and who left

After the migration round every city has a new population, and a chain of feedback effects fires. Cities that grew get a small productivity bump, which is agglomeration again, this time on the supply side. Housing costs respond to the new density, rising where cities grew and falling where they shrank, at a rate set by the housing elasticity parameter. Wages rise with productivity and fall with housing cost.

Housing elasticity does more work in this study than any other single parameter, so it's worth being exact. The default is 0.08. Low elasticity means housing supply can't expand easily, so a growing city's rent climbs steeply, which makes it less attractive to the next wave of movers even while its productivity stays high. The math pushes growth outward instead of letting it pile up. The Housing Constrained preset sets elasticity to 0.02, and it also raises agglomeration strength from 0.25 to 0.32 and migration propensity from 0.020 to 0.022. Three changes, not one, which turns out to matter a great deal.

Step 4: three sectors produce output

Every city makes three things each step. Resources, proportional to its mineral, energy and agriculture deposits, with those deposits drawing down over time. Manufactured goods, a function of industrial capacity, manufacturing specialization and proximity to raw materials. IP and technology, knowledge stock times tech specialization times human capital, with an R&D efficiency multiplier on top.

City GDP combines wage income with monetized sector output, weighted by the three price indices the scenario sets. Those indices change what a unit of output is worth, not how much gets physically produced, which is exactly why a scenario like Resource Crunch shifts the composition of GDP without moving its magnitude much. Finding 06 comes back to this.

Step 5: every twelve steps, the planner builds something

By default the autonomous infrastructure planner wakes on every twelfth timestep, scans for the highest scoring pair of currently unconnected cities, and builds a link between them. The default is policy_interval = 12, and over a 200 step run that fires at t=12 through t=192, which is exactly the sixteen build events every scenario recorded. The optimizer is free to tune the interval and settled on 15 for its own run, so both numbers show up in this report and they mean different things.

The autonomous infrastructure planner

Every twelve timesteps the simulator pauses and decides where to put one new piece of infrastructure. There's no machine learning in it. It's a fixed, deterministic opportunity scoring rule, and its behavior across the eight scenarios says something quite specific about the model.

How it picks a site

Each time it runs, the planner assembles a candidate set: the top 160 cities by current population, the top 160 by raw resource endowment, and the top 160 by innovation potential, meaning tech specialization times knowledge stock. Those three lists get unioned. It then walks every pair in the set that isn't already directly connected and scores it on four things:

The sum gets multiplied by a distance penalty that falls off gently, and the top scoring pair wins. Link type follows two rules applied in order: pipeline if either endpoint has resource endowment above 0.95, then shipping if both endpoints clear 0.7 port access and the pair is far apart, which overrides the pipeline choice. Everything else is rail. The cost comes out of the policy budget, and the planner simply stops when the money runs out.

What it actually built

There's no chart here because there's nothing to plot. Eight identical bars of the same color carry exactly as much information as the three numbers above, which is the first hint that something about this planner is less dynamic than it looks.

Why the count is identical everywhere

It's tempting to read that uniformity as the planner being blind to the macro regime. Reading the scoring function carefully, that isn't what's going on.

Sixteen is what a twelve step interval yields over a 200 step horizon when the budget never binds. No macro parameter could have moved that number, whatever the planner was paying attention to. And the planner does see the regime, just indirectly, because its demand term reads current population and population is exactly what the macro parameters redistribute. Under Fortress World it's scoring a different population map than under Migration Surge, so it may well be choosing different pairs while building the same number of links.

Whether it actually chose different pairs is something these runs can't answer, because the saved artifacts record build counts and types but not endpoints. That's a gap in the instrumentation rather than a property of the planner, and it's fixable in an afternoon. What the data does support is narrower: the planner's build cadence is regime independent by construction, and every link it built in this study was rail.

What I ran, and how

Setup

  • Eight scenarios from a library of twenty four
  • 320 cities, 1,800 edges
  • Seed 42 throughout
  • 200 timesteps each
  • One optimizer search: four iterations of twelve trials, 120 step horizon, three random shocks per trial
  • Driven directly from a Python script rather than through the frontend, so the runs are headless and clean

What I measured

  • Total world GDP, summed across cities
  • Migration volume per step
  • Top 10 concentration, meaning the share of world population in the ten largest cities
  • Sector outputs: resources, manufactured goods, IP
  • Per city population change over the run
  • Autonomous build events, with type breakdown
  • Wall clock time per scenario

The eight scenarios, with every parameter they change

Scenario names are misleading if you take them at face value, so here is what the library file actually sets. Note how few of these move only one lever. That alone constrains what any single scenario can prove.

ScenarioEvery override from defaultGDP growthFinal top 10
Baseline (Default)none, reference state+148.1%20.0%
Open Bordersborder friction 1.0→0.05, propensity 0.020→0.030, temperature 0.70→0.75+142.2%18.2%
Fortress Worldborder friction 1.0→3.2, propensity 0.020→0.008, connectivity 1.0→0.9, temperature 0.70→0.55+143.2%19.3%
High Connectivityconnectivity 1.0→2.2, budget 2000→3600, link cost 25→20, propensity 0.020→0.024+142.9%18.7%
Network Fragmentationconnectivity 1.0→0.55, link cost 25→55, budget 2000→1500, border friction 1.0→1.4+151.0%20.6%
Housing Constrainedhousing elasticity 0.08→0.02, agglomeration 0.25→0.32, propensity 0.020→0.022+163.9%17.5%
Migration Surgepropensity 0.020→0.060, temperature 0.70→1.4, border friction 1.0→0.6+143.2%19.2%
Resource Crunchresource price 1.0→0.6, goods price 1.0→1.3, budget 2000→1800+148.3%20.0%

Two rows deserve a second look. Housing Constrained moves three parameters, so it can't be read as a housing experiment. And Resource Crunch lowers the resource price index to 0.6 while raising the goods index to 1.3, which models resources becoming cheap relative to manufactured goods. That's close to the opposite of what the name suggests.

Findings

01

Output is strikingly insensitive to the policy levers

Across eight scenarios that vary border friction by a factor of 64, connectivity by a factor of four, migration propensity by a factor of 7.5 and the infrastructure budget by a factor of 2.4, final GDP growth lands between +142.2% and +163.9%. A 1.15× spread in outcome against enormous spreads in input, and it's the most robust thing in the study.

The obvious framing of this result, that migration moves 7.5× while GDP barely moves, doesn't hold up. Migration volume is propensity × population, population is conserved, and 61.1 million against 8.1 million per step is precisely the ratio of the Migration Surge and Fortress World propensity sliders. Putting an input on one axis and calling the comparison a finding is the sort of thing you catch only by going back to the code. The two charts below are still worth looking at, but they picture the experimental design rather than the world.

What does survive is the insensitivity itself, and it has a mechanism. The softmax allocates movers over local utilities, and once people are choosing well, more people choosing well adds very little. The marginal mover goes roughly where the average mover goes. Tripling the flux through the network doesn't meaningfully change the destinations, so it doesn't change aggregate output. With a single seed, though, I can't say how much of even that 21.6 point spread is noise.

Migration bar comparison
Average movers per timestep by scenario, from Fortress World at 8.1 million to Migration Surge at 61.1 million. Read this as a chart of the propensity slider, because arithmetically that's what it is.
Cumulative migration
Cumulative movement over 200 steps: 12.2 billion person moves under Migration Surge against 1.6 billion under Fortress World. The separation is real movement inside the model. It just isn't a discovered quantity.
02

Housing Constrained only wins if you stop at step 200

Housing Constrained finishes first on GDP growth at +163.9% and first on distribution with the lowest top 10 concentration at 17.5%. Moving both of those the right way at once is genuinely unusual, and it's the most quotable number in the study.

Then you plot its rank over the run. At step 100 it's last of eight. At step 120 it's seventh. At step 150 it's still seventh, sitting 12.2 points of growth below baseline. It doesn't overtake baseline until step 181, takes the top spot the step after that, and is still accelerating when the run ends eighteen steps later. Stop the same experiment at 150 and the headline reads “housing constraints are the worst policy in the panel.” Run it to 300 and nobody knows what it says, because the curves hadn't settled.

A second problem sits underneath the first. The preset isn't a housing experiment. It drops elasticity from 0.08 to 0.02, but it also lifts agglomeration strength from 0.25 to 0.32, a 28% increase in exactly the force that drives late compounding growth, and nudges migration propensity up as well. The late breakaway is at least as consistent with the agglomeration bump as with the housing mechanism, and this run can't separate them.

So the honest statement is small. A bundle of three changes, one of which is inelastic housing, produced the highest growth and the lowest concentration at one particular horizon, after spending three quarters of the run in last place. Attributing that to housing policy needs an experiment I haven't run: elasticity varied alone, across multiple seeds, to a horizon long enough for the curves to converge. Until then it's a hypothesis with a suggestive chart attached.

GDP vs concentration scatter
Each scenario as one point, concentration on the horizontal axis and growth on the vertical. Housing Constrained sits alone in the upper left. This is a snapshot at step 200; at step 150 the same chart puts it near the bottom.
GDP growth bars
Final growth, ranked. Housing Constrained clears Open Borders by 21.6 points. Whether that exceeds run to run noise is unknown, because every result here is one seed and no variance estimate exists.
03

Open Borders flattens, but the bottom is a four-way tie

Open Borders posts the lowest growth in the panel at +142.2%, alongside the lowest concentration of any scenario other than Housing Constrained at 18.2%. Removing friction lets people spill out of the largest cities into the next tier, which evens outcomes out, and the model doesn't pay extra output for it.

The ranking itself deserves very little weight. Open Borders at 142.2%, High Connectivity at 142.9%, Migration Surge at 143.2% and Fortress World at 143.2% are separated by a single percentage point across four scenarios with almost nothing in common. Calling one of them last is a statement about the fourth significant figure of a single seeded run. What is defensible: all four sit clearly below Housing Constrained and Network Fragmentation, and Open Borders is nowhere near the top.

That's still the opposite of the usual expectation. The standard argument says frictionless labor allocation should lift aggregate productivity by moving workers to where they're most valuable. In this model that effect exists but is small, and it's offset by the loss of agglomeration concentration when the biggest cities stop pulling hardest. Open borders reads as a distributional lever rather than a growth lever, with one caveat: the optimizer later picks near zero border friction as part of its highest scoring blend, so the lever isn't useless. It just isn't sufficient alone.

GDP curves
GDP trajectories. The curves cluster tightly for the first hundred steps and only separate once agglomeration feedback has had time to compound. The Housing Constrained curve (orange) is the lowest of the eight through the middle of the run and crosses above baseline only at step 181. That late crossover, not an early breakaway, is what produces its headline number.
GDP small multiples per scenario
The same data as one panel per scenario, with final growth annotated. Easier to compare trajectory shapes, and easier to see how similar most of them are.
04

The planner built sixteen rail links every time, for a dull reason

All eight scenarios produced exactly sixteen build events, and all 128 of those builds were rail. The uniformity looks like evidence that the planner ignores the macro regime, and it isn't, for the reasons set out in the planner section: sixteen is what a twelve step interval yields over 200 steps when the budget never binds, and the planner's demand term reads live population, which the scenarios redistribute. It may have built sixteen quite different links each time. The artifacts don't record endpoints, so there's no way to check from these runs.

The genuinely interesting part is the zeroes. Not one pipeline, not one shipping route, in any scenario. Those branches require resource endowment above 0.95, or two endpoints both above 0.7 port access at long range, and nothing in the generated world clears either bar. Two of the four infrastructure types the project advertises are, in practice, unreachable code. That's a real finding about the model, and a more useful one than the planner's build count.

05

Urban concentration lands in a narrow band regardless of policy

Final top 10 share runs from 17.5% under Housing Constrained to 20.6% under Network Fragmentation. Three percentage points of spread across eight scenarios designed to be as different from each other as the parameter space allows. Every run starts at 15.9% and climbs, and they all climb to roughly the same place.

There seems to be a settling point in the model for how concentrated population wants to be. Push migration up, drop borders, change the infrastructure budget, and the system finds its way back to somewhere around a fifth of people living in ten cities. The two scenarios sitting lowest, Housing Constrained at 17.5% and Open Borders at 18.2%, are the two that most directly weaken the pull of the largest cities, which is at least a coherent story.

Concentration over time
Top 10 share over the run, ending between 18.2% and 20.6%. Concentration looks like a deeply baked property of the agglomeration loop, hard to shift by much in either direction.
06

Prices move valuation, not production

The three sector outputs climb together in roughly fixed proportion across every run. Even in the scenario built specifically to shock prices, relative composition barely shifts.

The reason is structural. Price indices in this version of the model scale what a unit of output is worth, and they don't feed back into what gets produced. A city's resource extraction depends on its deposits and remaining reserves, not on the price it can get. Drop the resource price index to 0.6, which is what Resource Crunch actually does while lifting goods to 1.3, and the same physical tonnage simply books at a lower value. Useful for modeling inflation and terms of trade. Not useful for modeling supply response.

Taken with finding 04, this points at a real asymmetry: the model is strong on demography and weak on production allocation. Cities respond in detail to who lives in them and almost not at all to what things are worth. The obvious next version adds a specialization feedback so cities drift toward whichever sector carries the highest price index, which would also give the price scenarios something to actually do.

Sector outputs
Stacked sector outputs for the baseline run. The three layers grow in tandem rather than one taking share from another, which is what you'd expect when nothing in the production function reads the price.
07

The city-level tail deepens with mobility

Aggregates hide a lot. Even where total GDP and total movement barely differ, individual cities swing hard. Every scenario in the study has at least one city gaining more than 40%, and the worst single collapse ranges from −49.8% to −97.2% depending on the run.

That range is the finding, and it tracks mobility almost monotonically. Fortress World, with propensity at 0.008, has a worst case of −49.8%. Baseline, Network Fragmentation and Resource Crunch, all at 0.020, bottom out around −88%. High Connectivity at 0.024 reaches −93.1%, Migration Surge at 0.060 reaches −95.2%, and Open Borders at 0.030 reaches −96.9%. The more freely people can move, the deeper the hole under the cities they move away from.

It's worth resisting the stronger version of this claim. Not every scenario contains a near-total collapse: four of the eight do, and Fortress World's deepest loss is barely half the 90% mark. Stated as a dose-response relationship instead of a flat assertion, it's less dramatic and considerably more useful, because it's something you could go and test.

For any policy question that uses a model like this, the tail matters more than the mean. Two policies producing identical world GDP can differ enormously in who absorbs the cost, and the losers under one regime aren't the losers under another. One oddity worth flagging: the single largest gain anywhere in the study is Apia, Samoa, at +215.7% under Housing Constrained. A small Pacific capital, not a secondary city near a superstar, which doesn't fit the spillover story at all and probably deserves its own look.

Winners and losers by scenario
Top gainer and worst loser per scenario. The gainers vary; the depth of the losing tail scales with how much movement the scenario permits.

The optimizer's verdict, and its asterisk

The optimizer is the most interesting thing in the project you can't see in the UI: a cross entropy search asking which combination of fourteen parameters holds up best when shocks hit. It found something remarkable, and the project's own guardrails flagged it as unsafe. Both halves belong in the writeup.

What cross entropy search does

Say you're looking for the best picnic spot in a dark field. You can't see it, but you can drop a friend anywhere and they'll tell you how nice it is where they landed. So you scatter a dozen friends, collect their reports, and note which spots scored highest. The top thirty percent are your elites. Then you scatter a fresh dozen around the elites' average position, with a spread matching how spread out the elites were, and repeat. The cloud drifts toward the good part of the field.

That's the algorithm, with parameter sets instead of friends and a full shock augmented simulation instead of a verdict on the grass. It needs no derivatives, each sample is independent, and it copes with noisy, expensive, discontinuous objectives, which describes this simulation exactly.

What the score rewards

Higher final GDP and higher growth score well. Volatility, concentration and overpopulation all score badly. There's an additional penalty for spending beyond 92% of the infrastructure budget, to stop the search buying a few points of GDP by emptying the treasury. The GDP term enters logarithmically, so doubling output doesn't double the score. That's worth knowing when reading what follows.

The learning curve

Optimizer learning curve
Mean score climbs monotonically across the four iterations: 3.62, then 4.22, then 4.48, then 4.68. The best-of-iteration line is noisier and actually peaks in iteration two at 5.50, ending at 5.37. A single best sample can get lucky or unlucky, which is why the mean is the line to watch for whether the search is learning. It is.

The full parameter set

The highest scoring candidate came from iteration two rather than the final round, so this is the best set found rather than the point the search converged on. All fourteen parameters are listed, including the several that complicate the story.

ParameterPickDefaultDirection
Border friction0.0151.00near the floor of the search range, so effectively open borders
Connectivity multiplier0.941.00slightly below default
Agglomeration strength0.710.252.8× default. Let big cities pull hard.
Housing elasticity0.010.08the floor of the allowed range. Supply barely responds.
Migration propensity0.0260.020slightly raised
Temperature1.050.70choices noisier, less sharply optimal
Policy interval1512builds a little less often
Policy strength0.451.00each new link delivers under half the usual boost
Resource price index0.921.00marginally cheaper resources
Goods price index1.061.00marginally dearer goods
IP price index1.311.00premium tech prices
R&D efficiency0.701.00cut by 30%, against the grain of the IP price rise
Infrastructure budget2,9552,00048% more headroom
Link base cost11.52554% cheaper per link

The outcome, under shocks

Comparing that growth figure to the hand picked scenarios takes care, because the optimizer runs 120 steps under shocks and the scenarios ran 200 clean. Matched at 120 steps, the eight scenarios grew between +35% and +58%. The optimizer's blend grew +341%, roughly seven times the best hand picked result at the same horizon. A gap that large is a reason for suspicion rather than celebration.

The asterisk: the winning blend fails the project's own threshold registry

The result carries automated_gate_evaluation.pass = false. It clears four of five checks and fails on overpopulation share: 0.394 against a ceiling of 0.35. The rank one recommendation from the final iteration fails the same check and comes with a reliability card reading stoplight: "RED", confidence 0.57.

That reframes the whole result. The blend pins housing elasticity to the absolute floor of the search range and pushes agglomeration close to three times default, a combination that packs people into cities faster than the model says those cities can carry them. The +341% is what you get by overshooting the carrying capacity constraint and booking the output anyway. The search wasn't cheating. It was maximizing the score it was handed, and the score's overpopulation penalty was too weak to stop it. This is a textbook degenerate optimum, and the right response is to fix the objective rather than to quote the number.

It would be easy to publish the +341% and leave the gate result in a JSON file nobody opens. That would get the emphasis exactly backwards. The project built a guardrail specifically to catch this failure mode, the guardrail fired, and the interesting result is that it fired.

Does the blend tell a coherent story?

Partly. Open borders so people can move, strong agglomeration so productive places stay productive, inelastic housing so secondary cities catch the spillover, cheap links so the network can adapt. That reads well. But two of the fourteen picks cut against it. Connectivity came out slightly below default, and policy strength was more than halved, so each link the planner builds does less than usual. Premium IP prices sit next to a 30% cut in R&D efficiency, which is close to a contradiction if the story is that knowledge work is the engine.

A tidy narrative is easy to build by picking the parameters that fit it. With all fourteen on the table, the likelier reading is that a handful of picks (housing at the floor, agglomeration high, links cheap) are doing the work, and the rest is noise from a search that only ran 48 trials. The unspent 96.9% of the infrastructure budget rather supports that. The optimizer didn't discover the value of optionality so much as decline to use a lever that barely moved its score.

Deep dive: who wins and who collapses under Open Borders?

In the seed 42 run the biggest winner is Yunfu in Guangdong at +172.8%. The steepest declines are Bahawalpur at −96.9%, Athlone at −96.8%, and Bhubaneswar and Jaffna both at −96.6%. Near total depopulation, in other words, for a set of mid sized cities.

Open Borders winners and losers
Open Borders over 200 steps. Left, the ten fastest growing cities: Yunfu +173%, Lucknow +62%, Puyang +58%, Shantou +58%, Shijiazhuang +54%, Lima +53%, Abidjan +53%, Lagos +48%, Bobo-Dioulasso +45%, Foshan +45%. Right, the ten steepest declines, all between −96% and −97%.

The pattern

The winners fall into two groups. Five are secondary Chinese cities (Yunfu, Puyang, Shantou, Shijiazhuang, Foshan) close enough to Guangzhou, Beijing and Shanghai to catch agglomeration spillover without being absorbed by them. The rest are large emerging market centers: Lucknow, Lima, Abidjan, Lagos, each already the natural regional destination once friction drops.

The losers are harder to characterize than they first appear. It's tempting to describe Bahawalpur as a satellite of Lahore, Bhubaneswar of Kolkata, Jaffna of Colombo, but those pairs sit 350 km, 365 km and 305 km apart, and Bhubaneswar is a state capital and IT center in its own right. Athlone, between Dublin and Galway at roughly 125 km and 90 km, is the only one of the four where the description holds up cleanly.

There's a second problem with the story. The saved artifacts record each city's net population change, not where its residents went. Saying Bahawalpur lost its population to Lahore is an inference from the utility function rather than something these runs measured. What the data supports is narrower: in a low friction world a set of mid sized cities empties out almost completely, and the softmax makes nearby large cities the likely destination. Confirming that needs the flow matrix, which the simulator computes and the export throws away.

With those qualifications, the direction is the classic one economic geography has been arguing about for a century. As friction falls, secondary cities consolidate into fewer, larger ones, and the cities that suffer aren't the smallest but the ones nearest a winner.

What this actually means outside the simulator

Reading these results as a research artifact is one thing. Reading them as a way to think about real policy is another, and it's where a model like this is easiest to over-trust. Here's what I think transfers, and what doesn't.

Housing might be the lever the discourse underrates, but this model can't show that

The story I wanted to tell was that housing elasticity is the most important upstream lever in the urban system, ahead of borders or infrastructure budgets, and that it's unusually tractable because zoning, height limits, parking minimums and review processes set it rather than geography.

The first half of that is well supported outside the model. Japan moved to nationally set zoning under the 2002 Urban Renaissance Act, having weakened local control from the 1980s, and between 2003 and 2015 Tokyo added around 1.5 million people while permitting over 100,000 housing units a year, roughly eight times San Francisco's rate. Central Tokyo's Minato ward grew its population by 66% over two decades; a comparable increase in San Francisco came with a 231% price rise. Elasticity is a policy choice, and the choice has enormous consequences.

What I can't claim is that my simulation shows constrained housing raising aggregate growth. That result depended on stopping at step 200, and the scenario producing it moved three parameters at once. It also runs against the best known work in the area. Hsieh and Moretti estimate that housing constraints in high productivity US metros lowered aggregate US growth by about 36% between 1964 and 2009, leaving GDP roughly 9% below where it would otherwise have been, and that relaxing land use regulation in three cities alone would raise GDP by 3.7%. On the central question, most economic geographers have been arguing the opposite of what my run suggests.

The disagreement has a clean mechanical explanation, which is the genuinely interesting part. In Hsieh and Moretti's framework San Francisco is exogenously more productive than Boise, so blocking access to it destroys output that can't be recreated elsewhere. In FlowWorld productivity is endogenous to size, because it comes from the agglomeration bonus, so it regenerates wherever people accumulate. Push growth out of the superstar and the secondary city simply becomes the superstar. Whether real productivity is portable in that way is precisely the open question, and my model assumes an answer instead of testing one. The canonical result is itself contested, too: a 2026 comment in the same journal reports replication errors that reverse the sign of the counterfactual. The literature is unsettled, which argues for humility rather than for treating one simulation run as a vote.

Real world spillover patterns are messier than the clean version suggests. San Francisco's housing costs did help seed Austin, Boise, Denver and Miami from 2020 to 2022. But Bay Area outbound migration peaked in 2021 and 2022 and has slowed since, Austin now shows up as an origin about as often as a destination, and Miami recorded the highest domestic out-migration share of any US metro, about 1.8%, between July 2024 and July 2025. The mechanism is real. The straight line extrapolation isn't.

Open borders is a distributive lever, not a growth lever

This one holds up. Almost every popular argument about open borders is conducted as a dispute about total output, with more people and better matching on one side and pressure on services and wage suppression on the other, and the model suggests both sides are measuring the wrong thing.

Open Borders produced the lowest growth in the panel, though as noted it's in a four-way tie at the bottom and the ranking shouldn't be leaned on. What it clearly produced was a flatter distribution: less concentration, more activity in the second tier, and violent swings at the level of individual cities. That's a distribution story rather than a growth story.

Which suggests the debate is framed wrongly. The question isn't whether it makes us richer, it's where the prosperity lands and which places hollow out. In the model that's determined mostly by geography and by whether receiving cities can absorb growth. Border policy opens or closes the valve. It doesn't choose where the water goes.

Agglomeration is the strongest force in the model

Trace any dynamic here back far enough and you arrive at the same loop: bigger cities are more attractive, attract more people, become more productive, become more attractive. That isn't controversial. Edward Glaeser and his co-authors have been documenting it since Growth in Cities in 1992. But a simulation makes the force visible in a way prose can't, and you can watch the large cities pull harder as a run progresses.

The policy implication is that anything fighting agglomeration head on will underperform in this model. Spreading investment evenly across regions sounds fair and is, mechanically, a recipe for slower aggregate growth. The optimizer reached for nearly three times default agglomeration strength without being told to. I'd hold that conclusion loosely, though, given that the same optimizer run failed the overpopulation gate. Cranking agglomeration is exactly how it broke the carrying capacity constraint.

On optionality, and why I think it's overstated

The winning configuration spent 3.1% of its infrastructure budget, and the obvious reading is that optionality has value: hold capacity, deploy when something breaks, the way a treasury holds reserves. It's an appealing parallel to corporate finance and emergency planning.

Looking at the full parameter set, I'm not convinced. The same configuration more than halved policy strength, so each link it built delivered under half the usual benefit. A search that has made building nearly worthless and then declines to build isn't discovering option value. It's responding to a weak incentive. The optionality reading needs the unspent budget to be a deliberate hedge, and nothing in the score function rewards holding a reserve. It only penalizes spending past 92%.

The underlying principle may still be sound. Maintained capacity probably does beat headline projects, and a country able to build quickly is more resilient than one that has already spent everything. This run just isn't evidence for it, and it would be a stretch to say the optimizer independently rediscovered a principle of financial management.

Aggregates hide the distribution

Finding 07 is the one most likely to be overstated, because the dramatic version (every scenario containing a near-total collapse) is more quotable than the accurate version (half of them do). The accurate version is more useful anyway: the depth of the losing tail scales with how much movement a policy permits, from −50% under the most restrictive regime to −97% under the most permissive.

The real world version of that is the question of who pays. A policy raising aggregate GDP while emptying six mid sized cities isn't the same policy as one raising GDP while preserving them, however identical the GDP line looks. What a model like this contributes is that the cost stops being abstract. Bahawalpur, Athlone, Bhubaneswar, Jaffna: they're named, they're on the map, and whether the tradeoff is worth making is a judgment someone has to make out loud.

What the model is missing

The simulator is strong on demographic and migration dynamics, on agglomeration feedback, and on the budget logic for infrastructure. Here's what it doesn't have, and what each absence does to the findings.

Each is fixable in principle and none is trivial. The right way to read the findings is that they describe a world where every other uncertainty has been resolved, with climate stable, work co-located and institutions functioning, and only the narrow interaction of migration, infrastructure and prices left in question. That's a much smaller question than what the world looks like in 2050. It's also a sharper one, which is the only reason the answers are worth anything.

If these dynamics are real, what should we expect?

This is the speculative section, conditional on the model being roughly right about mechanisms, which the findings above give you several reasons to doubt. Each prediction names the evidence that would kill it. Two are already under pressure, and I've said which.

Prediction 1: secondary cities near constrained superstars keep gaining ground, currently under strain

Austin, Boise, Salt Lake City, Nashville, Raleigh, Miami, Calgary, Lyon, Lisbon. Not a random list: each sits in the gravitational shadow of a constrained superstar. The mechanism says they should keep accumulating economic weight for as long as the superstars refuse to build.

The honest update is that the last two years have run against this. Bay Area outbound migration peaked in 2021 and 2022 and has since slowed, Austin appears as an origin about as often as a destination, and Miami posted the largest domestic out-migration share of any US metro in the year to July 2025. None of that falsifies the mechanism, since a pandemic-era surge unwinding isn't the same as a trend reversing, but the straightforward version of this prediction isn't currently being borne out. I'd want another three years of job growth, price and venture data before claiming otherwise.

Prediction 2: cities beside a megahub hollow out faster than national figures suggest

In the seed 42 run Bahawalpur, Bhubaneswar and Jaffna each lose about 97% of their population under Open Borders. Those magnitudes are absurd and wouldn't occur, and as noted the runs don't record where anybody went, so pairing them with Lahore, Kolkata and Colombo is inference rather than measurement.

The directional claim is still testable. Second tier cities in developing countries sitting near a fast growing primary city should see accelerating population loss even while the national population grows, and national statistics will mask it because the primary city's gain nets it out. Watch second tier cities in India, Pakistan, Nigeria, Indonesia and Bangladesh. If the ones near a megahub aren't losing ground relative to the ones distant from one, the mechanism is wrong.

Prediction 3: countries that fix housing pull ahead on growth

This prediction points the conventional way, because the model result that would have inverted it turned out to be horizon dependent and confounded. Hsieh and Moretti's estimate says elastic housing in productive cities raises aggregate output, and nothing in this study is strong enough to argue against that.

Japan is the cleanest test available, being one of very few large countries with consistently elastic supply in its biggest city. If elastic housing lets a megahub keep absorbing growth, Tokyo's share of Japanese GDP should hold or rise over the next decade. If it stalls while Japanese growth turns up elsewhere, something pulls activity out of megahubs even when housing is cheap. That something would be the mechanism my simulator accidentally stumbled into.

Prediction 4: optionality looks better as the world gets shockier

The weakest of the six, for the reasons in the optimizer section. The unspent budget probably reflects a weak build incentive rather than a discovered preference for holding capacity. The underlying bet is still worth stating: where shocks are frequent, the ability to deploy capital quickly may be worth more than capital already deployed.

If that's right, countries holding dry powder (Norway's sovereign wealth fund, Singapore's reserve buffers, Switzerland's debt brake) should outperform countries front loading their infrastructure spend on resilience measures. The US comparison to make is the Infrastructure Investment and Jobs Act, the $1.2 trillion 2021 package that funds transport and infrastructure, rather than the Inflation Reduction Act, which is energy, climate and health legislation and gets misused in this argument constantly.

Prediction 5: concentration stays remarkably stable

Top 10 concentration lands between 17.5% and 20.6% across every scenario in the study, starting from 15.9% in all of them. If that band is a property of the underlying dynamics rather than of my parameter choices, the share of global population in the largest metros should be a slow, boring number that barely moves year to year. The exception would be any city running a genuinely aggressive supply program, which the model says should let it absorb a larger share. Very few cities will.

Prediction 6: open borders would surprise both sides

Advocates would be surprised that aggregate growth moves so little. Opponents would be surprised by which places actually receive people, since the model sends them to secondary cities in the right geographic position rather than to the global superstars that dominate the argument. If that's right, the political coalitions on both sides are organized around a phenomenon that wouldn't look much like either side's picture of it.

How to falsify all of this

Each prediction has a clear failure condition. Secondary city growth stalls. Megahub-adjacent cities hold their populations. Tokyo's share of Japanese GDP falls while housing stays cheap. Buffer countries lose to big spenders on resilience. Concentration moves sharply. Open borders, where tried, produces one of the two conventional outcomes. Any of those would mean the model has missed something real and the next version has to account for it, which is roughly what happened to two of the seven findings above once I went back through the code.

Caveats

Everything above carries the following asterisks. The first four are structural limits on what this study could ever have shown. The rest are specific weaknesses in these runs.