5. Lagrangian for LQ Control#
!pip install quantecon
Show code cell output
Requirement already satisfied: quantecon in /home/runner/miniconda3/envs/quantecon/lib/python3.11/site-packages (0.7.2)
Requirement already satisfied: numba>=0.49.0 in /home/runner/miniconda3/envs/quantecon/lib/python3.11/site-packages (from quantecon) (0.59.0)
Requirement already satisfied: numpy>=1.17.0 in /home/runner/miniconda3/envs/quantecon/lib/python3.11/site-packages (from quantecon) (1.26.4)
Requirement already satisfied: requests in /home/runner/miniconda3/envs/quantecon/lib/python3.11/site-packages (from quantecon) (2.31.0)
Requirement already satisfied: scipy>=1.5.0 in /home/runner/miniconda3/envs/quantecon/lib/python3.11/site-packages (from quantecon) (1.11.4)
Requirement already satisfied: sympy in /home/runner/miniconda3/envs/quantecon/lib/python3.11/site-packages (from quantecon) (1.12)
Requirement already satisfied: llvmlite<0.43,>=0.42.0dev0 in /home/runner/miniconda3/envs/quantecon/lib/python3.11/site-packages (from numba>=0.49.0->quantecon) (0.42.0)
Requirement already satisfied: charset-normalizer<4,>=2 in /home/runner/miniconda3/envs/quantecon/lib/python3.11/site-packages (from requests->quantecon) (2.0.4)
Requirement already satisfied: idna<4,>=2.5 in /home/runner/miniconda3/envs/quantecon/lib/python3.11/site-packages (from requests->quantecon) (3.4)
Requirement already satisfied: urllib3<3,>=1.21.1 in /home/runner/miniconda3/envs/quantecon/lib/python3.11/site-packages (from requests->quantecon) (2.0.7)
Requirement already satisfied: certifi>=2017.4.17 in /home/runner/miniconda3/envs/quantecon/lib/python3.11/site-packages (from requests->quantecon) (2024.2.2)
Requirement already satisfied: mpmath>=0.19 in /home/runner/miniconda3/envs/quantecon/lib/python3.11/site-packages (from sympy->quantecon) (1.3.0)
import numpy as np
from quantecon import LQ
from scipy.linalg import schur
5.1. Overview#
This is a sequel to this lecture linear quadratic dynamic programming
It can also be regarded as presenting invariant subspace techniques that extend ones that we encountered earlier in this lecture stability in linear rational expectations models
We present a Lagrangian formulation of an infinite horizon linear quadratic undiscounted dynamic programming problem.
Such a problem is also sometimes called an optimal linear regulator problem.
A Lagrangian formulation
carries insights about connections between stability and optimality
is the basis for fast algorithms for solving Riccati equations
opens the way to constructing solutions of dynamic systems that don’t come directly from an intertemporal optimization problem
A key tool in this lecture is the concept of an
A symplectic matrix has eigenvalues that occur in reciprocal pairs, meaning that if
This reciprocal pairs property of the eigenvalues of a matrix is a tell-tale sign that the matrix describes the joint dynamics of a system of equations describing the states and costates that constitute first-order necessary conditions for solving an undiscounted linear-quadratic infinite-horizon optimization problem.
The symplectic matrix that will interest us describes the first-order dynamics of state and co-state vectors of an optimally controlled system.
In focusing on eigenvalues and eigenvectors of this matrix, we capitalize on an analysis of invariant subspaces.
These invariant subspace formulations of LQ dynamic programming problems provide a bridge between recursive (i.e., dynamic programming) formulations and classical formulations of linear control and linear filtering problems that make use of related matrix decompositions (see for example this lecture and this lecture).
While most of this lecture focuses on undiscounted problems, later sections describe handy ways of transforming discounted problems to undiscounted ones.
The techniques in this lecture will prove useful when we study Stackelberg and Ramsey problem in this lecture.
5.2. Undiscounted LQ DP Problem#
The problem is to choose a sequence of controls
subject to
Here
The optimal
value function turns out to be quadratic,
Using the transition law to eliminate next period’s state, the Bellman equation becomes
The first-order necessary conditions for the maximum problem on the right side of equation (5.1) are
Note
We use the following rules for differentiating quadratic and bilinear matrix forms:
which implies that an optimal decision rule for
or
where
Substituting
Equation (5.2) is called an algebraic matrix Riccati equation.
There are multiple solutions of equation (5.2).
But only one of them is positive definite.
The positive define solution is associated with the maximum of our problem.
It expresses the matrix
Notice that the gradient of the value function is
We shall use fact (5.3) later.
5.3. Lagrangian#
For the undiscounted optimal linear regulator problem, form the Lagrangian
where
(We put the
First-order conditions for maximization with respect to
Define
which is a time
An important fact is that
where
Thus, from equations (5.3) and (5.6),
The Lagrange multiplier vector
It is useful to proceed with the following steps:
solve the first equation of (5.5) for
in terms of .substitute the result into the law of motion
.arrange the resulting equation and the second equation of (5.5) into the form
where
When
where
5.4. State-Costate Dynamics#
We seek to solve the difference equation system (5.8) for a sequence
an initial condition for
a terminal condition
This terminal condition reflects our desire for a stable solution, one that does not diverge as
We inherit our wish for stability of the
which requires that
5.5. Reciprocal Pairs Property#
To proceed, we study properties of the
It helps to introduce a
The rank of
Definition: A matrix
Salient properties of symplectic matrices that are readily verified include:
If
is symplectic, then is symplecticThe determinant of a symplectic, then
It can be verified directly that
It follows from equation (5.10) and from the fact
Equation (5.11) states that
For square matrices, recall that
similar matrices share eigenvalues
eigenvalues of the inverse of a matrix are inverses of eigenvalues of the matrix
a matrix and its transpose share eigenvalues
It then follows from equation (5.11) that
the eigenvalues of
Write equation (5.8) as
where
Consider a triangularization of
where
each block on the right side is
is nonsingularall eigenvalues of
exceed in modulusall eigenvalues of
are less than in modulus
5.6. Schur decomposition#
The Schur decomposition and the eigenvalue decomposition are two decompositions of the form (5.13).
Write equation (5.12) as
A solution of equation (5.14) for arbitrary initial condition
where
and where
Write equation (5.15) as
where
and where
Because
Let
To attain stability, we must impose
or
This equation replicates itself over time in the sense that it implies
But notice that because
which implies
Therefore,
So we can write
and
However, we know that
Thus, the preceding argument establishes that
Remarkably, formula (5.17) provides us with a computationally
efficient way of computing the positive definite matrix
This same method can be applied to compute the solution of
any system of the form (5.8) if a solution exists, even
if eigenvalues of
The method
will typically work so long as the eigenvalues of
Systems in which eigenvalues (properly adjusted for discounting) fail to occur in reciprocal pairs arise when the system being solved is an equilibrium of a model in which there are distortions that prevent there being any optimum problem that the equilibrium solves. See [Ljungqvist and Sargent, 2018], ch 12.
5.7. Application#
Here we demonstrate the computation with an example which is the deterministic version of an example borrowed from this quantecon lecture.
# Model parameters
r = 0.05
c_bar = 2
μ = 1
# Formulate as an LQ problem
Q = np.array([[1]])
R = np.zeros((2, 2))
A = [[1 + r, -c_bar + μ],
[0, 1]]
B = [[-1],
[0]]
# Construct an LQ instance
lq = LQ(Q, R, A, B)
Given matrices
def construct_LNM(A, B, Q, R):
n, k = lq.n, lq.k
# construct L and N
L = np.zeros((2*n, 2*n))
L[:n, :n] = np.eye(n)
L[:n, n:] = B @ np.linalg.inv(Q) @ B.T
L[n:, n:] = A.T
N = np.zeros((2*n, 2*n))
N[:n, :n] = A
N[n:, :n] = -R
N[n:, n:] = np.eye(n)
# compute M
M = np.linalg.inv(L) @ N
return L, N, M
L, N, M = construct_LNM(lq.A, lq.B, lq.Q, lq.R)
M
array([[ 1.05 , -1. , -0.95238095, 0. ],
[ 0. , 1. , 0. , 0. ],
[ 0. , 0. , 0.95238095, 0. ],
[ 0. , 0. , 0.95238095, 1. ]])
Let’s verify that
n = lq.n
J = np.zeros((2*n, 2*n))
J[n:, :n] = np.eye(n)
J[:n, n:] = -np.eye(n)
M @ J @ M.T - J
array([[0., 0., 0., 0.],
[0., 0., 0., 0.],
[0., 0., 0., 0.],
[0., 0., 0., 0.]])
We can compute the eigenvalues of np.linalg.eigvals
, arranged in ascending order.
eigvals = sorted(np.linalg.eigvals(M))
eigvals
[0.9523809523809523, 1.0, 1.0, 1.05]
When we apply Schur decomposition such that
the upper left block of
, , to have all of its eigenvalues less than 1 in modulus, andthe lower right block
to have eigenvalues that exceed 1 in modulus.
To get what we want, let’s define a sorting function that tells scipy.schur
to sort the corresponding eigenvalues with modulus smaller than 1 to the upper left.
stable_eigvals = eigvals[:n]
def sort_fun(x):
"Sort the eigenvalues with modules smaller than 1 to the top-left."
if x in stable_eigvals:
stable_eigvals.pop(stable_eigvals.index(x))
return True
else:
return False
W, V, _ = schur(M, sort=sort_fun)
W
array([[ 1. , -0.02316402, -1.00085948, -0.95000594],
[ 0. , 0.95238095, -0.00237501, -0.95325452],
[ 0. , 0. , 1.05 , 0.02432222],
[ 0. , 0. , 0. , 1. ]])
V
array([[ 0.99875234, 0.00121459, -0.04992284, 0. ],
[ 0.04993762, -0.02429188, 0.99845688, 0. ],
[ 0. , 0.04992284, 0.00121459, 0.99875234],
[ 0. , -0.99845688, -0.02429188, 0.04993762]])
We can check the modulus of eigenvalues of
Since they are both triangular matrices, eigenvalues are the diagonal elements.
# W11
np.diag(W[:n, :n])
array([1. , 0.95238095])
# W22
np.diag(W[n:, n:])
array([1.05, 1. ])
The following functions wrap
def stable_solution(M, verbose=True):
"""
Given a system of linear difference equations
y' = |a b| y
x' = |c d| x
which is potentially unstable, find the solution
by imposing stability.
Parameter
---------
M : np.ndarray(float)
The matrix represents the linear difference equations system.
"""
n = M.shape[0] // 2
stable_eigvals = list(sorted(np.linalg.eigvals(M))[:n])
def sort_fun(x):
"Sort the eigenvalues with modules smaller than 1 to the top-left."
if x in stable_eigvals:
stable_eigvals.pop(stable_eigvals.index(x))
return True
else:
return False
W, V, _ = schur(M, sort=sort_fun)
if verbose:
print('eigenvalues:\n')
print(' W11: {}'.format(np.diag(W[:n, :n])))
print(' W22: {}'.format(np.diag(W[n:, n:])))
# compute V21 V11^{-1}
P = V[n:, :n] @ np.linalg.inv(V[:n, :n])
return W, V, P
def stationary_P(lq, verbose=True):
"""
Computes the matrix :math:`P` that represent the value function
V(x) = x' P x
in the infinite horizon case. Computation is via imposing stability
on the solution path and using Schur decomposition.
Parameters
----------
lq : qe.LQ
QuantEcon class for analyzing linear quadratic optimal control
problems of infinite horizon form.
Returns
-------
P : array_like(float)
P matrix in the value function representation.
"""
Q = lq.Q
R = lq.R
A = lq.A * lq.beta ** (1/2)
B = lq.B * lq.beta ** (1/2)
n, k = lq.n, lq.k
L, N, M = construct_LNM(A, B, Q, R)
W, V, P = stable_solution(M, verbose=verbose)
return P
# compute P
stationary_P(lq)
eigenvalues:
W11: [1. 0.95238095]
W22: [1.05 1. ]
array([[ 0.1025, -2.05 ],
[-2.05 , 41. ]])
Note that the matrix
The small difference comes from computational errors and will decrease as we increase the maximum number of iterations or decrease the tolerance for convergence.
lq.stationary_values()
(array([[ 0.1025, -2.05 ],
[-2.05 , 41.01 ]]),
array([[-0.09761905, 1.95238095]]),
0)
Using a Schur decomposition is much more efficient.
%%timeit
stationary_P(lq, verbose=False)
72.4 µs ± 241 ns per loop (mean ± std. dev. of 7 runs, 10,000 loops each)
%%timeit
lq.stationary_values()
1.16 ms ± 2.52 µs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)
5.8. Other Applications#
The preceding approach to imposing stability on a system of potentially unstable linear difference equations is not limited to linear quadratic dynamic optimization problems.
For example, the same method is used in our Stability in Linear Rational Expectations Models lecture.
Let’s try to solve the model described in that lecture by applying the stable_solution
function defined in this lecture above.
def construct_H(ρ, λ, δ):
"contruct matrix H given parameters."
H = np.empty((2, 2))
H[0, :] = ρ,δ
H[1, :] = - (1 - λ) / λ, 1 / λ
return H
H = construct_H(ρ=.9, λ=.5, δ=0)
W, V, P = stable_solution(H)
P
eigenvalues:
W11: [0.9]
W22: [2.]
array([[0.90909091]])
5.9. Discounted Problems#
5.9.1. Transforming States and Controls to Eliminate Discounting#
A pair of useful transformations allows us to convert a discounted problem into an undiscounted one.
Thus, suppose that we have a discounted problem with objective
and that the state transition equation
is again
Define the transformed state and control variables
and the transformed transition equation matrices
so that the adjusted state and control variables obey the transition law
Then a discounted optimal control problem
defined by
and
It follows immediately from the definitions of
By exploiting these transformations, we can solve a discounted problem by solving an associated undiscounted problem.
In particular, we can first transform a discounted LQ problem to an undiscounted one and then solve that discounted optimal regulator problem using the Lagrangian and invariant subspace methods described above.
For example, when
These settings are adopted by default in the function stationary_P
defined above.
β = 1 / (1 + r)
lq.beta = β
stationary_P(lq)
eigenvalues:
W11: [0.97590007 0.97590007]
W22: [1.02469508 1.02469508]
array([[ 0.0525, -1.05 ],
[-1.05 , 21. ]])
We can verify that the solution agrees with one that comes from applying the routine LQ.stationary_values
in the quantecon package.
lq.stationary_values()
(array([[ 0.0525, -1.05 ],
[-1.05 , 21. ]]),
array([[-0.05, 1. ]]),
0.0)
5.9.2. Lagrangian for Discounted Problem#
For several purposes, it is useful explicitly briefly to describe a Lagrangian for a discounted problem.
Thus, for the discounted optimal linear regulator problem, form the Lagrangian
where
First-order conditions for maximization with respect
to
Define
which is a time
Proceeding as we did above with the undiscounted system (5.5), we can rearrange the first-order conditions into the system
which in the special case that
By staring at system (5.20), we can infer identities that shed light on the structure of optimal linear regulator problems, some of which will be useful in this lecture when we apply and extend the methods of this lecture to study Stackelberg and Ramsey problems.
First, note that the first block of equation system (5.20) asserts that when
which can be rearranged to sbe
This expression for the optimal closed loop dynamics of the state must agree with an alternative expression that we had derived with dynamic programming, namely,
But using
it follows that
Thus, our two expressions for the closed loop dynamics agree if and only if
Matrix equation (5.22) can be verified by applying a partitioned inverse formula.
Note
Just use the formula
Next, note that for any fixed
Evidently,
Next, note that the second equation of system (5.20) implies the “forward looking” equation for the Lagrange multiplier
whose solution is
where
where we must require that
Equations (5.23) and (5.24) provide different perspectives on the optimal value function.