Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions docs/research/proof-tool-coverage.md
Original file line number Diff line number Diff line change
Expand Up @@ -178,8 +178,7 @@ modes, chain rule pipeline.
- **PN-Counter, OR-Set, LWW-Register** (`Crdt.fs`) — only
G-Counter has the three semilattice properties tested.
- **DeltaCrdt** (`DeltaCrdt.fs`) — anti-entropy merge laws.
- **Residuated lattice** (`Residuated.fs`) — Galois-connection
axiom `(a ⇒ b) ≤ c ⟺ a ≤ (b ⇐ c)`.
- **Residuated lattice** (`Residuated.fs`) — Galois connection + residual under max + retraction equivalence laws **(FsCheck property-covered since 2026-05-24; all laws now fully covered)**.
- **Recursive fixpoint monotonicity** (`Recursive.fs`).
- **Merkle tree collision-freedom** at the leaf pair level.
- **Watermark monotonicity + bounded-lateness axiom**.
Expand Down
76 changes: 76 additions & 0 deletions tests/Tests.FSharp/Algebra/Residuated.Tests.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
module Zeta.Tests.Algebra.ResiduatedTests
#nowarn "0893"

open System
open System.Collections.Generic
open FsUnit.Xunit
open FsCheck
open FsCheck.FSharp
open global.Xunit
open Zeta.Core


// ═══════════════════════════════════════════════════════════════════
// Residuated-Lattice IVM Property Tests (B-0711)
// ═══════════════════════════════════════════════════════════════════

// 1. Galois connection: a · x ≤ b ⇔ x ≤ a \ b
// where · is max, and a \ b = (if a <= b then b else a)
[<FsCheck.Xunit.Property>]
let ``Galois connection holds for ResidualMax under natural order`` (a: int) (x: int) (b: int) =
let residualMax a b = if a <= b then Some b else None

let lhs = (max a x) <= b
let rhs =
match residualMax a b with
| Some bound -> x <= bound
| None -> false

lhs = rhs

// 2. Residual under max: a \ b = Some b if a ≤ b else None
[<FsCheck.Xunit.Property>]
let ``Residual under max properties`` (a: int) (b: int) =
let residualMax a b = if a <= b then Some b else None
residualMax a b = (if a <= b then Some b else None)

// 3. Retraction equivalence: ResidualMax(insert + retract trace) = max(positive-only trace)
// Oracle for max over active set
let private oracle (ops: (int * int64) list) =
let keyWeight = Dictionary<int, int64>()
let active = SortedSet<int>()

for (k, w) in ops do
let existing =
match keyWeight.TryGetValue k with
| true, v -> v
| false, _ -> 0L
let updated = existing + w
let wasActive = existing > 0L
let isActive = updated > 0L

if wasActive && not isActive then active.Remove k |> ignore
elif not wasActive && isActive then active.Add k |> ignore
Comment on lines +49 to +53
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Use an independent oracle for retraction equivalence

The oracle here mirrors the same state-transition rules used by ResidualMaxOp.StepAsync (updated > 0 drives active membership with the same add/remove branching), so a defect in that transition logic can still pass if it exists in both places. In that scenario this property reports success while the production behavior is wrong, which undermines the new claim of law coverage; compute expectations from a different spec path (e.g., rebuild max from accumulated counts) rather than duplicating operator internals.

Useful? React with 👍 / 👎.


if updated = 0L then keyWeight.Remove k |> ignore
else keyWeight.[k] <- updated
Comment on lines +40 to +56

if active.Count = 0 then ValueNone
else ValueSome (active.Max)

[<FsCheck.Xunit.Property>]
let ``ResidualMax retraction equivalence`` (ops: (int * int) list) =
// Limit weight changes to reasonable bounds to simulate typical active/retract traces
let opsMapped = ops |> List.map (fun (k, w) -> (k, int64 (w % 10)))

let c = Circuit()
let input = c.ZSetInput<int>()
let m = c.ResidualMax(input.Stream, Func<_, _>(id))
let out = OutputHandle m.Op
c.Build()

for (k, w) in opsMapped do
input.Send (ZSet.singleton k w)
c.Step()

out.Current = (oracle opsMapped)
1 change: 1 addition & 0 deletions tests/Tests.FSharp/Tests.FSharp.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
<Compile Include="Algebra/Units.Tests.fs" />
<Compile Include="Algebra/ZSet.Tests.fs" />
<Compile Include="Algebra/ZSet.Overflow.Tests.fs" />
<Compile Include="Algebra/Residuated.Tests.fs" />
<Compile Include="Algebra/IndexedZSet.Tests.fs" />
<Compile Include="Algebra/RobustStats.Tests.fs" />
<Compile Include="Algebra/TemporalCoordinationDetection.Tests.fs" />
Expand Down
Loading