Source code for finhjb.structure._boundary

from typing import (
    Callable,
    Generic,
    Self,
    TypedDict,
)

import jax.numpy as jnp
from flax import struct

from finhjb.interface.parameter import P
from finhjb.types import BoundaryName


class DependencyMethod(TypedDict):
    """
    This TypedDict represents a method to compute a boundary value along with its metadata.

    Attributes
    ----------
    order : int
        The order in which this method should be executed.
    name : BoundaryName
        The name of the boundary this method computes.
    deps : set[BoundaryName]
        The set of boundary names that this method depends on.
    method : Callable[..., float]
        The actual method that computes the boundary value.
    """

    order: int
    name: BoundaryName
    deps: set[BoundaryName]
    method: Callable[..., float]


[docs] class ImmutableBoundary(struct.PyTreeNode, Generic[P]): """ Immutable boundary values structure. Attributes ---------- s_min : float Minimum state variable value. s_max : float Maximum state variable value. v_left : float Value function at the left boundary. v_right : float Value function at the right boundary. """ s_min: float = struct.field(pytree_node=True) s_max: float = struct.field(pytree_node=True) v_left: float = struct.field(pytree_node=True) v_right: float = struct.field(pytree_node=True) graph: list[DependencyMethod] = struct.field(pytree_node=False, repr=False)
[docs] def get_boundaries(self) -> tuple[float, float, float, float]: """Return `(s_min, s_max, v_left, v_right)` as a tuple.""" return (self.s_min, self.s_max, self.v_left, self.v_right)
[docs] def get_boundary_dict(self) -> dict[BoundaryName, float]: """Return all boundary values as a dictionary keyed by boundary name.""" return { "s_min": self.s_min, "s_max": self.s_max, "v_left": self.v_left, "v_right": self.v_right, }
[docs] def update_boundaries(self, boundary_dict: dict[BoundaryName, float], p: P): """Return a new boundary object after applying dependency graph updates.""" for item in self.graph: boundary_dict[item["name"]] = item["method"]( **{ # Get the current value of the dependency from the boundary_dict or self dep: boundary_dict.get(dep, getattr(self, dep)) for dep in item["deps"] } | {"p": p} ) return self.replace(**boundary_dict)
[docs] def s_changed(self, boundary: Self): """Check whether state-space limits changed versus another boundary.""" return jnp.logical_or( self.s_min != boundary.s_min, self.s_max != boundary.s_max )