Tree utilities#
Tree rewriting accepts a single pybamm.Symbol root. To rewrite several
expressions with the same mapping, iterate explicitly and share a cache:
cache = {}
equations = {
variable: pybamm.replace(equation, mapping, cache=cache)
for variable, equation in model.rhs.items()
}
The cache belongs to this mapping; use a fresh cache for a different mapping.
Model parameter processing remains available through
parameter_values.process_model(model).
- class pybamm.TreeDef(node_type, n_children, children_container, leaf_spec, state)[source]#
The static structure of a symbol: everything needed to rebuild it from its leaves. Returned by
tree_flatten()and consumed bytree_unflatten().- Parameters:
node_type (type) – The symbol class.
n_children (int) – How many leading leaves are the node’s children.
children_container (type) –
listortuple, whichever the node stores its children in.leaf_spec (tuple) –
(field, kind, count, container)per extra leaf field, in order.state (dict) – The node’s remaining (non-leaf, non-derived) state.
- pybamm.tree_flatten(symbol: Symbol) tuple[tuple[Symbol, ...], TreeDef][source]#
Split a symbol into its leaves (children and the other symbols it depends on) and a
TreeDefdescribing how to put it back together.
- pybamm.tree_unflatten(treedef: TreeDef, leaves: Sequence[Symbol]) Symbol[source]#
Rebuild a symbol from a
TreeDefand its leaves, without simplification or validation. The result has no id yet.
- pybamm.rebuild(node: Symbol, new_leaves: Sequence[Symbol], simplify: bool = True) Symbol[source]#
Return
nodewith its leaves swapped fornew_leaves.nodeitself is returned when no leaf changed, so untouched subtrees are shared rather than copied. When only children changed the node’s constructor is used (so derived names, domains and, ifsimplify, simplifications are applied); otherwise the structure is rebuilt as-is withtree_unflatten().
- pybamm.tree_map(fn: Callable[[Symbol, tuple[Symbol, ...]], Symbol], tree: Symbol, cache: MutableMapping[Symbol, Symbol] | None = None, is_leaf: Callable[[Symbol], bool] | None = None) Symbol[source]#
Rewrite an expression tree bottom-up, out of place.
- Parameters:
fn (callable) –
fn(node, new_leaves)returns the node to use in place ofnode, given the already-rewritten leaves. Userebuild()for the default “same node with new leaves” behaviour.tree (
pybamm.Symbol) – The root of the expression tree to rewrite.cache (dict, optional) – Memo of
{original node: rewritten node}. Shared nodes are rewritten once; pass the same dict across calls to share work between trees.is_leaf (callable, optional) –
is_leaf(node)returning True stops the descent atnode:fnis then called asfn(node, ())with nothing below it rewritten.
- Return type:
The rewritten symbol.
treeis not modified.
- pybamm.replace(tree: Symbol, mapping: Mapping[Symbol, Any], cache: MutableMapping[Symbol, Symbol] | None = None, simplify: bool = True) Symbol[source]#
Substitute symbols throughout an expression tree, out of place.
Every node equal (by id) to a key of
mappingis replaced by the corresponding value, which is not itself traversed. All other nodes are rebuilt withrebuild()only if something below them changed, so the result shares every untouched subtree withtree.- Parameters:
tree (
pybamm.Symbol) – The root of the expression tree to substitute into.mapping (dict) –
{symbol: replacement}; replacements may be numbers.cache (dict, optional) – Memo shared between calls made with the same
mapping.simplify (bool, optional) – Whether rebuilt operators apply their usual simplifications (default True).
Migrating in-place updates#
Public symbol setters and domain/list edits remain available but emit
SymbolMutationDeprecationWarning. Prefer constructing replacements:
updated = variable.create_copy(scale=2, reference=0, bounds=(0, 1))
expression = pybamm.replace(expression, {variable: updated})
symbol = symbol.with_domains({"primary": ["negative electrode"]})
symbol = symbol.without_domains()
symbol = symbol.with_mesh(mesh)
For names, scalar values and function inputs, construct a new symbol of the
appropriate class. IDs update automatically; callers need not call set_id.
Deprecated updates invalidate cached IDs and derived values across live symbols.
This deliberately slow compatibility path adds no per-symbol tracking to normal
replacement. Public domain and child-list views support deprecated edits while
internal storage remains shared. Mutating symbols already used as dictionary keys
cannot repair those dictionaries: use a fresh replacement memo and rebuild any
parameter-processed or discretised model after legacy edits. Changes to a child’s
domains do not propagate into constructor-derived parent domains; reconstruct
those expressions as well. Legacy mutation is rejected while gc.freeze() is
active because frozen ancestors cannot be discovered for cache invalidation;
use out-of-place replacement or unfreeze the collector first.
The test suite forbids deprecated mutation even with warnings ignored. Only isolated compatibility subprocesses exercise the legacy path; notebook kernels inherit the same guard as other tests.