Contributor how to guides#
Adding class methods to the ValueSequence
and MultiPathSequence
classes#
Adding class methods to hpcflow.app.ValueSequence
#
ValueSequence
exposes class methods that can be used to generate sequences of values (i.e. multiple elements) within a task. Within a YAML workflow template, the requirement to generate a sequence via a class method is written using a double-colon syntax as follows:
tasks:
- objective: t1
sequences:
- path: inputs.p1
values::from_range:
start: 0
stop: 10
step: 1
In the case above, we are telling hpcFlow to use the method hpcflow.app.ValueSequence.from_range()
to generate multiple elements for the task. The inner block containing start
, stop
, and step
is then passed as a dict
of keyword arguments to that class method.
Follow these steps to add a new sequence-generating class method to hpcflow.app.ValueSequence
:
Consider how to name your new method. For consistency with existing methods, please consider a name that is prefixed by
from...
. For this example, let’s consider a method namedfrom_my_new_method
.Add a new method that generates a list of values using your new approach, named
_values_from_my_new_method`
(i.e. your new method name, prefixed by_values_
). If your new technique is parametrised by two arguments,arg_1
(an integer), andarg_2
(a list of strings), the signature of this method should look like this (the**kwargs
is optional—consider if it is necessary to include):@classmethod def _values_from_my_new_method( cls, arg_1: int, arg_2: list[str], **kwargs, ) -> Self: pass # implementation here
Add a new method that can be used within the API:
@classmethod def from_my_new_method( cls, path: str, arg_1: int, arg_2: list[str], nesting_order: float = 0, label: str | int | None = None, **kwargs, ) -> Self: """ Build a sequence from ... """ args = {"arg_1": arg_1, "arg_2": arg_2, **kwargs} values = cls._values_from_my_new_method(**args) obj = cls(values=values, path=path, nesting_order=nesting_order, label=label) obj._values_method = "from_my_new_method" obj._values_method_args = args return obj
Note that in addition to
arg_1
andarg_2
, this method signature must include some positional and keyword arguments:path
,nesting_order
, andlabel
, which should be passed directly to the constructor. We use the method added previously (_values_from_my_new_method
, in this case) to generate the values, and then pass those values on to the constructor. Note also that after object construction, we must assign two attributes:_values_method
which should be the name of this method_values_method_args
, which should be a mapping of argument names and values used to parametrise the value-generating method (_values_from_my_new_method
, in this case).
Write some tests. Please include somewhere within
hpcflow/tests/unit
at least one test to convince yourself that your new method generates the correct sequence of values:from hpcflow.app import app as hf def test_sequence_from_my_new_method(): seq = hf.ValueSequence.from_my_new_method(path="inputs.p1", arg_1=9, arg_2=['a', 'b']) # check the expected number of values generated: assert len(seq.values) == 2 # check the expected values, if possible: assert seq.values == ["val_a", "val_b"] # check the correct attributes are set: assert seq._values_method == "from_my_new_method" assert seq._values_method_args == {"arg_1": 9, "arg_2": ['a', 'b']}
Adding class methods to hpcflow.app.MultiPathSequence
#
MultiPathSequence
exposes class methods that can be used to generate multiple sequences of values (i.e. multiple elements) within a task, corresponding to multiple paths (i.e. inputs or resources). Within a YAML workflow template, the requirement to generate a multi-path sequence via a class method is written using a double-colon syntax as follows:
tasks:
- objective: t1
multi_path_sequences:
- paths: [inputs.p1, inputs.p2]
values::from_latin_hypercube:
num_samples: 5
In the case above, we are telling hpcFlow to use the method hpcflow.app.MultiPathSequence.from_latin_hypercube()
to generate five elements for the task, by combining five values for the input p1
with five values for the input p2
, where all ten values are generated at the same time via a Latin hypercube sampling. The inner block containing num_samples
, is passed as a dict
of keyword arguments to that class method.
The same process as above can be used for adding new class methods to MultiPathSequence
, with two exceptions. Firstly, a paths
postitional argument (note: plural) must be specified in the values-generating method as defined in step 2. above. Thus the method should look like this:
@classmethod
def _values_from_my_new_method(
cls,
paths: Seqeuence[str], # <- note additional `paths` argument
arg_1: int,
arg_2: list[str],
**kwargs,
) -> Self:
pass # implementation here
The reason for including the paths
argument is so we can know, for example, for how many paths the MultiPathSequence
should generate values for.
Secondly, the path
(note: singular) argument in the public-facing method (from_my_new_method
in this case) should be replaced by paths
(note: plural), which should have the type annotation: Sequence[str]
.