feat(afana/zx-ir): phase normalization + strict range validation (closes #850)#1069
Open
hiq-lab wants to merge 2 commits into
Open
feat(afana/zx-ir): phase normalization + strict range validation (closes #850)#1069hiq-lab wants to merge 2 commits into
hiq-lab wants to merge 2 commits into
Conversation
added 2 commits
May 27, 2026 11:26
Replaces the hand-rolled `RwLock<HashMap<CacheKey, CacheEntry>>` L1 with
`moka::sync::Cache`. Reasons:
- W-TinyLFU admission preserves the hot working set under cold-burst
pressure; the previous HashMap evicted by oldest timestamp, which lets
one-off lookups push out repeatedly-used circuits.
- Lock-free reads on the hot path; the old code took the RwLock on every
read and a write-lock on every stats bump.
- Native TTL eviction; the manual `evict_stale` scan is no longer needed.
Public API surface is preserved so existing callers (quasi-demo,
quasi-bridge) keep compiling without changes:
CacheStore::new(CacheConfig { max_l1_entries, l2_dir, max_age_seconds })
CacheStore::{get, put, contains, stats, clear, evict_stale}
Behavioural deltas worth knowing:
- `evict_stale(now: u64)` keeps its signature but ignores the `now`
parameter — moka uses wall clock for TTL. Callers that relied on
synthetic timestamps must move to real `Duration` waits in tests.
- `CacheStats.evictions` now only counts moka's TTL- and size-based
evictions, not explicit `clear()` calls. This matches the semantics
the field name implies.
- `CacheStats.{total_lookup_time_us, lookup_count}` were never populated
by the previous implementation either; they remain in the struct for
API stability but always read as 0. To be removed in a future cleanup.
Test plan:
- 16 existing tests in quasi-cache continue to pass
- New `ttl_evicts_after_max_age` test replaces the synthetic-timestamp
`evict_stale_removes_old_keeps_fresh` to validate real TTL eviction
- New `lookup_latency_is_sub_millisecond` test populates 10k entries
and asserts mean lookup < 1 ms — satisfies #812 AC#2
- Full workspace build green, no caller changes needed
#850) Adds two new methods to `ZxGraph`: fn normalize_phases(&mut self) fn validate_normalized(&self) -> Result<(), Vec<ZxValidationError>> and a new `ZxValidationError::PhaseOutOfRange` variant. The intended workflow for lowering passes: graph.normalize_phases(); graph.validate_normalized()?; // safe to emit QASM Why a separate strict variant: `lower.rs` already emits un-normalised phases by design — `Sdg` produces `-0.5`, `Tdg` produces `-0.25`, and `Rx(theta)` produces `theta / π` which can be any real number. The existing `validate()` must stay permissive for these intermediates; strict range checks live in `validate_normalized()` and run only after `normalize_phases()`. Phases are equivalent mod 2π in ZX-calculus, so `normalize_phases()` applies `rem_euclid(2.0)` to each finite phase — semantics-preserving. Non-finite phases (NaN/Inf) are left alone and continue to be flagged by the existing `validate()`. Tests: - afana/tests/zx_phase_range.rs (integration, per AC): - rejects phase = -0.5 - rejects phase = 7.0 - normalize + validate succeeds for both - afana/src/zx_ir.rs (inline): 7 new unit tests covering normalization edge cases (negative, > 2π, NaN passthrough, exact 2.0 boundary)
Open
4 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds spider-phase normalisation and a strict-mode validator to
afana/src/zx_ir.rs, with integration tests inafana/tests/zx_phase_range.rssatisfying the issue's acceptance criteria.New surface:
Why a separate strict variant
afana/src/lower.rsalready produces un-normalised phases on purpose:Sdg→ phase =-0.5Tdg→ phase =-0.25Rx(theta),Rz(theta)→ phase =theta / πfor any realthetaBolting a
[0, 2)check onto the existingvalidate()would break the lowering pipeline on every parametric rotation. So the existingvalidate()keeps its current permissive behaviour (structural integrity + finite-phase check only), and the strict range check lives in a newvalidate_normalized()that callers opt into after runningnormalize_phases().The intended workflow for emission paths:
What
normalize_phases()doesPhases in ZX-calculus are equivalent modulo 2π. The function calls
phase.rem_euclid(2.0)on every finite phase, which produces a non-negative result in[0, 2). Non-finite phases (NaN/Inf) are left alone and continue to be flagged by the existingvalidate().Tests
afana/tests/zx_phase_range.rs— integration tests per AC:validate_normalized_rejects_phase_negative_one_half✓ AC QUASI-001: Ehrenfest CBOR Schema — base type definitions #1validate_normalized_rejects_phase_seven✓ AC QUASI-002: HAL Contract Python bindings for quasi-agent #2normalize_phases_then_validate_succeeds— sanity check that the normalise → validate workflow accepts the same inputs after normalisationafana/src/zx_ir.rs— 7 new inline unit tests covering edge cases (in-range acceptance,[0, 2)half-open at the upper bound, negative input, large positive input, NaN passthrough, exact 2.0 boundary, round-trip).Test plan
cargo test -p afana --test zx_phase_range— 3/3 passcargo test -p afana --release— all 174+ existing tests still pass (no regressions inlower.rs,synthesis.rs, etc.)cargo build --workspace --releasegreenFollow-up worth considering
Calling
normalize_phases()automatically insidelower.rs::lower_program()(or a new publiclower_and_normalize()helper) would make the strict validation immediately usable everywhere. Not done in this PR — kept the change focused.