stormvogel.parametric

Parametric values for parametric Markov models.

A parametric value is a polynomial or rational function over a set of named parameters. Stormvogel represents such values as backend-native objects (by default, sympy.Expr), and exposes a small set of dispatching helpers in this package so that the rest of the codebase never has to know which backend produced a given value:

import sympy as sp
from stormvogel import model
from stormvogel import parametric

pmdp = model.new_mdp()
x = pmdp.declare_parameter("x")
# x is now a sp.Symbol that pmdp owns — reuse it everywhere.

parametric.free_symbol_names(1 - x)         # {"x"}
parametric.evaluate(1 - x, {"x": 0.25})     # 0.75
parametric.is_zero(x - x)                   # True

The helpers below (is_zero(), free_symbol_names(), degree(), evaluate(), numerator_denominator(), to_str()) are functools.singledispatch() generics. The stormvogel.parametric.sympy_backend module registers overloads for sympy.Expr and is imported by default. A future pycarl_backend would register overloads for its own types with the same public API — no changes outside this package.

Submodules

Attributes

Classes

ParametricBackend

The contract a parametric-value backend must implement.

Functions

backend_for(→ ParametricBackend)

Return the backend that owns value's concrete type.

get_default(→ ParametricBackend)

Return the currently-selected default backend.

register(→ None)

Register a parametric backend.

registered_types(→ tuple[type, Ellipsis])

Return the union of all expr_types across registered backends.

set_default(→ None)

Select the backend used by stormvogel.parametric.symbol() /

is_zero(→ bool)

Return True iff value is structurally / symbolically zero.

free_symbol_names(→ set[str])

Return the set of parameter names occurring in value.

degree(→ int)

Return the total degree of value (0 for constants).

evaluate(→ Number | Parametric)

Substitute the given parameter values.

numerator_denominator(→ tuple[Parametric, Parametric])

Return (numerator, denominator) of value as a rational function.

to_str(→ str)

Return a stable human-readable string form of value.

is_parametric(→ bool)

Return True iff value's concrete type is owned by any

symbol(→ Parametric)

Construct a parameter symbol using the currently-selected default

constant(→ Parametric)

Lift a Python Number into a parametric value via the default

Package Contents

class stormvogel.parametric.ParametricBackend

Bases: Protocol

The contract a parametric-value backend must implement.

A backend is responsible for constructing its values (symbols, constants) and for bridging them to pycarl. All introspection operations (is_zero, evaluate, …) are registered as functools.singledispatch() overloads on the generics in stormvogel.parametric; the backend itself does not need to expose them on its instance.

name: str
expr_types: tuple[type, Ellipsis]
symbol(name: str, **kwargs: Any) Any

Create a parameter symbol with the given name.

constant(n: Number) Any

Lift a Python number into a backend-native constant.

to_pycarl(value: Any, var_map: dict[str, Any]) Any

Convert value to a pycarl factorized rational function.

var_map maps parameter names to the pycarl Variable objects created by the stormpy bridge. Callers guarantee that every free symbol in value has an entry.

from_pycarl(pycarl_value: Any) Any

Convert a pycarl rational function into a backend-native value.

stormvogel.parametric.backend_for(value: Any) ParametricBackend

Return the backend that owns value’s concrete type.

Raises:

LookupError – If no registered backend claims the type.

stormvogel.parametric.get_default() ParametricBackend

Return the currently-selected default backend.

Raises:

RuntimeError – If no backend has been registered yet (should not happen in practice: importing stormvogel.parametric auto-registers the sympy backend).

stormvogel.parametric.register(backend: ParametricBackend) None

Register a parametric backend.

If no default has been set yet, the first backend registered becomes it. Re-registering the same backend (by name) is a no-op — the existing entry is replaced in-place — so importing a backend module twice is harmless.

stormvogel.parametric.registered_types() tuple[type, Ellipsis]

Return the union of all expr_types across registered backends.

Used by stormvogel.parametric.is_parametric() as the isinstance() check.

stormvogel.parametric.set_default(name: str) None

Select the backend used by stormvogel.parametric.symbol() / stormvogel.parametric.constant().

type stormvogel.parametric.Number = int | float | Fraction
type stormvogel.parametric.Parametric = sp.Expr
stormvogel.parametric.is_zero(value) bool

Return True iff value is structurally / symbolically zero.

stormvogel.parametric.free_symbol_names(value) set[str]

Return the set of parameter names occurring in value.

stormvogel.parametric.degree(value) int

Return the total degree of value (0 for constants).

stormvogel.parametric.evaluate(value, values: dict[str, Number]) Number | Parametric

Substitute the given parameter values.

String keys are resolved by parameter name. The result is returned as a native Python Number when all parameters are substituted; a symbolic expression otherwise.

stormvogel.parametric.numerator_denominator(value) tuple[Parametric, Parametric]

Return (numerator, denominator) of value as a rational function.

stormvogel.parametric.to_str(value) str

Return a stable human-readable string form of value.

stormvogel.parametric.is_parametric(value) bool

Return True iff value’s concrete type is owned by any registered parametric backend.

This is what downstream stormvogel code uses (in Model.is_parametric, Distribution.is_stochastic, the simulator, …) — never an isinstance() on a concrete backend class.

Pure Python numbers (int, float, Fraction, bool) are not parametric, even though e.g. sp.Integer(1) technically is a sp.Expr; we keep the split by excluding types that are also valid as a Number.

stormvogel.parametric.symbol(name: str, **kwargs) Parametric

Construct a parameter symbol using the currently-selected default backend.

Extra keyword arguments are forwarded to the backend’s factory (for the sympy backend, these are sympy.Symbol() assumptions such as positive=True).

stormvogel.parametric.constant(n: Number) Parametric

Lift a Python Number into a parametric value via the default backend. Rarely needed — stormvogel internals accept Number as a first-class Value — but useful when a parametric context demands a backend-native zero / one.