diff --git a/connect/index.html b/connect/index.html index 2c3a88ef..801ca498 100644 --- a/connect/index.html +++ b/connect/index.html @@ -285,7 +285,9 @@

Three routes to the same models. Start with MCP below — it needs no install, no PolicyEngine account and no API key, though some clients - gate custom connectors by plan. Use the CLI or Python when you want the + gate custom connectors by plan. That is true of the hosted route; a + local UK population or reform-scoring run additionally needs a + HuggingFace token, as below. Use the CLI or Python when you want the models in a script, or need one of the local-only members.

@@ -368,10 +370,19 @@

Connect in three steps.

takes around 20 seconds while the tax-benefit models load; a UK forecast runs a couple of minutes on first call. Other tools respond faster.

+

uk microdata + Running a UK population or reform score locally needs + HUGGING_FACE_TOKEN from an account with access to the + private policyengine/policyengine-uk-data-private + dataset. UK household calls, every US call, and every hosted call + need nothing.

local only OG-UK long-run scoring (score_reform with model='og') and dynamic_reform_impact take - tens of minutes — the hosted server points you to the local CLI. + tens of minutes and are excluded from the hosted image; + dynamic_reform_impact returns the two-step local recipe, + while model='og' returns an import error — use the local + CLI for both. The three define_* tools likewise return run instructions rather than results when called on the hosted server — the unlicensed upstream is never hosted. Everything else here runs hosted.

@@ -443,7 +454,7 @@

Use Python directly.

@@ -307,7 +307,9 @@

Against the OBR's own forecast, and HMRC's reckoner.

The spending multiplier is ~1.0 by construction, against the - OBR's own published 0.6. Under the demand closure a spending + OBR's own published + 0.6 for day-to-day public services and welfare spending. + Under the demand closure a spending shock lands directly in the GDP identity and the behavioural second round is largely inactive, so a £5bn injection returns almost exactly £5bn of GDP — roughly a diff --git a/olg/code/index.html b/olg/code/index.html index b6d66bdf..17730ddf 100644 --- a/olg/code/index.html +++ b/olg/code/index.html @@ -594,16 +594,6 @@

From a few lines of Python to a full UK transition path.

- - diff --git a/papers/frb-us/figures/fig_costpush.pdf b/papers/frb-us/figures/fig_costpush.pdf index 66636137..a9ee0309 100644 Binary files a/papers/frb-us/figures/fig_costpush.pdf and b/papers/frb-us/figures/fig_costpush.pdf differ diff --git a/papers/frb-us/figures/fig_fiscal_rules.pdf b/papers/frb-us/figures/fig_fiscal_rules.pdf index df31dfe8..b75c99ce 100644 Binary files a/papers/frb-us/figures/fig_fiscal_rules.pdf and b/papers/frb-us/figures/fig_fiscal_rules.pdf differ diff --git a/papers/frb-us/figures/fig_fiscal_vs_tax.pdf b/papers/frb-us/figures/fig_fiscal_vs_tax.pdf index 094189cb..25b19c24 100644 Binary files a/papers/frb-us/figures/fig_fiscal_vs_tax.pdf and b/papers/frb-us/figures/fig_fiscal_vs_tax.pdf differ diff --git a/papers/frb-us/figures/fig_mp100.pdf b/papers/frb-us/figures/fig_mp100.pdf index 4e5a7503..f7394ffe 100644 Binary files a/papers/frb-us/figures/fig_mp100.pdf and b/papers/frb-us/figures/fig_mp100.pdf differ diff --git a/papers/frb-us/figures/results.json b/papers/frb-us/figures/results.json index b1ca82c8..68f9031f 100644 --- a/papers/frb-us/figures/results.json +++ b/papers/frb-us/figures/results.json @@ -5,10 +5,10 @@ "density": 0.011158500297560008 }, "timing": { - "parse_s": 0.012448999998014187, - "init_trac_and_compile_s": 0.6833600000027218, - "solve_40q_s": 0.18090470799870673, - "solve_20q_s": 0.08809025000300608 + "parse_s": 0.007142832997487858, + "init_trac_and_compile_s": 0.4716686669999035, + "solve_40q_s": 0.14447266700153705, + "solve_20q_s": 0.06989166700077476 }, "mp100": { "rff_pp": [ @@ -520,256 +520,256 @@ }, "gov_fixed": { "rff_pp": [ - -0.07233501323000002, - -0.09397614343999994, - -0.1075430904200001, - -0.12312868583000025, - -0.10809279839999997, - -0.08287495489999985, - -0.07031361986999984, - -0.061175421409999764, - -0.01888804787999998, - 0.02275444253000014, - 0.023884224789999742, - 0.022175191520000137, - 0.01713229437000008, - 0.011989237229999894, - 0.010390462649999765, - 0.009720782310000065, - 0.00964067732000018, - 0.009493371810000095, - 0.00907071750999977, - 0.008483126410000263, - 0.007901040019999694, - 0.007432026270000058, - 0.007101524719999919, - 0.0068651469900000706, - 0.006668897920000028, - 0.0064784616899999925, - 0.006282877280000321, - 0.00609062907000002, - 0.005913243650000233, - 0.005757320570000335, - 0.005623481039999767, - 0.005507375870000164, - 0.005404051410000221, - 0.005310062119999959, - 0.005223732849999774, - 0.005144870780000144, - 0.005073581510000036, - 0.005009688249999922, - 0.004952650359999922, - 0.004901628739999886 + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 ], "xgdp_pct": [ - 0.7386532540090585, - 0.7203378610508349, - 0.8675596707995448, - 0.8396994672830616, - 0.9193692358608896, - 0.9749107634092979, - 1.0213775495412358, - 1.0593618856494347, - 0.36949542662017, - 0.42544565964157677, - 0.3108586196986174, - 0.36811384166581806, - 0.3184833986452862, - 0.29034613696476974, - 0.26606886860118806, - 0.24592506169285322, - 0.22635136565389846, - 0.207605493231644, - 0.1904458204082582, - 0.1744567983941181, - 0.15967479036522736, - 0.14599876779464172, - 0.133357711312998, - 0.12165490380715571, - 0.11082646432598597, - 0.10081639853281033, - 0.09159855028528785, - 0.083136147539431, - 0.07538737365511139, - 0.06830657328911194, - 0.06185315416891157, - 0.055987101362675595, - 0.050669850107532, - 0.0458648878445711, - 0.04153818351577687, - 0.037657529522761024, - 0.03419205788961488, - 0.031112188172754962, - 0.02838970535470331, - 0.025997799258070664 + 0.738698727784759, + 0.7065542860441143, + 0.8396414504558969, + 0.7911885979854594, + 0.8512800120901165, + 0.8900509145914226, + 0.9238126363443788, + 0.9518146552435436, + 0.2542676526136045, + 0.30827658491558996, + 0.19838033537535882, + 0.2607785285658304, + 0.21659707554808616, + 0.1910487913936887, + 0.16898181070774676, + 0.15075909871249404, + 0.13335386593185117, + 0.1170330163736466, + 0.10234716375165931, + 0.08882927356086423, + 0.07642294093872515, + 0.0650346888120712, + 0.05461259716723621, + 0.04508749417990732, + 0.0364092277960637, + 0.02852329220384142, + 0.021397408164847143, + 0.014987949434885905, + 0.009249222203489005, + 0.004134841676095569, + -0.0003945827244167255, + -0.004377720056303325, + -0.007852677355479898, + -0.010856354332011708, + -0.013423633710329952, + -0.015587519225057633, + -0.01737934838645927, + -0.018828847762752954, + -0.019964149670659292, + -0.0208118832603299 ], "lur_pp": [ - -0.3121923127891222, - -0.30310081661716026, - -0.38381510220707327, - -0.38966724241359163, - -0.43943210243161435, - -0.4764813791617195, - -0.5088488520486361, - -0.5379699339393955, - -0.25716349310924613, - -0.28107583123044, - -0.21777708979111132, - -0.22819004306951074, - -0.19763457547484187, - -0.17733804084414562, - -0.15853297463947325, - -0.14081283708137438, - -0.12365745451483079, - -0.10734344321964961, - -0.0922178148606152, - -0.07810373808204307, - -0.06502471724831071, - -0.05292581113412265, - -0.041761702568364534, - -0.03147984059491815, - -0.022041358260548982, - -0.01340915866026382, - -0.005555691259376161, - 0.001552593246496592, - 0.007952349536425984, - 0.013682619466659496, - 0.018780911446447668, - 0.02328471441565494, - 0.027230939258922504, - 0.0306556266974205, - 0.03359369980223992, - 0.03607907866659499, - 0.03814473843592836, - 0.03982260905503043, - 0.04114346865357632, - 0.0421368521693708 + -0.31221294680753253, + -0.29579991854882426, + -0.3691465051477776, + -0.3654693573173553, + -0.4059447752420273, + -0.43512988172025713, + -0.46167496610431424, + -0.48585453116803556, + -0.201050488728578, + -0.2238278243356353, + -0.1629342867956476, + -0.17531541914585436, + -0.1465740261720656, + -0.12705744073047942, + -0.10907678826201295, + -0.09227718525825246, + -0.07632851679023034, + -0.06145609427343324, + -0.04788578744533467, + -0.035393221079689674, + -0.023946483487877757, + -0.013485496831523669, + -0.003964728208157631, + 0.004666424042611972, + 0.012451143372273599, + 0.01943582987073178, + 0.025658737284671496, + 0.031162651104536643, + 0.035990736138807655, + 0.040186107159676965, + 0.04378893214244606, + 0.04683880838944088, + 0.04937469256745075, + 0.05143459810637463, + 0.053055171272113455, + 0.054271629863678506, + 0.055117758247876125, + 0.055625862155017636, + 0.05582676146308163, + 0.055749787665077655 ], "picxfe_pp": [ 0.0, - 0.01811453604014268, - 0.020538200297605336, - 0.02690694553987827, - 0.029698764473115258, - 0.03599236635720349, - 0.04232969807546816, - 0.048970954676412326, - 0.055618188063784046, - 0.04456560825447653, - 0.048431053828773774, - 0.048723642045627624, - 0.05318820812888747, - 0.05403792784151462, - 0.05456868862130193, - 0.054647663537947144, - 0.05454614646632616, - 0.0541815423015517, - 0.05358652289299681, - 0.05282145736695343, - 0.05190633412855772, - 0.050863717856613144, - 0.04970876134428481, - 0.04845639560014492, - 0.04711932795219531, - 0.04571034887102576, - 0.044241344456116494, - 0.042724053037568765, - 0.041168983143989646, - 0.03958560022139901, - 0.03798243261460987, - 0.03636743473735482, - 0.034747954823403315, - 0.0331307618980865, - 0.031522049953089715, - 0.029927473302024943, - 0.028352150682475985, - 0.02680067805998032, - 0.025277155782636784, - 0.023785218412329945 + 0.017376924518888615, + 0.018638001479915012, + 0.024038160774661144, + 0.025528851082696757, + 0.030769392528005035, + 0.03641157094452829, + 0.04244684167094759, + 0.04852895967590376, + 0.03733987330920119, + 0.041495883148344515, + 0.041940471564679616, + 0.04638689220087855, + 0.04718124501409782, + 0.04748273514920598, + 0.04728764979785249, + 0.04689554833293985, + 0.046247497274686244, + 0.0453893280311386, + 0.044378956662423974, + 0.04323816426096383, + 0.04199033667030627, + 0.04065316533215135, + 0.03924346447195459, + 0.037774944735261995, + 0.036260193738453905, + 0.03471018817969229, + 0.03313540322038122, + 0.031545096069912404, + 0.02994761139941282, + 0.02835043987025987, + 0.026760529032456848, + 0.0251841844647549, + 0.02362708208008324, + 0.02209429973201904, + 0.020590376740503125, + 0.019119343802010302, + 0.01768474484306637, + 0.016289661100094666, + 0.014936737988371362 ], "rg10_pp": [ - 0.1408303510799378, - -0.07352178269716969, - -0.06962265000027656, - -0.10573402969258217, - -0.08087314000873036, - -0.08136039119703709, - -0.08368454523348401, - -0.08400457032301833, - -0.23696669279356408, - -0.01882215270859522, - -0.03500986809880757, - -0.002464267464485026, - -0.028304955239454976, - -0.025159473858399117, - -0.020227648874135973, - -0.017842647903484554, - -0.01568293581647051, - -0.013846357834893297, - -0.01201716835459532, - -0.010575691224866901, - -0.009329709264211061, - -0.008221519946750355, - -0.007222503498444333, - -0.006336924883326489, - -0.005564641784012281, - -0.004907938334548945, - -0.0043559996920539135, - -0.0039003790248628434, - -0.00353026154088365, - -0.0032348166641273934, - -0.003004172227889512, - -0.0028322496347872317, - -0.0027137520837356988, - -0.00264326452312158, - -0.0026149796625487554, - -0.002623106787581264, - -0.002662262765452006, - -0.0027275003687607935, - -0.002814384653047952, - -0.002918961331567438 + 0.1648093862340705, + -0.04688877895462529, + -0.028656127031038814, + -0.06124961095588777, + -0.03830127988521781, + -0.04188946214731981, + -0.04694481875501122, + -0.049159366475644894, + -0.21328192399253743, + -0.006042965038957071, + -0.024426736070542354, + 0.0058559668821907, + -0.01672507815093649, + -0.013590253886193615, + -0.008833137486925047, + -0.00683481631288263, + -0.005498566723111331, + -0.004267373675450159, + -0.0029986186880144317, + -0.0020066533042149004, + -0.0011742993550134528, + -0.00047697472265273433, + 9.898836174127013e-05, + 0.0005605007688940233, + 0.0009211973463836287, + 0.0011895002271389288, + 0.0013788577939699564, + 0.001494125878608088, + 0.0015417354103632874, + 0.0015289101461641863, + 0.001464123376742954, + 0.0013537139524304465, + 0.001203431851043213, + 0.0010189527029424994, + 0.0008057633239246798, + 0.0005688955330445111, + 0.0003129190399517867, + 4.202341912140639e-05, + -0.00023995167270474127, + -0.0005295202460997572 ], "xgap2_pp": [ - 0.7348048579523563, - 0.7105535234200039, - 0.8489734441655757, - 0.8132745348121617, - 0.8836601332995977, - 0.9287998517786444, - 0.964016572508394, - 0.9901315515909372, - 0.29011780530501324, - 0.3383777468672151, - 0.2185377321940728, - 0.26972534197557413, - 0.214805555011579, - 0.182263832133034, - 0.15427064102120666, - 0.13098800899662702, - 0.10882781036465346, - 0.0880176500564977, - 0.06926480106263408, - 0.05211262607659966, - 0.03655912601320489, - 0.02246739156840733, - 0.009733415501910057, - -0.0017697004520425622, - -0.012133860862200608, - -0.02144111855075402, - -0.02974192122622879, - -0.03709578817976372, - -0.04356582886001076, - -0.04921750139152797, - -0.0541097773444696, - -0.05829973322630971, - -0.061841733705117474, - -0.06478687516176174, - -0.0671826272927484, - -0.06907356449292383, - -0.07050191429233915, - -0.07150766569602449, - -0.07212854469056257, - -0.07240002698874952 + 0.734857381856865, + 0.6969520526472421, + 0.8216367198490101, + 0.7661128973303816, + 0.8180546634029013, + 0.8479158708875378, + 0.8722099235660024, + 0.8903431019640822, + 0.18474234864683142, + 0.23327150745291544, + 0.12026143908282277, + 0.17861745480416596, + 0.13099620026822814, + 0.10275421656609883, + 0.07855719109975824, + 0.05866863883995113, + 0.04003729969195259, + 0.02290082572515121, + 0.0077619168393897355, + -0.005883021517512306, + -0.018122641830686608, + -0.029080319421144824, + -0.038835510326496, + -0.04748287670047338, + -0.05509642234687942, + -0.06175281155951055, + -0.0675048162728236, + -0.07241506219556555, + -0.07654687851615012, + -0.07996293577623796, + -0.0827176580888284, + -0.08486361195707966, + -0.08645170495874158, + -0.08753056342084987, + -0.08814577614196252, + -0.08834009553549682, + -0.08815371168535943, + -0.08762436264399544, + -0.08678740278466401, + -0.08567594282051778 ] }, "taxcut": { diff --git a/papers/frb-us/figures/run_experiments.py b/papers/frb-us/figures/run_experiments.py index 52d656a5..b2f1d92e 100644 --- a/papers/frb-us/figures/run_experiments.py +++ b/papers/frb-us/figures/run_experiments.py @@ -11,6 +11,7 @@ import json import time +import os from pathlib import Path import matplotlib @@ -23,7 +24,17 @@ from frbus import Frbus, load_data HERE = Path(__file__).resolve().parent -REPO = Path("/Users/janansadeqian/us-frb-model") +# Resolvable on any machine. The script previously hardcoded one developer's +# home directory, so the figures it produces could not be regenerated by +# anyone else — which is how a bad experiment stayed committed. +REPO = Path( + os.environ.get("US_FRB_MODEL", Path.home() / "us-frb-model") +).expanduser() +if not (REPO / "vendor").is_dir(): + raise SystemExit( + f"us-frb-model checkout not found at {REPO}. " + "Set US_FRB_MODEL to the checkout path." + ) XML = str(REPO / "vendor" / "pyfrbus_package" / "models" / "model.xml") LONGBASE = str(REPO / "vendor" / "data_only_package" / "LONGBASE.TXT") @@ -118,12 +129,32 @@ def dev(sim, ref, s=START, e=END40): sim_g_tay = gmodel.solve(START, END40, gdata) results["gov_taylor"] = dev(sim_g_tay, base) -gdata_fix = gdata.copy() -gdata_fix.loc[START:END40, "dmpintay"] = 0 -gdata_fix.loc[START:END40, "dmpex"] = 1 -gdata_fix.loc[START:END40, "rfffix"] = base.loc[START:END40, "rff"] +# The peg switches must be in the data BEFORE init_trac, and the deviation +# must be taken against a baseline that was itself solved under the peg. +# +# Flipping dmpintay->0 / dmpex->1 *after* tracking leaves the wedge between +# the inertial-Taylor rule's own value and the actual funds rate sitting in +# the rff block's add-factor, so it keeps being subtracted. The funds rate +# then settles 0.07-0.12pp BELOW its own rfffix target — an unintended +# monetary easing stacked on the fiscal stimulus, which inflated the year-2 +# multiplier from 0.90 to 0.99. A pegged run whose rate moves is not pegged, +# and the assertion below is the cheap check that says so. +fixdata = data.copy() +fixdata.loc[START:END40, "dmpintay"] = 0 +fixdata.loc[START:END40, "dmpex"] = 1 +fixdata.loc[START:END40, "rfffix"] = fixdata.loc[START:END40, "rff"] +base_fix = gmodel.init_trac(START, END40, fixdata) + +gdata_fix = base_fix.copy() +gdata_fix.loc[START:FISC_END, "egfe"] = ( + base_fix.loc[START:FISC_END, "egfe"] + + 0.01 * base_fix.loc[START:FISC_END, "xgdp"] +) sim_g_fix = gmodel.solve(START, END40, gdata_fix) -results["gov_fixed"] = dev(sim_g_fix, base) +drift = (sim_g_fix.loc[START:END40, "rff"] + - base_fix.loc[START:END40, "rff"]).abs().max() +assert drift < 1e-10, f"funds rate is not pegged: max |drift| = {drift:.3e}" +results["gov_fixed"] = dev(sim_g_fix, base_fix) # --- (c) personal tax cut, ex-ante 1% of GDP ------------------------------- tmodel = Frbus(XML) diff --git a/papers/frb-us/index.html b/papers/frb-us/index.html index e7a68e73..62856a40 100644 --- a/papers/frb-us/index.html +++ b/papers/frb-us/index.html @@ -140,15 +140,5 @@

Cite this work

- diff --git a/papers/obr-macro/index.html b/papers/obr-macro/index.html index f5a7a9dc..169fb522 100644 --- a/papers/obr-macro/index.html +++ b/papers/obr-macro/index.html @@ -139,15 +139,5 @@

Cite this work

- diff --git a/papers/pe-microsim/index.html b/papers/pe-microsim/index.html index 54230a8f..cb3aaada 100644 --- a/papers/pe-microsim/index.html +++ b/papers/pe-microsim/index.html @@ -139,15 +139,5 @@

Cite this work

- diff --git a/papers/psl-og/index.html b/papers/psl-og/index.html index c03916af..0c07f7e9 100644 --- a/papers/psl-og/index.html +++ b/papers/psl-og/index.html @@ -138,15 +138,5 @@

Cite this work

- diff --git a/pe/code/index.html b/pe/code/index.html index 36bc4f1a..0c178588 100644 --- a/pe/code/index.html +++ b/pe/code/index.html @@ -299,16 +299,6 @@

From one family to the whole distribution.

- - diff --git a/reveal.js b/reveal.js index abe08ebb..29c5e874 100644 --- a/reveal.js +++ b/reveal.js @@ -76,3 +76,25 @@ openTargetedDetails(); } })(); + + +/* Copy-to-clipboard, shared. This handler was pasted verbatim into fifteen + pages and omitted from models/index.html, which renders a copy button in + #score — the target of fifteen links — that did nothing. One delegated + copy, so a page gets the behaviour by loading this file. */ +(function () { + "use strict"; + document.addEventListener("click", function (event) { + var button = event.target.closest("[data-copy]"); + if (!button) return; + // /connect wraps its blocks in .code-window, so the code element is not + // a child of the button's parent there. + var container = button.closest(".code-window") || button.parentElement; + var code = container && container.querySelector("code"); + if (!code || !navigator.clipboard) return; + navigator.clipboard.writeText(code.textContent).then(function () { + button.textContent = "copied"; + setTimeout(function () { button.textContent = "copy"; }, 1400); + }); + }); +})(); diff --git a/site_nav.py b/site_nav.py index 2ec8ce87..f823628c 100644 --- a/site_nav.py +++ b/site_nav.py @@ -130,6 +130,11 @@ def crumb_chain(url: str) -> list[str]: # /notes retired into /forecasts#notes: never emit a crumb # link to the deleted index page. current = "/forecasts" + if current == "/economy/topics": + # Same treatment: /economy/topics is a redirect source, not a + # page, so the crumb on all six topic pages was a 308 hop. + # One retired prefix was handled here and the other was not. + current = "/economy" if current == "/": break return chain diff --git a/svar/code/index.html b/svar/code/index.html index c3679f45..7ea621d9 100644 --- a/svar/code/index.html +++ b/svar/code/index.html @@ -182,16 +182,6 @@

What lands where

- - diff --git a/us-hank/index.html b/us-hank/index.html index c9ddda2b..5f380e92 100644 --- a/us-hank/index.html +++ b/us-hank/index.html @@ -83,7 +83,7 @@

Two distributional cuts of the same shock: the model's and the microsimulati
Connection. Shock-to-incidence bridge: hank_shock_incidence scales the US microsimulation's employment income with the model's wage and labor impulse responses — the model's MPC-by-wealth story on one side, statutory tax-and-transfer incidence on the other. Experimental illustration. score_reform refuses model="hank": three stylized instruments map to no statute.
Worked example. One monetary shock, two answers: the HANK block reports consumption responses by wealth position; the microsimulation reports the same earnings change after taxes, SNAP, and the EITC, decile by decile.
-
Validation. Replicates Auclert et al. (2021) via the authors' toolkit — 18 gated tests on targets, market clearing, and shock responses.
+
Validation. Replicates Auclert et al. (2021) via the authors' toolkit — gated on targets, market clearing, and shock responses.
Results. Run hank_shock from any connected client.
diff --git a/us-hank/validation/index.html b/us-hank/validation/index.html index 7ec1ea10..298e9dc2 100644 --- a/us-hank/validation/index.html +++ b/us-hank/validation/index.html @@ -4,7 +4,7 @@ US HANK validation — PolicyEngine Macro - + @@ -84,8 +84,8 @@

A replication gate, not a forecast-accuracy claim.

The ground truth is the published paper. The model repository's - 18-test suite gates the steady-state calibration - targets, market clearing, and the signs and magnitudes of the shock + replication suite gates the steady-state calibration + targets, market clearing, and the signs and timing of the shock responses against the published Econometrica 2021 results; the implementation builds on the authors' own sequence-jacobian toolkit rather than re-deriving the @@ -143,7 +143,7 @@

A replication gate, not a forecast-accuracy claim.

2.7e-04 Market clearing: goods 3.3e-08, assets 3.1e-13. Achieved values solved from the hosted adapter; targets from the upstream test suite citing the paper. -
Hosted two-asset steady state against the paper's published calibration targets, solved 2026-07-29 at the production grids (nB=50, nA=70, nK=50). β is the calibrated free parameter. Source: papers/us-hank/figures/replication.json.
+
Hosted two-asset steady state against the paper's published calibration targets, solved 2026-07-29 at the production grids (nB=50, nA=70, nK=50). Read the top five rows as inputs, not results: Y, K, r, G and Bg are literal entries in the calibration dictionary, held fixed through the solve, so their zero deviation is an arithmetic tautology and carries no information. The informative rows are the last three — total wealth and liquid assets, which the solver has to hit through market clearing, and β, the one free parameter, whose 0.976274 is the only value here compared against a number the paper reports. Source: papers/us-hank/figures/replication.json.
This validates replication fidelity, not prediction. diff --git a/validation/figures/README.md b/validation/figures/README.md index 312c204c..8a9baa52 100644 --- a/validation/figures/README.md +++ b/validation/figures/README.md @@ -1,7 +1,14 @@ # validation/figures -Generating script for the twelve inline SVG charts on `/validation/`, in the same -spirit as the papers' `papers/*/figures/*.py` scripts. +Generating script for the fourteen inline SVG evidence charts on the model +validation pages, in the same spirit as the papers' `papers/*/figures/*.py` +scripts. + +There is no `/validation/` page: it was absorbed into `/models#validation`, and +each chart now lives inline on the validation subtab of the model it is evidence +for (the exception is `pe-costing`, which sits on the microsimulation overview). +The owning page for each chart is the third field of `BUILDERS` in +`make_charts.py`. ## Regenerate @@ -9,9 +16,9 @@ spirit as the papers' `papers/*/figures/*.py` scripts. python3 validation/figures/make_charts.py ``` -This rewrites the twelve `` blocks in `validation/index.html` -in place. Stdlib only — no dependencies. To assert in CI that the page has not -drifted from its sources: +This rewrites each `` block in place on its +owning page, keyed by the `data-chart` attribute. Stdlib only — no dependencies. +To assert in CI that no page has drifted from its sources: ```sh python3 validation/figures/make_charts.py --check # exits 1 if stale @@ -35,20 +42,22 @@ audit line by line, which is the point of the page. ## Where the numbers come from -| Chart (`data-chart`) | Plots | Source | -| --- | --- | --- | -| `obr-anchored` | Quarterly % deviation of the anchored emulator from the March-2026 EFO, 2025Q1–2027Q4, for real GDP and consumption | Computed from `papers/obr-macro/figures/fig_anchored_data.csv` (`*_model` vs `*_efo` levels) | -| `obr-reform` | 1pp basic-rate rise: PolicyEngine static costing vs HMRC ready reckoner, 2026–27 and 2028–29 | `chart_data.json` → `papers/obr-macro/sections/comparison.tex`, table `tab:comparison` panel B | -| `obr-computed-share` | Two stacked bars separating scorecard variables the model actually computes from passthroughs held at the OBR published value | `chart_data.json` (`obr_computed_share`) | -| `obr-freerun` | Free-running vs anchored emulator real GDP levels against the EFO path | `papers/obr-macro/figures/fig_free_running_data.csv` and `fig_anchored_data.csv` | -| `obr-outturn` | Grouped bars: quarter-on-quarter real GDP growth from the emulator, the EFO, and the ONS outturn | `papers/obr-macro/figures/fig_outturn_data.csv` | -| `svar-fevd` | Global-shock FEVD shares at the 1-year horizon, ours vs the paper, for UK GDP and CPI | `papers/boe-svar/figures/comparison_numbers.json` | -| `svar-fan` | Median and 68% forecast fans from the frozen 2024Q2 edge for UK GDP and CPI, with ONS outturns overlaid | `papers/boe-svar/figures/figure_numbers.json` | -| `svar-coverage` | Empirical 68/90% band coverage across 49 rolling origins, by horizon | `papers/boe-svar/figures/coverage_evaluation.json` (generated by `make_coverage.py`) | -| `frbus-residuals` | Max absolute residuals against the Fed's `pyfrbus`, log scale | `chart_data.json` → `papers/frb-us/sections/validation.tex`, tables `tab:tracking` and `tab:refnoise` | -| `hank-targets` | Hosted two-asset steady state against the ABRS (2021) published calibration targets | `papers/us-hank/figures/replication.json` | -| `svar-skill-all` | Rolling-origin RMSE ratios vs the benchmark for every variable–horizon pair, with significance | `papers/boe-svar/figures/rolling_evaluation.json` | -| `svar-winrate` | Per-horizon counts of variables where the SVAR beats the benchmark, split by significance | `papers/boe-svar/figures/rolling_evaluation.json` | +| Chart (`data-chart`) | Owning page | Plots | Source | +| --- | --- | --- | --- | +| `pe-costing` | `pe/index.html` | 1pp basic-rate rise: PolicyEngine static costing vs HMRC ready reckoner (the same two groups as `obr-reform`, framed as the microsimulation's own independent check) | `chart_data.json` (`obr_reform`) — one committed source, rendered on two pages | +| `obr-anchored` | `obr/validation` | Quarterly % deviation of the anchored emulator from the March-2026 EFO, 2025Q1–2027Q4, for real GDP and consumption | Computed from `papers/obr-macro/figures/fig_anchored_data.csv` (`*_model` vs `*_efo` levels) | +| `obr-reform` | `obr/validation` | 1pp basic-rate rise: PolicyEngine static costing vs HMRC ready reckoner, 2026–27 and 2028–29 | `chart_data.json` → `papers/obr-macro/sections/comparison.tex`, table `tab:comparison` panel B | +| `obr-computed-share` | `obr/validation` | Two stacked bars separating scorecard variables the model actually computes from passthroughs held at the OBR published value | `chart_data.json` (`obr_computed_share`) | +| `obr-freerun` | `obr/validation` | Free-running vs anchored emulator real GDP levels against the EFO path | `papers/obr-macro/figures/fig_free_running_data.csv` and `fig_anchored_data.csv` | +| `obr-outturn` | `obr/validation` | Grouped bars: quarter-on-quarter real GDP growth from the emulator, the EFO, and the ONS outturn | `papers/obr-macro/figures/fig_outturn_data.csv` | +| `svar-fevd` | `svar/validation` | Global-shock FEVD shares at the 1-year horizon, ours vs the paper, for UK GDP and CPI | `papers/boe-svar/figures/comparison_numbers.json` | +| `svar-fan` | `svar/validation` | Median and 68% forecast fans from the frozen 2024Q2 edge for UK GDP and CPI, with ONS outturns overlaid | `papers/boe-svar/figures/figure_numbers.json` | +| `svar-coverage` | `svar/validation` | Empirical 68/90% band coverage across 49 rolling origins, by horizon | `papers/boe-svar/figures/coverage_evaluation.json` (generated by `make_coverage.py`) | +| `svar-skill-all` | `svar/validation` | Rolling-origin RMSE ratios vs the benchmark for every variable–horizon pair, with significance | `papers/boe-svar/figures/rolling_evaluation.json` | +| `svar-winrate` | `svar/validation` | Per-horizon counts of variables where the SVAR beats the benchmark, split by significance | `papers/boe-svar/figures/rolling_evaluation.json` | +| `frbus-residuals` | `frb-us/validation` | Max absolute residuals against the Fed's `pyfrbus`, log scale | `chart_data.json` → `papers/frb-us/sections/validation.tex`, tables `tab:tracking` and `tab:refnoise` | +| `hank-targets` | `us-hank/validation` | Hosted two-asset steady state against the ABRS (2021) published calibration targets | `papers/us-hank/figures/replication.json` | +| `define-emissions` | `define/validation` | S1 baseline total UK emissions at the manual's benchmark years: the cached pinned run of upstream commit `846081a` against the manual's published Table 4 | `data/define_emissions_divergence.csv` (recorded in the define-uk-model repository's `VALIDATION.md`, target 1b) | Most charts read committed machine-readable data directly. The others plot numbers that exist in the project only as LaTeX table cells; those are diff --git a/validation/figures/chart_data.json b/validation/figures/chart_data.json index d000f57f..a6843e28 100644 --- a/validation/figures/chart_data.json +++ b/validation/figures/chart_data.json @@ -1,26 +1,44 @@ { "_comment": "Values that are NOT machine-readable in papers/*/figures/ are transcribed here from the papers' LaTeX sources, with an explicit source pointer per value. Everything else is read directly from the papers' committed CSV/JSON data by make_charts.py. Do not edit a number here without changing the cited source.", - "obr_reform": { "source": "papers/obr-macro/sections/comparison.tex, Table tab:comparison panel B (cross-checked against papers/obr-macro/sections/results.tex)", "groups": [ { "label": "basic rate +1pp, 2026–27", "bars": [ - { "name": "ours", "value": 6.46, "series": 1, "source": "comparison.tex panel B / results.tex L18 (PolicyEngine static costing)" }, - { "name": "HMRC", "value": 6.9, "series": 2, "source": "comparison.tex panel B; HMRC Direct effects of illustrative tax changes, June 2025" } + { + "name": "ours", + "value": 6.46, + "series": 1, + "source": "comparison.tex panel B / results.tex L18 (PolicyEngine static costing)" + }, + { + "name": "HMRC", + "value": 6.9, + "series": 2, + "source": "comparison.tex panel B; HMRC Direct effects of illustrative tax changes, June 2025" + } ] }, { "label": "basic rate +1pp, 2028–29", "bars": [ - { "name": "ours", "value": 6.92, "series": 1, "source": "comparison.tex panel B; interpolated between scored endpoints 6.46 (2026) and 7.38 (2030)" }, - { "name": "HMRC", "value": 8.2, "series": 2, "source": "comparison.tex panel B; HMRC June 2025 reckoner" } + { + "name": "ours", + "value": 6.92, + "series": 1, + "source": "comparison.tex panel B; interpolated between scored endpoints 6.46 (2026) and 7.38 (2030)" + }, + { + "name": "HMRC", + "value": 8.2, + "series": 2, + "source": "comparison.tex panel B; HMRC June 2025 reckoner" + } ] } ] }, - "obr_computed_share": { "source": "docs/calibration_scorecard.md in the PolicyEngine/obr-macroeconomic-model repository (March 2026 EFO vintage calibration scorecard); counts cross-checked against papers/obr-macro/sections/calibration.tex Table tab:rawscore (preserved November 2025 study)", "total": 21, @@ -29,29 +47,88 @@ "bands_note": "bands: rates ±1.0pp · net balances ±1.5% of GDP · levels ≤10% MAPE", "_grades_comment": "The 11 computed variables graded against the bands above. 'identity' is employment, where both inputs are themselves passthrough, so the accounting identity closes trivially and the pass carries no information.", "grades": [ - { "key": "fair", "label": "fair", "count": 3, "series": 1, "in_band": true, - "examples": ["real GDP 5.75 per cent", "consumption 9.56 per cent", "trade balance 0.70 per cent of GDP"], - "source": "calibration_scorecard.md, free-running MAPE column" }, - { "key": "identity", "label": "identity", "count": 1, "series": 2, "in_band": true, "trivial": true, - "examples": ["employment"], - "source": "calibration_scorecard.md; employment closes from two passthrough inputs" }, - { "key": "poor", "label": "poor", "count": 5, "series": 3, "in_band": false, + { + "key": "fair", + "label": "fair", + "count": 3, + "series": 1, + "in_band": true, + "examples": [ + "real GDP 5.75 per cent", + "consumption 9.56 per cent", + "trade balance 0.70 per cent of GDP" + ], + "source": "calibration_scorecard.md, free-running MAPE column" + }, + { + "key": "identity", + "label": "identity", + "count": 1, + "series": 2, + "in_band": true, + "trivial": true, + "examples": [ + "employment" + ], + "source": "calibration_scorecard.md; employment closes from two passthrough inputs" + }, + { + "key": "poor", + "label": "poor", + "count": 5, + "series": 3, + "in_band": false, "examples": [], - "source": "calibration_scorecard.md, variables outside band but within an order of magnitude" }, - { "key": "off", "label": "off", "count": 2, "series": 3, "in_band": false, - "examples": ["company profits 79.80 per cent", "the current account 4.17 per cent of GDP"], - "source": "calibration_scorecard.md, worst two computed variables" } + "source": "calibration_scorecard.md, variables outside band but within an order of magnitude" + }, + { + "key": "off", + "label": "off", + "count": 2, + "series": 3, + "in_band": false, + "examples": [ + "company profits 79.80 per cent", + "the current account 4.17 per cent of GDP" + ], + "source": "calibration_scorecard.md, worst two computed variables" + } ] }, - "frbus_residuals": { "source": "papers/frb-us/sections/validation.tex, Tables tab:tracking and tab:refnoise", "rows": [ - { "label": "ours vs LONGBASE (tracking invariant)", "value": 5.6e-17, "series": 1, "source": "validation.tex tab:tracking, max abs error" }, - { "label": "pyfrbus 1.1.1 vs LONGBASE", "value": 1.1e-08, "series": 3, "source": "validation.tex subsec:refnoise prose" }, - { "label": "ours vs pyfrbus 1.0.0 (shock)", "value": 6.0e-09, "series": 1, "source": "validation.tex tab:refnoise row 1" }, - { "label": "ours vs pyfrbus 1.1.1 (shock)", "value": 1.4e-08, "series": 1, "source": "validation.tex tab:refnoise row 2" }, - { "label": "pyfrbus 1.1.1 vs 1.0.0 — the Fed’s own two releases", "value": 1.3e-08, "series": 2, "source": "validation.tex tab:refnoise row 3" } + { + "label": "ours vs LONGBASE (identity — not evidence)", + "value": 5.6e-17, + "series": 1, + "source": "validation.tex tab:tracking, max abs error", + "note": "init_trac defines add-factors as minus the residuals at the input data, so re-solving reproduces it up to float64 for ANY input — verified: the same test passes at 6.7e-9 on randomly scrambled data. Shown for completeness; it measures arithmetic, not economics." + }, + { + "label": "pyfrbus 1.1.1 vs LONGBASE", + "value": 1.1e-08, + "series": 3, + "source": "validation.tex subsec:refnoise prose" + }, + { + "label": "ours vs pyfrbus 1.0.0 (shock)", + "value": 6e-09, + "series": 1, + "source": "validation.tex tab:refnoise row 1" + }, + { + "label": "ours vs pyfrbus 1.1.1 (shock)", + "value": 1.4e-08, + "series": 1, + "source": "validation.tex tab:refnoise row 2" + }, + { + "label": "pyfrbus 1.1.1 vs 1.0.0 — the Fed’s own two releases", + "value": 1.3e-08, + "series": 2, + "source": "validation.tex tab:refnoise row 3" + } ] } } diff --git a/validation/figures/make_charts.py b/validation/figures/make_charts.py index 3490aa29..66edd948 100644 --- a/validation/figures/make_charts.py +++ b/validation/figures/make_charts.py @@ -317,10 +317,12 @@ def chart_svar_fevd(): {"name": "paper", "value": paper["cpi"], "series": 2}]}, ] desc = (f"Grouped bar chart. Share of UK forecast-error variance attributed to identified " - f"global shocks (world demand, energy and supply) at the one-year horizon. For GDP, " - f"our 10,000-draw production run gives {ours['gdp']:.1f}% against the paper's " - f"{paper['gdp']:.1f}%; for CPI, {ours['cpi']:.1f}% against {paper['cpi']:.1f}%. " - f"Both deviations are a percentage point or less. The paper's values are approximate.") + f"global shocks (world demand, energy and supply) four quarters ahead, as a share of " + f"total variance — the statistic the paper's Figure 4 plots. For GDP, our 10,000-draw " + f"production run gives {ours['gdp']:.1f}% against the paper's {paper['gdp']:.1f}%, a " + f"match; for CPI, {ours['cpi']:.1f}% against {paper['cpi']:.1f}%, about eight points " + f"short. The posterior band on each is roughly plus or minus fourteen points, wider " + f"than the shortfall. The paper's values are approximate.") return grouped_bars( "svar-fevd", "boe-svar: global-shock FEVD shares, ours vs Brignone & Piffer (2025)", @@ -679,7 +681,8 @@ def xs(v): f"{meta['origins']} expanding-window origins. A ratio below 1.0 means the model " f"beats the benchmark; filled dots mark differences significant at 5 per cent by a " f"Diebold-Mariano test, hollow dots differences that are not statistically " - f"distinguishable. " + f"distinguishable; those p-values are pairwise and the grid runs 64 tests, none of " + f"which clears a 10 per cent false-discovery rate once adjusted together. " + "; ".join( f"{label} runs {ratio[horizons[0]][key]:.2f} at one quarter to " f"{ratio[last][key]:.2f} at {last}" @@ -806,8 +809,6 @@ def chart_svar_winrate(): return "\n".join(out) -# Order must match the order the SVGs appear in validation/index.html: the -# substitution below is positional. def chart_svar_coverage(): data = json.loads( (ROOT / "papers" / "boe-svar" / "figures" / "coverage_evaluation.json").read_text()