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:

  1. Explicit code_source (a CodeSource or dict on the study) — decouples the specification from where its code lives:
    • path — a directory (relative to the recipe YAML, or absolute); or
    • git — a repository shallow-cloned and cached under ~/.cache/tvbo/code_sources/<url+ref hash>, checked out at ref. An optional subdir narrows which directory of the source is used.
  2. Convention (no code_source) — the code/ 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.