Supported YAML Tags

YAML allows defining custom so-called tags which can be distinguished during loading and serialization of objects. paramspace makes heavy use of this possibility, as it greatly simplifies the definition and usage of configuration files.

Python builtins and basic operators

paramspace.yaml adds YAML constructors for a number of frequently used Python built-in functions and operators. Having these available while specifying configurations can make the definition of configurations files more versatile.

Warning

The YAML tags provided here are only meant to allow basic operations, i.e. summing two parameters to create a third. Don’t overdo it. Configuration files should remain easy to read.

The tags shown below call the equivalent Python builtin or the operators defined in the operator Python module. Example:

any:      !any        [false, 0, true]    # == True
all:      !all        [true, 5, 0]        # == False

abs:      !abs        -1          # +1
int:      !int        1.23        # 1
round:    !round      9.87        # 10
sum:      !sum        [1, 2, 3]   # 6
prod:     !prod       [2, 3, 4]   # 24

min:      !min        [1, 2, 3]   # 1
max:      !max        [1, 2, 3]   # 3

sorted:   !sorted     [2, 1, 3]   # [1, 2, 3]
isorted:  !isorted    [2, 1, 3]   # [3, 2, 1]

# Operators
add:      !add        [1, 2]      # 1 + 2
sub:      !sub        [2, 1]      # 2 - 1
mul:      !mul        [3, 4]      # 3 * 4
mod:      !mod        [3, 2]      # 3 % 2
pow:      !pow        [2, 4]      # 2 ** 4
truediv:  !truediv    [3, 2]      # 3 // 2
floordiv: !floordiv   [3, 2]      # 3 / 2
pow_mod:  !pow        [2, 4, 3]   # 2 ** 4 % 3

not:      !not        [true]
and:      !and        [true, false]
or:       !or         [true, false]
xor:      !xor        [true, true]

lt:       !lt         [1, 2]      # 1 <  2
le:       !le         [2, 2]      # 2 <= 2
eq:       !eq         [3, 3]      # 3 == 3
ne:       !ne         [3, 1]      # 3 != 1
ge:       !ge         [2, 2]      # 2 >= 2
gt:       !gt         [4, 3]      # 4 >  3

negate:   !negate     [1]             # -1
invert:   !invert     [true]          # ~true
contains: !contains   [[1,2,3], 4]    # 4 in [1,2,3] == False

concat:   !concat     [[1,2,3], [4,5], [6,7,8]]  # […]+[…]+[…]+…

# List generation
# ... using the paramspace.tools.create_indices function
list1:    !listgen    [0, 10, 2]   # [0, 2, 4, 6, 8]
list2:    !listgen
  from_range: [0, 10, 3]
  unique: true
  append: [100]
  remove: [0]
  sort: true

# ... using np.linspace, np.logspace, np.arange
lin:      !linspace   [-1, 1, 5]   # [-1., -.5, 0., .5, 1.]
log:      !logspace   [1, 4, 4]    # [10., 100., 1000., 10000.]
arange:   !arange     [0, 1, .2]   # [0., .2, .4, .6, .8]

# String formatting
format1:  !format     ["{} is not {}", foo, bar]
format2:  !format
  fstr: "{some_key:}: {some_value:}"
  some_key: fish
  some_value: spam
format3:  !format
  fstr: "results: {stats[mean]:.2f} ± {stats[std]:.2f}"
  stats:
    mean: 1.632
    std:  0.026

Recursively updating maps

While YAML already provides the << operator to update a mapping, this operator does not work recursively. The !rec-update YAML tag supplies exactly that functionality using the recursive_update() function.

some_map: &some_map
  foo: bar
  spam: fish
some_other_map: &some_other_map
  foo:
    bar: baz
    baz: bar
  fish: spam

# Create a new map by recursively updating the first map with
# the second one (uses deep copies to avoid side effects)
merged: !rec-update [<<: *some_map, <<: *some_other_map]
# NOTE: Need to use  ^^-- inheritance here, otherwise this will
#       result in empty mappings (for some reason)

Warning

Always include via <<: *my_ref!

If supplying references to mappings (as shown in the example), the references have to be included using <<: *my_ref!

Otherwise, if using the simple *my_ref as argument, the YAML parser does not properly resolve the reference to the anchor but only returns an empty mapping.