Steering Population Growth:
Introduction
Every growing society faces the same dilemma. Grow too fast, and the food supply collapses under the weight of new mouths. Grow too slowly, and the workforce is too small to produce enough food to sustain a prosperous economy.
In this article we turn that dilemma into a concrete optimization problem. At every turn, a planner chooses two quantities:
- the number of immigrants to accept, and
- the birth-support budget to spend.
The goal is to maximize the cumulative population over 30 turns. We build a small simulation model, solve it numerically in Python, and visualize the result with a 3D landscape and a 3D trajectory plot.
Problem Setting
Let $t = 0, 1, \dots, T-1$ with $T = 30$. The state consists of the population $P_t$ and the food stock $S_t$. The controls are normalized intensities $x_t, y_t \in [0, 1]$:
$$
I_t = I_{\max}, x_t, \qquad B_t = B_{\max}, y_t
$$
where $I_t$ is the requested number of immigrants and $B_t$ is the requested birth-support spending.
Production and food balance
Only a fraction $\lambda$ of the population works. Production has diminishing returns in labor:
$$
L_t = \lambda P_t, \qquad Y_t = A, L_t^{\beta}, \qquad 0 < \beta < 1
$$
Each person needs $c$ units of food per turn. The satisfaction ratio and the next food stock (with spoilage rate $\delta$ and storage capacity $S_{\max}$) are
$$
s_t = \min\left(1,\ \frac{S_t + Y_t}{c,P_t}\right), \qquad
S_{t+1} = \min\left(S_{\max},\ (1-\delta)\max\left(0,\ S_t + Y_t - c,P_t\right)\right)
$$
Budget constraint
The treasury collects a tax $\tau$ on production. Each immigrant costs $c_I$ (housing and integration). If the requested spending exceeds the budget, both requests are scaled down proportionally:
$$
\kappa_t = \min\left(1,\ \frac{\tau Y_t}{c_I I_t + B_t}\right), \qquad
\tilde I_t = \kappa_t I_t, \qquad \tilde B_t = \kappa_t B_t
$$
Demographics
Birth support has diminishing returns per capita, while food shortage raises mortality:
$$
b_t = b_0 + b_1\left(1 - \exp\left(-\frac{\tilde B_t}{\sigma P_t}\right)\right), \qquad
d_t = \min\left(1,\ d_0 + d_1 (1 - s_t)\right)
$$
$$
P_{t+1} = \max\left(1,\ P_t,(1 + b_t - d_t) + \tilde I_t\right)
$$
Objective
$$
\max_{x_t,, y_t \in [0,1]} \ J = \sum_{t=1}^{T} P_t
$$
Parameters
| Symbol | Meaning | Value |
|---|---|---|
| $T$ | Number of turns | 30 |
| $P_0,\ S_0$ | Initial population, initial food stock | 1000, 1500 |
| $\lambda$ | Working share of the population | 0.6 |
| $A,\ \beta$ | Productivity, labor elasticity | 18, 0.7 |
| $c$ | Food need per person | 1.0 |
| $\delta,\ S_{\max}$ | Spoilage rate, storage capacity | 0.05, 3000 |
| $\tau$ | Tax rate on production | 0.10 |
| $c_I,\ I_{\max},\ B_{\max}$ | Cost per immigrant, maximum requests | 2.0, 150, 100 |
| $b_0,\ b_1,\ \sigma$ | Base birth rate, birth-support gain, per-capita scale | 0.015, 0.030, 0.05 |
| $d_0,\ d_1$ | Base death rate, shortage mortality coefficient | 0.010, 0.60 |
With these values, the steady-state carrying capacity (where $Y = cP$) is roughly 4,600 people. The initial population is far below it, so there is room to grow, but a fast surge overshoots the capacity and triggers shortage deaths.
Solution Strategy
- Landscape analysis. Evaluate every constant policy $(x, y)$ on a $41 \times 41$ grid to see the shape of the objective in 3D.
- Full optimization. Optimize all 60 decision variables (30 turns × 2 controls) with multi-start L-BFGS-B, seeded with the best constant policy.
- Local polish. Refine the best solution with a sparse random local search that only accepts improvements.
- Comparison. Compare against four baseline policies.
Because the objective is evaluated thousands of times, the simulator uses only scalar Python arithmetic with the math module, which avoids NumPy’s per-call overhead on scalars and keeps one simulation in the tens-of-microseconds range.
Full Source Code
1 | import math |
Detailed Code Walkthrough
Parameters and helper
All constants sit at the top of the file and map one-to-one onto the table above. clip01 guarantees that any control passed to the simulator is a valid intensity in $[0, 1]$, so the optimizer can never push the model into an undefined region.
The simulator
simulate is the heart of the program. In each turn it performs these steps in order:
- Production. It computes $Y_t = A(\lambda P_t)^{\beta}$ and the available food $S_t + Y_t$.
- Satisfaction. If the available food covers the need $cP_t$, then $s_t = 1$ and the surplus is carried over. Otherwise the stock is emptied and $s_t$ equals the covered fraction.
- Budget scaling. The requested spending $c_I I_t + B_t$ is compared with the tax revenue $\tau Y_t$. If it is too large, both requests are shrunk by the same factor $\kappa_t$. This is why requesting more than the treasury can afford is simply wasteful, never infeasible.
- Demographics. Births use a saturating function of per-capita support, and deaths rise linearly with the shortage $1 - s_t$.
- State update. The new population is floored at 1 to keep the divisions well defined, and the new stock is capped by spoilage and storage capacity.
When trace=True, the function also records every intermediate quantity for plotting. The optimization calls use trace=False, so no list operations slow the inner loop.
Landscape analysis
The double loop evaluates $41 \times 41 = 1681$ constant policies. Besides powering the 3D surface, it supplies the best constant policy, which is both a strong baseline and a safe starting point for the optimizer.
Full optimization
The decision vector z concatenates the 30 immigration intensities and the 30 birth-support intensities. L-BFGS-B handles the box constraints natively. Gradients come from finite differences with a step of $10^{-4}$, which is large enough to cope with the kinks created by min, max, and the budget scaling. Nine starting points (the best constant policy, three flat policies, and five random ones) reduce the risk of getting stuck in a poor local optimum. Since best_val is initialized with the best constant policy’s value, the final answer can never be worse than that baseline.
Local polish
The random local search perturbs only five randomly chosen variables at a time and accepts a candidate only if it improves $J$. The step size shrinks from 0.05 to 0.01 halfway through. This cheaply repairs the small imperfections that finite-difference gradients leave behind on a non-smooth objective.
Reporting and plotting
The console report prints the timing, the best constant policy, a comparison table, and a turn-by-turn log of the optimized policy. The figure is assembled from six panels in a single plt.show() call, so all graphs appear as one image.
Execution Results
Result Image

Console Output
======================================================================== Population growth control: results ======================================================================== Landscape evaluation time : 0.08 s (41 x 41 constant policies) Optimization time : 11.94 s Best constant policy : x = 1.000, y = 0.625 ------------------------------------------------------------------------ Policy Cumulative pop. Final pop. Min satisf. No control 32,441 1,161 1.000 Max immigration 91,495 5,423 1.000 Max birth support 47,467 2,223 1.000 Best constant 94,805 5,891 1.000 Optimized 96,760 6,119 1.000 ------------------------------------------------------------------------ Gain of optimized policy over best constant policy: +2.06 % Turns where the budget constraint was binding : 17 / 30 ------------------------------------------------------------------------ Optimized policy, turn by turn Turn Population Food stock Immigrants Birth supp. Satisf. 1 1,085 1,981 74.6 9.2 1.000 2 1,174 2,445 78.9 9.9 1.000 3 1,270 2,892 83.3 10.8 1.000 4 1,370 3,000 87.9 11.5 1.000 5 1,476 3,000 92.6 12.5 1.000 6 1,588 3,000 97.4 13.5 1.000 7 1,707 3,000 102.3 14.4 1.000 8 1,831 3,000 107.4 15.6 1.000 9 1,962 3,000 112.7 16.7 1.000 10 2,100 3,000 118.1 17.8 1.000 11 2,244 3,000 123.6 19.2 1.000 12 2,396 3,000 129.2 20.6 1.000 13 2,555 3,000 135.2 21.7 1.000 14 2,722 3,000 141.3 23.0 1.000 15 2,897 3,000 147.3 24.9 1.000 16 3,079 3,000 150.0 33.7 1.000 17 3,269 3,000 150.0 48.2 1.000 18 3,467 3,000 150.0 63.2 1.000 19 3,672 3,000 150.0 78.4 1.000 20 3,885 3,000 150.0 94.0 1.000 21 4,101 3,000 150.0 100.0 1.000 22 4,319 2,997 150.0 100.0 1.000 23 4,539 2,937 150.0 100.0 1.000 24 4,760 2,819 150.0 100.0 1.000 25 4,983 2,644 150.0 100.0 1.000 26 5,207 2,412 150.0 100.0 1.000 27 5,433 2,123 150.0 100.0 1.000 28 5,660 1,779 150.0 100.0 1.000 29 5,889 1,379 150.0 100.0 1.000 30 6,119 924 150.0 100.0 1.000
Reading the Graphs
Panel 1: 3D landscape of constant policies
The surface height is the cumulative population achieved when the same immigration level $x$ and birth-support level $y$ are used in all 30 turns. Two forces shape it. Raising either control adds people, but pushing them too high drives the population past the carrying capacity, where shortage mortality eats into the gains. The budget scaling also flattens the surface where requests exceed what the treasury can pay for. The red marker shows the best constant policy. A broad ridge rather than a sharp peak means that the exact mix matters less than staying out of the overshoot region.
Panel 2: Population trajectories
This panel shows how each policy moves the population over time. “No control” shows pure natural dynamics. “Max immigration” and “Max birth support” show what happens when a single lever is pushed to its limit. A curve that rises steeply and then bends downward signals overshoot: the stock was drained and shortage deaths took over. The optimized curve is the one that keeps the area under the curve largest, since $J$ is exactly that area in discrete form.
Panel 3: Optimized controls
The bars show the immigrants actually accepted after budget scaling, and the line shows the actual birth-support spending. Because tax revenue grows with production, the affordable budget changes over time, and the shape of the optimal controls follows the economy’s capacity to pay. Where the two controls move in opposite directions, the planner is trading one lever for the other. The two levers differ in cost-effectiveness: an immigrant costs a fixed amount and arrives immediately, while birth support has diminishing returns per capita.
Panel 4: Food stock and satisfaction
This panel exposes the resource side of the trade-off. The food stock acts as a buffer that lets the population temporarily exceed what current production can feed. A satisfaction ratio of 1 means no one is short of food. Any dip below 1 means shortage mortality is active. Seeing exactly when the planner lets the buffer run down, and how close the satisfaction ratio gets to the danger zone, explains why the optimal policy is a controlled use of the stock rather than a strict avoidance of shortage.
Panel 5: 3D state-space trajectories
Here each policy is drawn as a curve in $(\text{turn},\ P_t,\ S_t)$ space. A healthy trajectory climbs in population while the food stock stays positive. A trajectory that heads toward the floor of the food-stock axis while the population is still high is living off its reserves and is exposed to a sudden shortage. Comparing the optimized curve with the extreme policies shows at a glance how the planner balances population growth against reserve depletion.
Panel 6: Objective value by policy
The bars compare the final objective $J$ for all five policies. The gap between the best constant policy and the optimized policy measures the value of adapting the controls over time rather than holding them fixed. The gaps to the single-lever policies show how costly it is to ignore one of the two instruments.
Discussion
Three structural features of the model explain the behavior of the optimal policy.
Diminishing returns on labor. Because $\beta < 1$, doubling the population does not double the food supply. Per-capita food falls as the population grows, which creates a natural carrying capacity and makes unlimited growth self-defeating.
A buffer with a cost. The food stock lets the society run a temporary deficit, but spoilage makes holding a large stock expensive. The optimizer therefore treats the stock as a resource to be spent wisely rather than a quantity to be maximized.
A finite horizon. The objective counts only the first 30 turns. Consequences that would arrive after the horizon do not affect $J$, so the optimal policy can legitimately behave differently near the final turns than in the middle of the horizon. This end-of-horizon effect is a general property of finite-horizon optimal control and is worth keeping in mind when interpreting the optimized controls.
The model is intentionally simple, yet it already contains the essential tension of the problem: growth is valuable in itself, but unchecked growth erodes the resource base that supports it. Extensions are straightforward. Introducing age structure with a maturation delay would make birth support slower to pay off, a stochastic harvest would turn the stock into a true insurance, and a closed-loop controller that reacts to the observed state could outperform the open-loop schedule found here.
Conclusion
We formulated population growth control as a 60-variable constrained optimization problem and solved it with a landscape scan, multi-start L-BFGS-B, and a sparse local search, all built on a fast scalar simulator. The 3D landscape revealed the trade-off between the two levers, and the 3D state-space trajectories showed how the optimized policy uses the food buffer. The key lesson is that the best way to maximize cumulative population is neither to grow as fast as possible nor to grow cautiously, but to steer the growth rate so that population, workforce, and food supply rise together.










