Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Production Networks

University of Central Florida
Valorum Data

Prerequisites

  • Introduction to Graphs

  • Weighted Graphs

Outcomes

  • Recall the key concepts of spectral theory from Linear Algebra

  • Desribe the key proprties of the Leintief family of production models

  • Explain the difference between “in”-based centrality and “out”-based centrality

  • Analyze the impact of sector specific shocks on other sectors of the US economy

References

Linear Algebra

  • Linear algebra is the backbone of modern computational algorithms

    • Graphics

    • Statistics

    • Data analysis

    • Optimization

    • Machine Learning

  • Workhorse for accelerated computing

    • GPUs work on matrices to efficiently parallellize common computations

    • Specialty hardware like the TPU (tensor processing unit) take this even further

Building Blocks

  • Vectors: arrays of numbers representing points in multi-dimensional space

  • Matrices

    • Rectangular arrays that transform vectors

    • Also used to represent certain datasets/relationships: e.g. adjacency matrix in Graph Theory

  • Tensors: higher dimensional collections of numbers that allow high-dimensional

    • Vectors are 1d tensors, matrices 2d tensors, etc.

    • Implemented in numpy as np.array and Julia as the build in array

Some Key Theories

  1. Linear systems of equations

  2. Inner product spaces (length, distance, and angles)

  3. Eigenvalues and eigen vectors (spectral theory)

Spectral Theory

  • Let’s dig into eigenvalues and eigenvectors

  • Let A∈Rn×nA \in \mathbb{R}^{n \times n}

  • A scalar λ∈C\lambda \in \mathbb{C} is an eigenvalue of AA if there exists a nonzero vector e∈Cne \in \mathbb{C}^n such that Ae=λeA e = \lambda e

  • A vector ee satisfying this equality is called an eigenvector corresponding to the eigenvalue λ\lambda

  • A vector ϵ\epsilon sastifying A′ϵ=λϵA ' \epsilon = \lambda \epsilon is a left-eigenvector of AA

using LinearAlgebra
A = [
    0 -1
    1 0
]
eigvals(A)
2-element Vector{ComplexF64}: 0.0 - 1.0im 0.0 + 1.0im

Definitions

  • The set of all eigenvalues of AA is called the spectrum of AA and is written σ(A)\sigma(A)

  • The spectral radius of AA is written r(A)≡max⁡{∣λ∣:λ∈σ(A)}r(A) \equiv \max \{ | \lambda | : \lambda \in \sigma(A) \}

    • The spectral radius important when considering the convergence of a dynamic system driven by AA

    • If xt+1=Axtx_{t+1} = A x_t and r(A)<1r(A)< 1 then the sequence {x}t\{x\}_t is finite and will converge

  • Some facts (not proven here)

    • AA will have at most nn distinct eigenvalues

    • Eigenvectors are only unique to a scalar multiple: if (λ,e)(\lambda, e) is an eigenpair, then so is (λ,αe)(\lambda, \alpha e) for any α∈R,α≠0\alpha \in \mathbb{R}, \alpha \ne 0

  • Sometimes a matrix will have a repeated eigenvalue (the same value works with multiple distinct eigenvectors)

  • The algebraic multiplicity of an eigenvalue is the number of times it is repeated with distinct eigenvectors

  • An eigenvalue with an algebraic multiplicity of one is called simple

Diagonalization

  • A matrix A∈Rn×nA \in \mathbb{R}^{n \times n} is diagonalizable if A=PDP−1A = PDP^{-1}

    • D=diag(λ1,…,λn)D = \text{diag}(\lambda_1, \dots, \lambda_n), PP is some invertible matrix

  • The decomposition is called the eigen decomposition or spectral decomposition of AA

  • The asymptotic properties of m↦Amm \mapsto A^m are determined by σ(A)\sigma(A)

    • Can be seen when AA is diagonalizable

    • If A=Pdiag(λ)P−1⟹Am=Pdiag(λim)P−1A = P \text{diag}(\lambda) P^{-1} \Longrightarrow A^m = P \text{diag}(\lambda_i^m) P^{-1}

Example: Worker Dynamics

  • Consider a continuum of workers (large number, not counted discretely)

  • Workers can be in one of two states: (1) employed and (2) unemployed

  • Each month employed workers become unemployed at rate α\alpha, while unemployed workers are hired at rate β\beta

  • We express these dynamics as a weighted directed graph with adjacency matrix:

Pw=[1−ααβ1−β]  α,β∈[0,1]P_w = \begin{bmatrix}1-\alpha & \alpha \\ \beta & 1-\beta \end{bmatrix} \; \alpha, \beta \in [0, 1]
  • Row 1 gives the probabilities of employment and unemployement for an employed worker

  • Row 2 gives the probabilities of employment and unemployement for an unemployed worker

Transitions

  • Suppose we have a vector x∈Δ(2)x \in \Delta(2) (2 element simplex) representing current fraction of workers that are employed and unemployed

  • Question... What matrix operation between PwP_w and xx will give the fraction of workers that start next month as employed and unemployed?

alpha = 0.3
beta = 0.1

P = [1-alpha alpha; beta 1-beta]
2×2 Matrix{Float64}: 0.7 0.3 0.1 0.9
x = [0.9, 0.1]
2-element Vector{Float64}: 0.9 0.1
# TODO: simulate for many periods
# TODO: compare to largest left eigenvector

The simulation should reveal the pattern summarized in Figure 1: repeated transitions move the population toward a steady state.

Worker transitions, the population update, and convergence to the dominant left eigenvector.

Figure 1:Worker transitions, the population update, and convergence to the dominant left eigenvector.

Neumann Series Lemma

  • We need one more linear alegbra result...

  • If A∈Rn×nA \in \mathbb{R}^{n \times n} and r(A)<1r(A) < 1, then I−AI-A is non-singular and

    (I−A)−1=∑m=0∞Am(I-A)^{-1} = \sum_{m=0}^{\infty} A^m
  • This is known as the Neumann series lemma

Production Networks

  • We will now study a family of economic models that allow us to analyze the economy as a collection of sectors

  • These models were proposed by nobel prize winner Wassily Leontief in 1941 and are still commonly used today

  • The key idea behind a Leontief model is the input-output table

Input-output tables

  • Firms (companies) are categorized into nn distinct sectors

  • Firms use materials produced by other sectors as part of their production process

  • The relationship of flows of value are organized into an input/output table

sector 1sector 2sector 3
sector 1a11a_{11}a12a_{12}a13a_{13}
sector 2a21a_{21}a22a_{22}a23a_{23}
sector 3a31a_{31}a32a_{32}a33a_{33}
  • aija_{ij} is called an input-output coefficient and is equal to:

aij=value of sector j inputs purchased from sector itotal sales of sector ja_{ij} = \frac{\text{value of sector j inputs purchased from sector i}}{\text{total sales of sector j}}

Figure 2 connects this coefficient to both the production network and its matrix representation.

A coefficient a_{ij} records inputs flowing from supplier i to buyer j, normalized by buyer j’s total sales.

Figure 2:A coefficient aija_{ij} records inputs flowing from supplier ii to buyer jj, normalized by buyer jj’s total sales.

  • If aija_{ij} is large, sector ii is an important supplier of intermediate goods to sector jj

  • The sum of column jj is the value of all inputs to sector jj

  • Row ii shows how intensively each sector uses good ii as an intermediate good

  • The input output table can be directly used as the adjacency matrix for a weighted directed graph

Input-output data

  • In the United States, the Bureau of Economic Analysis is responsible for compiling input-output tables for sectors

  • We’ll be studying the Input-Output Accounts Data

  • The main set of tables we’ll be using are called the Make-Use tables

  • The Make table shows the value of final goods produced by each sector

    • Note this is predominately a diagonal matrix as each sector primarily produces and sells final goods within their sector

    • Sometimes a firm will have secondary outputs as part of their production process

    • “Which industries produce which commodities?”

  • The Use tables show the intermediate and final use of commodities across sectors

    • “Who uses or consumes the commodities produced?”

using CSV, DataFrames, Graphs, SimpleWeightedGraphs, GraphPlot, ColorSchemes, Colors, Downloads, PlotlyBase
using Downloads

function read_remote_csv(url)
    bn = basename(url)
    if !isfile(bn)
        Downloads.download(url, bn)
    end
    CSV.read(bn, DataFrame)
end

make_15 = read_remote_csv("https://compsosci-resources.s3.amazonaws.com/graph-theory-lectures/data/QE-networks/make_15.csv");
use_15 = read_remote_csv("https://compsosci-resources.s3.amazonaws.com/graph-theory-lectures/data/QE-networks/use_15.csv");
codes = read_remote_csv("https://compsosci-resources.s3.amazonaws.com/graph-theory-lectures/data/QE-networks/codes.csv");

Example: US 15 sector data

Input-Output Graph
  • Color of nodes is according to their hub-based eigenvector centrality (see below)

  • Size of nodes is according to their total outputs (make table)

  • Thickness of edges is according to amount of goods

  • Represent aija_{ij} and point from sector creating the intermediate good (sector ii) to sector using intermediate good (sector jj)

Eigenvector Centrality

  • The node size above shows the hub-based eigenvector centrality

  • This is equal to the dominant eigenvector of the adjacency matrix (eigenvector associated with largest eigenvalue)

  • This measure of centrality measures the influence of a node in a network

Example

  • Let’s compute the eigenvector centrality for the data in the image above

  • I have some code to import the make/use files into a helpful struct

  • We’ll see what all the fields are as we progress through the lecture

to_int(x) = parse(Int, x)
to_int(x::Integer) = Int(x)

struct SectorData
    Z::Matrix{Int}
    X::Vector{Int}
    A::Matrix{Float64}
    F::Matrix{Float64}
    Z_df::DataFrame
    names::Vector{String}
    codes::Vector{String}
    N::Int
    G::SimpleWeightedDiGraph{Int64, Float64}
end

const CODES = let
    df = read_remote_csv("codes.csv")
    Dict(zip(df.name, df.code))
end

function load_sector(N)
    # read csv
    df = CSV.read("use_$(N).csv", DataFrame)

    # replace `---` with `0`
    df .= ifelse.(df .== "---", "0", df)

    # conver to int
    df[!, 2:end] .= to_int.(df[!, 2:end])

    # first column is industry name, next columns are sector values
    # first N rows are values
    Z = Array(df[1:N, 2:(N+1)])
    names = df[1:N, 1]
    codes = [CODES[n] for n in names]

    # total industry outputs come from teh `make_N.csv` file
    X = CSV.read("make_$(N).csv", DataFrame)[1:N, "Total Industry Output"]

    # value of sector j's inputs purchased from i / sales of `j`
    # or ...
    A = Z ./ X'

    F = Z ./ X

    # make copy of A where small values are set to zero to make plotting clearer
    A_copy = copy(A)
    A_copy[A .<= 0.01] .= 0;
    G = SimpleWeightedDiGraph(A_copy)

    SectorData(Z, X, A, F, df, names, codes, N, G)
end
load_sector (generic function with 1 method)
data15 = load_sector(15);
lambda15 = eigenvector_centrality(data15.G)
15-element Vector{Float64}: 0.20015876206170186 0.15938537489092003 0.024488792126254755 0.01655605810501688 0.863307958233566 4.7179251777605e-17 9.919510282578063e-17 0.0160904862243379 0.03706307448992309 0.2744196179986345 0.33276423164786867 1.5167914026463714e-5 0.026176029930141512 0.0014956985704648131 0.00042398130095220904
Plot(bar(x=data15.codes, y=lambda15))
Loading...

Accounting

  • Let...

    • did_i be the final consumer demand for good ii

    • xix_i be total sales of sector ii

    • zijz_{ij} be inter-industry sales from sector ii to sector jj

Figure 3 shows how these quantities fit together before we write the accounting identity in matrix form.

A sector’s sales split between intermediate sales and final demand; stacking the identities across sectors gives x=Ax+d.

Figure 3:A sector’s sales split between intermediate sales and final demand; stacking the identities across sectors gives x=Ax+dx=Ax+d.

  • For accounts to add up we must have

    xi=∑j=1nzij+dix_i = \sum_{j=1}^n z_{ij} + d_i
  • Notice that

    zijxj=aij=dollar value of inputs from i per dollar output from j\frac{z_{ij}}{x_j} = a_{ij} = \text{dollar value of inputs from $i$ per dollar output from $j$}
  • This means

    xi=∑j=1aijxj+dix_i = \sum_{j=1} a_{ij} x_j + d_i
  • Which can be written x=Ax+dx = Ax + d

Equilibrium

  • An equilibrium in a Leonteif model with input-output coefficient matrix AA and a vector d∈Rnd \in \mathbb{R}^n of final consumer demands for each sector is a vector xx for sector-specific output such that x=Ax+dx = Ax + d is satisfied

    • Note that dd and AA are treated as given

  • To find this vector requires that we trace the impact of final demand on the intermediate linkages through AA

  • Example:

Simple production network
  • Example

    • Suppose d3↑d_3 \uparrow

    • Will cause 3 to consume more from its suppliers (2 and 4)

    • Which will cause 2 to demand more from 1

    • Which will cause 1 to demand more from 2 and 4

    • ... and so on

  • Computing an equilibrium is not entirely straight forward...

Computing an Equilibrium

  • Define vj≡xj−∑i=1nzijv_j \equiv x_j - \sum_{i=1}^n z_{ij} as the value added by sector jj

  • Assumption: The input-output adjacency matrix AA satisfies

    ηj≡∑i=1naij<1  ∀j∈[n]\eta_j \equiv \sum_{i=1}^n a_{ij} < 1 \; \forall j \in [n]
    • This holds whenever vj>0∀jv_j > 0 \forall j

  • When this assumption holds, for each d≥0d \ge 0 there is a unique, nonnegative output solution given by

    x∗=Ldwhere L≡(I−A)−1x^* = Ld \qquad \text{where } L \equiv (I-A)^{-1}
  • The matrix LL is called the Leontief inverse associated with the coefficient matrix AA

L15 = inv(I - data15.A)

# propose a demand vector where each element is between [100, 600]
d = rand(15).* 500 .+ 100

L15 * d
15-element Vector{Float64}: 529.5876385695651 539.6471159147281 504.4562459246958 213.10961424757355 1684.102232985288 490.039956568086 363.5297064550896 418.5497166984117 443.79727522648716 750.0649628686189 1149.3512051482644 303.1862869553239 192.75063112958023 476.7757695206679 337.41500397173866

Question

We don’t have a field for d in our SectorData struct.

How could we compute/derive d given the fields we do have?

fieldnames(SectorData)
(:Z, :X, :A, :F, :Z_df, :names, :codes, :N, :G)

Demand Shocks

  • A common form of economic analysis is to consider the impact of external “shocks”

  • Often, these are modeled as a one time change in the value of a variable

  • Let’s consider a demand shock of size Δd\Delta d such that demand moves from d0d_0 to d1=Δd+d0d_1 = \Delta d + d_0

  • The equilibrium vector shifts from x0=Ld0x_0 = L d_0 to x1=Ld1x_1 = L d_1

  • Define Δx=L(d1−d0)=LΔd\Delta x = L (d_1 - d_0) = L \Delta d

What happens between the initial change in demand and the final change in output? Figure 4 traces the response one round at a time.

A demand shock propagates upstream through direct and indirect suppliers; the Leontief inverse accumulates every round of the response.

Figure 4:A demand shock propagates upstream through direct and indirect suppliers; the Leontief inverse accumulates every round of the response.

NSL

  • We will further assume that r(A)<1r(A)<1 so that we can write the expression for Δx\Delta x as an infinite sum using the Neumann Series Lemma:

    Δx=Δd+A(Δd)+A2(Δd)+⋯\Delta x = \Delta d + A (\Delta d) + A^2 (\Delta d) + \cdots
    1. Δd\Delta d is the initial response in each sector,

    2. A(Δd)A (\Delta d) is the response generated by the first round of backward linkages,

    3. A2(Δd)A^2 (\Delta d) is the response generated by the second round, and so on.

  • The total response is the sum of the responses at each round

  • The typical element lijl_{ij} of L=∑m=0∞AmL = \sum_{m=0}^{\infty} A^m shows total impact on sector i of a unit change in the demand for good jj

L15
15×15 Matrix{Float64}: 1.29184 0.013976 … 0.021353 0.0081746 0.0192996 0.0298015 1.14899 0.0146944 0.00990947 0.0300173 0.0172818 0.0233041 0.0316325 0.00965223 0.0183746 0.0070151 0.00826026 0.00638848 0.00764268 0.0342044 0.406849 0.24425 0.190964 0.136599 0.286737 0.0044889 0.0013509 … 0.00111709 0.000775398 0.00129499 7.51567e-8 7.41503e-8 3.39839e-7 2.05879e-5 3.82796e-7 0.00610972 0.00983046 0.0116882 0.00995762 0.0189601 0.00518775 0.00943438 0.0271543 0.0277547 0.0459451 0.107845 0.0933879 0.147118 0.131159 0.120384 0.0462756 0.134203 … 0.201764 0.114416 0.159921 0.000123414 0.000127331 0.00356867 0.00797317 0.0142819 0.00422839 0.00511009 1.03468 0.0154229 0.0140417 0.00369676 0.00364725 0.0167158 1.01266 0.0188287 0.000730455 0.000825878 0.00497073 0.00307743 1.00398
Plot(heatmap(z=L15))
Loading...