{
const widget = makeMatrixEntryPrototype({id: "m11-matrix-slide", presentation: true});
invalidation.then(() => widget.destroy());
return widget;
}Module 11 · The equations are real but the parameters don’t matter.
Econ 6376 · The George Washington University
What changes when both variables respond?
| Mixing-board channel | Today |
|---|---|
| Own memory | Keep one lag |
| Cross-variable history | Let both outcomes depend on it |
| Current connections | Allow reciprocal dependence |
| Equations | Give each outcome its own equation |
A disturbance starts somewhere.
Connections transmit it elsewhere.
Think about inflation and the policy rate.
Our shocks have zero means, positive variances, no serial/past dependence, and zero covariance with each other.
Solve for today’s values using yesterday’s information.
First, xt and yt are determined together. Each appears as a regressor in the other equation and can be correlated with that equation’s structural shock.
Replace by the entire right-hand side of its equation:
Into the transformed intercept, lag coefficients, and innovation.
Replace by the entire right-hand side of its structural equation:
The compact notation must reproduce our scalar answer.
makeMatrixEntryPrototype = function (options = {}) {
// Reveal treats descendant sections as slides; this component is a control group.
const root = document.createElement("div");
root.className = "m11-matrix-prototype";
root.setAttribute("role", "group");
if (options.id) root.id = options.id;
root.setAttribute("aria-label", "Matrix-entry explorer");
const state = {row: 0, column: 1, mode: "symbolic", revealed: false};
const inverse = [[8 / 7, 4 / 7], [2 / 7, 8 / 7]];
const lag = [[0.3, 0], [0.1, 0.4]];
const expected = [[2 / 5, 8 / 35], [1 / 5, 16 / 35]];
const variables = ["y", "x"];
const subscript = [["yy", "yx"], ["xy", "xx"]];
const inverseSymbols = [["1", "bᵧₓ"], ["bₓᵧ", "1"]];
const lagSymbols = [["φᵧᵧ", "φᵧₓ"], ["φₓᵧ", "φₓₓ"]];
const inverseFractions = [["8/7", "4/7"], ["2/7", "8/7"]];
const lagFractions = [["3/10", "0"], ["1/10", "2/5"]];
const answerFractions = [["2/5", "8/35"], ["1/5", "16/35"]];
const productFractions = [[["12/35", "2/35"], ["0", "8/35"]],
[["3/35", "4/35"], ["0", "16/35"]]];
const numerators = [["φ<sub>yy</sub> + b<sub>yx</sub>φ<sub>xy</sub>",
"φ<sub>yx</sub> + b<sub>yx</sub>φ<sub>xx</sub>"],
["b<sub>xy</sub>φ<sub>yy</sub> + φ<sub>xy</sub>",
"b<sub>xy</sub>φ<sub>yx</sub> + φ<sub>xx</sub>"]];
// All four orientations must match the notes' solve(B, Gamma1), not only the default cell.
for (let r = 0; r < 2; r++) for (let c = 0; c < 2; c++) {
const actual = inverse[r][0] * lag[0][c] + inverse[r][1] * lag[1][c];
if (Math.abs(actual - expected[r][c]) > 1e-12) throw new Error("Matrix-entry numeric check failed");
}
const fraction = (top, bottom) => `<span class="m11-me-frac"><span>${top}</span><span>${bottom}</span></span>`;
const symbolHTML = value => value.replace(/ᵧ/g, "y").replace(/ₓ/g, "x")
.replace(/^([bφ])([xy]{2})$/, "$1<sub>$2</sub>");
root.innerHTML = `<style>
.m11-matrix-prototype{--me-ink:#17354b;--me-row:#146b91;--me-col:#a54319;color:var(--me-ink);font:16px/1.45 system-ui,sans-serif;max-width:1100px;margin:1rem auto;padding:1.2rem;border:1px solid #d6e1e8;border-radius:12px;background:#fff;box-sizing:border-box}
.m11-matrix-prototype *{box-sizing:border-box}
.m11-matrix-prototype button{font:inherit;cursor:pointer;border:1px solid #aabcc8;border-radius:7px;padding:.45rem .75rem;background:#fff;color:var(--me-ink)}
.m11-matrix-prototype button:hover{background:#eef5f9}
.m11-matrix-prototype button:focus-visible{outline:3px solid #d68b21;outline-offset:3px}
.m11-matrix-prototype button[aria-pressed=true]{background:#173f59;color:white;border-color:#173f59}
.m11-matrix-prototype button:disabled{cursor:default;opacity:.65}
.m11-matrix-prototype .m11-me-controls{display:flex;align-items:center;justify-content:space-between;gap:.8rem;flex-wrap:wrap;margin-bottom:.8rem}
.m11-matrix-prototype .m11-me-modes{display:flex;gap:.4rem;align-items:center;flex-wrap:wrap}
.m11-matrix-prototype .m11-me-state{margin:.5rem 0 .7rem;font-weight:650}
.m11-matrix-prototype .m11-me-matrices{display:grid;grid-template-columns:minmax(0,1fr) 24px minmax(0,1fr) 24px minmax(0,1fr);gap:.3rem;align-items:center}
.m11-matrix-prototype .m11-me-panel{min-width:0;text-align:center}
.m11-matrix-prototype .m11-me-panel h4{font:650 1.12rem/1.3 system-ui,sans-serif;margin:.2rem 0}
.m11-matrix-prototype .m11-me-panel p{font-size:.86rem;margin:.3rem 0;min-height:2.6em}
.m11-matrix-prototype .m11-me-svg{display:block;width:100%;height:auto;max-height:215px}
.m11-matrix-prototype .m11-me-operator{font:2rem Georgia,serif;text-align:center}
.m11-matrix-prototype .m11-me-output{display:grid;grid-template-columns:1fr 1fr;gap:.4rem;border-left:2px solid #17354b;border-right:2px solid #17354b;padding:.7rem .5rem;margin:.7rem .3rem .3rem}
.m11-matrix-prototype .m11-me-output button{font:1.4rem Georgia,serif;min-height:55px}
.m11-matrix-prototype .m11-me-output button[aria-pressed=true]{box-shadow:inset 0 0 0 2px #fff;outline:2px solid #173f59;outline-offset:1px}
.m11-matrix-prototype .m11-me-output-hint{font-size:.86rem;line-height:1.3;margin:.4rem 0}
.m11-matrix-prototype .m11-me-product{margin-top:1rem;padding:1rem;background:#f3f7fa;border-radius:8px}
.m11-matrix-prototype .m11-me-product p{margin:0 0 .6rem}
.m11-matrix-prototype .m11-me-pairs{display:flex;align-items:center;gap:.7rem;flex-wrap:wrap;margin:.6rem 0}
.m11-matrix-prototype .m11-me-pair{font:1.25rem Georgia,serif;padding:.55rem .75rem;border:1px solid #c4d2dc;border-radius:6px;background:white}
.m11-matrix-prototype .m11-me-frac{display:inline-flex;vertical-align:middle;flex-direction:column;text-align:center;margin:0 .15em;line-height:1.4}
.m11-matrix-prototype .m11-me-frac>span:first-child{border-bottom:1px solid currentColor;padding:0 .15em}
.m11-matrix-prototype .m11-me-frac>span:last-child{padding:0 .15em}
.m11-matrix-prototype .m11-me-reveal{background:#173f59;color:white;font-weight:600}
.m11-matrix-prototype .m11-me-result{margin-top:.85rem;border-left:4px solid #146b91;padding:.2rem 0 .2rem .9rem}
.m11-matrix-prototype .m11-me-equation{font:1.35rem/1.5 Georgia,serif;overflow-x:auto;padding:.3rem 0}
.m11-matrix-prototype .m11-me-result p:last-child{margin:0}
.m11-matrix-prototype .m11-me-row-key{color:var(--me-row)}
.m11-matrix-prototype .m11-me-col-key{color:var(--me-col)}
.m11-matrix-prototype .m11-me-muted{font-size:.88rem;color:#526b7c}
@media(max-width:700px){.m11-matrix-prototype{padding:.8rem}.m11-matrix-prototype .m11-me-matrices{grid-template-columns:1fr;gap:.2rem}.m11-matrix-prototype .m11-me-panel{width:100%;max-width:350px;margin:auto}.m11-matrix-prototype .m11-me-panel p{min-height:0}.m11-matrix-prototype .m11-me-svg{max-height:175px}.m11-matrix-prototype .m11-me-operator{font-size:1.5rem}.m11-matrix-prototype .m11-me-output button{min-height:45px}}
</style>
<div class="m11-me-controls"><div class="m11-me-modes" role="group" aria-label="Display mode"><span>View:</span><button type="button" data-mode="symbolic">Symbols</button><button type="button" data-mode="numeric">Numbers</button></div><button type="button" data-reset>Reset</button></div>
<p class="m11-me-state" data-state aria-live="polite"></p>
<div class="m11-me-matrices">
<div class="m11-me-panel"><h4>B<sup>−1</sup></h4><div data-inverse></div><p class="m11-me-row-key" data-row-key></p></div>
<div class="m11-me-operator" aria-hidden="true">×</div>
<div class="m11-me-panel"><h4>Γ<sub>1</sub></h4><div data-lag></div><p class="m11-me-col-key" data-col-key></p></div>
<div class="m11-me-operator" aria-hidden="true">=</div>
<div class="m11-me-panel"><h4>A<sub>1</sub></h4><div class="m11-me-output" role="group" aria-label="Select an entry in the reduced lag matrix">
<button type="button" data-cell="0,0"></button><button type="button" data-cell="0,1"></button>
<button type="button" data-cell="1,0"></button><button type="button" data-cell="1,1"></button>
</div><p class="m11-me-output-hint">Choose a cell.<br>Rows: y, x equations · Columns: y, x lags</p></div>
</div>
<div class="m11-me-product"><p data-prompt></p><div class="m11-me-pairs" data-pairs></div><button type="button" class="m11-me-reveal" data-reveal>Show calculation</button><div data-result aria-live="polite"></div></div>`;
const field = name => root.querySelector(`[data-${name}]`);
function matrixSVG(values, selected, isRow, outsideFactor) {
const color = isRow ? "#146b91" : "#a54319";
const rect = isRow
? `<rect x="58" y="${49 + selected * 70}" width="188" height="54" rx="7" fill="${isRow ? "#e6f3fa" : "#fff0e7"}" stroke="${color}" stroke-width="2"/>`
: `<rect x="${65 + selected * 92}" y="43" width="82" height="132" rx="7" fill="#fff0e7" stroke="${color}" stroke-width="2"/>`;
let entries = "";
for (let r = 0; r < 2; r++) for (let c = 0; c < 2; c++) {
entries += `<text x="${106 + 92 * c}" y="${83 + 70 * r}" text-anchor="middle" font-size="25" fill="${(isRow ? r : c) === selected ? color : "#17354b"}">${values[r][c]}</text>`;
}
return `<svg class="m11-me-svg" viewBox="0 0 290 215" role="img" aria-label="${isRow ? "Inverse matrix, selected row" : "Structural lag matrix, selected column"} ${selected + 1}">
${outsideFactor ? `<text x="27" y="100" text-anchor="middle" font-size="19">1</text><path d="M16 108H38" stroke="#17354b"/><text x="27" y="131" text-anchor="middle" font-size="19">D</text>` : ""}
${rect}<path d="M63 34H52V185H63 M242 34H253V185H242" fill="none" stroke="#17354b" stroke-width="2.5"/>
<g font-family="Georgia, serif">${entries}</g>
<text x="152" y="208" text-anchor="middle" font-size="14" fill="#526b7c">${outsideFactor ? "D = 1 − bᵧₓbₓᵧ" : isRow ? "D = 7/8 for these numbers" : "Rows: y, x · Columns: y lag, x lag"}</text>
</svg>`;
}
function draw() {
const {row: r, column: c, mode, revealed} = state;
const numeric = mode === "numeric";
root.querySelectorAll("[data-mode]").forEach(button => button.setAttribute("aria-pressed", String(button.dataset.mode === mode)));
field("state").textContent = `Selected: ${variables[r]} equation, ${variables[c]} lag — row ${r + 1} × column ${c + 1}`;
field("inverse").innerHTML = matrixSVG(numeric ? inverseFractions : inverseSymbols, r, true, !numeric);
field("lag").innerHTML = matrixSVG(numeric ? lagFractions : lagSymbols, c, false, false);
field("row-key").textContent = `Outlined row ${r + 1}: receiving ${variables[r]} equation`;
field("col-key").textContent = `Outlined column ${c + 1}: source ${variables[c]} lag`;
root.querySelectorAll("[data-cell]").forEach(button => {
const [br, bc] = button.dataset.cell.split(",").map(Number);
const active = br === r && bc === c;
const name = subscript[br][bc];
button.setAttribute("aria-pressed", String(active));
button.setAttribute("aria-label", `a ${name}: ${variables[br]} equation, ${variables[bc]} lag`);
button.innerHTML = active && revealed && numeric ? answerFractions[r][c] : `a<sub>${name}</sub>${active && !revealed ? " = ?" : ""}`;
});
const pairs = [0, 1].map(k => {
const left = numeric ? inverseFractions[r][k] : fraction(symbolHTML(inverseSymbols[r][k]), "D");
const right = numeric ? lagFractions[k][c] : symbolHTML(lagSymbols[k][c]);
return `<span class="m11-me-pair">${left} × ${right}</span>`;
});
field("prompt").innerHTML = `Read the outlined row and column. Multiply these two pairs, then add to obtain <strong>a<sub>${subscript[r][c]}</sub></strong>.`;
field("pairs").innerHTML = `${pairs[0]}<span aria-label="plus">+</span>${pairs[1]}<span>= ?</span>`;
field("reveal").disabled = revealed;
field("reveal").textContent = revealed ? "Calculation shown" : "Show calculation";
field("result").innerHTML = "";
if (revealed) {
const symbolic = `a<sub>${subscript[r][c]}</sub> = ${fraction(numerators[r][c], "D")}`;
const numericLine = `${productFractions[r][c][0]} + ${productFractions[r][c][1]} = <strong>${answerFractions[r][c]}</strong> ≈ ${expected[r][c].toFixed(4)}`;
const meaning = r === 0 && c === 1
? `The direct structural coefficient is φ<sub>yx</sub> = 0, yet the reduced coefficient is a<sub>yx</sub> = 8/35. The same indirect connection appeared in the scalar substitution.`
: `This is the coefficient on ${variables[c]}<sub>t−1</sub> in the reduced ${variables[r]} equation. It combines the structural terms shown in the numerator; it is not simply φ<sub>${subscript[r][c]}</sub>.`;
field("result").innerHTML = `<div class="m11-me-result"><div class="m11-me-equation">${symbolic}</div>${numeric ? `<div class="m11-me-equation">${numericLine}</div>` : ""}<p>${numeric ? meaning : "This is the scalar coefficient already obtained by substitution. Switch to Numbers to check it using the notes’ fixed example."}</p></div>`;
}
root.dispatchEvent(new Event("input", {bubbles: true}));
}
const click = event => {
const button = event.target.closest("button");
if (!button || !root.contains(button)) return;
if (button.hasAttribute("data-cell")) {
[state.row, state.column] = button.dataset.cell.split(",").map(Number);
state.revealed = false;
} else if (button.hasAttribute("data-mode")) {
state.mode = button.dataset.mode;
state.revealed = false;
} else if (button.hasAttribute("data-reveal")) {
state.revealed = true;
} else if (button.hasAttribute("data-reset")) {
Object.assign(state, {row: 0, column: 1, mode: "symbolic", revealed: false});
}
draw();
};
root.addEventListener("click", click);
const keydown = event => {
if ((event.key === "Enter" || event.key === " ") && event.target.closest("button")) event.stopPropagation();
};
root.addEventListener("keydown", keydown);
Object.defineProperty(root, "value", {get: () => ({...state,
coefficient: `a_${subscript[state.row][state.column]}`,
receivingVariable: variables[state.row], sourceLag: variables[state.column],
numericValue: expected[state.row][state.column],
products: [inverse[state.row][0] * lag[0][state.column], inverse[state.row][1] * lag[1][state.column]]
})});
root.destroy = () => {
root.removeEventListener("click", click);
root.removeEventListener("keydown", keydown);
};
draw();
return root;
}identificationPrototypeModel = {
const A0 = [2 / 7, 6 / 35];
const A1 = [[2 / 5, 8 / 35], [1 / 5, 16 / 35]];
const Sigma = [[80 / 49, 48 / 49], [48 / 49, 68 / 49]];
const multiply = (a, b) => a.map(row => b[0].map((_, j) => row[0] * b[0][j] + row[1] * b[1][j]));
const transpose = a => [[a[0][0], a[1][0]], [a[0][1], a[1][1]]];
const inverse = a => {
const d = a[0][0] * a[1][1] - a[0][1] * a[1][0];
return [[a[1][1] / d, -a[0][1] / d], [-a[1][0] / d, a[0][0] / d]];
};
const error = (a, b) => Math.max(...a.flat().map((v, i) => Math.abs(v - b.flat()[i])));
const recover = order => {
const yFirst = order === "y";
const b_yx = yFirst ? 0 : Sigma[0][1] / Sigma[1][1];
const b_xy = yFirst ? Sigma[1][0] / Sigma[0][0] : 0;
const omega_y = yFirst ? Sigma[0][0] : Sigma[0][0] - Sigma[0][1] ** 2 / Sigma[1][1];
const omega_x = yFirst ? Sigma[1][1] - Sigma[1][0] ** 2 / Sigma[0][0] : Sigma[1][1];
const B = [[1, -b_yx], [-b_xy, 1]];
const Omega = [[omega_y, 0], [0, omega_x]];
const Gamma0 = multiply(B, A0.map(v => [v])).map(row => row[0]);
const Gamma1 = multiply(B, A1);
const Binv = inverse(B);
const errors = {
intercept: error(multiply(Binv, Gamma0.map(v => [v])), A0.map(v => [v])),
lags: error(multiply(Binv, Gamma1), A1),
covariance: error(multiply(multiply(Binv, Omega), transpose(Binv)), Sigma)
};
return {order, b_yx, b_xy, phi_yx: Gamma1[0][1], omega_y, omega_x, B, Omega, Gamma0, Gamma1, errors};
};
// Small checks guard the two reverse maps and all three reconstructed reduced objects.
for (const [order, expected] of [["y", [0, 3 / 5, 8 / 35, 80 / 49, 4 / 5]], ["x", [12 / 17, 0, -8 / 85, 16 / 17, 68 / 49]]]) {
const result = recover(order);
const actual = [result.b_yx, result.b_xy, result.phi_yx, result.omega_y, result.omega_x];
if (error([actual], [expected]) > 1e-12 || Math.max(...Object.values(result.errors)) > 1e-12) {
throw new Error("Identification prototype reverse-map check failed.");
}
}
return {A0, A1, Sigma, recover};
}makeIdentificationPrototype = function (options = {}) {
const model = identificationPrototypeModel;
// Reveal treats descendant sections as slides; this component is a control group.
const root = document.createElement("div");
root.className = options.presentation ? "m11id m11id-presentation" : "m11id";
root.setAttribute("role", "group");
root.setAttribute("aria-label", "VAR identification explorer");
root.id = options.id || `m11id-${crypto.randomUUID()}`;
const markerId = `${root.id.replace(/[^a-zA-Z0-9_-]/g, "-")}-arrow`;
let order = null, recovered = false, truthVisible = false;
const fraction = value => {
if (Math.abs(value) < 1e-12) return "0";
for (let q = 1; q <= 500; q++) {
const p = Math.round(value * q);
if (Math.abs(value - p / q) < 1e-10) return q === 1 ? `${p}` : `${p}/${q}`;
}
return value.toFixed(4);
};
const number = value => `${fraction(value)}${Math.abs(value - Math.round(value)) > 1e-10 ? ` <span class="m11id-decimal">≈ ${value.toFixed(4)}</span>` : ""}`;
const matrix = (label, values, description) => {
const cols = values[0].length, width = cols === 1 ? 150 : 250;
const xs = cols === 1 ? [75] : [78, 174];
const entries = values.map((row, i) => row.map((value, j) => `<text x="${xs[j]}" y="${48 + i * 42}" text-anchor="middle">${fraction(value)}</text>`).join("")).join("");
const accessibleValues = values.map((row, i) => `row ${i + 1}: ${row.map(fraction).join(", ")}`).join("; ");
return `<div class="m11id-matrix-card"><div class="m11id-matrix-label">${label}</div><svg viewBox="0 0 ${width} 110" role="img" aria-label="${description}; ${accessibleValues}"><path d="M28 20 H18 V98 H28 M${width - 28} 20 H${width - 18} V98 H${width - 28}" fill="none" stroke="currentColor" stroke-width="2"/>${entries}</svg><div class="m11id-matrix-description">${description}</div></div>`;
};
const bLabel = key => `<i>b</i><sub>${key}</sub>`;
const currentTerm = (key, variable) => {
const excluded = (order === "y" && key === "yx") || (order === "x" && key === "xy");
return `<span class="m11id-coefficient m11id-${key}${excluded ? " m11id-zero" : ""}" data-coefficient="${key}" aria-label="${excluded ? `b_${key} is set to zero` : `b_${key}`} times current ${variable}">${excluded ? "0" : bLabel(key)} ${variable}<sub>t</sub></span>`;
};
const structuralMatrix = () => {
const removedYX = order === "y", removedXY = order === "x";
const cell = (key, x, y, removed, color) => `<g data-b-cell="${key}"><rect x="${x - 49}" y="${y - 29}" width="98" height="54" rx="6" fill="${removed ? "#fff" : key === "yx" ? "#fff1e7" : "#e6f3f8"}" stroke="${color}" stroke-width="${removed ? 2.5 : 1.5}" ${removed ? 'stroke-dasharray="5 3"' : ''}/><text x="${x}" y="${y + 6}" text-anchor="middle" fill="${color}" font-family="Georgia,serif" font-size="25">${removed ? "0" : `−b<tspan baseline-shift="sub" font-size="16">${key}</tspan>`}</text><text x="${x}" y="${y + 44}" text-anchor="middle" fill="${color}" font-size="13">${removed ? `b${key === "yx" ? "ᵧₓ" : "ₓᵧ"} = 0` : key === "yx" ? "current x → y" : "current y → x"}</text></g>`;
return `<svg class="m11id-B" viewBox="0 0 460 255" role="img" aria-label="Structural current-coefficient matrix B. Rows: y equation, x equation. Columns: current y, current x. First row: 1, ${removedYX ? "0 because b_yx is zero" : "minus b_yx"}. Second row: ${removedXY ? "0 because b_xy is zero" : "minus b_xy"}, 1."><g font-family="system-ui,sans-serif" font-size="15" fill="#456476"><text x="232" y="25" text-anchor="middle">current y<tspan baseline-shift="sub" font-size="11">t</tspan></text><text x="357" y="25" text-anchor="middle">current x<tspan baseline-shift="sub" font-size="11">t</tspan></text><text x="160" y="88" text-anchor="end">y equation</text><text x="160" y="178" text-anchor="end">x equation</text></g><g font-family="Georgia,serif" font-size="29" fill="#17364a"><text x="35" y="133">B =</text><text x="232" y="90" text-anchor="middle">1</text><text x="357" y="180" text-anchor="middle">1</text></g><path d="M193 48 H181 V212 H193 M398 48 H410 V212 H398" fill="none" stroke="#17364a" stroke-width="2"/>${cell("yx",357,84,removedYX,"#a74b15")}${cell("xy",232,174,removedXY,"#16668a")}</svg>`;
};
const graph = (kind, truth = false) => {
const description = truth ? "Generating simultaneous system: b_xy labels current y to x, b_yx labels current x to y." : !kind ? "Possible current links: b_xy labels y to x; b_yx labels x to y." : kind === "y" ? "y first: b_yx is zero, removing x to y; b_xy labels the remaining y to x arrow." : "x first: b_xy is zero, removing y to x; b_yx labels the remaining x to y arrow.";
const tag = truth ? "-truth" : "";
const arrow = (key, color, path, labelY) => `<defs><marker id="${markerId}${tag}-${key}" markerWidth="8" markerHeight="8" refX="7" refY="4" orient="auto"><path d="M0 0 L8 4 L0 8 Z" fill="${color}"/></marker></defs><path d="${path}" fill="none" stroke="${color}" stroke-width="3" marker-end="url(#${markerId}${tag}-${key})"/><text x="180" y="${labelY}" text-anchor="middle" fill="${color}" font-family="Georgia,serif" font-size="23">b<tspan baseline-shift="sub" font-size="15">${key}</tspan>${truth ? key === "xy" ? " = 1/4" : " = 1/2" : ""}</text>`;
return `<svg class="m11id-graph" viewBox="0 -8 360 183" role="img" aria-label="${description}">${truth || kind !== "x" ? arrow("xy", "#16668a", "M106 61 C145 28 215 28 254 61", 18) : ""}${truth || kind !== "y" ? arrow("yx", "#a74b15", "M254 105 C215 139 145 139 106 105", 164) : ""}<g fill="#fff" stroke="#24485e" stroke-width="2"><ellipse cx="75" cy="83" rx="35" ry="27"/><ellipse cx="285" cy="83" rx="35" ry="27"/></g><g text-anchor="middle" fill="#17364a" font-size="24" font-family="Georgia,serif"><text x="75" y="91">y<tspan baseline-shift="sub" font-size="15">t</tspan></text><text x="285" y="91">x<tspan baseline-shift="sub" font-size="15">t</tspan></text></g></svg>`;
};
const parameterRows = [
["b_yx", "<i>b</i><sub>yx</sub>", "Current x → y"],
["b_xy", "<i>b</i><sub>xy</sub>", "Current y → x"],
["phi_yx", "φ<sub>yx</sub>", "Direct lagged x → y"],
["omega_y", "ω<sub>y</sub>", "Structural y-shock variance"],
["omega_x", "ω<sub>x</sub>", "Structural x-shock variance"]
];
root.innerHTML = `<style>
.m11id{--ink:#17364a;--blue:#16668a;--line:#cfdae2;color:var(--ink);font:17px/1.5 system-ui,sans-serif;max-width:100%;padding:clamp(14px,2vw,26px);border:1px solid var(--line);border-radius:14px;background:#fff;box-sizing:border-box}
.m11id *{box-sizing:border-box}.m11id h3{font-size:1.13em;margin:0 0 .5em}.m11id p{margin:.55em 0}.m11id .m11id-kicker{font-size:.78em;text-transform:uppercase;letter-spacing:.06em;font-weight:750;color:#527083}
.m11id .m11id-structural{background:#f8fafb;border:1px solid var(--line);border-radius:10px;padding:16px}.m11id .m11id-equations{font:clamp(18px,1.8vw,23px)/1.9 Georgia,serif;overflow-x:auto;margin:12px 0}.m11id .m11id-equation{white-space:nowrap;padding:3px 0}.m11id .m11id-coefficient{padding:3px 5px;border-radius:5px;border:1px solid currentColor}.m11id .m11id-yx{color:#a74b15;background:#fff1e7}.m11id .m11id-xy{color:#16668a;background:#e6f3f8}.m11id .m11id-zero{border-style:dashed;background:white}.m11id .m11id-linked{display:grid;grid-template-columns:minmax(0,1.15fr) minmax(0,.85fr);gap:12px;align-items:center}.m11id .m11id-B{display:block;width:100%;max-width:500px;margin:auto}.m11id .m11id-link-caption{font-size:.88em;color:#456476;text-align:center}.m11id .m11id-vector-equation{font:20px Georgia,serif;text-align:center;margin:4px 0 12px}.m11id .m11id-number-details{margin-top:16px;border:1px solid var(--line);border-radius:8px;padding:10px 13px}.m11id .m11id-number-details summary{cursor:pointer;font-weight:650}.m11id .m11id-number-details summary:focus-visible{outline:3px solid #c67a10;outline-offset:3px}
.m11id .m11id-fixed{background:#f3f7fa;border:1px solid var(--line);border-radius:10px;padding:15px}.m11id .m11id-matrices{display:flex;gap:12px;flex-wrap:wrap;align-items:start}.m11id .m11id-matrix-card{flex:1 1 220px;text-align:center;min-width:0}.m11id .m11id-matrix-card:first-child{flex:0.6 1 145px}.m11id .m11id-matrix-card svg{width:100%;max-width:250px;height:110px;font:23px Georgia,serif}.m11id .m11id-matrix-label{font:italic 24px Georgia,serif}.m11id .m11id-matrix-description{font-size:.79em;color:#506878}.m11id sub{font-size:.72em}
.m11id .m11id-controls{display:flex;gap:9px;flex-wrap:wrap;margin:16px 0}.m11id button{font:inherit;font-weight:650;border:2px solid #9bb0bd;border-radius:7px;background:#fff;color:var(--ink);padding:9px 14px;cursor:pointer}.m11id button[aria-pressed="true"]{background:#e3f2f7;border-color:var(--blue);box-shadow:inset 0 -3px 0 var(--blue)}.m11id button:focus-visible{outline:3px solid #c67a10;outline-offset:3px}.m11id button:disabled{opacity:.5;cursor:default}.m11id .m11id-reset{margin-left:auto;font-weight:500}.m11id .m11id-recover{background:var(--blue);border-color:var(--blue);color:white}
.m11id .m11id-stage{border-top:1px solid var(--line);padding-top:18px}.m11id .m11id-graph{width:100%;max-width:360px;display:block;margin:auto}.m11id .m11id-assumption{padding:12px;border-left:4px solid var(--blue);background:#f3f8fa}.m11id .m11id-formulas{font:20px/1.75 Georgia,serif;margin:12px 0;overflow-x:auto}.m11id .m11id-formulas div{white-space:nowrap}.m11id .m11id-prompt{color:#465f70}.m11id .m11id-status{font-size:.9em;margin:.7em 0}.m11id .m11id-table-wrap{overflow-x:auto}
.m11id table{width:100%;border-collapse:collapse;font-size:.91em;margin:10px 0}.m11id th,.m11id td{padding:8px 10px;border-bottom:1px solid var(--line);text-align:left;vertical-align:top}.m11id th{background:#edf4f7}.m11id .m11id-decimal{white-space:nowrap;font-size:.83em;color:#607280}.m11id .m11id-check{padding:10px 13px;background:#eef7ef;border:1px solid #bfd6c1;border-radius:7px;font-size:.87em}.m11id .m11id-truth{margin-top:18px;padding:14px;border:2px dashed #a17a32;border-radius:8px;background:#fffbf0}.m11id .m11id-truth h3{font-size:1.02em}.m11id .m11id-truth .m11id-graph{max-width:300px}.m11id [hidden]{display:none!important}
@media(max-width:650px){.m11id{font-size:16px}.m11id .m11id-linked{grid-template-columns:1fr}.m11id .m11id-reset{margin-left:0}.m11id .m11id-matrix-card{flex-basis:140px}.m11id .m11id-matrix-card:first-child{flex-basis:100%}.m11id .m11id-matrix-card svg{height:100px}}
</style>
<div class="m11id-structural"><div class="m11id-kicker">Structural model · possible current connections</div><div class="m11id-equations" data-panel="equations"></div><div class="m11id-linked"><div><div data-panel="B"></div><p class="m11id-vector-equation">BY<sub>t</sub> = Γ<sub>0</sub> + Γ<sub>1</sub>Y<sub>t−1</sub> + η<sub>t</sub></p></div><div><div data-panel="graph"></div><p class="m11id-link-caption" data-panel="link-caption"></p></div></div><p class="m11id-status">The first subscript names the <strong>receiving equation</strong>; the second names the <strong>entering variable</strong>. Moving a current term to the left gives its <strong>minus sign in B</strong>.</p></div>
<p class="m11id-prompt">Which current connection will you rule out? Follow the same coefficient in the equation, arrow, and B. Assume the two structural shocks are uncorrelated, with positive variances (Ω is diagonal).</p>
<div class="m11id-controls" role="group" aria-label="Choose a contemporaneous restriction"><button type="button" data-order="y" aria-pressed="false">Assume y first · b<sub>yx</sub> = 0</button><button type="button" data-order="x" aria-pressed="false">Assume x first · b<sub>xy</sub> = 0</button><button type="button" class="m11id-reset" data-action="reset">Reset</button></div>
<div class="m11id-stage" hidden><div data-panel="recovery"></div><button type="button" class="m11id-recover" data-action="recover">Recover structure</button></div>
<div data-panel="result" aria-live="polite"></div>
<details class="m11id-number-details" data-panel="numbers"><summary>Check with the fixed numerical example</summary><p class="m11id-status">These are <strong>reduced-form</strong> quantities, not the structural b coefficients. They stay fixed under either restriction.</p><div class="m11id-fixed"><div class="m11id-matrices">${matrix("A<sub>0</sub>", model.A0.map(v => [v]), "Reduced intercepts")}${matrix("A<sub>1</sub>", model.A1, "Reduced lag coefficients")}${matrix("Σ", model.Sigma, "Reduced innovation covariance")}</div><p class="m11id-status">Rows and columns remain y, x. Population quantities remove sampling noise.</p></div><div data-panel="numeric-result"></div></details>
<div class="m11id-controls"><button type="button" data-action="truth" hidden>Show simulation truth</button></div>
<div data-panel="truth" hidden></div>
<p class="m11id-status" data-panel="status" role="status">No restriction selected. The b symbols are unknown structural coefficients.</p>`;
if (options.presentation) {
// Keep the equation/B/arrow guide and restriction controls visible in a deck.
const guide = document.createElement("div");
guide.className = "m11id-guide";
const work = document.createElement("div");
work.className = "m11id-work";
const secondary = document.createElement("div");
secondary.className = "m11id-secondary";
secondary.setAttribute("role", "region");
secondary.setAttribute("aria-label", "Recovery and optional checks");
secondary.tabIndex = 0;
let node = root.querySelector(".m11id-stage");
while (node) {
const next = node.nextSibling;
secondary.appendChild(node);
node = next;
}
guide.appendChild(root.querySelector(".m11id-structural"));
work.append(root.querySelector(".m11id-prompt"), root.querySelector(".m11id-controls"), secondary);
root.append(guide, work);
}
const stage = root.querySelector(".m11id-stage");
const recoverButton = root.querySelector('[data-action="recover"]');
const truthButton = root.querySelector('[data-action="truth"]');
const panels = Object.fromEntries([...root.querySelectorAll("[data-panel]")].map(el => [el.dataset.panel, el]));
const draw = () => {
root.querySelectorAll("[data-order]").forEach(button => button.setAttribute("aria-pressed", String(order === button.dataset.order)));
stage.hidden = !order;
recoverButton.disabled = recovered;
recoverButton.textContent = recovered ? "Structure recovered" : "Recover structure";
truthButton.hidden = !recovered;
truthButton.textContent = truthVisible ? "Hide simulation truth" : "Show simulation truth";
panels.truth.hidden = !truthVisible;
panels.result.innerHTML = "";
panels.truth.innerHTML = "";
panels["numeric-result"].innerHTML = "";
panels.equations.innerHTML = `<div class="m11id-equation">y<sub>t</sub> = α<sub>y</sub> + ${currentTerm("yx", "x")} + φ<sub>yy</sub>y<sub>t−1</sub> + φ<sub>yx</sub>x<sub>t−1</sub> + η<sub>y,t</sub></div><div class="m11id-equation">x<sub>t</sub> = α<sub>x</sub> + ${currentTerm("xy", "y")} + φ<sub>xy</sub>y<sub>t−1</sub> + φ<sub>xx</sub>x<sub>t−1</sub> + η<sub>x,t</sub></div>`;
panels.B.innerHTML = structuralMatrix();
panels.graph.innerHTML = graph(order);
panels["link-caption"].innerHTML = !order ? "The two arrows are <strong>possible</strong> current links. Their coefficients are unknown." : order === "y" ? `${bLabel("yx")} = 0 removes <strong>x → y</strong> and sets <strong>B<sub>12</sub> = 0</strong>. ${bLabel("xy")} remains in B<sub>21</sub> with a minus sign.` : `${bLabel("xy")} = 0 removes <strong>y → x</strong> and sets <strong>B<sub>21</sub> = 0</strong>. ${bLabel("yx")} remains in B<sub>12</sub> with a minus sign.`;
const result = order ? model.recover(order) : null;
if (order) {
const yFirst = order === "y";
const excluded = yFirst ? "<i>b</i><sub>yx</sub> = 0" : "<i>b</i><sub>xy</sub> = 0";
panels.recovery.innerHTML = `<h3>With ${excluded}, solve these covariance relationships</h3><p>Σ describes the <strong>reduced-form innovations</strong>. Can you recover the remaining current coefficient and the structural shock variances?</p><div class="m11id-formulas">${yFirst ? '<div>Σ<sub>yy</sub> = ω<sub>y</sub></div><div>Σ<sub>yx</sub> = <i>b</i><sub>xy</sub> ω<sub>y</sub></div><div>Σ<sub>xx</sub> = <i>b</i><sub>xy</sub><sup>2</sup> ω<sub>y</sub> + ω<sub>x</sub></div>' : '<div>Σ<sub>xx</sub> = ω<sub>x</sub></div><div>Σ<sub>yx</sub> = <i>b</i><sub>yx</sub> ω<sub>x</sub></div><div>Σ<sub>yy</sub> = <i>b</i><sub>yx</sub><sup>2</sup> ω<sub>x</sub> + ω<sub>y</sub></div>'}</div>`;
}
if (recovered) {
const truth = {b_yx: 0.5, b_xy: 0.25, phi_yx: 0, omega_y: 1, omega_x: 1};
const symbolic = order === "y" ? {b_yx: "0 (assumed)", b_xy: "Σ<sub>yx</sub> / Σ<sub>yy</sub>", phi_yx: "<i>a</i><sub>yx</sub>", omega_y: "Σ<sub>yy</sub>", omega_x: "Σ<sub>xx</sub> − Σ<sub>yx</sub><sup>2</sup> / Σ<sub>yy</sub>"} : {b_yx: "Σ<sub>yx</sub> / Σ<sub>xx</sub>", b_xy: "0 (assumed)", phi_yx: "<i>a</i><sub>yx</sub> − (Σ<sub>yx</sub> / Σ<sub>xx</sub>) <i>a</i><sub>xx</sub>", omega_y: "Σ<sub>yy</sub> − Σ<sub>yx</sub><sup>2</sup> / Σ<sub>xx</sub>", omega_x: "Σ<sub>xx</sub>"};
const recoveredBEntry = order === "y" ? "B<sub>21</sub> = −<i>b</i><sub>xy</sub> = −Σ<sub>yx</sub> / Σ<sub>yy</sub>" : "B<sub>12</sub> = −<i>b</i><sub>yx</sub> = −Σ<sub>yx</sub> / Σ<sub>xx</sub>";
panels["link-caption"].innerHTML += `${options.presentation ? "<br><strong>Recovered entry:</strong> " : "<br><br><strong>Recovered entry:</strong><br>"}${recoveredBEntry}`;
panels.result.innerHTML = `<div class="m11id-table-wrap"><table><caption>Symbolic structure under the ${order}-first restriction</caption><thead><tr><th scope="col">Parameter</th><th scope="col">Meaning</th><th scope="col">Recovered from the reduced form</th></tr></thead><tbody>${parameterRows.map(([key, label, meaning]) => `<tr><th scope="row">${label}</th><td>${meaning}</td><td>${symbolic[key]}</td></tr>`).join("")}</tbody></table></div><p>The lag result follows from Γ<sub>1</sub> = BA<sub>1</sub>: <i>a</i> entries are <strong>reduced lag coefficients</strong>, while φ entries are <strong>structural lag coefficients</strong>.</p><p>The restriction gives a recoverable structure; the covariance relationships do not establish that its economic timing is correct.</p>`;
panels["numeric-result"].innerHTML = `<div class="m11id-table-wrap"><table><caption>Numerical recovery under the ${order}-first restriction</caption><thead><tr><th scope="col">Parameter</th><th scope="col">Recovered value</th></tr></thead><tbody>${parameterRows.map(([key,label]) => `<tr><th scope="row">${label}</th><td>${number(result[key])}</td></tr>`).join("")}</tbody></table></div><div class="m11id-check"><strong>Same reduced form:</strong> reconstructed A<sub>0</sub>, A<sub>1</sub>, and Σ agree with the given objects (maximum difference ${Math.max(...Object.values(result.errors)) < 1e-12 ? 'below 10<sup>−12</sup>' : Math.max(...Object.values(result.errors)).toExponential(2)}).</div>`;
if (truthVisible) panels.truth.innerHTML = `<div class="m11id-truth"><h3>Instructor-known simulation reference</h3><p>This comparison uses generating information withheld from the analyst. Both current connections were present in the simulation:</p>${graph(null, true)}<div class="m11id-table-wrap"><table><thead><tr><th scope="col">Parameter</th><th scope="col">${order}-first recovery</th><th scope="col">Simulation truth</th></tr></thead><tbody>${parameterRows.map(([key,label]) => `<tr><th scope="row">${label}</th><td>${number(result[key])}</td><td>${number(truth[key])}</td></tr>`).join("")}</tbody></table></div><p>Neither recursive zero matches this generator. Both still reproduce its reduced-form information. The original direct structural cross-lag was zero; the recovered alternatives show how the restriction changes the interpretation.</p></div>`;
}
panels.status.textContent = !order ? "No restriction selected. The b symbols are unknown structural coefficients; no generating values are given." : !recovered ? `${order}-first restriction selected. Its zero is shown in the equation, B, and current-arrow diagram. Predict the recovery before revealing it.` : `${order}-first structure recovered symbolically. The numerical reduced form remains unchanged.${truthVisible ? " Simulation truth is shown separately for comparison." : " Simulation truth remains hidden."}`;
root.value = {order, recovered, truthVisible, reduced: {A0: model.A0.map(v => v), A1: model.A1.map(row => [...row]), Sigma: model.Sigma.map(row => [...row])}, structure: recovered ? result : null};
root.dispatchEvent(new Event("input", {bubbles: true}));
};
const click = event => {
const button = event.target.closest("button");
if (!button || !root.contains(button) || button.disabled) return;
if (button.dataset.order) {order = button.dataset.order; recovered = false; truthVisible = false;}
if (button.dataset.action === "recover" && order) recovered = true;
if (button.dataset.action === "truth" && recovered) truthVisible = !truthVisible;
if (button.dataset.action === "reset") {order = null; recovered = false; truthVisible = false; panels.numbers.open = false;}
draw();
};
root.addEventListener("click", click);
const keydown = event => {
const activation = (event.key === "Enter" || event.key === " ") && event.target.closest("button,summary");
const paneNavigation = event.target.closest(".m11id-secondary") && [" ", "ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight", "PageUp", "PageDown", "Home", "End"].includes(event.key);
if (activation || paneNavigation) event.stopPropagation();
};
root.addEventListener("keydown", keydown);
root.destroy = () => {
root.removeEventListener("click", click);
root.removeEventListener("keydown", keydown);
};
draw();
return root;
}Each object is 2 × 1.
Row 1 belongs to the equation. Row 2 belongs to the equation.
| Rows receive | Columns enter | |
|---|---|---|
| y equation, x equation | current y, x | |
| y equation, x equation | lagged y, x |
| Piece | Meaning | Resulting dimension |
|---|---|---|
| Current values and their connections | ||
| Structural intercepts | ||
| Structural lag terms | ||
| Structural shocks |
An inverse satisfies
It undoes multiplication by .
Row 1 receives in the y equation; column 2 enters as lagged x.
Row 1 of B−1 times column 2 of Γ1:
(1/D)φyx + (byx/D)φxx = ayx.
Receiving y equation; entering lagged x. For the fixed example: (1×0 + 0.5×0.4)/0.875 = 8/35.
B <- matrix(c(1, -0.5, -0.25, 1), 2, byrow = TRUE)
Gamma1 <- matrix(c(0.3, 0, 0.1, 0.4), 2, byrow = TRUE)
solve(B, Gamma1)| y lag | x lag | |
|---|---|---|
| y equation | 0.4 | 0.2286 |
| x equation | 0.2 | 0.4571 |
is invertible.
Eigenvalues: 0.644 and 0.213.
Eigenvalue: A number λ satisfying A1v = λv for a nonzero vector v. It describes how the lag matrix scales a particular pattern across the variables. Eigenvalues can be real or complex.
Modulus: A number’s distance from zero. For a real number it is the absolute value: |−0.8| = 0.8. For a complex number a + bi, it is √(a² + b²).
Here: Both eigenvalue moduli are below 1, so the effect of a one-time disturbance eventually dies away.
Generate the system we know. Inspect the output we would usually read.
The two innovations share structural-shock components.
for (tt in seq_len(nrow(eta))) {
Y[tt + 1, ] <- A0 + A1 %*% Y[tt, ] +
B_inverse %*% eta[tt, ]
}
fit_y <- lm(y ~ y_lag + x_lag, data = dat)
fit_x <- lm(x ~ y_lag + x_lag, data = dat)| Entry | Structural | Population reduced | Estimated reduced |
|---|---|---|---|
| y intercept | 0.2 | 0.2857 | 0.2550 |
| y: y lag | 0.3 | 0.4000 | 0.3756 |
| y: x lag | 0.0 | 0.2286 | 0.2742 |
| x intercept | 0.1 | 0.1714 | 0.1577 |
| x: y lag | 0.1 | 0.2000 | 0.1852 |
| x: x lag | 0.4 | 0.4571 | 0.4838 |
| Estimate | Std. Error | t value | Pr(>|t|) | |
|---|---|---|---|---|
| x_lag | 0.2742 | 0.0287 | 9.5477 | <0.001 |
OLS uses innovations with conditional mean zero given the history. Cross-equation innovation covariance can remain.
| Connection | True structural | Naive OLS |
|---|---|---|
| current x into y | 0.50 | 0.6841 |
| current y into x | 0.25 | 0.6143 |
Current x contains part of the structural disturbance in the y equation.
Now work backward. Put the generating coefficients out of view.
| Available to the analyst | Sought under identifying assumptions |
|---|---|
| : reduced intercepts | : structural intercepts |
| : reduced lags | : structural lags |
| : innovation covariance | : current links; : shock variances |
For a proposed compatible invertible , set
The unrestricted structural intercepts and lags can adapt to that choice.
| Imposed/recovered y first | Instructor-known generator | |
|---|---|---|
| 0 assumed | 0.5 | |
| 0.6 recovered | 0.25 |
The imposed zero is false in this simulation. The recursive structure still reproduces the reduced form.
| Parameter | Generating | y first | x first |
|---|---|---|---|
All three reproduce the same , , and .
Neither recursive zero is true for the generating system.
| Parameter | Generating | y first | x first |
|---|---|---|---|
| byx | 1/2 | 0 | 12/17 |
| bxy | 1/4 | 3/5 | 0 |
| φyx | 0 | 8/35 | −8/85 |
| ωy | 1 | 80/49 | 16/17 |
| ωx | 1 | 4/5 | 68/49 |
Both recursive restrictions reproduce the same reduced intercepts, lag matrix, and covariance. Neither zero matches the instructor-known generator.
collects the intercept and all three one-period lags. They remain unrestricted.
| Current/shock unknowns | Count |
|---|---|
| Current cross-variable coefficients | |
| Structural shock variances | |
| Total | 9 |
Three variances + three distinct covariances = 6.
Σ describes reduced-form innovations after conditioning on lags, not the raw macroeconomic series.
| Receiving equation | Assumed current response within the quarter |
|---|---|
| Unemployment | Does not respond to current inflation or policy |
| Inflation | May respond to unemployment; does not respond to current policy |
| Federal funds rate | May respond to both current unemployment and inflation |
The first two equations repeat our bivariate recovery:
The two policy coefficients must be solved together:
Three new exclusions:
GDP growth does not respond to current unemployment, inflation, or policy within the quarter.
The later equations may respond to current GDP growth.
| Three equations | Four equations | |
|---|---|---|
| Current cross-variable coefficients | ||
| Structural shock variances | ||
| Current/shock unknowns | 9 | 16 |
| Distinct reduced covariance entries | ||
| Additional restrictions needed by the count | 3 | 6 |
Maintain unit diagonal B, uncorrelated positive-variance structural shocks, and unrestricted structural intercepts and lags.
| Current/shock structure | Distinct reduced innovation moments |
|---|---|
| off-diagonal current coefficients | innovation variances |
| structural shock variances | innovation covariances |
| unknowns | entries of Σ |
The normalizations and shock-uncorrelatedness assumptions are already in the baseline.
The stars are unrestricted current entries.
First equation: exclude m − 1 later current variables.
Second: exclude m − 2.
Continue down to 1, then 0.
R returns the upper factor; transpose it for the lower factor.
The economic argument chooses the restriction.
Policy may respond to current inflation news. Measured inflation does not respond to an unexpected policy disturbance within the month.
Inflation may respond to current policy. Policy does not respond to current inflation within the month.
Coefficients are transformed. Recall but .
Structure needs assumptions. A valid decomposition does not prove its timing story.
Disturbances move through the system. One coefficient is only one part of that account.
| Next question | Later module |
|---|---|
| How does each variable respond over time to an identified disturbance? | IRF · 12 |
| Which disturbances account for forecast-error variance at a given horizon? | FEVD · 13 |