9.3.3. Replicating parameters

The previous example with reading a set of parameters from the dictionary does not do itself too much functionality. Let us now use the feature of multi dimensional indexing, described in Indexing.

9.3.3.1. The bundle specification

The bundle field of the configuration may contain two options: nidx and major. The latter is either NIndex instance or NIndex specification. nidx will provide information on actual computational chain multiplicity. The former specifies a subset of nidx indices that should be considered as major.

It is supposed that bundle will create some distinct configuration for each nidx_major, while nidx_minor define exact replicas of each nidx_major state.

When applied to the parameters, there should be provided a value with uncertainties for each nidx_major iteration. The parameter will be replicated for each nidx_minor iteration.

We expect that the configuration will contain the name of the parameter (parameter), a set of the values and uncertainties (pars) and, optionally, a format string for parameter labels (label). The code of the bundle reads as follows:

 1class parameters_ex02(TransformationBundle):
 2    def __init__(self, *args, **kwargs):
 3        TransformationBundle.__init__(self, *args, **kwargs)
 4
 5    def define_variables(self):
 6        parname = self.cfg.parameter
 7        pars = self.cfg.pars
 8        labelfmt = self.cfg.get('label', '')
 9
10        for it_major in self.nidx_major:
11            major_values = it_major.current_values()
12            if major_values:
13                parcfg = pars[major_values]
14            else:
15                parcfg = pars
16
17            for it_minor in self.nidx_minor:
18                it=it_major+it_minor
19                label = it.current_format(labelfmt) if labelfmt else ''
20
21                par = self.reqparameter(parname, it, cfg=parcfg, label=label)

Note

The parameters_ex02 bundle from tutorial is a simplified copy of the parameters_v01 bundle, provided by GNA.

We start iterating over major indices and for each iteration request a value from the pars:

1        for it_major in self.nidx_major:
2            major_values = it_major.current_values()
3            if major_values:
4                parcfg = pars[major_values]
5            else:
6                parcfg = pars

In a special case of 0 dimensions, current_values() return empty tuple. In this case we expect pars to contain a single parameter specification.

The next few lines

1            for it_minor in self.nidx_minor:
2                it=it_major+it_minor

implement the loop over minor indices. We create a combined index it and use it to produce a formatted label.

And as the last action we define a parameter:

1                par = self.reqparameter(parname, it, cfg=parcfg, label=label)

The reqparameter() method is similar to the environments reqparameter(), described previously. The main difference as it takes current iteration of multi index to properly format the name.

Note, also, that we are using reqparameter() instead of defparameter(). This method enables the user to predefine the parameter by the user before executing the bundle.

Let us now check three examples.

9.3.3.2. Only major indices

The first one has the following configuration:

 1cfg1 = NestedDict(
 2    bundle = dict(
 3        # Bundle name
 4        name='parameters',
 5        # Bundle version
 6        version='ex02',
 7        # Multi-index specification (may be set outside)
 8        nidx=[
 9            ('s', 'source',   ['SA', 'SB']),
10            'name',
11            ('d', 'detector', ['D1', 'D2', 'D3']),
12            ],
13        ),
14    # Parameter name to be defined
15    parameter = 'rate0',
16    # Label format
17    label='Flux normalization {source}->{detector}',
18    # Dictionary with parameter values and uncertainties
19    pars = uncertaindict(
20        [
21            ( 'SA.D1', 1.0 ),
22            ( 'SB.D1', 2.0 ),
23            ( 'SA.D2', 3.0 ),
24            ( 'SB.D2', 4.0 ),
25            ( 'SA.D3', 5.0 ),
26            ( 'SB.D3', 6.0 ),
27            ( 'SA.D4', 7.0 ),
28            ( 'SB.D4', 8.0 ),
29            ],
30        uncertainty = 1.0,
31        mode = 'percent',
32        ),
33)
34b1 = execute_bundle(cfg1, namespace=env.globalns('bundle1'))
35env.globalns('bundle1').printparameters(labels=True); print()

Now in the bundle configuration we have specified indices:

1        nidx=[
2            ('s', 'source',   ['SA', 'SB']),
3            'name',
4            ('d', 'detector', ['D1', 'D2', 'D3']),
5            ],

We have also defined the order of the keys to be {source}.{name}.{detector}.

The bundle will create parameters with name rate0 and the following label:

1    parameter = 'rate0',
2    label='Flux normalization {source}->{detector}',

The parameters’ values and uncertainties are specified further:

 1    pars = uncertaindict(
 2        [
 3            ( 'SA.D1', 1.0 ),
 4            ( 'SB.D1', 2.0 ),
 5            ( 'SA.D2', 3.0 ),
 6            ( 'SB.D2', 4.0 ),
 7            ( 'SA.D3', 5.0 ),
 8            ( 'SB.D3', 6.0 ),
 9            ( 'SA.D4', 7.0 ),
10            ( 'SB.D4', 8.0 ),
11            ],
12        uncertainty = 1.0,
13        mode = 'percent',
14        ),

As soon as have taken out uncertainty and mode specification, all the parameters will have the uncertainty of 1%.

Let us now execute the bundle and print the namespace contents.

1b1 = execute_bundle(cfg1, namespace=env.globalns('bundle1'))
2env.globalns('bundle1').printparameters(labels=True); print()

Note, that we have specified, that the bundle used predefined namespace instead of the global one. And the output is here:

Variables in namespace 'bundle1.SA.rate0':
  D1                   =          1 │           1±        0.01 [          1%] │ Flux normalization SA->D1
  D2                   =          3 │           3±        0.03 [          1%] │ Flux normalization SA->D2
  D3                   =          5 │           5±        0.05 [          1%] │ Flux normalization SA->D3
Variables in namespace 'bundle1.SB.rate0':
  D1                   =          2 │           2±        0.02 [          1%] │ Flux normalization SB->D1
  D2                   =          4 │           4±        0.04 [          1%] │ Flux normalization SB->D2
  D3                   =          6 │           6±        0.06 [          1%] │ Flux normalization SB->D3

9.3.3.3. Major and minor indices

Let us now slightly tweak the previous example by adding an extra index and setting it to be minor.

 1cfg2 = NestedDict(
 2    bundle = dict( name='parameters',
 3                   version='ex02',
 4                   nidx=[
 5                       ('s', 'source',   ['SA', 'SB']),
 6                       ('d', 'detector', ['D1', 'D2', 'D3']),
 7                       ('e', 'element', ['e0', 'e1'])
 8                       ],
 9                   major=('s', 'd')
10                   ),
11    parameter = 'rate1',
12    label='Flux normalization {source}->{detector} ({element})',
13    pars = uncertaindict(
14        [
15            ( 'SA.D1', (1.0, 1.0) ),
16            ( 'SB.D1', (2.0, 2.0) ),
17            ( 'SA.D2', (3.0, 3.0) ),
18            ( 'SB.D2', (4.0, 4.0) ),
19            ( 'SA.D3', (5.0, 5.0) ),
20            ( 'SB.D3', (6.0, 6.0) ),
21            ( 'SA.D4', (7.0, 7.0) ),
22            ( 'SB.D4', (8.0, 8.0) ),
23            ],
24        mode = 'percent',
25        ),
26)

We have also provided a special format for label to include new index. As one can see, each of the 6 permutations of the values of the major index now contains two copies for each of the values of index e.

Variables in namespace 'bundle2.rate1.SA.D1':
  e0                   =          1 │           1±        0.01 [          1%] │ Flux normalization SA->D1 (e0)
  e1                   =          1 │           1±        0.01 [          1%] │ Flux normalization SA->D1 (e1)
Variables in namespace 'bundle2.rate1.SA.D2':
  e0                   =          3 │           3±        0.09 [          3%] │ Flux normalization SA->D2 (e0)
  e1                   =          3 │           3±        0.09 [          3%] │ Flux normalization SA->D2 (e1)
Variables in namespace 'bundle2.rate1.SA.D3':
  e0                   =          5 │           5±        0.25 [          5%] │ Flux normalization SA->D3 (e0)
  e1                   =          5 │           5±        0.25 [          5%] │ Flux normalization SA->D3 (e1)
Variables in namespace 'bundle2.rate1.SB.D1':
  e0                   =          2 │           2±        0.04 [          2%] │ Flux normalization SB->D1 (e0)
  e1                   =          2 │           2±        0.04 [          2%] │ Flux normalization SB->D1 (e1)
Variables in namespace 'bundle2.rate1.SB.D2':
  e0                   =          4 │           4±        0.16 [          4%] │ Flux normalization SB->D2 (e0)
  e1                   =          4 │           4±        0.16 [          4%] │ Flux normalization SB->D2 (e1)
Variables in namespace 'bundle2.rate1.SB.D3':
  e0                   =          6 │           6±        0.36 [          6%] │ Flux normalization SB->D3 (e0)
  e1                   =          6 │           6±        0.36 [          6%] │ Flux normalization SB->D3 (e1)

9.3.3.4. Sanity check: 0d case

Let us double check the setup by passing the 0d index.

 1cfg3 = NestedDict(
 2    bundle = dict( name='parameters',
 3                   version='ex02',
 4                   ),
 5    parameter = 'constant',
 6    label='some constant',
 7    pars = uncertain( -1.0, uncertainty=4.0, mode='percent')
 8)
 9b3 = execute_bundle(cfg3, namespace=env.globalns('bundle3'))
10env.globalns.printparameters(labels=True)

As we have printed the whole namespace it not contains the parameters from all the examples in their own namespaces. The last line represents the 0d case as defined by the configuration:

Variables in namespace 'bundle3':
  constant             =         -1 │          -1±       -0.04 [          4%] │ some constant