kups.core.assertion
¶
JAX-compatible assertion tracing system with optional automatic fixing.
This package provides a comprehensive assertion system that works seamlessly with JAX transformations, including JIT compilation, automatic differentiation, and vectorization. Assertions can include optional fix functions for automatic error recovery.
Key Components:
- RuntimeAssertion: Core assertion dataclass with optional fixing capabilities
- runtime_assert: Function to create assertions that work with JAX transformations
- with_runtime_assertions: Decorator to enable assertion tracing in functions
- LoopMerge: How loops fold an assertion's arguments across iterations
ELEMENTWISE_MAX = LoopMerge(_lowest_like, _keep_largest_failure)
module-attribute
¶
Report the elementwise maximum over failing iterations (the default).
Suits fixes that must cover every iteration, such as capacity requirements.
FIRST_FAILURE = LoopMerge(jnp.empty_like, _keep_first_failure)
module-attribute
¶
Report the arguments of the first failing iteration.
LAST_FAILURE = LoopMerge(jnp.empty_like, _keep_last_failure)
module-attribute
¶
Report the arguments of the last failing iteration.
NO_ARGS = _NO_ARGS()
module-attribute
¶
Sentinel instance to distinguish between no arguments and None as an argument.
InterpreterPolicy
¶
Bases: Enum
What to do with an effect the interpreter cannot reach.
That is an effect primitive without a handler, or an unhandled primitive whose sub-jaxprs contain an effect primitive.
Source code in src/kups/core/effects/interpreter.py
IGNORE = 'ignore'
class-attribute
instance-attribute
¶
Bind the primitive unchanged. Its effects are lost.
RAISE = 'raise'
class-attribute
instance-attribute
¶
Raise NotImplementedError.
WARN = 'warn'
class-attribute
instance-attribute
¶
Warn, then bind the primitive unchanged. Its effects are lost.
LoopMerge
dataclass
¶
How a loop folds an assertion's fmt_args/fix_args across iterations.
The predicate of an assertion inside scan or while_loop always
accumulates as a conjunction over iterations. LoopMerge decides, leaf by
leaf, which values the reported fmt_args and fix_args carry.
Attributes:
| Name | Type | Description |
|---|---|---|
init |
Callable[[Array], Array]
|
Value of an argument leaf before the first iteration, given a leaf from the loop body. |
combine |
Callable[[Array, Array, Array, Array], Array]
|
|
Source code in src/kups/core/assertion/runtime_assertion.py
RuntimeAssertion
¶
A runtime assertion that validates computations with optional automatic fixing.
This class encapsulates a predicate that should evaluate to True, along with metadata for error reporting and optional repair mechanisms. Assertions are designed to work seamlessly with JAX transformations while providing rich debugging information.
Class Type Parameters:
| Name | Bound or Constraints | Description | Default |
|---|---|---|---|
State
|
The type of state that can be modified by the fix function |
required | |
FixArgs
|
The types of arguments passed to the fix function (can be a PyTree) |
required |
Attributes:
| Name | Type | Description |
|---|---|---|
predicate |
Array
|
A scalar boolean array indicating whether the assertion passes |
message |
str
|
Human-readable error message with optional format placeholders |
fmt_args |
dict[str, Array]
|
Dictionary of values to substitute into the message format string |
exception_type |
type[Exception]
|
Type of exception to raise on assertion failure |
static_info |
dict[str, Any]
|
Additional metadata for debugging (not traced by JAX) |
fix_fn |
Fix[State, FixArgs] | None
|
Optional function to repair the state when assertion fails |
fix_args |
FixArgs | _NO_ARGS
|
Arguments to pass to the fix function (can be complex PyTree structures) |
loop_merge |
LoopMerge
|
How loops fold |
Example
Note
The fix_args can be complex PyTree structures including nested dictionaries, tuples, and arrays. The assertion system properly handles flattening and unflattening these structures during JAX transformations.
Source code in src/kups/core/assertion/runtime_assertion.py
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 | |
exception
property
¶
Create the exception instance that would be raised on assertion failure.
Returns:
| Type | Description |
|---|---|
Exception
|
An exception instance with the formatted error message |
__str__()
¶
check()
¶
Check the assertion and raise an exception if it fails.
Raises:
| Type | Description |
|---|---|
Exception
|
The configured exception type if the assertion fails |
Source code in src/kups/core/assertion/runtime_assertion.py
failed()
¶
fix(state)
¶
Attempt to fix the assertion failure by modifying the provided state.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
state
|
State
|
The current state that needs to be repaired |
required |
Returns:
| Type | Description |
|---|---|
State
|
The modified state after applying the fix function |
Raises:
| Type | Description |
|---|---|
NotImplementedError
|
If no fix function is available |
AssertionError
|
If fix arguments are missing when a fix function exists |
Source code in src/kups/core/assertion/runtime_assertion.py
check_assertions(like=None)
¶
A primitive that returns a scalar bool. When not wrapped in with_runtime_assertions, it always returns True. When wrapped, it will return the conjunction of all runtime assertions in the current context.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
like
|
Array | None
|
An array whose device placement and sharding will be used for the output. If None, the output will be placed on the default device. |
None
|
Returns: A scalar boolean array indicating whether all assertions pass.
Source code in src/kups/core/assertion/primitives.py
runtime_assert(predicate, message='', fmt_args=None, exception_type=AssertionError, static_info=None, fix_fn=None, fix_args=NO_ARGS, loop_merge=ELEMENTWISE_MAX)
¶
Create a runtime assertion that integrates with JAX transformations.
This function creates assertions that can be traced through JAX transformations including JIT compilation, automatic differentiation, and vectorization. The assertion acts as an identity function during execution but records assertion metadata for later inspection.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
predicate
|
Array
|
A boolean array indicating whether the assertion passes |
required |
message
|
str
|
Error message with optional format placeholders (e.g., "Value {val} is invalid") |
''
|
fmt_args
|
dict[str, Array] | None
|
Dictionary mapping format placeholder names to values |
None
|
exception_type
|
type[Exception]
|
Type of exception to raise if assertion fails during checking |
AssertionError
|
static_info
|
dict[str, Any] | None
|
Additional metadata for debugging (not traced by JAX) |
None
|
fix_fn
|
Fix[State, FixArgs] | None
|
Optional function to repair state when assertion fails |
None
|
fix_args
|
FixArgs | _NO_ARGS
|
Arguments for the fix function (can be complex PyTree structures) |
NO_ARGS
|
loop_merge
|
LoopMerge
|
How |
ELEMENTWISE_MAX
|
Type Parameters:
| Name | Bound or Constraints | Description | Default |
|---|---|---|---|
State
|
Type of state that can be modified by the fix function |
required | |
FixArgs
|
Type of arguments passed to the fix function (supports PyTree structures) |
required |
Example
Basic assertion:
x = jnp.array(5.0)
runtime_assert(
predicate=x > 0,
message="Value must be positive, got {val}",
fmt_args={"val": x}
)
Assertion with fixing:
runtime_assert(
predicate=x > threshold,
message="Value {val} below threshold {thresh}",
fmt_args={"val": x, "thresh": threshold},
fix_fn=lambda state, args: jnp.maximum(state, args["min_val"]),
fix_args={"min_val": jnp.array(1.0)}
)
Complex PyTree fix_args:
Note
The fix_args parameter supports arbitrarily nested PyTree structures including dictionaries, tuples, and arrays. These are automatically flattened and unflattened during JAX transformations.
Source code in src/kups/core/assertion/primitives.py
137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 | |
with_runtime_assertions(fn, policy=InterpreterPolicy.RAISE, context_sharding=None)
¶
Decorator that enables runtime assertion tracing for JAX functions.
This decorator wraps a function to intercept and collect all runtime assertions
created with runtime_assert during execution. The wrapped function returns
both the original result and a tuple of all assertions that were evaluated,
allowing for post-execution analysis, debugging, and optional error recovery.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
fn
|
Callable[P, R]
|
The function to wrap with assertion tracing capabilities |
required |
policy
|
InterpreterPolicy
|
What to do with assertions the interpreter cannot reach, e.g.
inside |
RAISE
|
context_sharding
|
PartitionSpec | None
|
Optional sharding specification for distributed contexts. When provided, assertion contexts are sharded according to this spec for multi-device computations. |
None
|
Returns:
| Type | Description |
|---|---|
Callable[P, tuple[R, tuple[RuntimeAssertion[Any, Any], ...]]]
|
A wrapped function that returns a tuple of (original_result, assertions_tuple). |
Callable[P, tuple[R, tuple[RuntimeAssertion[Any, Any], ...]]]
|
The assertions tuple contains all RuntimeAssertion instances encountered |
Callable[P, tuple[R, tuple[RuntimeAssertion[Any, Any], ...]]]
|
during execution, preserving order and enabling post-hoc analysis. |
Type Parameters:
| Name | Bound or Constraints | Description | Default |
|---|---|---|---|
P
|
Parameter specification of the wrapped function (ParamSpec) |
required | |
R
|
Return type of the wrapped function |
required |
Example
Basic usage:
@with_runtime_assertions
def validate_computation(x):
runtime_assert(x > 0, "x must be positive")
return x ** 2
result, assertions = validate_computation(jnp.array(5.0))
# result = 25.0, assertions contains one RuntimeAssertion
With custom policy:
traced_fn = with_runtime_assertions(
my_function,
policy=InterpreterPolicy.WARN
)
result, assertions = traced_fn(inputs)
Distributed computation:
sharded_fn = with_runtime_assertions(
distributed_computation,
context_sharding=jax.sharding.PartitionSpec('data', None)
)
Error analysis and recovery:
result, assertions = traced_fn(initial_state)
# Check for failures
failed_assertions = [a for a in assertions if a.failed()]
if failed_assertions:
# Attempt automatic fixing
fixed_state = initial_state
for assertion in failed_assertions:
if assertion.fix_fn is not None:
fixed_state = assertion.fix(fixed_state)
# Re-run with fixed state
result, _ = traced_fn(fixed_state)
Note
The decorator integrates seamlessly with JAX transformations including jit, vmap, grad, and scan. Assertions are properly threaded through control flow operations and maintain correct semantics under transformations.
Source code in src/kups/core/assertion/tracing.py
465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 | |