Skip to content

kups.application.utils.propagate

Shared propagation utilities for simulation loops.

Provides warmup, sampling, and data-parallelism helpers used across MD, MCMC, and relaxation application modules.

make_cycle_function(propagator)

JIT a propagator into a reusable per-cycle function with state donation.

Pass the result as cycle_fn to both :func:run_warmup_cycles and :func:run_simulation_cycles so a single traced-and-compiled program is shared across the warmup and sampling phases. For blocked stepping, compose the propagator with :class:~kups.core.propagator.LoopPropagator before passing it in.

Parameters:

Name Type Description Default
propagator Propagator[State]

Step propagator to compile.

required

Returns:

Type Description
CycleFunction[State]

A jitted (key, state) -> Result cycle function.

Source code in src/kups/application/utils/propagate.py
def make_cycle_function[State](propagator: Propagator[State]) -> CycleFunction[State]:
    """JIT a propagator into a reusable per-cycle function with state donation.

    Pass the result as ``cycle_fn`` to both :func:`run_warmup_cycles` and
    :func:`run_simulation_cycles` so a single traced-and-compiled program is shared
    across the warmup and sampling phases. For blocked stepping, compose the propagator
    with :class:`~kups.core.propagator.LoopPropagator` before passing it in.

    Args:
        propagator: Step propagator to compile.

    Returns:
        A jitted ``(key, state) -> Result`` cycle function.
    """
    return jit(as_result_function(propagator), donate_argnums=(1,))

propagate_and_fix(fn, key, state, *, max_tries=10)

Execute a propagator repeatedly until all assertions pass or retries are exhausted.

On each attempt, failed assertions are repaired via their fix functions. Raises if a failed assertion has no fix function or retries run out.

Parameters:

Name Type Description Default
fn Callable[[Array, State], Result[State, State]]

Assertion-aware propagator produced by :func:propagator_with_assertions.

required
key Array

JAX PRNG key.

required
state State

Current simulation state.

required
max_tries int

Maximum number of repair attempts.

10

Returns:

Type Description
State

Propagated state with all assertions satisfied.

Raises:

Type Description
ValueError

If called inside a JAX transform.

RuntimeError

If assertions still fail after max_tries attempts.

Source code in src/kups/core/propagator.py
def propagate_and_fix[State](
    fn: Callable[[Array, State], Result[State, State]],
    key: Array,
    state: State,
    *,
    max_tries: int = 10,
) -> State:
    """Execute a propagator repeatedly until all assertions pass or retries are exhausted.

    On each attempt, failed assertions are repaired via their fix functions.
    Raises if a failed assertion has no fix function or retries run out.

    Args:
        fn: Assertion-aware propagator produced by :func:`propagator_with_assertions`.
        key: JAX PRNG key.
        state: Current simulation state.
        max_tries: Maximum number of repair attempts.

    Returns:
        Propagated state with all assertions satisfied.

    Raises:
        ValueError: If called inside a JAX transform.
        RuntimeError: If assertions still fail after ``max_tries`` attempts.
    """
    is_traced = any(isinstance(x, jax.core.Tracer) for x in jax.tree.leaves(state))
    if is_traced:
        raise ValueError("propagate_and_fix cannot be jax transformed.")

    for _ in range(max_tries):
        out = fn(key, state)
        state = out.value
        if not out.failed_assertions:
            return state
        state = out.fix_or_raise(state)
    raise RuntimeError("Failed to resolve potential after multiple attempts")

propagator_with_assertions(propagator)

Wrap a propagator to capture assertion results alongside the state.

Parameters:

Name Type Description Default
propagator Propagator[State]

Propagator to wrap.

required

Returns:

Type Description
Callable[[Array, State], Result[State, State]]

Function returning a Result that pairs the new state with assertion metadata.

Source code in src/kups/core/propagator.py
def propagator_with_assertions[State](
    propagator: Propagator[State],
) -> Callable[[Array, State], Result[State, State]]:
    """Wrap a propagator to capture assertion results alongside the state.

    Args:
        propagator: Propagator to wrap.

    Returns:
        Function returning a Result that pairs the new state with assertion metadata.
    """
    return as_result_function(propagator)

run_simulation_cycles(key, cycle_fn, state, num_cycles, logger, *, convergence_fn=None)

Run simulation steps with logging and optional early stopping.

Parameters:

Name Type Description Default
key Array

JAX PRNG key for stochastic propagators (e.g. MD thermostats).

required
cycle_fn CycleFunction[State]

Compiled per-cycle function from :func:make_cycle_function.

required
state State

Initial state.

required
num_cycles int

Maximum number of steps.

required
logger Logger[State]

Logger receiving state each step.

required
convergence_fn Callable[[State], bool] | None

If provided, called after each step; stops early when it returns True.

None

Returns:

Type Description
State

State after all steps or early convergence.

Source code in src/kups/application/utils/propagate.py
def run_simulation_cycles[State](
    key: Array,
    cycle_fn: CycleFunction[State],
    state: State,
    num_cycles: int,
    logger: Logger[State],
    *,
    convergence_fn: Callable[[State], bool] | None = None,
) -> State:
    """Run simulation steps with logging and optional early stopping.

    Args:
        key: JAX PRNG key for stochastic propagators (e.g. MD thermostats).
        cycle_fn: Compiled per-cycle function from :func:`make_cycle_function`.
        state: Initial state.
        num_cycles: Maximum number of steps.
        logger: Logger receiving state each step.
        convergence_fn: If provided, called after each step; stops early when
            it returns True.

    Returns:
        State after all steps or early convergence.
    """
    chain = key_chain(key)
    with logger:
        for i in range(num_cycles):
            state = propagate_and_fix(cycle_fn, next(chain), state)
            logger.log(state, i)
            if convergence_fn is not None and convergence_fn(state):
                logging.info("Converged at step %d", i + 1)
                break
    return state

run_warmup_cycles(key, cycle_fn, state, num_cycles)

Run warmup propagation cycles without logging.

Parameters:

Name Type Description Default
key Array

JAX PRNG key.

required
cycle_fn CycleFunction[State]

Compiled per-cycle function from :func:make_cycle_function.

required
state State

Initial simulation state.

required
num_cycles int

Number of warmup steps.

required

Returns:

Type Description
State

State after warmup.

Source code in src/kups/application/utils/propagate.py
def run_warmup_cycles[State](
    key: Array, cycle_fn: CycleFunction[State], state: State, num_cycles: int
) -> State:
    """Run warmup propagation cycles without logging.

    Args:
        key: JAX PRNG key.
        cycle_fn: Compiled per-cycle function from :func:`make_cycle_function`.
        state: Initial simulation state.
        num_cycles: Number of warmup steps.

    Returns:
        State after warmup.
    """
    chain = key_chain(key)
    for _ in tqdm.trange(num_cycles):
        state = propagate_and_fix(cycle_fn, next(chain), state)
    return state