utils
utils
Utilities Module for TVB-O.
Core utilities: Bunch container, PyTree formatting, YAML I/O, and metadata traversal helpers.
Plotting utilities (colors, colormaps, multiview) have moved to tvbo.plot.utils and are re-exported here for backward compatibility.
Analysis functions (per_window_fc, ttest_correlation_strength) have moved to tvbo.analysis.
Attributes
| Name | Description |
|---|---|
| INTEGRATION_METHODS | Every integration method tvbo accepts, and the extra spellings that name it. |
| ROOT_DIR | |
| cm |
Classes
| Name | Description |
|---|---|
| Bunch | Dictionary with attribute access and optional JAX PyTree support. |
Bunch
utils.Bunch()Dictionary with attribute access and optional JAX PyTree support.
Extends dict to allow both bunch["key"] and bunch.key access. If JAX is installed, registered as a PyTree via register_pytree_node_class with deterministic (sorted-key) traversal order.
Based on scikit-learn’s sklearn.utils.Bunch.
See Also
https://scikit-learn.org/stable/modules/generated/sklearn.utils.Bunch.html
Methods
| Name | Description |
|---|---|
| copy | Return a shallow copy as a new Bunch. |
| tree_flatten | Flatten the Bunch into JAX pytree (children, aux_data). |
| tree_unflatten | Reconstruct a Bunch from JAX pytree aux_data and children. |
copy
utils.Bunch.copy()Return a shallow copy as a new Bunch.
Returns
| Name | Type | Description |
|---|---|---|
A Bunch containing the same key/value pairs as this instance. |
tree_flatten
utils.Bunch.tree_flatten()Flatten the Bunch into JAX pytree (children, aux_data).
Keys are sorted so traversal order is deterministic across calls.
tree_unflatten
utils.Bunch.tree_unflatten(aux_data, children)Reconstruct a Bunch from JAX pytree aux_data and children.
Functions
| Name | Description |
|---|---|
| add_to_parameters_collection | Adds a value to a Bunch object using the provided path, without inserting a redundant sub-level. |
| as_list | Normalize a keyed-dict-or-list collection to a list of its members. |
| bind_function_arguments | Pair a model function’s declared arguments with one call site’s actual arguments. |
| deep_merge | Recursively merge override onto base, returning a new dict. |
| domain_enforcement | Normalise a state-variable domain’s enforcement mode to a plain string. |
| edge_label | Canonical Network.matrix() label for a network reference, else None. |
| edge_param | A named quantity off an Edge: its parameters entry, else its own slot. |
| format_pytree_as_string | Recursively formats a JAX pytree structure as a string with Unicode box-drawing characters. |
| from_yaml | Load a LinkML datamodel object from a YAML file. |
| initial_value | The initial value a state variable declares, else default. |
| integration_method | The canonical name of a declared integration method. Raises on a spelling tvbo does not know. |
| is_array_valued | Return True if a parameter value is an array constant rather than a scalar. |
| keyed_items | A keyed collection’s (key, member) pairs, whichever shape holds it. |
| network_couplings | network’s couplings, keyed by the role each fills. |
| noise_sigma | The noise standard deviation σ off a declared Noise, or None. |
| normalize_params | Normalize a parameters collection to a flat {name: param} dict. |
| numbered_print | Print text with each line prefixed by a zero-padded line number. |
| parameter_number | A parameter’s declared value as plain numbers, uniform sequences collapsed. |
| pretty_print_pytree | Prints a pretty formatted representation of a JAX pytree structure. |
| register_recipe_code_paths | Make a recipe’s callable code importable — the code/ convention, or a declared :class:CodeSource (a local directory or a git repository). |
| sanitize_name | Sanitise a name into a filesystem- and rule-safe token (keep alnum, _, -). |
| to_yaml | Dump a LinkML datamodel object to YAML. |
| transform_target | The edge attribute a transforms: entry rewrites, or None. |
| traverse_metadata | Recursively traverses the attributes of a metadata object, calling a callback on each Parameter. |
add_to_parameters_collection
utils.add_to_parameters_collection(key, value, path, parameters)Adds a value to a Bunch object using the provided path, without inserting a redundant sub-level.
A Parameter may carry both a scalar value AND a nested distribution (e.g. omega_mean_hz = 10 Hz + Normal(mean, std)): its scalar and the distribution’s sub-parameters navigate through the same name. The two must coexist rather than overwrite — a scalar already stored at a name is preserved under a reserved value key when that name has to become a sub-Bunch, and a scalar written onto a name that is already a sub-Bunch is stored under value instead of clobbering the sub-tree.
as_list
utils.as_list(obj)Normalize a keyed-dict-or-list collection to a list of its members.
TVBO keyed collections (parameters, space, …) are dicts keyed by each member’s identifier, but may also appear as plain lists. Returns the member values in either case (None -> []).
A scalar becomes a one-element list. Strings especially: they are iterable, so list("/data") would silently yield one entry per character — which is how a single --set container_binds=/data/cephfs-1 turned into a bind of /,d,a,t,a,…. No caller ever wants a string split into characters. A bare JsonObj — the shape an assigned collection slot takes — is iterable over its keys for the same reason, and is read through :mod:jsonasobj2 instead; the test is on the exact type, since every LinkML entity subclasses JsonObj and a lone one is a scalar here.
bind_function_arguments
utils.bind_function_arguments(func_name, formal, actual)Pair a model function’s declared arguments with one call site’s actual arguments.
A mismatch names the function and both arities. arguments: is an optional slot, so a schema-legal recipe can declare a function whose declaration and calls disagree; the two failure modes either side of this are both unhelpful. Silently truncating (a bare zip) inlines a body with a formal symbol left unbound, which surfaces much later as a wrong equation. Raising zip()’s own message names neither the function nor the recipe, and in the NeuroML path it fires inside a sympy replace callback, so the traceback is all sympy internals.
Returns {formal: actual}, keyed by whatever the caller passed as formal — names or Symbols alike.
deep_merge
utils.deep_merge(base, override)Recursively merge override onto base, returning a new dict.
Nested dicts are merged key-by-key, so an override can replace a single leaf while inheriting its siblings from base — e.g. {parameters: {a: {value: 1}}} overrides only a.value and keeps every other parameter from base. Any key whose two sides are not both dicts is taken from override. Neither input is mutated.
This is the field-level precedence used when a spec sourced by iri is refined by inline metadata: the inline value supervenes and the source (registry entry / ontology default) fills the gaps.
domain_enforcement
utils.domain_enforcement(domain)Normalise a state-variable domain’s enforcement mode to a plain string.
Returns one of 'none' (default — descriptive metadata only), 'clamp' (hard-clip to [lo, hi]) or 'wrap' (periodic). Accepts a Range/domain object (reads its enforce slot), a bare DomainEnforcement value, or None. Normalises across both generated representations of the enum — the pydantic (str, Enum) (compare via .value) and the gen-python permissible value (compare via str()) — so callers can simply test domain_enforcement(sv.domain) == 'clamp'.
edge_label
utils.edge_label(ref)Canonical Network.matrix() label for a network reference, else None.
The one resolver for every way a recipe can point at a connectome matrix, so a transform equation, an observation source and an exploration axis cannot mean different matrices by the same name. Accepts the fully-qualified form (network.weight, network.edges.length), the explicit edges.<label> form (any label), and the bare weight(s)/length(s) shortcut. Returns None for anything that is not a connectome-matrix reference (state variables, network.observations.*, …), which callers route through their normal path.
edge_param
utils.edge_param(edge, name, default=None)A named quantity off an Edge: its parameters entry, else its own slot.
weight/delay/distance are both first-class Edge slots and valid entries in the generic parameters collection, so a recipe may spell either. parameters wins when both are set. This is the single reader every backend goes through, so one recipe cannot mean different connectomes on different backends. Returns the value verbatim (no coercion), or default.
format_pytree_as_string
utils.format_pytree_as_string(
pytree,
name='root',
prefix='',
is_last=False,
show_numerical_only=False,
is_root=True,
hide_none=False,
show_array_values=False,
)Recursively formats a JAX pytree structure as a string with Unicode box-drawing characters.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| pytree | Any | The pytree to format. | required |
| name | str | The name of the current node. | 'root' |
| prefix | str | Current line prefix. | '' |
| is_last | bool | Whether the current node is the last child of its parent. | False |
| show_numerical_only | bool | If True, only show arrays and numerical types (float, int, etc.). | False |
| is_root | bool | Whether this node is the root of the tree. | True |
| hide_none | bool | If True, fields with None values will be hidden. | False |
| show_array_values | bool | If True, print full array values instead of summaries. | False |
Returns
| Name | Type | Description |
|---|---|---|
| str | str | The formatted string representation of the pytree. |
from_yaml
utils.from_yaml(filepath, cls)Load a LinkML datamodel object from a YAML file.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| filepath | str | Path to the YAML file. | required |
| cls | type | The datamodel class to instantiate. | required |
Returns
| Name | Type | Description |
|---|---|---|
| object | object | An instance of the datamodel class populated with data from the YAML file. |
initial_value
utils.initial_value(sv, default=0.1)The initial value a state variable declares, else default.
StateVariable.initial_value has no schema default: undeclared is None and means “the spec did not say”, which is what makes the fallback the caller’s to name. A model state starts at the generic 0.1; an observation reduction’s accumulator starts at its reduction identity 0.0, which is a different question and so is passed explicitly.
The slot used to carry ifabsent: float(0.1), which materialised 0.1 for every state variable. That made “undeclared” unrepresentable — every consumer’s own is None fallback was unreachable, and a reduction observer could not distinguish a declared 0.1 from a spec that said nothing.
integration_method
utils.integration_method(method, *, strict=True)The canonical name of a declared integration method. Raises on a spelling tvbo does not know.
A recipe writes rk4, RungeKutta4thOrder or runge_kutta and means one method; the ontology that holds its update expression and the adapter that picks a backend solver each need the one name. Reading the spelling through here is what stops the two from answering differently — the failure this replaces was a recipe that ran correctly on tvboptim, which knew rk4, and died in the tvb template on 'NoneType' object has no attribute 'equation', because the ontology did not and left the update expression unfilled.
An unrecognised spelling raises rather than resolving to a default: silently integrating by a scheme the recipe did not ask for changes the numbers it reports. strict=False answers None for it instead, which is what a caller that only wants to look the method up needs: Integrator.method is an open vocabulary for a backend that supplies its own solver — a NetworkDynamics.jl recipe naming AutoTsit5 hands that string to Julia’s solve and is not a mistake — so failing to recognise a spelling may not be an error at the point of the lookup.
is_array_valued
utils.is_array_valued(value)Return True if a parameter value is an array constant rather than a scalar.
Array-valued parameters (e.g. mode-coupling matrices, Gaussian-quadrature vectors) are stored as nested lists/tuples in YAML or as np.ndarray when set programmatically. Scalar-only call sites (float(p.value) substitution, sympy subs) must skip them. Single source of truth so list/tuple and ndarray are treated consistently everywhere.
keyed_items
utils.keyed_items(collection, kind='collection')A keyed collection’s (key, member) pairs, whichever shape holds it.
The generated dataclasses wrap a keyed collection in a JsonObj when it is assigned, and a JsonObj has no .items; the Pydantic models keep a plain dict. The schema also allows the list spelling, whose members carry their own name. Anything else raises: a reader that answers “nothing here” for a shape it did not recognise reports an empty collection as an empty result, which is how an unchecked from_datamodel load went unchecked.
The JsonObj test is on the exact type, because every LinkML entity subclasses it: a lone Coupling read as a collection would answer with its own 22 field names, which is a shape mismatch reported as data rather than as an error.
network_couplings
utils.network_couplings(network)network’s couplings, keyed by the role each fills.
A coupling acts over a connectivity, so this slot is the only place one is declared and every backend reads it through here. Assigning a mapping to a keyed multivalued slot leaves a JsonObj on the generated dataclass — no .values(), no .items() — so a reader that reaches for either sees a coupling on one form of the record and an AttributeError on the other.
noise_sigma
utils.noise_sigma(noise)The noise standard deviation σ off a declared Noise, or None.
The one reader for every spelling the schema allows, so a recipe cannot mean a different amplitude on different backends. Each spelling has exactly one meaning:
parameters: {sigma: {value: s}}→s. Wins whenever present.parameters: {nsig: {value: D}}→sqrt(2 D). The dispersion spelling (D = σ²/2) — what a TVB import writes.
Returns None when the noise declares no amplitude at all (and for a missing Noise), leaving “absent” distinguishable from an explicit zero.
normalize_params
utils.normalize_params(params)Normalize a parameters collection to a flat {name: param} dict.
Accepts the keyed mapping {weight: Parameter(...)} (LinkML JsonObj or plain dict), the list-of-mappings [{weight: {value: 1.0}}, ...] that raw YAML may produce, and a list of Parameter objects. Applies to edge, node and dynamics parameter collections alike.
A bare JsonObj — what an assigned collection slot holds — has no .items, and iterating it yields its keys; it is read through :mod:jsonasobj2 instead. The test is on the exact type, because every LinkML entity subclasses JsonObj and a single Parameter read that way would answer with its own field names.
numbered_print
utils.numbered_print(text)Print text with each line prefixed by a zero-padded line number.
Line numbers start at 1 and are padded to the width of the largest number so the printed numbers stay aligned.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| text | The multi-line string to print. | required |
parameter_number
utils.parameter_number(value)A parameter’s declared value as plain numbers, uniform sequences collapsed.
Parameter.value is scalar for most models, one entry per mode for a multi-mode one, and a matrix for a mode-coupled one (ReducedSetHindmarshRose’s A_ik), so it nests to arbitrary depth. A sequence whose entries are all equal collapses to the scalar it means; anything else keeps its shape, because reducing a genuinely heterogeneous value to its first entry would silently change the model.
Backends that can only emit scalars use this to decide, rather than each deciding differently — or, as the PyRates emitter did, calling float() and raising.
pretty_print_pytree
utils.pretty_print_pytree(
pytree,
name='root',
prefix='',
show_numerical_only=False,
hide_none=False,
)Prints a pretty formatted representation of a JAX pytree structure.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| pytree | Any | The pytree to print. | required |
| name | str | The name of the current node. | 'root' |
| prefix | str | Current line prefix. | '' |
| show_numerical_only | bool | If True, only show arrays and numerical types (float, int, etc.). | False |
| hide_none | bool | If True, fields with None values will be hidden. | False |
Returns
| Name | Type | Description |
|---|---|---|
| None | None |
register_recipe_code_paths
utils.register_recipe_code_paths(source_file, code_source=None)Make a recipe’s callable code importable — the code/ convention, or a declared :class:CodeSource (a local directory or a git repository).
A recipe references custom builders and analysis callables by bare module name (e.g. module: taher2019_analysis); their directory must be on sys.path for import to resolve them. Resolution:
- Explicit
code_source(aCodeSourceor dict on the study) — decouples the specification from where its code lives:path— a directory (relative to the recipe YAML, or absolute); orgit— a repository shallow-cloned and cached under~/.cache/tvbo/code_sources/<url+ref hash>, checked out atref. An optionalsubdirnarrows which directory of the source is used.
- Convention (no
code_source) — thecode/subdir beside the recipe YAML.
Registering at load time, once and left in place (callables resolve lazily during a run), lets tvbo run / tvbo workflow and notebooks load a recipe without a PYTHONPATH prefix. The dir goes to the front of sys.path (matching PYTHONPATH) and is skipped when already present. Returns the paths newly inserted.
sanitize_name
utils.sanitize_name(name)Sanitise a name into a filesystem- and rule-safe token (keep alnum, _, -).
to_yaml
utils.to_yaml(obj, filepath=None)Dump a LinkML datamodel object to YAML.
- If filepath is provided, write YAML to that file and return the path.
- If filepath is None, return the YAML string.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| obj | object | Datamodel object to serialize. | required |
| filepath | str | None | Optional path to write YAML. | None |
Returns
| Name | Type | Description |
|---|---|---|
| str | str | File path when written to disk, otherwise the YAML string. |
transform_target
utils.transform_target(func)The edge attribute a transforms: entry rewrites, or None.
A transform’s identifier is its target, so a recipe spells it target: — the schema declares that an alias of name, which LinkML requires to stay the identifier. Reading it through here says which of the two meanings a call site wants, since name: weight beside rhs: weight / max(weight) otherwise reads as if the two were different things.
traverse_metadata
utils.traverse_metadata(
metadata,
target_instance=None,
path=None,
callback=None,
callback_kwargs=None,
keys_to_exclude=(),
)Recursively traverses the attributes of a metadata object, calling a callback on each Parameter.