Experiment configuration file#
The experiment configuration file describes the instruments required by an experiment, their parameter space and how to iterate through it.
experiment.yaml content#description: This experiment does this and that
instrument1:
interface: actuator1
parameter1: value1
parameter2: value2
instrument2:
interface: sensor1
parameter: value
connections:
- instrument3
instrument3:
parameter: value
Purpose#
This file should contain most of the parameters required by an experiment, be it to do it for the first time or to replicate or share it. This includes the parameters of the instruments used on the bench, how they are connected together, and any other information useful for the experimenter (description, expected results, manual configurations, etc.).
Syntax#
It is a YAML file, containing a top level mapping. During the initialization of
an ExperimentFactory, it is parsed to an
IterationTree which is then stored in its
experiment_config attribute.
In order to ease the representation of the different iteration methods and
leaves, custom YAML types are supported. Whereas YAML only supports lists,
mapping, integer, floats, booleans, strings and none values, this file can also
represent Phileas objects. They are identified by a ! prefix. For example, a
sequence of values can be represented by !sequence [1, 2, 3].
See also
See Custom YAML types for more details about the supported types.
Instrument entries#
Each of the mapping entries with an interface key represents an experiment
instrument. Its value is matched with the
interfaces values of bench instruments’
loaders. Think of it this way:
in a bench configuration file, the set of available instruments is listed;
in the experiment configuration file, you list the requirements of an instrument (eg. it must be an oscilloscope);
Phileas takes care of providing you with a suitable instrument.
The other non-reserved entries of the instrument represent an
IterationTree which contains the set of its
configurations. You can use the methods
configure_instrument() and
configure_experiment() of
ExperimentFactory to apply them. Internally, they
use phileas.factory.Loader.configure().
Bench instrument filtering#
Multiple bench instruments can have the same interface. For example, there might
be multiple motors or lights on the same bench. In this case, they would all
expose the same interfaces, which would prevent Phileas from knowing which
instrument to provide you. This issue can be solved by the filter entry. It
contains a set of attributes and values to be matched against the bench
instruments attributes.
Consider the following bench file:
bench.yaml with instruments exposing the same interfaces#light-motor:
loader: brandXX-modelXX
moves: light
probe-motor:
loader: brandYY-modelYY
moves: probe
Let us say that only the probe motor is used, while the light remains
stationary. Both loaders brandXX-modelXX and brandYY-modelYY expose
the same motor interface. Using filters, this experiment configuration file
selects only probe-motor:
experiment.yaml using filters to chose a bench instrument#probe-position:
interface: motor
filter:
moves: probe
x: 0.5
y: 1.2
z: 0
probe-position is matched to probe-motor, because it is the only suitable
bench instrument with the attribute moves = probe.
Connections#
An experiment is represented by its instruments, how they are configured, but also how they interact together. An experiment configuration file can represent those interactions with so-called connections, which are the edges in the global interaction graph of the experiment, where instruments are nodes.
The optional top-level connections entry contains the list of the
interaction graph edges. Each edge contains:
a mandatory origin, stored in the
fromentry,a mandatory destination, stored in the
toentry,and, optionally, arbitrary data stored in its
attributesentry.
The origin and destination of an edge are usually instruments. You can suffix
them with ports, separating them with dots. For example, oscilloscope.chA
states that the connections involves the A channel of the oscilloscope.
connections:
- from: probe
to: oscilloscope.chA
- from: holder
to: probe
attributes: holds
In this example, the probe is connected to the channel A of the oscilloscope,
and is hold by holder.
However, it is not always convenient to define the experiment graph in a
distinct entry, separating it from the instruments. In these cases, you can
define interactions directly in the entry of an instrument. The instrument
entry connections can be used similarly as the top-level entry. However, you
can use a simplified notation: if the origin or destination of an edge is the
instrument itself, you can simply omit the instrument name, starting directly
at its first port.
temperature-measurement:
interface: oscilloscope
connections:
- from: chA
to: temperature-probe
This examples states that channel A of the oscilloscope is connected to the temperature probe.
This experiment graph is built by Phileas, but it does not use it. You can
access it through
get_experiment_graph().
Documentation#
Phileas ignores all the top-level entries which are not mappings with key
interface. It is advised to document the experiment with them. For example,
you can add a description entry, which gives a unique name to the
experiment, describes the tested hypothesis, its measurements and the
expected-results.
This whole file is parsed and made available in the factory
experiment_config. You can then
retrieve that information in simple nested Python dict and list by using
to_pseudo_data_tree().
Custom YAML types#
The experiment configuration file is parsed to an
IterationTree, which is built using the
different Python classes defined in iteration. Custom YAML
data types can be used to represent these. They are identified by a !
prefix.
Cartesian product#
By default, a YAML mapping or sequence is parsed as a cartesian product:
parameter1: !sequence [1, 2, 3]
parameter2: !sequence [a, b, c]
|
v
CartesianProduct(children={'parameter1': Sequence(elements=[1, 2, 3],
default_value=_NoDefault()),
'parameter2': Sequence(elements=['a', 'b', 'c'],
default_value=_NoDefault())},
order=None,
lazy=False,
snake=False)
You can also directly invoke !product, which allows to set the node parameters, prefixed by an underscore:
!product
_lazy: true
_snake: true
parameter1: !sequence [1, 2, 3]
parameter2: !sequence [a, b, c]
|
v
CartesianProduct(children={'parameter1': Sequence(elements=[1, 2, 3],
default_value=_NoDefault()),
'parameter2': Sequence(elements=['a', 'b', 'c'],
default_value=_NoDefault())},
order=None,
lazy=True,
snake=True)
Other iteration methods#
Similarly, you can use !union, !pick and !configurations to build
Union, Pick and
Configurations.
Unary nodes#
!shuffle builds Shuffle node, and requires its
only child to be stored in the child key:
!shuffle
child: !sequence [1, 2, 3]
|
v
Shuffle(child=Sequence(elements=[1, 2, 3], default_value=_NoDefault()),
seed=None)
For now, transform nodes are not supported. You have to add them to the iteration tree manually, from a Python script.
Iteration leaves#
!sequence builds Sequence nodes. It can be used
in the short form !sequence [a, b, c], or its long form
!sequence
elements: [a, b, c]
default: z
!range is often used, as it represents different kinds of numeric ranges. It
expects a start and end parameters, and either
steps, the total number of points in the range, orresolution, the maximum distance between two points in the range.
!random represent random distribution, and is parsed to
NumpyRNG. It expects a distribution argument,
which is the string name of a numpy distribution. You can find them in
numpy.random.Generator. Its parameters are given in the
parameters field, as a mapping. You can additionally give it a finite size,
and a default value.
If you want to generate arbitrarily big integers, you can’t use !random, as
Numpy does not support generating them. Instead, you can use
!random_uniform_bigint to generate uniformly sampled integers. It takes two
mandatory arguments low and high, which are the inclusive bounds of the
generated interval.
You can generate prime numbers with !random_prime, which has the same
arguments as !random_uniform_bigint.
Template generation#
A bench template file can be generated automatically by Phileas. See python -m phileas generate experiment -h for the available options.