Source code for sequince.utils.optimize
from __future__ import annotations
from typing import Any, Callable
from acai import while_loop
from array_api_compat import is_jax_namespace
from ..array_api import namespace
from ..typing import Array
[docs]
def bisect_monotone_decreasing(
criterion: Callable[[Array], Array],
lower: Array,
upper: Array,
target: Array,
*,
tolerance: Array,
max_iterations: int,
) -> tuple[Array, Array]:
"""Refine a bracket around a decreasing criterion's target crossing.
The returned lower bound is the largest tested value whose criterion is at
least ``target``; the upper bound is the smallest tested value below it.
Callers own endpoint validation and the policy for selecting a final value
from the returned bracket.
The loop uses :func:`acai.while_loop`, preserving structured control flow
under JAX compilation while retaining eager behavior on other backends.
"""
xp = namespace(lower, upper, target, tolerance)
uses_structured_loop = is_jax_namespace(xp)
def continue_bisection(carry: tuple[Any, Array, Array]) -> Any:
iteration, low, high = carry
above_tolerance = high - low > tolerance
if uses_structured_loop:
return xp.logical_and(
iteration < max_iterations,
above_tolerance,
)
return iteration < max_iterations and above_tolerance
def refine_bracket(
carry: tuple[Any, Array, Array],
) -> tuple[Any, Array, Array]:
iteration, low, high = carry
middle = 0.5 * (low + high)
value = criterion(middle)
move_low = value >= target
return (
iteration + 1,
xp.where(move_low, middle, low),
xp.where(move_low, high, middle),
)
_, low, high = while_loop(
continue_bisection,
refine_bracket,
(0, lower, upper),
xp=xp,
)
return low, high