Source code for witness

"""Witness generation modules.

This module provides per-AIR witness generation that replaces the generic
hint-driven witness computation. Each AIR has its own WitnessModule that
computes intermediate columns and grand sums directly in readable Python code.

For AIRs without hand-written modules, the bytecode adapter provides a fallback
using compiled expression bytecode. The BYTECODE_AIRS dict controls which AIRs
use bytecode vs hand-written modules.

Zisk AIRs are auto-discovered from the proving key directory. All discovered
AIRs without hand-written modules are registered for bytecode evaluation.
"""

from constraints import _discover_zisk_airs
from constraints.base import ConstraintContext
from primitives.field import FF3Poly

from .base import WitnessModule
from .lookup2_12 import Lookup2_12Witness
from .permutation1_6 import Permutation1_6Witness
from .simple_left import SimpleLeftWitness
from .simple_right import SimpleRightWitness
from .specified_ranges import SpecifiedRangesWitness
from .u8_air import U8AirWitness
from .u16_air import U16AirWitness

# Registry mapping AIR names to hand-written witness module classes
[docs] WITNESS_REGISTRY: dict[str, type[WitnessModule]] = { 'SimpleLeft': SimpleLeftWitness, 'SimpleRight': SimpleRightWitness, 'U8Air': U8AirWitness, 'U16Air': U16AirWitness, 'SpecifiedRanges': SpecifiedRangesWitness, 'Lookup2_12': Lookup2_12Witness, 'Permutation1_6': Permutation1_6Witness, }
# AIRs that should use bytecode interpreter for witness generation. # Includes auto-discovered Zisk AIRs (shared with constraints.BYTECODE_AIRS).
[docs] BYTECODE_AIRS: dict[str, str] = _discover_zisk_airs()
class _FallbackWitnessModule(WitnessModule): """Wraps a primary module, falling back to bytecode when it returns empty dicts. Used when an AIR has a hand-written module that does not implement Stage-2 (compute_intermediates / compute_grand_sums), deferring to the compiled expression bytecode for those columns. """ def __init__(self, primary: WitnessModule, expressions_bin: str) -> None: self._primary = primary self._expressions_bin = expressions_bin self._bytecode: WitnessModule | None = None def compute_intermediates(self, ctx: ConstraintContext) -> dict[str, dict[int, FF3Poly]]: result = self._primary.compute_intermediates(ctx) if result: return result from .bytecode_adapter import BytecodeWitnessModule self._bytecode = BytecodeWitnessModule(self._expressions_bin) return self._bytecode.compute_intermediates(ctx) def compute_grand_sums(self, ctx: ConstraintContext) -> dict[str, FF3Poly]: if self._bytecode is not None: result = self._bytecode.compute_grand_sums(ctx) self._bytecode = None return result return self._primary.compute_grand_sums(ctx)
[docs] def get_witness_module(air_name: str, expressions_bin: str | None = None) -> WitnessModule: """Get witness module instance for an AIR. When expressions_bin is provided (from AirConfig), prefers hand-written modules in WITNESS_REGISTRY to avoid naming collisions between pilouts that share an AIR name (e.g., SpecifiedRanges appears in both Simple pilout and Zisk). If the hand-written module does not implement Stage-2, wraps it with a bytecode fallback. Without expressions_bin, checks BYTECODE_AIRS first (Zisk AIRs), then falls back to hand-written modules in WITNESS_REGISTRY. Args: air_name: Name of the AIR (e.g., 'SimpleLeft', 'Lookup2_12') expressions_bin: Optional path to .bin bytecode. When provided, takes priority over BYTECODE_AIRS to prevent cross-pilout naming collisions. Returns: WitnessModule instance for the AIR Raises: KeyError: If no witness module is registered for the AIR (and no expressions_bin) """ from .bytecode_adapter import BytecodeWitnessModule if expressions_bin is not None: if air_name in WITNESS_REGISTRY: return _FallbackWitnessModule(WITNESS_REGISTRY[air_name](), expressions_bin) return BytecodeWitnessModule(expressions_bin) if air_name in BYTECODE_AIRS: return BytecodeWitnessModule(BYTECODE_AIRS[air_name]) if air_name in WITNESS_REGISTRY: return WITNESS_REGISTRY[air_name]() raise KeyError(f"No witness module for AIR '{air_name}'. " f"Available: {list(WITNESS_REGISTRY.keys())}")
__all__ = [ 'WitnessModule', 'SimpleLeftWitness', 'SimpleRightWitness', 'U8AirWitness', 'U16AirWitness', 'SpecifiedRangesWitness', 'Lookup2_12Witness', 'Permutation1_6Witness', 'WITNESS_REGISTRY', 'BYTECODE_AIRS', 'get_witness_module', ]