# Simulating Molecules using VQE

In this tutorial, we introduce the Variational Quantum Eigensolver (VQE), motivate its use, explain the necessary theory, and demonstrate its implementation in finding the ground state energy of molecules.

## Contents
1. [Introduction](#introduction)
2. [The Variational Method of Quantum Mechanics](#varmethod)
    1. [Mathematical Background](#backgroundmath)
    2. [Bounding the Ground State](#groundstate)
3. [The Variational Quantum Eigensolver](#vqe)
    1. [Variational Forms](#varforms)
    2. [Simple Variational Forms](#simplevarform)
    3. [Parameter Optimization](#optimization)
    4. [Example with a Single Qubit Variational Form](#example)
    5. [Structure of Common Variational Forms](#commonvarforms)
4. [VQE Implementation in Qiskit](#implementation)
    1. [Running VQE on a Statevector Simulator](#implementationstatevec)
    2. [Running VQE on a Noisy Simulator](#implementationnoisy)
5. [Problems](#problems)
6. [References](#references)

## Introduction
In many applications it is important to find the minimum eigenvalue of a matrix. For example, in chemistry, the minimum eigenvalue of a Hermitian matrix characterizing the molecule is the ground state energy of that system. In the future, the quantum phase estimation algorithm may be used to find the minimum eigenvalue. However, its implementation on useful problems requires circuit depths exceeding the limits of hardware available in the NISQ era. Thus, in 2014, Peruzzo *et al.* proposed VQE to estimate the ground state energy of a molecule using much shallower circuits [1].

Formally stated, given a Hermitian matrix $H$ with an unknown minimum eigenvalue $\lambda_{min}$, associated with the eigenstate $|\psi_{min}\rangle$, VQE provides an estimate $\lambda_{\theta}$ bounding $\lambda_{min}$:

\begin{align*}
    \lambda_{min} \le \lambda_{\theta} \equiv \langle \psi(\theta) |H|\psi(\theta) \rangle
\end{align*}

where $|\psi(\theta)\rangle$ is the eigenstate associated with $\lambda_{\theta}$. By applying a parameterized circuit, represented by $U(\theta)$, to some arbitrary starting state $|\psi\rangle$, the algorithm obtains an estimate $U(\theta)|\psi\rangle \equiv |\psi(\theta)\rangle$ on $|\psi_{min}\rangle$. The estimate is iteratively optimized by a classical controller changing the parameter $\theta$ minimizing the expectation value of $\langle \psi(\theta) |H|\psi(\theta) \rangle$.

## The Variational Method of Quantum Mechanics
### Mathematical Background

VQE is an application of the variational method of quantum mechanics. To better understand the variational method, some preliminary mathematical background is provided. An eigenvector, $|\psi_i\rangle$, of a matrix $A$ is invariant under transformation by $A$ up to a scalar multiplicative constant (the eigenvalue $\lambda_i$). That is,

\begin{align*}
    A |\psi_i\rangle = \lambda_i |\psi_i\rangle
\end{align*}

Furthermore, a matrix $H$ is Hermitian when it is equal to its own conjugate transpose.

\begin{align*}
    H = H^{\dagger}
\end{align*}

The spectral theorem states that the eigenvalues of a Hermitian matrix must be real. Thus, any eigenvalue of $H$ has the property that $\lambda_i = \lambda_i^*$. As any measurable quantity must be real, Hermitian matrices are suitable for describing the Hamiltonians of quantum systems. Moreover, $H$ may be expressed as

\begin{align*}
    H = \sum_{i = 1}^{N} \lambda_i |\psi_i\rangle \langle \psi_i |
\end{align*}

where each $\lambda_i$ is the eigenvalue corresponding to the eigenvector $|\psi_i\rangle$. Furthermore, the expectation value of the observable $H$ on an arbitrary quantum state $|\psi\rangle$ is given by

\begin{align}
    \langle H \rangle_{\psi} &\equiv \langle \psi | H | \psi \rangle
\end{align}

Substituting $H$ with its representation as a weighted sum of its eigenvectors,

\begin{align}
    \langle H \rangle_{\psi} = \langle \psi | H | \psi \rangle &= \langle \psi | \left(\sum_{i = 1}^{N} \lambda_i |\psi_i\rangle \langle \psi_i |\right) |\psi\rangle\\
    &= \sum_{i = 1}^{N} \lambda_i \langle \psi | \psi_i\rangle \langle \psi_i | \psi\rangle \\
    &= \sum_{i = 1}^{N} \lambda_i | \langle \psi_i | \psi\rangle |^2
\end{align}

The last equation demonstrates that the expectation value of an observable on any state can be expressed as a linear combination using the eigenvalues associated with $H$ as the weights. Moreover, each of the weights in the linear combination is greater than or equal to 0, as $| \langle \psi_i | \psi\rangle |^2 \ge 0$ and so it is clear that

\begin{align}
    \lambda_{min} \le \langle H \rangle_{\psi} = \langle \psi | H | \psi \rangle = \sum_{i = 1}^{N} \lambda_i | \langle \psi_i | \psi\rangle |^2
\end{align}

The above equation is known as the **variational method** (in some texts it is also known as the variational principle) [2]. It is important to note that this implies that the expectation value of any wave function will always be at least the minimum eigenvalue associated with $H$. Moreover, the expectation value of state $|\psi_{min}\rangle$ is given by $\langle \psi_{min}|H|\psi_{min}\rangle = \langle \psi_{min}|\lambda_{min}|\psi_{min}\rangle = \lambda_{min}$. Thus, as expected, $\langle H \rangle_{\psi_{min}}=\lambda_{min}$.

### Bounding the Ground State
When the Hamiltonian of a system is described by the Hermitian matrix $H$ the ground state energy of that system, $E_{gs}$, is the smallest eigenvalue associated with $H$. By arbitrarily selecting a wave function $|\psi \rangle$ (called an *ansatz*) as an initial guess approximating $|\psi_{min}\rangle$, calculating its expectation value, $\langle H \rangle_{\psi}$, and iteratively updating the wave function, arbitrarily tight bounds on the ground state energy of a Hamiltonian may be obtained.

## The Variational Quantum Eigensolver
### Variational Forms
A systematic approach to varying the ansatz is required to implement the variational method on a quantum computer. VQE does so through the use of a parameterized circuit with a fixed form. Such a circuit is often called a *variational form*, and its action may be represented by the linear transformation $U(\theta)$. A variational form is applied to a starting state $|\psi\rangle$ (such as the vacuum state $|0\rangle$, or the Hartree Fock state) and generates an output state $U(\theta)|\psi\rangle\equiv |\psi(\theta)\rangle$. Iterative optimization over $|\psi(\theta)\rangle$ aims to yield an expectation value $\langle \psi(\theta)|H|\psi(\theta)\rangle \approx E_{gs} \equiv \lambda_{min}$. Ideally, $|\psi(\theta)\rangle$ will be close to $|\psi_{min}\rangle$ (where 'closeness' is characterized by either state fidelity, or Manhattan distance) although in practice, useful bounds on $E_{gs}$ can be obtained even if this is not the case.

Moreover, a fixed variational form with a polynomial number of parameters can only generate transformations to a polynomially sized subspace of all the states in an exponentially sized Hilbert space. Consequently, various variational forms exist. Some, such as Ry and RyRz are heuristically designed, without consideration of the target domain. Others, such as UCCSD, utilize domain specific knowledge to generate close approximations based on the problem's structure. The structure of common variational forms is discussed in greater depth later in this document.

### Simple Variational Forms
When constructing a variational form we must balance two opposing goals. Ideally, our $n$ qubit variational form would be able to generate any possible state $|\psi\rangle$ where $|\psi\rangle \in \mathbb{C}^N$ and $N=2^n$. However, we would like the variational form to use as few parameters as possible. Here, we aim to give intuition for the construction of variational forms satisfying our first goal, while disregarding the second goal for the sake of simplicity.

Consider the case where $n=1$. The U3 gate takes three parameters, $\theta, \phi$ and $\lambda$, and represents the following transformation:

\begin{align}
    U3(\theta, \phi, \lambda) = \begin{pmatrix}\cos(\frac{\theta}{2}) & -e^{i\lambda}\sin(\frac{\theta}{2}) \\ e^{i\phi}\sin(\frac{\theta}{2}) & e^{i\lambda + i\phi}\cos(\frac{\theta}{2}) \end{pmatrix}
\end{align}

Up to a global phase, any possible single qubit transformation may be implemented by appropriately setting these parameters. Consequently, for the single qubit case, a variational form capable of generating any possible state is given by the circuit:

     alt="U3 Variational Form"
     width="350"/>

Moreover, this universal 'variational form' only has 3 parameters and thus can be efficiently optimized. It is worth emphasising that the ability to generate an arbitrary state ensures that during the optimization process, the variational form does not limit the set of attainable states over which the expectation value of $H$ can be taken. Ideally, this ensures that the minimum expectation value is limited only by the capabilities of the classical optimizer.

A less trivial universal variational form may be derived for the 2 qubit case, where two body interactions, and thus entanglement, must be considered to achieve universality. Based on the work presented by *Shende et al.* [3] the following is an example of a universal parameterized 2 qubit circuit:

     alt="Two Qubit Variational Form"
     width="800"/>

Allow the transformation performed by the above circuit to be represented by $U(\theta)$. When optimized variationally, the expectation value of $H$ is minimized when $U(\theta)|\psi\rangle \equiv |\psi(\theta)\rangle \approx |\psi_{min}\rangle$. By formulation, $U(\theta)$ may produce a transformation to any possible state, and so this variational form may obtain an arbitrarily tight bound on two qubit ground state energies, only limited by the capabilities of the classical optimizer.

### Parameter Optimization
Once an efficiently parameterized variational form has been selected, in accordance with the variational method, its parameters must be optimized to minimize the expectation value of the target Hamiltonian. The parameter optimization process has various challenges. For example, quantum hardware has various types of noise and so objective function evaluation (energy calculation) may not necessarily reflect the true objective function. Additionally, some optimizers perform a number of objective function evaluations dependent on cardinality of the parameter set. An appropriate optimizer should be selected by considering the requirements of a application.

A popular optimization strategy is gradient decent where each parameter is updated in the direction yielding the largest local change in energy. Consequently, the number of evaluations performed depends on the number of optimization parameters present. This allows the algorithm to quickly find a local optimum in the search space. However, this optimization strategy often gets stuck at poor local optima, and is relatively expensive in terms of the number of circuit evaluations performed. While an intuitive optimization strategy, it is not recommended for use in VQE.

An appropriate optimizer for optimizing a noisy objective function is the *Simultaneous Perturbation Stochastic Approximation* optimizer (SPSA). SPSA approximates the gradient of the objective function with only two measurements. It does so by concurrently perturbing all of the parameters in a random fashion, in contrast to gradient decent where each parameter is perturbed independently. When utilizing VQE in either a noisy simulator or on real hardware, SPSA is a recommended as the classical optimizer.

When noise is not present in the cost function evaluation (such as when using VQE with a statevector simulator), a wide variety of classical optimizers may be useful. Two such optimizers supported by Qiskit Aqua are the *Sequential Least Squares Programming* optimizer (SLSQP) and the *Constrained Optimization by Linear Approximation* optimizer (COBYLA). It is worth noting that COBYLA only performs one objective function evaluation per optimization iteration (and thus the number of evaluations is independent of the parameter set's cardinality). Therefore, if the objective function is noise-free and minimizing the number of performed evaluations is desirable, it is recommended to try COBYLA.

### Example with a Single Qubit Variational Form

We will now use the simple single qubit variational form to solve a problem similar to ground state energy estimation. Specifically, we are given a random probability vector $\vec{x}$ and wish to determine a possible parameterization for our single qubit variational form such that it outputs a probability distribution that is close to $\vec{x}$ (where closeness is defined in terms of the Manhattan distance between the two probability vectors).

We first create the random probability vector in python:

```python
import numpy as np
np.random.seed(999999)
target_distr = np.random.rand(2)
# We now convert the random vector into a valid probability vector
target_distr /= sum(target_distr)
```

We subsequently create a function that takes the parameters of our single U3 variational form as arguments and returns the corresponding quantum circuit:

```python
from qiskit import QuantumCircuit, ClassicalRegister, QuantumRegister
def get_var_form(params):
    qr = QuantumRegister(1, name="q")
    cr = ClassicalRegister(1, name='c')
    qc = QuantumCircuit(qr, cr)
    qc.u3(params[0], params[1], params[2], qr[0])
    qc.measure(qr, cr[0])
    return qc
```

Now we specify the objective function which takes as input a list of the variational form's parameters, and returns the cost associated with those parameters:

```python
from qiskit import Aer, execute
backend = Aer.get_backend("qasm_simulator")
NUM_SHOTS = 10000

def get_probability_distribution(counts):
    output_distr = [v / NUM_SHOTS for v in counts.values()]
    if len(output_distr) == 1:
        output_distr.append(0)
    return output_distr

def objective_function(params):
    # Obtain a quantum circuit instance from the paramters
    qc = get_var_form(params)
    # Execute the quantum circuit to obtain the probability distribution associated with the current parameters
    result = execute(qc, backend, shots=NUM_SHOTS).result()
    # Obtain the counts for each measured state, and convert those counts into a probability vector
    output_distr = get_probability_distribution(result.get_counts(qc))
    # Calculate the cost as the distance between the output distribution and the target distribution
    cost = sum([np.abs(output_distr[i] - target_distr[i]) for i in range(2)])
    return cost
```

Finally, we create an instance of the COBYLA optimizer, and run the algorithm. Note that the output varies from run to run. Moreover, while close, the obtained distribution might not be exactly the same as the target distribution, however, increasing the number of shots taken will increase the accuracy of the output.

```python
from qiskit.aqua.components.optimizers import COBYLA

# Initialize the COBYLA optimizer
optimizer = COBYLA(maxiter=500, tol=0.0001)

# Create the initial parameters (noting that our single qubit variational form has 3 parameters)
params = np.random.rand(3)
ret = optimizer.optimize(num_vars=3, objective_function=objective_function, initial_point=params)

# Obtain the output distribution using the final parameters
qc = get_var_form(ret[0])
counts = execute(qc, backend, shots=NUM_SHOTS).result().get_counts(qc)
output_distr = get_probability_distribution(counts)

print("Target Distribution:", target_distr)
print("Obtained Distribution:", output_distr)
print("Output Error (Manhattan Distance):", ret[1])
print("Parameters Found:", ret[0])

```

    Target Distribution: [0.51357006 0.48642994]
    Obtained Distribution: [0.5195, 0.4805]
    Output Error (Manhattan Distance): 0.019459881261160827
    Parameters Found: [1.6124448  1.05596402 0.65378055]

### Structure of Common Variational Forms
As already discussed, it is not possible for a polynomially parameterized variational form to generate a transformation to any state. Variational forms can be grouped into two categories, depending on how they deal with this limitation. The first category of variational forms use domain or application specific knowledge to limit the set of possible output states. The second approach uses a heuristic circuit without prior domain or application specific knowledge.

The first category of variational forms exploit characteristics of the problem domain to restrict the set of transformations that may be required. For example, when calculating the ground state energy of a molecule, the number of particles in the system is known *a priori*. Therefore, if a starting state with the correct number of particles is used, by limiting the variational form to only producing particle preserving transformations, the number of parameters required to span the new transformation subspace can be greatly reduced. Indeed, by utilizing similar information from Coupled-Cluster theory, the variational form UCCSD can obtain very accurate results for molecular ground state energy estimation when starting from the Hartree Fock state. Another example illustrating the exploitation of domain-specific knowledge follows from considering the set of circuits realizable on real quantum hardware. Extant quantum computers, such as those based on super conducting qubits, have limited qubit connectivity. That is, it is not possible to implement 2-qubit gates on arbitrary qubit pairs (without inserting swap gates). Thus, variational forms have been constructed for specific quantum computer architectures where the circuits are specifically tuned to maximally exploit the natively available connectivity and gates of a given quantum device. Such a variational form was used in 2017 to successfully implement VQE for the estimation of the ground state energies of molecules as large as BeH$_2$ on an IBM quantum computer [4].

In the second approach, gates are layered such that good approximations on a wide range of states may be obtained. Qiskit Aqua supports three such variational forms: RyRz, Ry and SwapRz (we will only discuss the first two). All of these variational forms accept multiple user-specified configurations. Three essential configurations are the number of qubits in the system, the depth setting, and the entanglement setting. A single layer of a variational form specifies a certain pattern of single qubit rotations and CX gates. The depth setting says how many times the variational form should repeat this pattern. By increasing the depth setting, at the cost of increasing the number of parameters that must be optimized, the set of states the variational form can generate increases. Finally, the entanglement setting selects the configuration, and implicitly the number, of CX gates. For example, when the entanglement setting is linear, CX gates are applied to adjacent qubit pairs in order (and thus $n-1$ CX gates are added per layer). When the entanglement setting is full, a CX gate is applied to each qubit pair in each layer. The circuits for RyRz corresponding to `entanglement="full"` and `entanglement="linear"` can be seen by executing the following code snippet:

```python
from qiskit.aqua.components.variational_forms import RYRZ
entanglements = ["linear", "full"]
for entanglement in entanglements:
    form = RYRZ(num_qubits=4, depth=1, entanglement=entanglement)
    if entanglement == "linear":
        print("=============Linear Entanglement:=============")
    else:
        print("=============Full Entanglement:=============")
    # We initialize all parameters to 0 for this demonstration
    print(form.construct_circuit([0] * form.num_parameters).draw(fold=100))
    print()
```

    =============Linear Entanglement:=============
            ┌───────────┐┌───────┐ ░                                                                 ░ »
    q_0: |0>┤ U3(0,0,0) ├┤ U1(0) ├─░───────────────■─────────────────────────────────────────────────░─»
            ├───────────┤├───────┤ ░ ┌──────────┐┌─┴─┐┌──────────┐                                   ░ »
    q_1: |0>┤ U3(0,0,0) ├┤ U1(0) ├─░─┤ U2(0,pi) ├┤ X ├┤ U2(0,pi) ├──■────────────────────────────────░─»
            ├───────────┤├───────┤ ░ ├──────────┤└───┘└──────────┘┌─┴─┐┌──────────┐                  ░ »
    q_2: |0>┤ U3(0,0,0) ├┤ U1(0) ├─░─┤ U2(0,pi) ├─────────────────┤ X ├┤ U2(0,pi) ├──■───────────────░─»
            ├───────────┤├───────┤ ░ ├──────────┤                 └───┘└──────────┘┌─┴─┐┌──────────┐ ░ »
    q_3: |0>┤ U3(0,0,0) ├┤ U1(0) ├─░─┤ U2(0,pi) ├──────────────────────────────────┤ X ├┤ U2(0,pi) ├─░─»
            └───────────┘└───────┘ ░ └──────────┘                                  └───┘└──────────┘ ░ »
    «     ┌───────────┐┌───────┐ ░
    «q_0: ┤ U3(0,0,0) ├┤ U1(0) ├─░─
    «     ├───────────┤├───────┤ ░
    «q_1: ┤ U3(0,0,0) ├┤ U1(0) ├─░─
    «     ├───────────┤├───────┤ ░
    «q_2: ┤ U3(0,0,0) ├┤ U1(0) ├─░─
    «     ├───────────┤├───────┤ ░
    «q_3: ┤ U3(0,0,0) ├┤ U1(0) ├─░─
    «     └───────────┘└───────┘ ░

    =============Full Entanglement:=============
            ┌───────────┐┌───────┐ ░                                                                »
    q_0: |0>┤ U3(0,0,0) ├┤ U1(0) ├─░───────────────■────────────────■────────────────■──────────────»
            ├───────────┤├───────┤ ░ ┌──────────┐┌─┴─┐┌──────────┐  │                │              »
    q_1: |0>┤ U3(0,0,0) ├┤ U1(0) ├─░─┤ U2(0,pi) ├┤ X ├┤ U2(0,pi) ├──┼────────────────┼──────────────»
            ├───────────┤├───────┤ ░ ├──────────┤└───┘└──────────┘┌─┴─┐┌──────────┐  │  ┌──────────┐»
    q_2: |0>┤ U3(0,0,0) ├┤ U1(0) ├─░─┤ U2(0,pi) ├─────────────────┤ X ├┤ U2(0,pi) ├──┼──┤ U2(0,pi) ├»
            ├───────────┤├───────┤ ░ ├──────────┤                 └───┘└──────────┘┌─┴─┐├──────────┤»
    q_3: |0>┤ U3(0,0,0) ├┤ U1(0) ├─░─┤ U2(0,pi) ├──────────────────────────────────┤ X ├┤ U2(0,pi) ├»
            └───────────┘└───────┘ ░ └──────────┘                                  └───┘└──────────┘»
    «                                                                            ░ ┌───────────┐»
    «q_0: ───────────────────────────────────────────────────────────────────────░─┤ U3(0,0,0) ├»
    «                                                                            ░ ├───────────┤»
    «q_1: ─────■────────────────────■────────────────────────────────────────────░─┤ U3(0,0,0) ├»
    «        ┌─┴─┐    ┌──────────┐  │                                            ░ ├───────────┤»
    «q_2: ───┤ X ├────┤ U2(0,pi) ├──┼────────────────────────────■───────────────░─┤ U3(0,0,0) ├»
    «     ┌──┴───┴───┐└──────────┘┌─┴─┐┌──────────┐┌──────────┐┌─┴─┐┌──────────┐ ░ ├───────────┤»
    «q_3: ┤ U2(0,pi) ├────────────┤ X ├┤ U2(0,pi) ├┤ U2(0,pi) ├┤ X ├┤ U2(0,pi) ├─░─┤ U3(0,0,0) ├»
    «     └──────────┘            └───┘└──────────┘└──────────┘└───┘└──────────┘ ░ └───────────┘»
    «     ┌───────┐ ░
    «q_0: ┤ U1(0) ├─░─
    «     ├───────┤ ░
    «q_1: ┤ U1(0) ├─░─
    «     ├───────┤ ░
    «q_2: ┤ U1(0) ├─░─
    «     ├───────┤ ░
    «q_3: ┤ U1(0) ├─░─
    «     └───────┘ ░

Assume the depth setting is set to $d$. Then, RyRz has $n\times (d+1)\times 2$ parameters, Ry with linear entanglement has $2n\times(d + \frac{1}{2})$ parameters, and Ry with full entanglement has $d\times n\times \frac{(n + 1)}{2} + n$ parameters.

## VQE Implementation in Qiskit
This section illustrates an implementation of VQE using the programmatic approach. Qiskit Aqua also enables a declarative implementation, however, it reveals less information about the underlying algorithm. This code, specifically the preparation of qubit operators, is based on the code found in the Qiskit Tutorials repository (and as of July 2019, may be found at: https://github.com/Qiskit/qiskit-tutorials ).

The following libraries must first be imported.

```python
from qiskit.aqua.algorithms import VQE, ExactEigensolver
import matplotlib.pyplot as plt
%matplotlib inline
%config InlineBackend.figure_format = 'svg' # Makes the images look nice
import numpy as np
from qiskit.chemistry.components.variational_forms import UCCSD
from qiskit.chemistry.components.initial_states import HartreeFock
from qiskit.aqua.components.variational_forms import RYRZ
from qiskit.aqua.components.optimizers import COBYLA, SPSA, SLSQP
from qiskit.aqua.operators import Z2Symmetries
from qiskit import IBMQ, BasicAer, Aer
from qiskit.chemistry.drivers import PySCFDriver, UnitsType
from qiskit.chemistry import FermionicOperator
from qiskit import IBMQ
from qiskit.aqua import QuantumInstance
from qiskit.ignis.mitigation.measurement import CompleteMeasFitter
from qiskit.providers.aer.noise import NoiseModel
```

### Running VQE on a Statevector Simulator
We demonstrate the calculation of the ground state energy for LiH at various interatomic distances. A driver for the molecule must be created at each such distance. Note that in this experiment, to reduce the number of qubits used, we freeze the core and remove two unoccupied orbitals. First, we define a function that takes an interatomic distance and returns the appropriate qubit operator, $H$, as well as some other information about the operator.

```python
def get_qubit_op(dist):
    driver = PySCFDriver(atom="Li .0 .0 .0; H .0 .0 " + str(dist), unit=UnitsType.ANGSTROM,
                         charge=0, spin=0, basis='sto3g')
    molecule = driver.run()
    freeze_list = [0]
    remove_list = [-3, -2]
    repulsion_energy = molecule.nuclear_repulsion_energy
    num_particles = molecule.num_alpha + molecule.num_beta
    num_spin_orbitals = molecule.num_orbitals * 2
    remove_list = [x % molecule.num_orbitals for x in remove_list]
    freeze_list = [x % molecule.num_orbitals for x in freeze_list]
    remove_list = [x - len(freeze_list) for x in remove_list]
    remove_list += [x + molecule.num_orbitals - len(freeze_list)  for x in remove_list]
    freeze_list += [x + molecule.num_orbitals for x in freeze_list]
    ferOp = FermionicOperator(h1=molecule.one_body_integrals, h2=molecule.two_body_integrals)
    ferOp, energy_shift = ferOp.fermion_mode_freezing(freeze_list)
    num_spin_orbitals -= len(freeze_list)
    num_particles -= len(freeze_list)
    ferOp = ferOp.fermion_mode_elimination(remove_list)
    num_spin_orbitals -= len(remove_list)
    qubitOp = ferOp.mapping(map_type='parity', threshold=0.00000001)
    qubitOp = Z2Symmetries.two_qubit_reduction(qubitOp, num_particles)
    shift = energy_shift + repulsion_energy
    return qubitOp, num_particles, num_spin_orbitals, shift
```

First, the exact ground state energy is calculated using the qubit operator and a classical exact eigensolver. Subsequently, the initial state $|\psi\rangle$ is created, which the VQE instance uses to produce the final ansatz $\min_{\theta}(|\psi(\theta)\rangle)$. The exact result and the VQE result at each interatomic distance is stored. Observe that the result given by `vqe.run(backend)['energy'] + shift` is equivalent the quantity $\min_{\theta}\left(\langle \psi(\theta)|H|\psi(\theta)\rangle\right)$, where the minimum is not necessarily the global minimum.

When initializing the VQE instance with `VQE(qubitOp, var_form, optimizer, 'matrix')` the expectation value of $H$ on $|\psi(\theta)\rangle$ is directly calculated through matrix multiplication. However, when using an actual quantum device, or a true simulator such as the `qasm_simulator` with `VQE(qubitOp, var_form, optimizer, 'paulis')` the calculation of the expectation value is more complicated. A Hamiltonian may be represented as a sum of a Pauli strings, with each Pauli term acting on a qubit as specified by the mapping being used. Each Pauli string has a corresponding circuit appended to the circuit corresponding to $|\psi(\theta)\rangle$. Subsequently, each of these circuits is executed, and all of the results are used to determine the expectation value of $H$ on $|\psi(\theta)\rangle$. In the following example, we initialize the VQE instance with `matrix` mode, and so the expectation value is directly calculated through matrix multiplication.

Note that the following code snippet may take a few minutes to run to completion.

```python
backend = BasicAer.get_backend("statevector_simulator")
distances = np.arange(0.5, 4.0, 0.1)
exact_energies = []
vqe_energies = []
optimizer = SLSQP(maxiter=5)
for dist in distances:
    qubitOp, num_particles, num_spin_orbitals, shift = get_qubit_op(dist)
    result = ExactEigensolver(qubitOp).run()
    exact_energies.append(result['energy'] + shift)
    initial_state = HartreeFock(
        qubitOp.num_qubits,
        num_spin_orbitals,
        num_particles,
        'parity'
    )
    var_form = UCCSD(
        qubitOp.num_qubits,
        depth=1,
        num_orbitals=num_spin_orbitals,
        num_particles=num_particles,
        initial_state=initial_state,
        qubit_mapping='parity'
    )
    vqe = VQE(qubitOp, var_form, optimizer)
    results = vqe.run(backend)['energy'] + shift
    vqe_energies.append(results)
    print("Interatomic Distance:", np.round(dist, 2), "VQE Result:", results, "Exact Energy:", exact_energies[-1])

print("All energies have been calculated")
```

    Interatomic Distance: 0.5 VQE Result: -7.039710214022651 Exact Energy: -7.039732521635199
    Interatomic Distance: 0.6 VQE Result: -7.313344302875791 Exact Energy: -7.313345828761002
    Interatomic Distance: 0.7 VQE Result: -7.500921095664164 Exact Energy: -7.500922090905933
    Interatomic Distance: 0.8 VQE Result: -7.630976915015178 Exact Energy: -7.630978249333208
    Interatomic Distance: 0.9 VQE Result: -7.720810794782946 Exact Energy: -7.7208124121347765
    Interatomic Distance: 1.0 VQE Result: -7.7822406552139896 Exact Energy: -7.782242402637011
    Interatomic Distance: 1.1 VQE Result: -7.8235974930545105 Exact Energy: -7.8235992763628115
    Interatomic Distance: 1.2 VQE Result: -7.850696622684379 Exact Energy: -7.850698377596023
    Interatomic Distance: 1.3 VQE Result: -7.86756160202507 Exact Energy: -7.86756329011005
    Interatomic Distance: 1.4 VQE Result: -7.876999876501396 Exact Energy: -7.877001491818365
    Interatomic Distance: 1.5 VQE Result: -7.881014173828513 Exact Energy: -7.8810157156469955
    Interatomic Distance: 1.6 VQE Result: -7.881070663197581 Exact Energy: -7.881072044030919
    Interatomic Distance: 1.7 VQE Result: -7.878267162022751 Exact Energy: -7.878268167584995
    Interatomic Distance: 1.8 VQE Result: -7.873440112073885 Exact Energy: -7.873440293132823
    Interatomic Distance: 1.9 VQE Result: -7.867233666630739 Exact Energy: -7.8672339648160285
    Interatomic Distance: 2.0 VQE Result: -7.860152327951687 Exact Energy: -7.860153207378775
    Interatomic Distance: 2.1 VQE Result: -7.852595105557609 Exact Energy: -7.852595827876735
    Interatomic Distance: 2.2 VQE Result: -7.844878726138799 Exact Energy: -7.844879093009717
    Interatomic Distance: 2.3 VQE Result: -7.8372574395978996 Exact Energy: -7.837257967615504
    Interatomic Distance: 2.4 VQE Result: -7.829935044891065 Exact Energy: -7.82993700262339
    Interatomic Distance: 2.5 VQE Result: -7.823070191830957 Exact Energy: -7.8230766421340885
    Interatomic Distance: 2.6 VQE Result: -7.816782591567235 Exact Energy: -7.816795150472929
    Interatomic Distance: 2.7 VQE Result: -7.811153437932534 Exact Energy: -7.8111682848033706
    Interatomic Distance: 2.8 VQE Result: -7.806218297918316 Exact Energy: -7.806229560089847
    Interatomic Distance: 2.9 VQE Result: -7.801962398097704 Exact Energy: -7.8019736023325486
    Interatomic Distance: 3.0 VQE Result: -7.798352410789593 Exact Energy: -7.798363430915124
    Interatomic Distance: 3.1 VQE Result: -7.795326815962968 Exact Energy: -7.79534045163753
    Interatomic Distance: 3.2 VQE Result: -7.792800695646012 Exact Energy: -7.792834806738606
    Interatomic Distance: 3.3 VQE Result: -7.7906037936688435 Exact Energy: -7.790774009971015
    Interatomic Distance: 3.4 VQE Result: -7.788715355920877 Exact Energy: -7.789088897991478
    Interatomic Distance: 3.5 VQE Result: -7.7872157787821665 Exact Energy: -7.787716973466143
    Interatomic Distance: 3.6 VQE Result: -7.786080391119175 Exact Energy: -7.786603763673841
    Interatomic Distance: 3.7 VQE Result: -7.78520350042346 Exact Energy: -7.785702912499934
    Interatomic Distance: 3.8 VQE Result: -7.784479534655066 Exact Energy: -7.784975591697901
    Interatomic Distance: 3.9 VQE Result: -7.783853360515218 Exact Energy: -7.784389611670773
    All energies have been calculated

```python
# Note: If you experience BrokenProcessPool error in the next
#       chapter, delete this cell and re-run the notebook.
#       The error is due to a bug in qiskit and is being looked into.
plt.plot(distances, exact_energies, label="Exact Energy")
plt.plot(distances, vqe_energies, label="VQE Energy")
plt.xlabel('Atomic distance (Angstrom)')
plt.ylabel('Energy')
plt.legend()
plt.show()

```

Note that the VQE results are very close to the exact results, and so the exact energy curve is hidden by the VQE curve.

### Running VQE on a Noisy Simulator

Here, we calculate the ground state energy for H$_2$ using a noisy simulator and error mitigation.

First, we prepare the qubit operator representing the molecule's Hamiltonian:

```python
driver = PySCFDriver(atom='H .0 .0 -0.3625; H .0 .0 0.3625', unit=UnitsType.ANGSTROM, charge=0, spin=0, basis='sto3g')
molecule = driver.run()
num_particles = molecule.num_alpha + molecule.num_beta
qubitOp = FermionicOperator(h1=molecule.one_body_integrals, h2=molecule.two_body_integrals).mapping(map_type='parity')
qubitOp = Z2Symmetries.two_qubit_reduction(qubitOp, num_particles)
```

Now, we load a device coupling map and noise model from the IBMQ provider and create a quantum instance, enabling error mitigation:

```python
IBMQ.load_account()
provider = IBMQ.get_provider(hub='ibm-q')
backend = Aer.get_backend("qasm_simulator")
device = provider.get_backend("ibmqx2")
coupling_map = device.configuration().coupling_map
noise_model = NoiseModel.from_backend(device.properties())
quantum_instance = QuantumInstance(backend=backend, shots=1000,
                                   noise_model=noise_model,
                                   coupling_map=coupling_map,
                                   measurement_error_mitigation_cls=CompleteMeasFitter,
                                   cals_matrix_refresh_period=30,)
```

Finally, we must configure the optimizer, the variational form, and the VQE instance. As the effects of noise increase as the number of two qubit gates circuit depth increase, we use a heuristic variational form (RYRZ) rather than UCCSD as RYRZ has a much shallower circuit than UCCSD and uses substantially fewer two qubit gates.

The following code may take a few minutes to run to completion.

```python
exact_solution = ExactEigensolver(qubitOp).run()
print("Exact Result:", exact_solution['energy'])
optimizer = SPSA(max_trials=100)
var_form = RYRZ(qubitOp.num_qubits, depth=1, entanglement="linear")
vqe = VQE(qubitOp, var_form, optimizer=optimizer)
ret = vqe.run(quantum_instance)
print("VQE Result:", ret['energy'])
```

    Exact Result: -1.8671209783412634
    VQE Result: -1.8626866641642543

When noise mitigation is enabled, even though the result does not fall within chemical accuracy (defined as being within 0.0016 Hartree of the exact result), it is fairly close to the exact solution.

## Problems
1. You are given a Hamiltonian $H$ with the promise that its ground state is close to a maximally entangled $n$ qubit state. Explain which variational form (or forms) is likely to efficiently and accurately learn the ground state energy of $H$. You may also answer by creating your own variational form, and explaining why it is appropriate for use with this Hamiltonian.
2. Calculate the number of circuit evaluations performed per optimization iteration, when using the COBYLA optimizer, the `qasm_simulator` with 1000 shots, and a Hamiltonian with 60 Pauli strings.
3. Use VQE to estimate the ground state energy of BeH$_2$ with an interatomic distance of $1.3$Å. You may re-use the function `get_qubit_op(dist)` by replacing `atom="Li .0 .0 .0; H .0 .0 " + str(dist)` with `atom="Be .0 .0 .0; H .0 .0 -" + str(dist) + "; H .0 .0 " + str(dist)` and invoking the function with `get_qubit_op(1.3)`. Note that removing the unoccupied orbitals does not preserve chemical precision for this molecule. However, to get the number of qubits  required down to 6 (and thereby allowing efficient simulation on most laptops), the loss of precision is acceptable. While beyond the scope of this exercise, the interested reader may use qubit tapering operations to reduce the number of required qubits to 7, without losing any chemical precision.

## References
1. Peruzzo, Alberto, et al. "A variational eigenvalue solver on a photonic quantum processor." *Nature communications* 5 (2014): 4213.
2. Griffiths, David J., and Darrell F. Schroeter. Introduction to quantum mechanics. *Cambridge University Press*, 2018.
3. Shende, Vivek V., Igor L. Markov, and Stephen S. Bullock. "Minimal universal two-qubit cnot-based circuits." arXiv preprint quant-ph/0308033 (2003).
4. Kandala, Abhinav, et al. "Hardware-efficient variational quantum eigensolver for small molecules and quantum magnets." Nature 549.7671 (2017): 242.

```python
import qiskit
qiskit.__qiskit_version__
```

    {'qiskit-terra': '0.12.0',
     'qiskit-aer': '0.4.0',
     'qiskit-ignis': '0.2.0',
     'qiskit-ibmq-provider': '0.4.6',
     'qiskit-aqua': '0.6.4',
     'qiskit': '0.15.0'}

```python

```


## Arithmétique

### Nombre rationels

```python
# Étant donné un nombre rationnel trouver le plus petit ratio a/b permettant de l'exprimer

def plus_petit_ratio(nombre):
    b = 1
    a = 1
    while (a/b) != nombre:
        if (a/b) < nombre:
            a = a + 1
        elif (a/b) > nombre:
            b = b + 1
    return a, b

a, b = plus_petit_ratio(1.2376)

print(a)
print(b)
print(a/b)
```

    1547
    1250
    1.2376

### Nombre irrationnels

```python
# Comment les nombres irrationels sont représentés par l'ordinateur
# (nombre limité d'espace)
print(2**(1/2))

from math import pi
print(pi)

```

    1.4142135623730951
    3.141592653589793

```python
# modifier la fonction précédente pour qu'elle donne un message d'erreur dans le cas ou le nombre
# est irrationnel

nombre = pi

def plus_petit_ratio2(nombre):
    b = 1
    a = 1
    compteur = 0
    while (a/b) != nombre:
        compteur += 1
        if (a/b) < nombre:
            a = a + 1
        elif (a/b) > nombre:
            b = b + 1
        if compteur > 5000:
            print('Le nombre est probablement irrationnel')
            break
    return a, b

a, b = plus_petit_ratio2(nombre)

print(a)
print(b)
print(a/b)

```

    Le nombre est probablement irrationnel
    3795
    1208
    3.1415562913907285

```python
# Faire une fonction pour ordonner les nombre irrationnels (niveau 2)

def ordonner(nombre1, nombre2):
    if nombre1 > nombre2:
        print(' le nombre 1 est plus grand ')
    elif nombre1 < nombre2:
        print(' le nombre 2 est plus grand ')
    else:
        print(' les nombres sont égaux')

ordonner(pi, 2**(1/2))
```

     le nombre 1 est plus grand

### Ensembles de nombres

```python
# Exercice pour comprendre la fonction range() de python
from support import *

# L'ensemble des nombres naturels impairs compris entre 2 et 10.
print(range(3, 10, 2))

# L'ensemble des multiples de 4 compris entre  3 et  101.
print(range(4, 101, 4))
```

    [3, 5, 7, 9]
    [4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 48, 52, 56, 60, 64, 68, 72, 76, 80, 84, 88, 92, 96, 100]

```python
# Classifier les nombres selon à quel ensemble ils appartiennent
def plus_petit_ratio3(nombre):
    b = 1
    a = 1
    compteur = 0
    while (a/b) != nombre:
        compteur += 1
        if (a/b) < nombre:
            a = a + 1
        elif (a/b) > nombre:
            b = b + 1
        if compteur > 1000:
            return -1, -1
    return a, b

def classificateur(nombre):

    if (nombre // 1) == nombre:
        if nombre >= 0:
            print('je suis un entier positif dans N')
        elif nombre < 0:
            print('je suis un entier négatif dans Z')

    else:
        if nombre > 0:
            a, b = plus_petit_ratio3(nombre)
            if (a+b) == -2:
                print("Je suis un nombre irrationnel positif Q'")
            else:
                print('Je suis un nombre rationel positif Q')
        elif nombre < 0:
            a, b = plus_petit_ratio3(-nombre)
            if (a+b) == -2:
                print("Je suis un nombre irrationnel négatif Q'")
            else:
                print('Je suis un nombre rationel négatif Q')

```

    Je suis un nombre irrationnel positif Q'

### La notation exponentielle

```python
# Faire un calculateur d'exposants (niveau 1)
# Calcul les exposants de la forme (a/b)**(c/d)

def calculateur_exposant(a, b, c, d):
    return (a/b)**(c/d)

calculateur_exposant(1, 27, 1, 3)

# Changer la fonction pour être certain de pas avoir de résultat complexe
```

    0.33333333333333337

### Lois sur les exposants

```python
from sympy import symbols, Eq, solve
# niveau 2

x = symbols('x')

# Equation : 0.96^7 = 0.96^2 * 0.96^x
solve(Eq(0.96**7, 0.96**2 * 0.96**x))

solve(Eq(1.15**4 / 1.15**2, 1.15**x))
```

    [2.00000000000000]

### La notation scientifique

```python
# Crée une fonction qui imprime un nombre selon sa notation exponentielle

def expo_print(nombre, exposant):
    # Transformer en lettres
    nombre_lettres = str(nombre)
    # enlever le point
    nombre_lettres = nombre_lettres[0] + nombre_lettres[2:]
    # ajouter les zéros
    nombre_lettres = nombre_lettres + '0'*(exposant - len(nombre_lettres) + 1)

    print(nombre_lettres)

expo_print(6.022, 23)
```

    602200000000000000000000

### Les nombres cubiques et racines cubiques

```python
# Faire un programme qui donne les n premières racines cubiques

def racines_cubiques(n):
    racines_trouvees = 0
    nombre = 1

    while racines_trouvees < n:

        racine = nombre **(1/3)

        if abs(round(racine) -  racine) < 1e-6:
            print('Voici un nombre cubique : ', nombre)
            racines_trouvees = racines_trouvees + 1
            nombre = nombre + 1
        else:
            nombre = nombre + 1

```

### Algèbre

```python
# niveau 3

def intersection(a1, b1, a2, b2):
    if a1 == a2:
        if b1 == b2:
            print('droites parallèles confondues')
        else:
            print('droites parallèles disjointes')
    else:
        x = (b2 - b1) / (a1 - a2)
        y = a1*x + b1

        if a1*a2 == -1:
            print('droites perpendiculaires')
            print('intersection : (', x,',', y, ')')
        else:
            print('droites sécantes')
            print('intersection : (', x, ',',  y, ')')

```

### Factorisation

```python
from sympy import factor, symbols

m = symbols('m')

expression = 22*m - 55

factor(expression)

```

$\displaystyle 11 \left(2 m - 5\right)$

### Algèbre sec. 2

```python
x, y = symbols('x y')

3*x + 4*y - 2*x
```

$\displaystyle x + 4 y$

```python
from sympy import solve, Eq

x = symbols('x')

solve(Eq(2*x + 8, 16), x)
```

    [4]

### Fonctions imbriquées et géométrie

```python
def aire_prisme(hauteur, perimetre, aire_base):
    A_totale = perimetre*hauteur + 2*aire_base
    return A_totale

def aire_triangle(b, h):
    A = b*h /2
    return A

def aire_rectangle(b, h):
    return b*h

def perimetre_rectangle(b, h):
    return 2*b + 2*h

```

Aire d'un prisme à base rectangulaire h = 10, b = 3, a = 2

```python
h, b, a = 10, 3, 2

aire_base = aire_rectangle(b, a)
perimetre_base = perimetre_rectangle(b, a)
A_prisme = aire_prisme(h, perimetre_base, aire_base)
print('Aire du prisme : ', A_prisme)

```

    Aire du prisme :  112

```python

```

```python
from matplotlib.pyplot import plot as tracer
```

```python
tracer([-2, 0, 0.5, 2.1, 4])
```

### Fonctions

```python
def f(x):
    y = 1/x
    return y
```

```python
import numpy as np
import matplotlib.pyplot as plt

def plot_function(f):
    x = np.linspace(-1, 1)
    y = f(x)
    plt.plot(x, y)
```

```python
f1 = lambda x: 2*x + 1
f2 = lambda x: 2*x + 2
f3 = lambda x: 2*x + 2

plot_function(f1)
plot_function(f2)

```

```python

```


### A von Neumann growth model
##### and a generalization

This notebook uses the class  `neumann` to calculate key objects of a linear growth model of John von Neumann (1937) that was generalized by Kemeny, Moregenstern and Thompson (1956). Objects of interest are the maximal expansion rate (`alpha`), the interest factor (`beta`), and the optimal intensities (`x`) and prices (`p`).

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 notebook  shows how fruitfully to  employ the following important tools:

  * a zero-sum two player game

  * linear programming

  * the Perron-Frobenius theorem

```python
%matplotlib inline

import numpy as np
import matplotlib.pyplot as plt

np.set_printoptions(precision = 2)
```

The next few lines of code fetch the file `neumann.py` that does most of the calculations.

```python
from quantecon import fetch_nb_dependencies
fetch_nb_dependencies(["neumann.py"])
from neumann import neumann
```

-------
**Notation:** We use the following notation. $\mathbf{0}$ denotes a vector of zeros. We call an $n$-vector
- positive or $x\gg \mathbf{0}$ if $x_i>0$ for all $i=1,2,\dots,n$
- nonnegative or $x\geq \mathbf{0}$ if $x_i\geq 0$ for all $i=1,2,\dots,n$
- semipositive or $x > \mathbf{0}$ if $x\geq \mathbf{0}$ and $x\neq \mathbf{0}$

For two comformable vectors $x$ and $y$, $x\gg y$,  $x\geq y$ and $x> y$ mean $x-y\gg \mathbf{0}$,  $x-y \geq \mathbf{0}$, and $x-y > \mathbf{0}$.

By default, all vectors are column vectors, $x^{T}$ denotes the transpose of $x$ (i.e. a row vector). Let $\iota_n$ denote a column vector composed of $n$ ones, i.e. $\iota_n = (1,1,\dots,1)^T$. Let $e^i$ denote the vector (of arbitrary size) containing zeros except for the $i$th position where it is one.

We denote matrices by capital letters. For an arbitrary matrix $A$, $a_{i,j}$ represents the entry in its $i$th row and $j$th column.  $a_{\cdot j}$ and $a_{i\cdot}$ denote the $j$th column and $i$th row of  $A$, respectively.

------

## Model ingredients and assumptions:

A pair $(A,B)$ of $m\times n$ nonnegative matrices defines an economy.

  *  $m$ is the number of *activities* (or sectors)

  *  $n$ is the number of *goods* (produced and/or used in the economy)

  *  $A$ is called the *input matrix*;  $a_{i,j}$ denotes the amount of good $j$ consumed by activity $i$.

  * $B$ is called the *output matrix*; $b_{i,j}$ represents the amount of good $j$ produced by activity $i$.

Two key assumptions restrict economy $(A,B)$:
- **Assumption I:** (every good which is consumed is also produced)
    $$ b_{.,j} > \mathbf{0}\hspace{5mm}\forall j=1,2,\dots,n$$
- **Assumption II:** (no free lunch)
    $$ a_{i,.} > \mathbf{0}\hspace{5mm}\forall i=1,2,\dots,m$$

A semipositive $m$-vector $x$ denotes the levels at which activities are operated (_intensity vector_).

Therefore,

   *  vector $x^TA$ gives the total amount of *goods used in production*

   *  vector $x^TB$ gives *total outputs*

An  economy $(A,B)$ is said to be _productive_, if there exists a nonnegative intensity vector $x \geq 0$ such that $x^T B > x^TA$.

The semipositive $n$-vector $p$ contains prices assigned to the $n$ goods.

The $p$ vector implies *cost* and *revenue* vectors

   * the vector $Ap$ tells *costs* of the vector of activities

   * the vector  $Bp$ tells *revenues* from the vector of activities
-------------------------

A property of an input-output pair $(A,B)$ called *irreducibility* (or indecomposability) determines whether an economy can be decomposed into multiple ''sub-economies'':

**Definition:** Given an economy $(A,B)$, the set of goods $S\subset \{1,2,\dots,n\}$ is called an _independent subset_ if it is possible to produce every good in $S$ without consuming any good outside $S$. Formally, the set $S$ is independent if $\exists T\subset \{1,2,\dots,m\}$ (subset of activities) such that $a_{i,j}=0$, $\forall i\in T$ and $j\in S^c$ and for all $j\in S$, $\exists i\in T$, s.t. $b_{i,j}>0$. The economy is **irreducible** if there are no proper independent subsets.

We study two examples, both coming from Chapter 9.6 of Gale (1960).

```python
# (1) Irreducible (A, B) example: alpha_0 = beta_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: beta_0 < alpha_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

```python
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

```python
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

## Dynamic interpretation

Attach a time index $t$ to the preceding objects, regard aneconomy as a dynamic system, and study  sequences

$$\{(A_t,B_t)\}_{t\geq 0}, \hspace{1cm}\{x_t\}_{t\geq 0},\hspace{1cm} \{p_t\}_{t\geq 0}$$

An interesting special case holds the technology process constant and investigates the dynamics of quantities and prices only.

Accordingly, in the rest of this notebook we assume that $(A_t,B_t)=(A,B)$ for all $t\geq 0$.

-------
A crucial element of the dynamic iterpretation involves the timing of production.

We assume that production (consumption of inputs) takes place in period $t$, while the associated output materializes in period $t+1$, i.e. consumption of $x_{t}^TA$ in period $t$ results in $x^T_{t}B$ amounts of output in period $t+1$.

These timing conventions imply the following feasibiliy condition:

\begin{align}
x^T_{t}B \geq x^T_{t+1} A \hspace{1cm}\forall t\geq 1,
\end{align}

which asserts that no more goods can be used today than were produced yesterday.

Accordingly,  $Ap_t$ tells the costs of production in period $t$ and $Bp_t$ tells revenues in period $t+1$.

## Balanced growth

We follow John von Neumann in studying "balanced growth"

Let `./` denote elementwise division of one vector by another and let $\alpha >0$ be a scalar.

Then *balanced growth* is a situation in which

$$ x_{t+1}./x_t = \alpha , \quad \forall t \geq 0$$

With balanced growth, the law of motion of $x$ is evidently $ x_{t+1}=\alpha x_t $ and so we can rewrite the feasibility constraint as

$$x^T_{t}B \geq \alpha x^T_t A \hspace{1cm}\forall t$$

In the same spirit, define $\beta\in\mathbb{R}$ as the **interest factor** per unit of time.

We assume that it is always possible to earn a gross return equal to the constant interest factor $\beta$ by  investing "outside the model".

Under this assumption about outside investment opportunities, a no-arbitrage condition gives rise to the following (no profit) restriction on the price sequence:

$$\beta Ap_{t} \geq B p_{t} \hspace{1cm}\forall t$$

This says that production cannot yield a return greater than that offered by the investment opportunity (note that we compare values in period $t+1$).

The balanced growth assumption allows us to drop time subscripts and conduct an analysis purely in terms of a time-invariant growth rate $\alpha$ and interest factor $\beta$

## Duality

The following two problems are connected by a remarkable dual relationship between the technological and valuation characteristics of the economy:

**Definition:** The *technological expansion problem* (TEP) for economy $(A,B)$ is to find a semipositive $m$-vector $x>0$ and a number $\alpha\in\mathbb{R}$, s.t.

\begin{align*}
    &\max_{\alpha} \hspace{2mm} \alpha\\
    &\text{s.t. }\hspace{2mm}x^T B \geq \alpha x^T A
\end{align*}

Theorem 9.3 of David Gale's book assets that if Assumptions I and II are both satisfied, then a maximum value of $\alpha$ exists and it is positive. It is called the _technological expansion rate_ and is denoted by $\alpha_0$. The associated intensity vector $x_0$ is the _optimal intensity vector_.

--------------

**Definition:** The *economical expansion problem* (EEP) for $(A,B)$ is to find a semipositive $n$-vector $p>0$ and a number $\beta\in\mathbb{R}$, such that

\begin{align*}
    &\min_{\beta} \hspace{2mm} \beta\\
    &\text{s.t. }\hspace{2mm}Bp \leq \beta Ap
\end{align*}

Assumptions I and II imply existence of a minimum value $\beta_0>0$ called the _economic expansion rate_. The corresponding price vector $p_0$ is the _optimal price vector_.

---------------

Evidently, the criterion functions in  *technological expansion* problem and the *economical expansion problem*  are both linearly homogeneous, so the optimality of $x_0$ and $p_0$ are defined only up to a positive scale factor.

For simplicity (and to emphasize a close connection to zero-sum games), in the following, we normalize both vectors  $x_0$ and $p_0$ to have unit length.

A standard duality argument (see Lemma 9.4. in (Gale, 1960)) implies that under Assumptions I and II, $\beta_0\leq \alpha_0$

But in the other direction, that is $\beta_0\geq \alpha_0$, Assumptions I and II are not sufficient.

Nevertheless, von Neumann (1937) proved the following remarkable "duality-type" result connecting TEP and EEP.

**Theorem 1 (von Neumann):** If the economy $(A,B)$ satisfies Assumptions I and II, then there exists a set $\left(\gamma^{*}, x_0, p_0\right)$, where $\gamma^{*}\in[\beta_0, \alpha_0]\subset\mathbb{R}$, $x_0>0$ is an $m$-vector, $p_0>0$ is an $n$-vector and the following holds true

\begin{align*}
x_0^T B &\geq \gamma^{*} x_0^T A \\
Bp_0 &\leq \gamma^{*} Ap_0 \\
x_0^T\left(B-\gamma^{*} A\right)p_0 &= 0
\end{align*}

> *Proof (Sketch):* Assumption I and II imply that there exist $(\alpha_0, x_0)$ and $(\beta_0, p_0)$ solving the TEP and EEP, repspectively. If $\gamma^*>\alpha_0$, then by defintion of $\alpha_0$, there cannot exist a semipositive $x$ that satisfies $x^T B \geq \gamma^{*} x^T A$. Similarly, if $\gamma^*<\beta_0$, there is no semipositive $p$ so that $Bp \leq \gamma^{*} Ap$. Let $\gamma^{*}\in[\beta_0, \alpha_0]$, then $x_0^T B \geq \alpha_0 x_0^T A \geq \gamma^{*} x_0^T A$. Moreover, $Bp_0\leq \beta_0 A p_0\leq \gamma^* A p_0$. This two inequalities imply $x_0\left(B - \gamma^{*} A\right)p_0 = 0$.

Here the constant $\gamma^{*}$ is both expansion and interest factor (not neccessarily optimal). We have already encountered and discussed the first two inequalities that represent feasibility and no-profit conditions. Moreover, the equality compactly captures the requirements that if any good grows at a rate larger than $\gamma^{*}$ (i.e., if it is ''oversupplied''), then its price must be zero;  and that if any activity provides negative profit, it must be unused. Therefore, these expressions encode all equilbrium conditions and Theorem I essentially states that under Assumptions I and II there always exists an equilibrium $\left(\gamma^{*}, x_0, p_0\right)$ with balanced growth.

Note that Theorem I is silent about uniqueness of the equilibrium. In fact, it does not rule out (trivial) cases with $x_0^TBp_0 = 0$ so that nothing of value is produced. To exclude  such uninteresting cases, Kemeny, Morgenstern and Thomspson (1956) add an extra requirement

$$x^T_0 B p_0 > 0$$

and call the resulting equilibria *economic solutions*. They show that this extra condition does not affect the existence result, while it significantly reduces the number of (relevant) solutions.

----------------------------

## Interpretation as a game theoretic problem (two-player zero-sum game)

To compute the equilibrium $(\gamma^{*}, x_0, p_0)$, we follow the algorithm proposed by Hamburger, Thompson and Weil (1967), building on the key insight that the equilibrium (with balanced growth) can be considered as a solution of a particular two-player zero-sum game. First, we introduce some notations.

Consider the $m\times n$ matrix $C$ as a payoff matrix, with the entries representing payoffs from the **minimizing** column player to the **maximizing** row player and assume that the players can use mixed strategies:
- row player chooses the $m$-vector $x > \mathbf{0}$, s.t. $\iota_m^T x = 1$
- column player chooses the $n$-vector $p > \mathbf{0}$, s.t. $\iota_n^T p = 1$

**Definition:** The $m\times n$ matrix game $C$ has the *solution* $(x^*, p^*, V(C))$ in mixed strategies, if

\begin{align}
(x^*)^T C e^j \geq V(C)\quad \forall j\in\{1, \dots, n\}\quad \quad \text{and}\quad\quad (e^i)^T C p^* \leq V(C)\quad \forall i\in\{1, \dots, m\}
\end{align}

The number $V(C)$ is called the *value* of the game.

From the above definition, it is clear that the value $V(C)$ has two alternative interpretations:
* by playing the appropriate mixed stategy, the maximizing player can assure himself at least $V(C)$ (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 $V(C)$ (irrespective of what is the maximizing player's choice)

From the famous theorem of Nash (1951), it follows that there always exists a mixed strategy Nash equilibrium for any *finite* two-player zero-sum game. Moreover, von Neumann's Minmax Theorem (1928) implies that

$$V(C) = \max_x \min_p \hspace{2mm} x^T C p = \min_p \max_x \hspace{2mm} x^T C p = (x^*)^T C p^*$$

### Connection with Linear Programming (LP)

Finding Nash equilibria of a finite two-player zero-sum game can be formulated as a linear programming problem. To see this, we introduce the following notation
- For a fixed $x$, let $v$ be the value of the minimization problem:  $v \equiv \min_p x^T C p = \min_j x^T C e^j$
- For a fixed $p$, let $u$ be the value of the maximization problem:  $u \equiv \max_x x^T C p = \max_i (e^i)^T C p$

Then the *max-min problem* (the game from the maximizing player's point of view) can be written as the *primal* LP

\begin{align}
V(C) = & \max \hspace{2mm} v \\
\text{s.t. } \hspace{2mm} v \iota_n^T &\leq x^T C  \\
x &\geq \mathbf{0} \\
\iota_n^T x & = 1
\end{align}

while the *min-max problem* (the game from the minimizing player's point of view) is the *dual* LP

\begin{align}
V(C) = &\min \hspace{2mm} u \\
\text{s.t. } \hspace{2mm}u \iota_m &\geq Cp  \\
p &\geq \mathbf{0} \\
\iota_m^T p & = 1
\end{align}

---------------------------------------------------------

Hamburger, Thompson and Weil (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

$$V(-A) < 0\quad\quad \text{and}\quad\quad V(B)>0$$

> *Proof (Sketch)*:
* $\Rightarrow$ $V(B)>0$ implies $x_0^T B \gg \mathbf{0}$, where $x_0$ is a maximizing vector. Since $B$ is non-negative, this requires that each column of $B$ has at least one positive entry, which is Assumption I.
* $\Leftarrow$ From Assumption I and the fact that $p>\mathbf{0}$, it follows that $Bp > \mathbf{0}$. This implies that the maximizing player can always choose $x$ so that $x^TBp>0$, that is it must be the case that $V(B)>0$.

In order to (re)state Theorem I in terms of a particular two-player zero-sum game, we define the matrix for $\gamma\in\mathbb{R}$

$$M(\gamma) \equiv B - \gamma A$$

For fixed $\gamma$, treating $M(\gamma)$ as a matrix game, we can calculate the solution of the game.

* If $\gamma > \alpha_0$, then for all $x>0$, there $\exists j\in\{1, \dots, n\}$, s.t. $[x^T M(\gamma)]_j < 0$ implying that $V(M(\gamma)) < 0$
* If $\gamma < \beta_0$, then for all $p>0$, there $\exists i\in\{1, \dots, m\}$, s.t. $[M(\gamma)p]_i > 0$ implying that $V(M(\gamma)) > 0$
* If $\gamma \in \{\beta_0, \alpha_0\}$, then (by Theorem I) the optimal intensity and price vectors $x_0$ and $p_0$ satisfy

\begin{align}
x_0^T M(\gamma) \geq \mathbf{0}^T \quad \quad \text{and}\quad\quad M(\gamma) p_0 \leq \mathbf{0}
\end{align}

That is, $(x_0, p_0, 0)$ is a solution of the game $M(\gamma)$ so that $V\left(M(\beta_0)\right) = V\left(M(\alpha_0)\right) = 0$.
* If $\beta_0 < \alpha_0$ and $\gamma \in (\beta_0, \alpha_0)$, then $V(M(\gamma)) = 0$. Moreover, if $x'$ is optimal for the maximizing player in $M(\gamma')$ for $\gamma'\in(\beta_0, \alpha_0)$ and $p''$ is optimal for the minimizing player in $M(\gamma'')$ where $\gamma''\in(\beta_0, \gamma')$, then $(x', p'', 0)$ is a solution for $M(\gamma)$, $\forall \gamma\in (\gamma'', \gamma')$.
> *Proof (Sketch):* If $x'$ is optimal for a maximizing player in game $M(\gamma')$, then $(x')^T M(\gamma')\geq \mathbf{0}^T$ and so for all $\gamma<\gamma'$
$$(x')^T M(\gamma) = (x')^T M(\gamma') + (x')^T(\gamma' - \gamma)A \geq \mathbf{0}^T $$
hence $V(M(\gamma))\geq 0$. If $p''$ is optimal for a minimizing player in game $M(\gamma'')$, then $ M(\gamma'')p''\leq \mathbf{0}$ and so for all $\gamma''<\gamma$
$$ M(\gamma)p'' = M(\gamma'') + (\gamma'' - \gamma)Ap'' \leq \mathbf{0}$$
hence $V(M(\gamma))\leq 0$.

It is clear from the above argument that $\beta_0$, $\alpha_0$ are the minimal and maximal $\gamma$ for which $V(M(\gamma))=0$. Moreover, Hamburger et al. (1967) show that the function $\gamma \mapsto V(M(\gamma))$ is continuous and nonincreasing in $\gamma$. This suggests an algorithm to compute $(\alpha_0, x_0)$ and $(\beta_0, p_0)$ for a given input-output pair $(A, B)$.

## Algorithm

Hamburger, Thompson and Weil (1967) propose a simple bisection algorithm to find the minimal and maximal roots (i.e. $\beta_0$ and $\alpha_0$) of the function $\gamma \mapsto V(M(\gamma))$.

### Step 1

First, notice that we can easily find trivial upper and lower bounds for $\alpha_0$ and $\beta_0$.
 * TEP requires that $x^T(B-\alpha A)\geq \mathbf{0}^T$ and $x > \mathbf{0}$, so if $\alpha$ is so large that $\max_i\{[(B-\alpha A)\iota_n]_i\} < 0$, then TEP ceases to have a solution. Accordingly, let **`UB`** be the $\alpha^{*}$ that solves $\max_i\{[(B-\alpha^{*} A)\iota_n]_i\} = 0$.
 * Similar to the upper bound, if $\beta$ is so low that $\min_j\{[\iota^T_m(B-\beta A)]_j\}>0$, then the EEP has no solution and so we can define **`LB`** as the $\beta^{*}$ that solves $\min_j\{[\iota^T_m(B-\beta^{*} A)]_j\}=0$.

The `bounds` method calculates these trivial bounds for us

```python
N1.bounds()
```

    (1.0, 2.0)

### Step 2

Compute $\alpha_0$ and $\beta_0$

* Finding $\alpha_0$
    1. Fix $\gamma = \frac{UB + LB}{2}$ and compute the solution of the two-player zero-sum game associated with $M(\gamma)$. We can use either the primal or the dual LP problem.
    2. If $V(M(\gamma)) \geq 0$, then set $LB = \gamma$, otherwise let $UB = \gamma$
    3. Iterate on 1. and 2. until $|UB - LB| < \epsilon$

* Finding $\beta_0$
    1. Fix $\gamma = \frac{UB + LB}{2}$ and compute the solution of the two-player zero-sum game associated with $M(\gamma)$. We can use either the primal or the dual LP problem.
    2. If $V(M(\gamma)) > 0$, then set $LB = \gamma$, otherwise let $UB = \gamma$
    3. Iterate on 1. and 2. until $|UB - LB| < \epsilon$

>*Existence*: Since $V(M(LB))>0$ and $V(M(UB))<0$ and $V(M(\cdot))$ is a continuous, nonincreasing function, there is at least one $\gamma\in[LB, UB]$, s.t. $V(M(\gamma))=0$.

The `zerosum` method calculates the value and optimal strategies assocaited with a given $\gamma$

```python
gamma = 2

print('Value of the game with gamma = {}'.format(gamma))
print(N1.zerosum(gamma = gamma)[0])
print('Intensity vector (from the primal)')
print(N1.zerosum(gamma = gamma)[1])
print('Price vector (from the dual)')
print(N1.zerosum(gamma = gamma, dual = True)[1])
```

    Value of the game with gamma = 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.  ]

```python
numb_grid = 100
gamma_grid = np.linspace(0.4, 2.1, numb_grid)

value_ex1_grid = np.asarray([N1.zerosum(gamma = gamma_grid[i])[0] for i in range(numb_grid)])
value_ex2_grid = np.asarray([N2.zerosum(gamma = gamma_grid[i])[0] for i in range(numb_grid)])

fig, ax = plt.subplots(1, 2, figsize = (14, 5), sharey = True)
fig.suptitle(r'The function $V(M(\gamma))$', fontsize = 16)

ax[0].plot(gamma_grid, value_ex1_grid, lw = 2)
ax[0].set_title(r'Example 1', fontsize = 15)
ax[0].axhline(0, color = 'k', lw =1)
ax[0].set_xlabel(r'$\gamma$', fontsize = 14)
ax[0].axvline(N1.bounds()[0], color = 'r', lw = 1, linestyle = '--', label = 'lower bound')
ax[0].axvline(N1.bounds()[1], color = 'g', lw = 1, linestyle = '--', label = 'upper bound')
ax[0].legend(loc = 'best')

ax[1].plot(gamma_grid, value_ex2_grid, lw = 2)
ax[1].set_title(r'Example 2', fontsize = 15)
ax[1].axhline(0, color = 'k', lw =1)
ax[1].set_xlabel(r'$\gamma$', fontsize = 14)
ax[1].axvline(N2.bounds()[0], color = 'r', lw = 1, linestyle = '--', label = 'lower bound')
ax[1].axvline(N2.bounds()[1], color = 'g', lw = 1, linestyle = '--', label = 'upper bound')
ax[1].legend(loc = 'best')
```

The `expansion` method implements the bisection algorithm for $\alpha_0$ (and uses the primal LP problem for $x_0$)

```python
alpha0, x, p = N1.expansion()
print('alpha_0 = {}'.format(alpha0))
print('x_0 = {}'.format(x))
print('The corresponding p from the dual = {}'.format(p))
```

    alpha_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 $\beta_0$ (and uses the dual LP problem for $p_0$)

```python
beta0, x, p = N1.interest()
print('beta_0 = {}'.format(beta0))
print('p_0 = {}'.format(p))
print('The corresponding x from the primal = {}'.format(x))
```

    beta_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 $\gamma^*$ is unique, it is irrelevant which one of the two methods we use. In particular, as will be shown below, in case of an irreducible $(A,B)$ (like in Example 1), the maximal and minimal roots of $V(M(\gamma))$ necessarily coincide implying a ''full duality'' result, i.e. $\alpha_0 = \beta_0 = \gamma^*$, and that the expansion (and interest) rate $\gamma^*$ is unique.

## Uniqueness and irreducibility

As an illustration, compute first the maximal and minimal roots of $V(M(\cdot))$ for Example 2, which displays a reducible input-output pair $(A, B)$.

```python
alpha0, x, p = N2.expansion()
print('alpha_0 = {}'.format(alpha0))
print('x_0 = {}'.format(x))
print('The corresponding p from the dual = {}'.format(p))
```

    alpha_0 = 1.2528658034279943
    x_0 = [ 0.    0.    0.33  0.26  0.41]
    The corresponding p from the dual = [ 0.56  0.44  0.    0.    0.    0.  ]

```python
beta0, x, p = N2.interest()
print('beta_0 = {}'.format(beta0))
print('p_0 = {}'.format(p))
print('The corresponding x from the primal = {}'.format(x))
```

    beta_0 = 1.0000000009313226
    p_0 = [ 0.5  0.5  0.   0.   0.   0. ]
    The corresponding x from the primal = [ 0.33  0.33  0.33  0.    0.  ]

As we can see, with a reducible $(A,B)$, the roots found by the bisection alhorithms might differ, so there might be multiple $\gamma^*$ that make the value of the game with $M(\gamma^*)$ zero. (see the figure above)

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 $\gamma^*$ that satisfy $V(M(\gamma^*)) = 0$ and $x_0^TBp_0 > 0$ and that for each such $\gamma^*_i$, there is a self-sufficient part of the economy (a sub-economy) that in equilibrium can expand independently  with the expansion coefficient $\gamma^*_i$.

The following theorem (see Theorem 9.10. in Gale, 1960) asserts that imposing irreducibility is sufficient for uniqueness of $(\gamma^*, x_0, p_0)$.

**Theorem II:** Consider the conditions of Theorem 1. If the economy $(A,B)$ is irreducible, then $\gamma^*=\alpha_0=\beta_0$.

## A special case

There is a special $(A,B)$ that allows us to simplify the solution method significantly by invoking the powerful Perron-Frobenius theorem for nonnegative matrices.

**Definition:** We call an economy _simple_ if it satisfies
 1. $n=m$
 2. Each activity produces exactly one good
 3. Each good is produced by one and only one activity

These assumptions imply that $B=I_n$, i.e., that $B$ can be written as an identity matrix (possibly after reshuffling its rows and columns).

The simple model has the following special property (Theorem 9.11. in Gale): if $x_0$ and $\alpha_0>0$ solve the TEP with $(A,I_n)$, then

$$x_0^T = \alpha_0 x_0^T A\hspace{1cm}\Leftrightarrow\hspace{1cm}x_0^T A=\left(\frac{1}{\alpha_0}\right)x_0^T$$

The latter shows that $1/\alpha_0$ is a positive eigenvalue of $A$ and $x_0$ is the correponding nonnegative left eigenvector. The classical result of **Perron and Frobenius** implies that a nonnegative matrix always has a nonnegative eigenvalue-eigenvector pair. Moreover, if $A$ is irreducible, then the optimal intensity vector $x_0$ is positive and _unique_ up to multiplication by a positive scalar.

Suppose that $A$ is reducible with $k$ irreducible subsets $S_1,\dots,S_k$. Let $A_i$ be the submatrix corresponding to $S_i$ and let $\alpha_i$ and $\beta_i$ be the associated expansion and interest factors, respectively. Then we have

$$\alpha_0 = \max_i \{\alpha_i\}\hspace{1cm}\text{and}\hspace{1cm}\beta_0 = \min_i \{\beta_i\}$$

#### References

Gale, David, **The Theory of Linear Economic Models**, New York: McGraw-Hill Book Company, 1960.

Hamburger, Michael J.,  Gerald L. Thompson and Roman L. Weil, Jr., 1967, "Computation of Expansion Rates for the Generalized von Neumann Model of an Expanding Economy", Econometrica Vol. 35, No. 3/4,  pp. 542-547

Kemeny, John G.; Morgenstern, Oskar; Thompson, Gerald L., 1956, "A generalization of the von Neumann model of an expanding economy". Econometrica. 24. pp. 115–135.

Nash, John. 1951. "Non-Cooperative Games." Annals of Mathematics, Second Series, 54, no. 2, pp 286-95.

von Neumann, John, 1928, "Zur theories der gesellschaftsspiele". Mathematische Annalen 100, pp 295–320. English translation by S. Bergmann in Contributions to the Theory of Games IV, ed. R.D. Luce and A.W. Tucker. Princeton: Princeton University Press, 1959

von Neumann, John, 1937, "Über ein ökonomisches Gleichuns-system und eine Verallgeneinerung des Brouwerschen Fixpunksatzes," Ergebnisse eines mathematischen Kolloquium, 8, , pp. 73-83, translated as "A Model of General Economic Equilibrium," Review of Economic Studies, 13. 1945, pp. 1-9.

```python

```