3. Portable model documents

A parsed RISE model is a large object full of derived structure: expanded equations, auxiliary variables the parser introduced, symbolic derivatives, solver scaffolding. Saving it with save produces a binary file that cannot be read, cannot be reviewed, and cannot be relied on across versions.

A portable document is the same model written as readable JSON. It is versioned, so a reader knows what it is looking at. It is complete enough to rebuild the model. And it is comparable, so two documents can be diffed the way source is diffed, in model terms rather than as text lines.

3.1. Writing a model out

m = dsge_model('fs2000');

rise.to_portable(m, 'fs2000.json');       % write a file
doc = rise.to_portable(m);                % or keep the document

Options:

Force (default false)

Write even when part of the model cannot be carried in readable form. The document then describes less than the model does, so the default is to refuse rather than to produce a document that quietly omits something.

IncludeValues (default true)

Carry parameter values alongside the structure.

The document records a format version, and that version is the contract:

doc.format
% rise-portable/1.0.0

3.2. Reading one back

m2 = rise.from_portable('fs2000.json');
m2 = rise.from_portable(doc);

Options:

KeepSource

Keep the reconstructed RISE model file at a path you name rather than in a temporary one. Useful when you want to see exactly what was parsed.

ModelOptions

A cell array forwarded to the model constructor.

A document written in a format this build does not read is refused by name:

RISE:portable:unsupportedFormat
This document is written in format rise-portable/99.0.0. This
build reads rise-portable/1.0.0. Rewrite it with the version of
RISE that produced it, or read it with a build that supports it.

3.3. Comparing two models

rise.portable_diff('baseline.json', 'revision.json')   % prints
report = rise.portable_diff(mOld, mNew);               % returns

Either side may be a file, a document or a model. The report has a field per section – endogenous, exogenous, parameters, observables, markov_chains, equations, values – and an identical flag.

The comparison is in model terms. Reordering a declaration block, renaming a file or reformatting a comment produces no difference, which is what makes this more useful than diffing two model files. A changed value or a changed equation does:

values changed (1):
  delta                    0.02           -> 0.025

equations changed (1):
  [1]
    before: efficiency{t}=rho*efficiency{t-1}+(std_EfficiencyInnovation*EfficiencyInnovation{t});
    after : efficiency{t}=rho*efficiency{t-1}+0.1*efficiency{t-2}+(std_EfficiencyInnovation*EfficiencyInnovation{t});

3.4. What the round trip guarantees

Writing a model out, reading it back, and writing it out again must produce the same document. That is the property the implementation is built around and the one the tests check:

doc    = rise.to_portable(m);
m2     = rise.from_portable(doc);
report = rise.portable_diff(doc, rise.to_portable(m2));

report.identical    % true

Two parser details make this less obvious than it looks, and both are handled rather than worked around.

Definitions are not a block of their own. They are #-prefixed statements inside @model, so a document that emitted them separately would declare them twice on the way back in.

The dynamic equation list includes equations the parser generated for auxiliary variables, such as the extra lag of a variable that appears at {-2}. Carrying those would declare the auxiliary twice, so equations that mention one are dropped: the parser recreates them on the way back.

3.5. What it is for

  • Review. A model change becomes a readable diff in a pull request instead of an opaque binary.

  • Archival. A document outlives the version of RISE that wrote it, because the format is versioned and the reader says so when it cannot read one.

  • Comparison. Two vintages of the same model, or a model and its Dynare conversion, compared in model terms.

Worked example: rise-modern-tutorials/WorkingWithAModel/portable_model.