18. Von Neumann Growth Model (and a Generalization)#
Contents
This lecture uses the class Neumann
to calculate key objects of a
linear growth model of John von Neumann [von Neumann, 1937] that was generalized by
Kemeny, Morgenstern and Thompson [Kemeny et al., 1956].
Objects of interest are the maximal expansion rate (
In addition to watching how the towering mind of John von Neumann formulated an equilibrium model of price and quantity vectors in balanced growth, this lecture shows how fruitfully to employ the following important tools:
a zero-sum two-player game
linear programming
the Perron-Frobenius theorem
We’ll begin with some imports:
import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import fsolve, linprog
from textwrap import dedent
np.set_printoptions(precision=2)
The code below provides the Neumann
class
class Neumann(object):
"""
This class describes the Generalized von Neumann growth model as it was
discussed in Kemeny et al. (1956, ECTA) and Gale (1960, Chapter 9.5):
Let:
n ... number of goods
m ... number of activities
A ... input matrix is m-by-n
a_{i,j} - amount of good j consumed by activity i
B ... output matrix is m-by-n
b_{i,j} - amount of good j produced by activity i
x ... intensity vector (m-vector) with non-negative entries
x'B - the vector of goods produced
x'A - the vector of goods consumed
p ... price vector (n-vector) with non-negative entries
Bp - the revenue vector for every activity
Ap - the cost of each activity
Both A and B have non-negative entries. Moreover, we assume that
(1) Assumption I (every good which is consumed is also produced):
for all j, b_{.,j} > 0, i.e. at least one entry is strictly positive
(2) Assumption II (no free lunch):
for all i, a_{i,.} > 0, i.e. at least one entry is strictly positive
Parameters
----------
A : array_like or scalar(float)
Part of the state transition equation. It should be `n x n`
B : array_like or scalar(float)
Part of the state transition equation. It should be `n x k`
"""
def __init__(self, A, B):
self.A, self.B = list(map(self.convert, (A, B)))
self.m, self.n = self.A.shape
# Check if (A, B) satisfy the basic assumptions
assert self.A.shape == self.B.shape, 'The input and output matrices \
must have the same dimensions!'
assert (self.A >= 0).all() and (self.B >= 0).all(), 'The input and \
output matrices must have only non-negative entries!'
# (1) Check whether Assumption I is satisfied:
if (np.sum(B, 0) <= 0).any():
self.AI = False
else:
self.AI = True
# (2) Check whether Assumption II is satisfied:
if (np.sum(A, 1) <= 0).any():
self.AII = False
else:
self.AII = True
def __repr__(self):
return self.__str__()
def __str__(self):
me = """
Generalized von Neumann expanding model:
- number of goods : {n}
- number of activities : {m}
Assumptions:
- AI: every column of B has a positive entry : {AI}
- AII: every row of A has a positive entry : {AII}
"""
# Irreducible : {irr}
return dedent(me.format(n=self.n, m=self.m,
AI=self.AI, AII=self.AII))
def convert(self, x):
"""
Convert array_like objects (lists of lists, floats, etc.) into
well-formed 2D NumPy arrays
"""
return np.atleast_2d(np.asarray(x))
def bounds(self):
"""
Calculate the trivial upper and lower bounds for alpha (expansion rate)
and beta (interest factor). See the proof of Theorem 9.8 in Gale (1960)
"""
n, m = self.n, self.m
A, B = self.A, self.B
f = lambda α: ((B - α * A) @ np.ones((n, 1))).max()
g = lambda β: (np.ones((1, m)) @ (B - β * A)).min()
UB = fsolve(f, 1).item() # Upper bound for α, β
LB = fsolve(g, 2).item() # Lower bound for α, β
return LB, UB
def zerosum(self, γ, dual=False):
"""
Given gamma, calculate the value and optimal strategies of a
two-player zero-sum game given by the matrix
M(gamma) = B - gamma * A
Row player maximizing, column player minimizing
Zero-sum game as an LP (primal --> α)
max (0', 1) @ (x', v)
subject to
[-M', ones(n, 1)] @ (x', v)' <= 0
(x', v) @ (ones(m, 1), 0) = 1
(x', v) >= (0', -inf)
Zero-sum game as an LP (dual --> beta)
min (0', 1) @ (p', u)
subject to
[M, -ones(m, 1)] @ (p', u)' <= 0
(p', u) @ (ones(n, 1), 0) = 1
(p', u) >= (0', -inf)
Outputs:
--------
value: scalar
value of the zero-sum game
strategy: vector
if dual = False, it is the intensity vector,
if dual = True, it is the price vector
"""
A, B, n, m = self.A, self.B, self.n, self.m
M = B - γ * A
if dual == False:
# Solve the primal LP (for details see the description)
# (1) Define the problem for v as a maximization (linprog minimizes)
c = np.hstack([np.zeros(m), -1])
# (2) Add constraints :
# ... non-negativity constraints
bounds = tuple(m * [(0, None)] + [(None, None)])
# ... inequality constraints
A_iq = np.hstack([-M.T, np.ones((n, 1))])
b_iq = np.zeros((n, 1))
# ... normalization
A_eq = np.hstack([np.ones(m), 0]).reshape(1, m + 1)
b_eq = 1
res = linprog(c, A_ub=A_iq, b_ub=b_iq, A_eq=A_eq, b_eq=b_eq,
bounds=bounds)
else:
# Solve the dual LP (for details see the description)
# (1) Define the problem for v as a maximization (linprog minimizes)
c = np.hstack([np.zeros(n), 1])
# (2) Add constraints :
# ... non-negativity constraints
bounds = tuple(n * [(0, None)] + [(None, None)])
# ... inequality constraints
A_iq = np.hstack([M, -np.ones((m, 1))])
b_iq = np.zeros((m, 1))
# ... normalization
A_eq = np.hstack([np.ones(n), 0]).reshape(1, n + 1)
b_eq = 1
res = linprog(c, A_ub=A_iq, b_ub=b_iq, A_eq=A_eq, b_eq=b_eq,
bounds=bounds)
if res.status != 0:
print(res.message)
# Pull out the required quantities
value = res.x[-1]
strategy = res.x[:-1]
return value, strategy
def expansion(self, tol=1e-8, maxit=1000):
"""
The algorithm used here is described in Hamburger-Thompson-Weil
(1967, ECTA). It is based on a simple bisection argument and utilizes
the idea that for a given γ (= α or β), the matrix "M = B - γ * A"
defines a two-player zero-sum game, where the optimal strategies are
the (normalized) intensity and price vector.
Outputs:
--------
alpha: scalar
optimal expansion rate
"""
LB, UB = self.bounds()
for iter in range(maxit):
γ = (LB + UB) / 2
ZS = self.zerosum(γ=γ)
V = ZS[0] # value of the game with γ
if V >= 0:
LB = γ
else:
UB = γ
if abs(UB - LB) < tol:
γ = (UB + LB) / 2
x = self.zerosum(γ=γ)[1]
p = self.zerosum(γ=γ, dual=True)[1]
break
return γ, x, p
def interest(self, tol=1e-8, maxit=1000):
"""
The algorithm used here is described in Hamburger-Thompson-Weil
(1967, ECTA). It is based on a simple bisection argument and utilizes
the idea that for a given gamma (= alpha or beta),
the matrix "M = B - γ * A" defines a two-player zero-sum game,
where the optimal strategies are the (normalized) intensity and price
vector
Outputs:
--------
beta: scalar
optimal interest rate
"""
LB, UB = self.bounds()
for iter in range(maxit):
γ = (LB + UB) / 2
ZS = self.zerosum(γ=γ, dual=True)
V = ZS[0]
if V > 0:
LB = γ
else:
UB = γ
if abs(UB - LB) < tol:
γ = (UB + LB) / 2
p = self.zerosum(γ=γ, dual=True)[1]
x = self.zerosum(γ=γ)[1]
break
return γ, x, p
18.1. Notation#
We use the following notation.
We call an
We call a vector non-negative and write
We call a vector semi-positive and written
For two conformable vectors
We let all vectors in this lecture be column vectors;
Let
Let
We denote matrices by capital letters. For an arbitrary matrix
18.2. Model Ingredients and Assumptions#
A pair
is the number of activities (or sectors) is the number of goods (produced and/or consumed). is called the input matrix; denotes the amount of good consumed by activity is called the output matrix; represents the amount of good produced by activity
Two key assumptions restrict economy
Assumption I: (every good that is consumed is also produced)
Assumption II: (no free lunch)
A semi-positive intensity
Therefore,
vector
gives the total amount of goods used in productionvector
gives total outputs
An economy
The semi-positive
The
the vector
tells costs of the vector of activitiesthe vector
tells revenues from the vector of activities
Satisfaction or a property of an input-output pair
Definition: For an economy
We study two examples, both in Chapter 9.6 of Gale [Gale, 1989]
# (1) Irreducible (A, B) example: α_0 = β_0
A1 = np.array([[0, 1, 0, 0],
[1, 0, 0, 1],
[0, 0, 1, 0]])
B1 = np.array([[1, 0, 0, 0],
[0, 0, 2, 0],
[0, 1, 0, 1]])
# (2) Reducible (A, B) example: β_0 < α_0
A2 = np.array([[0, 1, 0, 0, 0, 0],
[1, 0, 1, 0, 0, 0],
[0, 0, 0, 1, 0, 0],
[0, 0, 1, 0, 0, 1],
[0, 0, 0, 0, 1, 0]])
B2 = np.array([[1, 0, 0, 1, 0, 0],
[0, 1, 0, 0, 0, 0],
[0, 0, 1, 0, 0, 0],
[0, 0, 0, 0, 2, 0],
[0, 0, 0, 1, 0, 1]])
The following code sets up our first Neumann economy or Neumann
instance
n1 = Neumann(A1, B1)
n1
Generalized von Neumann expanding model:
- number of goods : 4
- number of activities : 3
Assumptions:
- AI: every column of B has a positive entry : True
- AII: every row of A has a positive entry : True
Here is a second instance of a Neumann economy
n2 = Neumann(A2, B2)
n2
Generalized von Neumann expanding model:
- number of goods : 6
- number of activities : 5
Assumptions:
- AI: every column of B has a positive entry : True
- AII: every row of A has a positive entry : True
18.3. Dynamic Interpretation#
Attach a time index
An interesting special case holds the technology process constant and investigates the dynamics of quantities and prices only.
Accordingly, in the rest of this lecture, we assume that
A crucial element of the dynamic interpretation involves the timing of production.
We assume that production (consumption of inputs) takes place in period
These timing conventions imply the following feasibility condition:
which asserts that no more goods can be used today than were produced yesterday.
Accordingly,
18.3.1. Balanced Growth#
We follow John von Neumann in studying “balanced growth”.
Let
Then balanced growth is a situation in which
With balanced growth, the law of motion of
In the same spirit, define
We assume that it is always possible to earn a gross return equal to the
constant interest factor
Under this assumption about outside investment opportunities, a no-arbitrage condition gives rise to the following (no profit) restriction on the price sequence:
This says that production cannot yield a return greater than that
offered by the outside investment opportunity (here we compare values in
period
The balanced growth assumption allows us to drop time subscripts and
conduct an analysis purely in terms of a time-invariant growth rate
18.4. Duality#
Two problems are connected by a remarkable dual relationship between technological and valuation characteristics of the economy:
Definition: The technological expansion problem (TEP) for the economy
Theorem 9.3 of David Gale’s book [Gale, 1989] asserts that if Assumptions I and II are
both satisfied, then a maximum value of
The maximal value is called the technological expansion rate and is denoted
by
Definition: The economic expansion problem (EEP) for
Assumptions I and II imply existence of a minimum value
The corresponding price vector
Because the criterion functions in the technological expansion problem
and the economical expansion problem are both linearly homogeneous,
the optimality of
For convenience (and to emphasize a close connection to zero-sum games), we normalize both vectors
A standard duality argument (see Lemma 9.4. in (Gale, 1960) [Gale, 1989]) implies
that under Assumptions I and II,
But to deduce that
Therefore, von Neumann [von Neumann, 1937] went on to prove the following remarkable “duality” result that connects TEP and EEP.
Theorem 1 (von Neumann): If the economy
Note
Proof (Sketch): Assumption I and II imply that there exist
Here the constant
We have already encountered and discussed the first two inequalities that represent feasibility and no-profit conditions.
Moreover, the equality
Therefore, the conditions stated in Theorem I ex encode all equilibrium conditions.
So Theorem I essentially states that under Assumptions I and II there
always exists an equilibrium
Note that Theorem I is silent about uniqueness of the equilibrium. In
fact, it does not rule out (trivial) cases with
To exclude such uninteresting cases, Kemeny, Morgenstern and Thomspson [Kemeny et al., 1956] add an extra requirement
and call the associated equilibria economic solutions.
They show that this extra condition does not affect the existence result, while it significantly reduces the number of (relevant) solutions.
18.5. Interpretation as Two-player Zero-sum Game#
To compute the equilibrium
Consider the
the row player chooses the
-vector subject tothe column player chooses the
-vector subject to .
Definition: The
The number
From the above definition, it is clear that the value
by playing the appropriate mixed stategy, the maximizing player can assure himself at least
(no matter what the column player chooses)by playing the appropriate mixed stategy, the minimizing player can make sure that the maximizing player will not get more than
(irrespective of what is the maximizing player’s choice)
A famous theorem of Nash (1951) tells us that there always exists a mixed strategy Nash equilibrium for any finite two-player zero-sum game.
Moreover, von Neumann’s Minmax Theorem [von Neumann, 1928] implies that
18.5.1. Connection with Linear Programming (LP)#
Nash equilibria of a finite two-player zero-sum game solve a linear programming problem.
To see this, we introduce the following notation
For a fixed
, let be the value of the minimization problem:For a fixed
, let be the value of the maximization problem:
Then the max-min problem (the game from the maximizing player’s point of view) can be written as the primal LP
while the min-max problem (the game from the minimizing player’s point of view) is the dual LP
Hamburger, Thompson and Weil [Hamburger et al., 1967] view the input-output pair of the economy as payoff matrices of two-player zero-sum games.
Using this interpretation, they restate Assumption I and II as follows
Note
Proof (Sketch):
implies , where is a maximizing vector. Since is non-negative, this requires that each column of has at least one positive entry, which is Assumption I. From Assumption I and the fact that , it follows that . This implies that the maximizing player can always choose so that so that it must be the case that .
In order to (re)state Theorem I in terms of a particular two-player
zero-sum game, we define a matrix for
For fixed
If
, then for all , there , s.t. implying that .If
, then for all , there , s.t. implying that .If
, then (by Theorem I) the optimal intensity and price vectors and satisfy
That is,
If
and , then .
Moreover, if
Proof (Sketch): If
hence
hence
It is clear from the above argument that
Furthermore, Hamburger et al. [Hamburger et al., 1967] show that the
function
This suggests an algorithm to compute
18.5.2. Algorithm#
Hamburger, Thompson and Weil [Hamburger et al., 1967] propose a simple bisection algorithm
to find the minimal and maximal roots (i.e.
18.5.2.1. Step 1#
First, notice that we can easily find trivial upper and lower bounds for
TEP requires that
and , so if is so large that , then TEP ceases to have a solution.
Accordingly, let UB
be the
Similar to the upper bound, if
is so low that , then the EEP has no solution and so we can defineLB
as the that solves .
The bounds method calculates these trivial bounds for us
n1.bounds()
(1.0, 2.0)
18.5.2.2. Step 2#
Compute
Finding
Fix
and compute the solution of the two-player zero-sum game associated with . We can use either the primal or the dual LP problem.If
, then set , otherwise let .Iterate on 1. and 2. until
.
Finding
Fix
and compute the solution of the two-player zero-sum game associated. with . We can use either the primal or the dual LP problem.If
, then set , otherwise let .Iterate on 1. and 2. until
.
Existence: Since
and and is a continuous, nonincreasing function, there is at least one , s.t. .
The zerosum method calculates the value and optimal strategies
associated with a given
γ = 2
print(f'Value of the game with γ = {γ}')
print(n1.zerosum(γ=γ)[0])
print('Intensity vector (from the primal)')
print(n1.zerosum(γ=γ)[1])
print('Price vector (from the dual)')
print(n1.zerosum(γ=γ, dual=True)[1])
Value of the game with γ = 2
-0.24
Intensity vector (from the primal)
[0.32 0.28 0.4 ]
Price vector (from the dual)
[0.4 0.32 0.28 0. ]
numb_grid = 100
γ_grid = np.linspace(0.4, 2.1, numb_grid)
value_ex1_grid = np.asarray([n1.zerosum(γ=γ_grid[i])[0]
for i in range(numb_grid)])
value_ex2_grid = np.asarray([n2.zerosum(γ=γ_grid[i])[0]
for i in range(numb_grid)])
fig, axes = plt.subplots(1, 2, figsize=(14, 5), sharey=True)
fig.suptitle(r'The function $V(M(\gamma))$', fontsize=16)
for ax, grid, N, i in zip(axes, (value_ex1_grid, value_ex2_grid),
(n1, n2), (1, 2)):
ax.plot(γ_grid, grid)
ax.set(title=f'Example {i}', xlabel='$\gamma$')
ax.axhline(0, c='k', lw=1)
ax.axvline(N.bounds()[0], c='r', ls='--', label='lower bound')
ax.axvline(N.bounds()[1], c='g', ls='--', label='upper bound')
plt.show()
<>:15: SyntaxWarning: invalid escape sequence '\g'
<>:15: SyntaxWarning: invalid escape sequence '\g'
/tmp/ipykernel_33646/1811931462.py:15: SyntaxWarning: invalid escape sequence '\g'
ax.set(title=f'Example {i}', xlabel='$\gamma$')

The expansion method implements the bisection algorithm for
α_0, x, p = n1.expansion()
print(f'α_0 = {α_0}')
print(f'x_0 = {x}')
print(f'The corresponding p from the dual = {p}')
α_0 = 1.2599210478365421
x_0 = [0.33 0.26 0.41]
The corresponding p from the dual = [0.41 0.33 0.26 0. ]
The interest method implements the bisection algorithm for
β_0, x, p = n1.interest()
print(f'β_0 = {β_0}')
print(f'p_0 = {p}')
print(f'The corresponding x from the primal = {x}')
β_0 = 1.2599210478365421
p_0 = [0.41 0.33 0.26 0. ]
The corresponding x from the primal = [0.33 0.26 0.41]
Of course, when
In particular, as will be shown below, in
case of an irreducible
18.5.3. Uniqueness and Irreducibility#
As an illustration, compute first the maximal and minimal roots of
α_0, x, p = n2.expansion()
print(f'α_0 = {α_0}')
print(f'x_0 = {x}')
print(f'The corresponding p from the dual = {p}')
α_0 = 1.259921052493155
x_0 = [5.27e-10 0.00e+00 3.27e-01 2.60e-01 4.13e-01]
The corresponding p from the dual = [0. 0.21 0.33 0.26 0.21 0. ]
β_0, x, p = n2.interest()
print(f'β_0 = {β_0}')
print(f'p_0 = {p}')
print(f'The corresponding x from the primal = {x}')
β_0 = 1.0000000009313226
p_0 = [ 5.00e-01 5.00e-01 -1.55e-09 -1.24e-09 -9.31e-10 0.00e+00]
The corresponding x from the primal = [-0. 0. 0.25 0.25 0.5 ]
As we can see, with a reducible
Indeed, although the von Neumann theorem assures existence of the
equilibrium, Assumptions I and II are not sufficient for uniqueness.
Nonetheless, Kemeny et al. (1967) show that there are at most finitely
many economic solutions, meaning that there are only finitely many
The following theorem (see Theorem 9.10. in Gale [Gale, 1989]) asserts that
imposing irreducibility is sufficient for uniqueness of
Theorem II: Adopt the conditions of Theorem 1. If the economy
18.5.4. A Special Case#
There is a special
Definition: We call an economy simple if it satisfies
Each activity produces exactly one good
Each good is produced by one and only one activity.
These assumptions imply that
The simple model has the following special property (Theorem 9.11. in Gale [Gale, 1989]): if
The latter shows that
The classic result of Perron and Frobenius implies that a non-negative matrix has a non-negative eigenvalue-eigenvector pair.
Moreover, if
Suppose that