bayesmith.graph.evaluate
Source-derived signatures and contracts. See the reference index for coverage and conventions.
Module contract
Running a graph forward, and reading its log-density. Evaluation is one linear scan in topological order: every node's parents are already in the environment by the time it is reached, which is what the ordering rule in :class:`~bayesmith.graph.graph.Graph` buys.
Env
Defined in bayesmith.graph.evaluate, line 21.
Env = dict[str, Any]apply_deterministic
Defined in bayesmith.graph.evaluate, line 40.
apply_deterministic(graph: Graph, node: Deterministic, env: Env) -> AnyCall ``node.fn``, vmapped over the plate the node lives in. Public because the NumPyro bridge re-runs the same scan and must apply plates identically; a private name used across modules is not private.
apply_probabilistic
Defined in bayesmith.graph.evaluate, line 60.
apply_probabilistic(graph: Graph, node: Probabilistic, env: Env) -> AnyBuild ``node``'s distribution, vmapping ``dist_fn`` over its plate. Public for the same reason as :func:`apply_deterministic`: ``log_joint`` and the NumPyro bridge must build the *same* distribution for the *same* node, so both call this one function instead of each independently calling ``node.dist_fn`` unmapped. Before this function existed, both call sites made that same independent (and wrong) choice, which is exactly why the bridge/log_joint cross-check could not catch it -- two scans with an identical blind spot agree with each other by construction, whether or not they agree with the truth. Deliberately does **not** raise when a plated node has no plated parent, unlike :func:`apply_deterministic`. That refusal exists there because ``fn``'s return value is an arbitrary array that must already be the right shape -- there is no fallback that is still correct, so refusing is the only safe option. A distribution has a real fallback: calling ``dist_fn`` once, unmapped, yields one shared distribution whose ``log_prob``/``sample`` already know how to broadcast themselves against a plate-shaped value -- plain array broadcasting in ``log_joint``, and ``numpyro.plate``'s own ``.expand()`` in ``to_numpyro``. That is not a workaround, it is the ordinary "N iid draws from one shared prior" pattern -- already relied on today by a plated node with *no* parents at all (see ``test_a_plated_latent_site_carries_the_plate_axis``). Refusing it here would break that already-correct, already-tested pattern for no safety benefit: there is no shape for dist_fn's output to get wrong, only a distribution object whose own broadcasting contract handles it.
evaluate
Defined in bayesmith.graph.evaluate, line 106.
evaluate(graph: Graph | ReducedGraph, values: Mapping[str, Any] | None=None) -> EnvCompute every node's value.
Args:
graph: the graph to run.
values: values for the latent nodes. Observed nodes take their own
data and must not appear here; deterministic and constant nodes
are computed.
Returns:
A mapping from node name to value, covering every node in the graph.
Raises:
GraphError: if a latent node has no value, or ``values`` names
something that is not a latent node.graph_density
Defined in bayesmith.graph.evaluate, line 176.
graph_density(graph: Graph, term: Any, env: Env, *, label: str, names: tuple[str, ...]) -> jax.ArrayEvaluate one graph-level term and require one scalar contribution. Shared by both density scans. NumPyro otherwise sums a vector-valued ``factor`` while :func:`log_joint` broadcasts it, so accepting a vector would make the two scans disagree without either one raising.
log_joint
Defined in bayesmith.graph.evaluate, line 201.
log_joint(graph: Graph | ReducedGraph, values: Mapping[str, Any] | None=None) -> jax.ArrayThe joint log-density of the graph at ``values``. Every probabilistic node contributes ``log_prob`` of its value under the distribution its parents parameterise; deterministic nodes contribute nothing but the dependence they carry. A node's :attr:`~bayesmith.graph.nodes.Probabilistic.observed_mask` drops the samples it says were never taken. That is the whole of masking on THIS side -- there is no infinite scale to take a limit of, because a sample that was not observed contributes no term rather than a term whose width went to infinity, and the two differ by exactly the ``log sigma`` that would have sent the joint to ``-inf``.