Configuration#

To facilitate collaboration and better visual representation, mrfmsim allows using YAML configuration files to define experiments. The configuration files also make the experiments more portable for sharing and archiving.

For example, an Experiment object can be defined:

# example.yaml
!Experiment
name: test_experiment
graph:
    !Graph
    name: test_graph
    grouped_edges:
        - [add, [subtract, power, log]]
        - [[subtract, power], multiply]
    node_objects:
        add:
            func: !func:add "lambda a, h: a + h"
            doc: Add a and h.
            inputs: [a, h]
            output: c
        subtract:
            func: !import operator.sub
            output: e
            inputs: [c, d]
        power:
            func: !import math.pow
            output: g
            inputs: [c, f]
        multiply:
            func: !import numpy.multiply
            output: k
            inputs: [e, g]
            output_unit: m^2
        log:
            func: !import math.log
            output: m
            inputs: [c, b]
components: {comp: [[a, a1], [b, b1]]}
doc: Test experiment with components.
modifiers: [!import:mmodel.modifier.loop_input {parameter: d}]
defaults:
    h: 2

The above YAML string is stored in the “experiment.yaml” file. The above experiment’s graph defines a model that calculates the following:

\[k = (a + h - d)(a + h)^f m = log(a + h, b)\]

The model is equivalent to the function with the modifiers and component replacements, The h value is defaulted to 2:

def test_experiment(comp, d_loop, f, h=2):

    a = comp.a1
    b = comp.b1

    # the following is executed in steps
    # to show the intermediate values that the model presents
    c = a + h
    result = []
    for d in d_loop:
        e = c - d
        g = c ** f
        k = e * g
        m = log(c, b)
        result.append((k, m))
    return result

To convert the above string from YAML to a Python object:

import yaml
from mrfmsim.configuration import MrfmSimLoader


with open('experiment.yaml', 'r') as f:
    experiment = yaml.load(f, Loader=MrfmSimLoader)

If the YAML is defined as a string, read from the string directly.

The experiment object is an instance of the Experiment class. The modifier creates a loop over the d parameter. We can print the experiment:

>>> print(experiment)
test_experiment(comp, d_loop, f, h=2)
returns: (k, m)
return_units: {'k': 'm^2'}
graph: test_graph
handler: MemHandler
modifiers:
- loop_input('d')
components:
- comp: [['a', 'a1'], ['b', 'b1']]

Test experiment with components.

YAML Tags#

YAML tags add additional functionality to the YAML parsing. For detailed usage see yaml tags. The following tags are available in mrfmsim:

  • import a Python object

!import:object
  • import a Python class and instantiate

!import:types.SimpleNamespace
a: 1
b: 2
  • create a mmodel.Graph instance

!graph
name: example_graph
grouped_edges:
    - [add, [subtract, power, log]]
    - [[subtract, power], multiply]
node_objects:
    add:
        func: !import numpy.add
        output: c
        inputs: [a, h]
    subtract:
        func: !import operator.sub
        output: e
        inputs: [c, d]
    power:
        func: !import math.pow
        output: g
        inputs: [c, f]
    multiply:
        func: !import numpy.multiply
        output: k
        inputs: [e, g]
    log:
        func: !import math.log
        output: m
        inputs: [c, b]
  • create a Python function called “test” from an expression

!func:test "lambda x: x+1"
  • create an Experiment object

!Experiment
name: test_experiment
graph:
    !Graph
    name: test_graph
    grouped_edges:
        - [add, [subtract, power, log]]
        - [[subtract, power], multiply]
    node_objects:
        add:
            func: !func:add "lambda a, h: a + h"
            doc: Add a and h.
            inputs: [a, h]
            output: c
        subtract:
            func: !import operator.sub
            output: e
            inputs: [c, d]
        power:
            func: !import math.pow
            output: g
            inputs: [c, f]
        multiply:
            func: !import numpy.multiply
            output: k
            inputs: [e, g]
            output_unit: m^2
        log:
            func: !import math.log
            output: m
            inputs: [c, b]
components: {comp: [[a, a1], [b, b1]]}
doc: Test experiment with components.
modifiers: [!import:mmodel.modifier.loop_input {parameter: d}]
defaults:
    h: 2
  • create a Collection object

    !Collection
name: test_collection
description: Test collection object.
node_objects:
    add:
        func: !func:add "lambda a, h: a + h"
        doc: Add a and h.
        inputs: [a, h]
        output: c
    subtract:
        func: !import operator.sub
        output: e
        inputs: [c, d]
    power:
        func: !import math.pow
        output: g
        inputs: [c, f]
    multiply:
        func: !import numpy.multiply
        output: k
        inputs: [e, g]
        output_unit: m^2
    log:
        func: !import math.log
        output: m
        inputs: [c, b]
instructions:
    test1:
        grouped_edges:
            - [add, [subtract, power, log]]
            - [[subtract, power], multiply]
        returns: [k]
    test2:
        grouped_edges:
            - [add, [subtract, power, log]]
        doc: Shortened graph.
        returns: [c, m]
settings:
    components: {comp: [[a, a1], [b, b1]]}
    doc: Global docstring.
    defaults:
        h: 2

Security#

Since the configuration files allow direct Python function expression definition, there is always a risk of malicious code execution. Since running the Python code directly to define the experiment has the same issue, we do not consider this a significant problem. We encourage users to inspect the code or the experiment file before executing.

configuration module#

Load and dump configuration files for experiment and jobs.

mrfmsim.configuration.collection_constructor(loader, node)[source]#

Parse the “!Collection” tag into ExperimentCollection object.

mrfmsim.configuration.experiment_constructor(loader, node)[source]#

Parse the “!Experiment” tag into Experiment object.

mrfmsim.configuration.func_multi_constructor(loader, tag_suffix, node)[source]#

Load the “!func:” tag from yaml string.

The constructor parses !func:function “lambda a, b: a + b”. In the example, the name of the function is set to “function”, the function is the lambda expression. The doc is None, set doc at the node level.

mrfmsim.configuration.graph_constructor(loader, node)[source]#

Parse the “!Graph” tag into Graph object.

The node are listed as a dictionary of node name and node object. The design is for a clearer view of nodes. To change this behavior, change the graph_constructor.

mrfmsim.configuration.import_constructor(loader, node)[source]#

Parse the “!import” tag into object.

mrfmsim.configuration.import_multi_constructor(loader, tag_suffix, node)[source]#

Parse the “!import:” multi tag into an object with parameters.

The node is parsed as a dictionary.

mrfmsim.configuration.import_object(path)[source]#

Load object from the path.

The path is split by the rightmost ‘.’ and the module is imported. If the module is not found, the path is split by the next rightmost dot and the module is imported. This is repeated until the module is found. Otherwise, a ModuleNotFoundError is raised.

Parameters:

path (str) – dotpath for importing

mrfmsim.configuration.yaml_loader(constructor)[source]#

Create a yaml loader with special constructors.

Parameters:

constructor_dict (dict) – dictionary of constructors

Returns:

yaml loader class