1.2. What’s new
This page records user-visible changes to the RISE Toolbox – new features, behaviour changes, and things you may need to know when upgrading. Newest entries go on top.
Note
This log is being (re)started together with the documentation overhaul. It is not yet exhaustive of the toolbox’s full history; older changes are summarised in passing where relevant.
1.2.1. Current release
New
rise.microfound– write the economics, not the algebra. A model definition builder: declare the atoms and each agent’s optimization problem (period objective, choice variables, named constraints) in MATLAB, and RISE derives the first-order conditions symbolically and generates a plain RISE model – no@optimization_problemblock, nothing hidden. Hand the builder todsge_modeldirectly, as a cell array viacode(p), or as a written.rsfile viars(p,file); calibration and solving remaindsge_model’s job. Derived Lagrange multipliers are named<agent>_lambda_<constraint>and every generated equation is tagged with its origin. Switching parameters in an agent’s problem yield the correct probability-weighted switching FOCs automatically – a capability gEcon lacks. Validated by re-authoring nine gEcon example models. Heterogeneous-agent (HANK) support is a planned extension. See Deriving models with rise.microfound.Leads greater than one are reduced without violating Jensen’s inequality. When you write a variable two or more periods ahead, RISE rewrites the model so that no equation carries a lead greater than one. Terms that are nonlinear in a forward variable (e.g.
exp(x{+2})) are now wrapped in an auxiliary carrying the whole term’s one-period-ahead conditional expectation, soE[g(x)]is preserved rather than collapsed tog(E[x]); affine leads (a constant-weighted sum such as0.08*V{+2}) use the ordinary atomic chain. This matters only beyond first order – at first order and under perfect foresight there is certainty equivalence and the wrapping is inert. Every auxiliary keeps a contemporaneous variable, so the contemporaneous matrixA0stays non-singular and iterative solvers (cyclic reduction, themfioptimal-policy iteration) still apply. The oldviolate_jensen_inequalityoption and its whole-equation backshift alternative have been removed: there is now a single, always-correct reduction, and linear models and derived optimal-policy first-order conditions are reduced exactly as before. A note deriving the effect in closed form (including where thesigma^2term comes from) ships underdevelopment/jensen_memo.New ``get(m,’options’)``. Returns a model’s current settable options and their values – the get-side companion to
set. Handy for inspecting what a model is actually configured with.Model objects now record the RISE version that built and estimated them. Every constructed model carries a provenance stamp, queryable with
get(m,'rise_version')– a struct with fieldsconstruction(the version that parsed/built the object) andestimation(the version that ranestimate,''until the model is estimated). The shortcutsget(m,'construction_version')andget(m,'estimation_version')return the individual strings, andsummary(m)now prints a Built with RISE / Estimated with RISE line. The stamps are frozen into the object and survive asaveObj/loadObjround-trip, so a model built under one version and estimated under a later one reports both faithfully. This is distinct fromrise.version, which reports the currently installed toolbox. Objects saved before this change load unchanged and report''(unknown) for the missing stamps.MCMC correctness: independence Metropolis-Hastings and DRAM fixed. Two procedural defects in the samplers were corrected:
The independence sampler drew its proposal as a random walk centered on the current state rather than from the fixed proposal distribution, while the acceptance applied the independence-sampler density correction – so the chain targeted
pi(x)/q(x)instead ofpi(x). It now draws from the fixed distribution. (Note: tuning an independence sampler to a fixed random-walk acceptance target such as 0.234 is not meaningful – its acceptance is governed by how well the fixed proposal matches the target.)The delayed-rejection (DRAM) second-stage acceptance omitted the
1/adascale^2scaling of the first-stage proposal covariance in its proposal-density ratio, biasing the second-stage acceptance wheneveradascale ~= 1(the default2.38/sqrt(d)).
Posterior density plots: complex-valued bivariate kernel density fixed. The multivariate kernel-density bandwidth used a matrix square root
cov(X)^0.5that turned complex when the sample covariance of near-degenerate draws acquired a tiny negative eigenvalue from roundoff, making the whole density complex and breakingsurf/mesh. It now uses a real symmetric positive-definite square root – identical to the old value when the covariance is well conditioned, real and finite when it is not.set: combined optimal-policy options no longer silently dropped (MPE + discretion). The internal slot that pushes the special policy parameters (
ole,commitment) was a scalar struct overwritten on each match, so passing bothsolve_policy_equilibriumandsolve_policy_typein a singleset/solvecall dropped one of them –MPE+discretionactually ranOLE+discretion. The slot now accumulates its entries, so combined optimal-policy option pushes are all honored.Unified predict-step for filtering: OBC anticipation and observed expectations now share one code path. Two long-standing gaps in the filtering layer have been closed, in the same surgical edit to a single shared predict-step closure:
Filtering under OBC with an anticipation horizon. With
solve_shock_horizon > 0on a model that declaressimul_constraints, the filter’s predict step now runs a constraint-aware multi-step forecast across the fullk+1anticipation window viaobcas.optim.forecast_with_constraints, and keeps only the one-step-ahead result. The previous fast path (one-step unconstrained, then check, then optionally invoke the complementarity engine) is preserved bit-identical for the no-anticipation case (shock_horizon == 0), so locked filtering baselines on OBC models do not move.Filtering with observed expectations. Multi-page conditional data (
forecast_cond_endo_varsset,tsobservables with more than one page so eachtcarries observed values fort+1, t+2, ...) now enters the same multi-step solve as equality restrictions. The previous dispatch tomsre_kalman_cell_real_timewas broken: that function had been moved into+archive/and was no longer on the MATLAB path, so any user callingfilteron a model withforecast_cond_endo_varsand multi-page data hit an undefined-function error. The dispatch is removed; the regular regime-switching Kalman filter handles real-time conditioning natively by handing per-ttarget values into the predict-step closure, which pre-fills them at thet+1slot of the multi-step forecast array and letsobcas.optim.forecast_with_constraintssolve for the stochastic shocks that satisfy them.
Both cases use the same gating: when an anticipation horizon is active or per-
tconditioning targets are supplied, the predict step routes through the multi-step solver; when neither is active it falls through to the fast path. Newcpfauto-route gate also excludes any non-zero shock-horizon model so the wrapped constant-parameter filter is not selected on models whose impact matrix is 3-D.Documentation build no longer leaves a dated PDF backup. Previously every successful
update_documentation.mrun renamedhelp/risetoolbox.pdftohelp/risetoolbox <date>.pdfbefore the new PDF was copied in, as a defensive fallback against bad builds. In practice it just clutteredhelp/and the repo with dated copies. The fallback has been removed;copyfileoverwrites in place, and bad builds can be reverted via git like any other commit.bvar_dsge estimation: fixed regression introduced by the @epilogue auto-append work. The recent rewiring of
@transition_functions/@epilogueinto every output engine (see the entry below) introduced a method-dispatch failure on the bvar_dsge likelihood path. The bvar_dsge solver leaves its internal dsge representation in struct form (rather than as a@dsgeobject), and the added call toappend_epilogueinside the Kalman save-filters step then could not resolve. The runtime evaluator has been moved out of@dsgeinto thedsge_tools.epiloguenamespace so it dispatches uniformly on either representation. No change to user-facing API; bvar_dsgeestimate,filter,forecastandmode_curvaturego through the standard paths again.sstate_endo_param_swap: target values now preserved to full double precision. The internal constraint equation built from a
sstate_endo_param_swap = {endogName, value, paramName}triple was rendered withsprintf('%g', value), which truncates to six significant digits. For irrational targets such aslog(1/3), this baked a ~2e-6 rounding error directly into the residual the solver was driving to zero. The render now uses%.17g(round-trip precision for IEEE-754 doubles), so constraints withlog,exp,piand similar transcendental targets solve to machine precision again.@transition_functions and @epilogue: fully wired into all output paths. Time-varying transition probabilities and post-solve derived series declared in
@transition_functions/@epilogueblocks now appear automatically on the output of every engine that produces a simulatedtsdatabase:simulate,irf,forecast,filter,perfect_foresight,counterfactual,resimulate, andbrute_force_simulate. Previously onlysimulate/irf/forecast(which share a common engine) auto-appended them; users on the other paths saw the series silently missing and had to callobj.append_epilogue(db)by hand.Also fixed in the same round:
Switching parameters now follow the realised regime path. A switching parameter referenced on the RHS of an
@epilogueor@transition_functionsequation used to take its regime-1 value everywhere — wrong whenever the realised regime trajectory included other regimes. The runtime now builds atsindexed bydb.regimefor switching parameters, so the value at each period follows the realised path. Const-chain parameters are unchanged.#-definitions are usable on the RHS. A
#-definition like# mid_p = (p_lo + p_hi)/2;can now be referenced by its bare name inside a@transition_functionsor@epilogueequation; previously the name was left unbound and the runtime crashed on first call.Multiple ``@transition_functions`` / ``@epilogue`` blocks in the same model file are stitched together — you can split a chain’s two probabilities across two blocks or interleave them with other declarations.
Perfect-foresight solver: faster and more robust. The deterministic perfect_foresight solver gained several improvements, all behaviour-preserving at their defaults (see Deterministic and quasi-deterministic solutions):
the default
'sparse'solver now judges convergence on the max-abs (infinity-norm) residual rather than the 2-norm. The 2-norm of a stacked residual grows like \(\sqrt{T\times\text{endo\_nbr}}\), so a fixed tolerance was unreachable for long horizons even when every equation residual was tiny; the max-abs criterion is per-equation and size-independent.a new JacobianUpdate option on the
'sparse'solver enables modified-Newton factorization reuse: factorize the stacked Jacobian once and reuse the LU factors (inf), or refresh everyKiterations;1(default) is full Newton. On transition problems this leaves the solution unchanged while substantially cutting the time in the linear solve.a new simul_reuse_sstate option (default
false) skips the internal steady-state re-solve when the model is already solved – for pre-solved / repeated / extended-path runs.
Steady-state solve: false “could not solve” failures fixed. The steady-state success verdict compared the residual against the solver’s iterate tolerance (
sqrt(eps)), which sits right at the achievable residual floor, so well-converged solutions on some models (e.g. the canonicalramst) were falsely reported as failures. The acceptance verdict is now decoupled from the solver tolerance via a floor, judged on the max-abs residual; achieved precision is unchanged. The engine also refreshes the parameter/definition values on every solve, so parameter changes (estimation, calibration) propagate correctly into the steady state.Non-cooperative games: Markov-perfect equilibrium (MPE) as a solve-time option. Optimal-policy models that declare more than one policymaker (Nash or Stackelberg) now expose a second equilibrium concept alongside the historical open-loop equilibrium (OLE):
m = solve(m, 'solve_policy_equilibrium', 'OLE'); % default m = solve(m, 'solve_policy_equilibrium', 'MPE');
Under OLE each player optimises against the opponent’s path; under MPE each player optimises against the opponent’s policy function. For single-player models MPE and OLE coincide (Proposition 3); for multi-player Nash on the standard benchmark, the gap matches the theoretical reference. Internally the MPE branch adds a small cross-player chain-rule term to each predetermined-state FOC row – no new endogenous variables, the augmented system has the same size as the OLE-only system, and at the default
solve_policy_equilibrium='OLE'results are byte-identical to releases that did not have the option. See Optimal (Ramsey) policy for the conceptual details and the small restrictions (stochastic replanning rejects the option;@no_u_turn = truemakes the MPE addition a no-op).Very large models now parse end-to-end. Models with thousands of equations that previously could not be loaded by
rise(...)at all (notably themarco.rs/ “very large quest” model used internally) now parse in roughly three minutes on a desktop machine. The fix removed several O(n^2)-in-#equations hotspots in the post-parse_modelpipeline (shadowize_engine,occurrences,regexpmatch,update_leads_and_lags,create_functions,initialize_symbols,stamper.status), switchedrsymbdiff.occurrencefrom a dense1 x nwrtlogical mask to a sorted index list (peak memory on the same model dropped from ~9.6 GB to ~4.6 GB), and broke the reference cycle in the symbolic-differentiation CSE backend that used to keep MATLAB’s cycle-collector busy for tens of minutes at workspace teardown. The validation suite (model_creation,modeling_language,optimal_policy,steadyState,loglinear,solvers_generic,perturbation,dynare) passes with no regressions.Reporting figure x-axis fixed. Figures produced through
rprt.create_figurewith highlighted date ranges (e.g.highlighted_dates) were rendering with the data crushed into a thin strip at the left edge of the plot and the x-axis extending to year ~4016. Cause:fillwas receivingrise_dates.datesvalues that MATLAB silently downcast todatenumagainst adatetimeaxis. The highlight band is now converted todatetimebefore being passed tofill, so the patch lands in the same coordinate system as the line plot andxlimstays at the real data range.Progress monitor: explicit close handle.
progress_monitornow returns a fourth optional output, a function handle that tears down the GUI window (and tidies up the cancel-flag file) on demand. The built-in auto-close still fires when every chain reaches its announced iteration count; the new handle covers the case where the work terminates early (MaxTime/MaxFunEvals/ an exception in one chain), where the auto-close condition would never have held and the GUI used to orphan.``@log_variables`` / ``@level_variables`` are deprecated. The recommended way to declare log-approximated variables is the inline
@endogenous(log)form (with@endogenous(log_vars)and@endogenous(log_variables)as accepted aliases). The legacy block-level keywords@log_variables,@log_varsand@level_variables(with the@all_butcomplement) are still parsed for backwards compatibility but the documentation now flags them as deprecated. The two styles cannot be mixed inside the same model file.Time series indexing overhaul (partial break). The @ts class now distinguishes
()and{}the same way built-intableandtimetabledo:db(...)returns a sub-time-series,db{...}returns the raw numeric block.db.VARNAMEreturns the corresponding single-variable ts when the name is not a property. The familiardb{-1}/db(+3)“time-shift” shortcut is kept on dated time series, where a scalar integer is unambiguous (no date has a plain numeric value) - it is equivalent tolag(db, 1)/lead(db, 3)and returns a ts (NaN-padded, same length). On undated time series, a scalar numeric is the implicit date value instead (db{1990}onts(1990, data)returns row 1); time shifts on undated ts require the explicitlag/lead/shiftmethods. Scalar-numeric assignment (db{-1} = X) is not supported in either case. See Indexing and assignment for the full contract.``transform`` preserves the date axis. Period-to-period growth and difference variants (
pct_ch,diff,log_chand their YoY cousins) now return a time series of the same length as the input, with the leading rows that have no defined past observation set toNaN. The previous behaviour silently dropped those rows.Mixed-frequency date selection. Indexing a quarterly time series with an annual date, e.g.
db(ry(1991)), now correctly returns the four quarters of 1991 (previously it returned only the closest single quarter).Chow-Lin temporal disaggregation works again.
chowlinhad been calling a helper that no longer existed in the toolbox path; rewired on the liverise_datesAPI and gated on integer high-/low-frequency ratios so non-supported pairs (e.g. W->D) error cleanly. Inputs and outputs are unchanged.Date system polish.
'A'is now accepted as a public annual synonym for'Y'(date2serial('1990A1')is equivalent todate2serial('1990')/ra(1990)/ry(1990); previously the date parser rejected'A'even though every other part of the date system already accepted it).is_dateshort-circuits onrise_dates.datesand finite-integerdoubleinputs instead of going through the heavytry/catchdate2serialpath.@ts/convert.mnow usesdictionaryrather thancontainers.Mapfor its frequency-step lookups.MATLAB R2023b is now required. RISE uses the
dictionary/configureDictionarydata type, introduced in R2023b;rise_startupchecks the running release and stops with a clear error on anything older.Distribution and licensing. This edition (the “Beta” edition) is shipped in compiled (p-coded) form and is distributed personally by the author; it is proprietary and not for redistribution. See RISE Toolbox License (the full terms are also in
LICENSE.txt).The ``+rise`` package. Housekeeping commands are grouped under the
risepackage:rise.startup(rise_startup; acceptsquietfor a one-line load, andtrueto unload),rise.exit(rise_exit),rise.demo(rise_demo– installation check + a quick tour),rise.doc(opens the PDF manual),rise.version,rise.release,rise.toolbox. See Documentation of the +rise package.Refreshed start-up banner summarising your MATLAB version, the optional toolboxes (Optimization, Statistics and Machine Learning), the LaTeX status, and where the manual lives.
PDF reporting degrades gracefully: if
pdflatexis not on thePATH, RISE still loads – only PDF reporting is disabled, and the banner says so.Documentation overhaul in progress: a corrected Setting up your RISE environment page, a single-source license, this page and a Quickstart, with a chapter-by-chapter refresh to follow (see
DOC_STRATEGY.mdin the docs repository).
1.2.2. Where the canonical changelog lives
The single source of truth for release notes is now CHANGELOG.md
in the toolbox root, next to README.md. This page mirrors it; the
two are kept in sync by the documentation build (or by hand during
the transition). New entries should be added to CHANGELOG.md
first, then reflected here under the appropriate section – not the
other way around.
In MATLAB, the most recent entries are surfaced by rise.whatsnew
(or its shortcut rise_whatsnew):
rise.whatsnew % print the entire CHANGELOG
rise.whatsnew(3) % print the 3 most recent dated/Unreleased sections
rise_whatsnew(1) % shortcut, same semantics
The function lives at +rise/whatsnew.m in the toolbox; the
shortcut at m/shortcuts/rise_whatsnew.m mirrors the pattern of
rise_demo / rise_doc.