diff --git a/docs/tracking/2026-04-23_eigentrust_comparison.md b/docs/tracking/2026-04-23_eigentrust_comparison.md new file mode 100644 index 000000000..dd36baea9 --- /dev/null +++ b/docs/tracking/2026-04-23_eigentrust_comparison.md @@ -0,0 +1,400 @@ +# EigenTrust: math vs network vs Prologos math + +_Session 2026-04-23 + 2026-04-29. Comparing **how the same algorithm +runs through different layers of abstraction**, with float-like +scalar types throughout. The four axes:_ + +| Axis | What it measures | Implementation | +|---|---|---| +| **math** | Algorithm in plain Racket. Just function calls. | `plain-fl` — `eigentrust-plain.rkt` | +| **network** | Same algorithm built on Prologos's propagator network infrastructure (cells + propagators + BSP scheduler), bypassing the surface language. | `float` — `eigentrust-propagators-float.rkt` | +| **Prologos math** | Algorithm in Prologos surface language (no propagators), going through the elaborator + reducer. | `List + Posit32` (P) — `examples/eigentrust-posit.prologos` | +| **Prologos network** | (future) Surface Prologos code that compiles to a propagator network. Not yet a thing. | — | + +All four use float-like types: Racket flonums for the first two, +Prologos's `Posit32` (hardware posits) for the surface variant. + +Algorithm: the standard EigenTrust update with column-stochastic M: +``` +t_{k+1} = (1 − α) · M · t_k + α · p +``` +`eigentrust` enforces `col-stochastic?` at the entry point. + +## W3 reference workload — n=4, k=4, α=3/10 + +A 4-peer ring matrix with all pre-trust on peer 0 (`p = [1, 0, 0, 0]`), +4 forced iterations. Smallest workload that fairly exercises the +algorithm; result is `[5401/10000, 21/100, 147/1000, 1029/10000]` +(or ~`[0.5401, 0.21, 0.147, 0.1029]` in float). + +| Axis | reduce_ms | vs math | Notes | +| ----------------- | --------: | --------: | ----- | +| **math** (plain-fl) | ~0.03 | 1× | Racket flvector arithmetic, no overhead. | +| **network** (float) | 0.10 | ~3× | Same arithmetic kernel inside fire functions; +~70 µs of cell + BSP overhead. | +| **Prologos math** (List + Posit32) | 15 047 | **~500 000×** | Surface language, evaluated by Prologos's reducer. Pure reduction time for the single W3 expression — elaboration, type-check, qtt, zonk, prelude load, and Racket startup are all excluded (PHASE-TIMINGS:reduce_ms only). | +| Prologos network | — | — | _doesn't exist yet_. | + +The 5-to-6 orders-of-magnitude gap between **math** and **Prologos +math** is the cost of the Prologos surface language + reducer on a hot +path: term-tree walks, pattern-match dispatch, pretty-printing, +no flonum specialization, plus the O(k²) reduce-tree blowup +(see `2026-04-23_eigentrust_pitfalls.md` §11) that makes deeper +iteration progressively more expensive. This is _just_ reduction — +the other Prologos phases (elaborate, type-check, qtt, zonk) sum +to ~1.5 s on this workload, ~100× smaller than reduce. + +The **network** vs **math** gap is small in absolute terms (~70 µs +per call) and dominated by per-iteration constant cost (cell +allocation, BSP round dispatch, fire-fn invocation, CHAMP path +copies on each cell-write). It amortizes as n grows — see scaling +below. + +### A note on what `reduce_ms` measures + +For the Prologos surface variant, `reduce_ms` is the +PHASE-TIMINGS:reduce_ms emitted by `driver.rkt`'s `process-file` +— **just** the reducer phase, not elaboration, type-checking, qtt, +trait resolution, or zonking. So the 500 000× gap is not "Prologos +is slow at startup", it's "the reducer walks term trees instead of +calling flonum primitives". The `eigentrust-list-posit-w3only.prologos` +fixture isolates the W3 expression specifically so its reduce_ms +isn't bundled with W1/W2 (which the full benchmark file also +contains). + +## Scaling across n — math vs network + +Only the float-typed variants (math, network) are measured at large +n. The Prologos surface variants take ~60 s of fixed overhead per +run (Prologos prelude load + Racket startup) and the fixture +generators are surface code, so a Prologos-surface-at-large-n bench +is a separate piece of work. Today's data is the Racket-direct +side. + +`benchmarks/comparative/eigentrust-propagators-scaling.rkt` — +K=4, 30 s per-sample timeout, median of 3 runs (n=8..4096). +`benchmarks/comparative/eigentrust-scaling-large.rkt` — K=4, 180 s +per-sample timeout, median of 2 runs (n=4096..16384). + +| n | math (plain-fl) | network (float) | network overhead | source | +| ----: | -------------: | --------------: | ---------------: | -----: | +| 8 | 0.03 ms | 0.12 ms | +300 % | prior | +| 16 | 0.03 ms | 0.14 ms | +367 % | prior | +| 32 | 0.05 ms | 0.16 ms | +220 % | prior | +| 64 | 0.09 ms | 0.28 ms | +211 % | prior | +| 128 | 0.27 ms | 0.57 ms | +111 % | prior | +| 256 | 0.99 ms | 2.10 ms | +112 % | prior | +| 512 | 4.59 ms | 9.00 ms | +96 % | prior | +| 1024 | 19.40 ms | 33.86 ms | +75 % | prior | +| 2048 | 88.37 ms | 151.62 ms | +72 % | prior | +| 4096 | 442.14 ms | 619.37 ms | +40 % | prior | +| 4096 | 584.38 ms | 958.30 ms | +64 % | extended | +| 8192 | 3015.74 ms | 4439.84 ms | +47 % | extended | +| 16384 | 15989.23 ms | 20674.00 ms | +29 % | extended | + +The "extended" rows come from a separate measurement run +(`benchmarks/comparative/eigentrust-scaling-large.rkt`, K=4, median of +2 measured runs after 1 warmup, 180 s timeout). The two n=4096 numbers +diverge by 32 % on math and 55 % on network — both are real, just +different runs/cache states. Both rows are kept to give an honest +picture of run-to-run variance at this scale, which is bigger than +the model can explain. n=32768 was attempted but the fixture (8 GB) ++ Racket's still-rooted previous matrix (2 GB) + working set drove +the 16 GB host into GC thrashing during fixture allocation; on a 32+ +GB box appending `32768` to `SIZES` would let the data point land. + +### What the scaling shows + +* **Math scales slightly worse than O(n²)** in the cache-bound + regime. Doubling factors past n=512: + + | n→2n | factor | + | --- | ---: | + | 256 → 512 | 4.64× | + | 512 → 1024 | 4.23× | + | 1024 → 2048 | 4.56× | + | 2048 → 4096 | 5.00× | + | 4096 → 8192 | 5.16× | + | 8192 → 16384 | 5.30× | + + The drift from 4× toward 5× is the working set (n² doubles = + flvector data + temporary flvectors per iteration) outgrowing + L2/L3 and hitting DRAM bandwidth on every entry. + +* **Network overhead drops monotonically as n grows**: from +300 % + at n=8 down to +29 % at n=16384 — every doubling past n=4096 takes + ~17 percentage points off. The drop comes from the same n² + math kernel running inside the fire function (so the math part + scales identically) while network's overhead is in lower-order + terms — per-cell-write CHAMP path-copy (O(n) per write × K writes + = O(K·n)) and BSP scheduler bookkeeping (constant per round). + +### Trend / breakeven analysis + +A two-term model fit on n ≥ 128 with a SHARED n² coefficient +(both run the identical math kernel): + +``` +math(n) ≈ 6.18·10⁻⁵ · n² − 334 +network(n) ≈ 6.18·10⁻⁵ · n² + 0.230 · n − 526 +overhead = network − math = 0.230 · n − 191 (linear in n) +overhead / math → 0 as n → ∞ (drops as ~b/(a·n) from above) +``` + +The fit is good at the largest n where measurement noise matters +least: error 1.7 % on math at n=16384, −4.0 % on network. Smaller +n have larger relative errors as expected (constants matter more). + +**Predicted overhead at n we did not measure**: + +| n | math (extrap) | net (extrap) | overhead | +| ------: | ------------: | -----------: | -------: | +| 32 768 | ~66 000 ms | ~73 000 ms | +11 % | +| 65 536 | ~265 000 ms | ~280 000 ms | +5.6 % | +| 131 072 | ~1 060 000 ms | ~1 090 000 ms | +2.8 % | +| 262 144 | ~4 250 000 ms | ~4 310 000 ms | +1.4 % | + +So the empirical answer to "is there a breakeven where network beats +math?": **no, there isn't, and there can't be**. The fit confirms +what the structure of the code already implies — the network's fire +function calls the *same* math kernel that the plain Racket version +calls, plus extra bookkeeping (cell allocation, BSP round dispatch, +CHAMP path-copy on cell writes). Network's work is strictly a +superset of math's work, so the overhead is always non-negative. +What changes with n is the *ratio*: math grows as n² while overhead +grows as n, so the ratio collapses asymptotically. At n=10⁶ the +overhead would be ~0.4 %; at n=10⁷ ~0.04 %. But it never crosses. + +The same is true under any model where network does math plus +strictly-positive bookkeeping. The only way network could win +would be: + +* **Real parallelism on multiple cores** — the current BSP + scheduler runs one round at a time on one thread. If propagators + fired in parallel (broadcast scheduler, multi-thread BSP), large + N could partition across cores while math stays single-threaded. + This is not what's measured here. +* **Incremental computation** — if only some cells change, the + network only re-fires the dependents while math has to recompute + the whole iteration. Not measured here either; one-shot dense + matrix-vector is the worst case for incremental. +* **Working-set effects** — if the network's per-fire allocation + pattern happened to be more cache-friendly than math's at some + specific n. The data shows the opposite: network's CHAMP + path-copy is *less* cache-friendly than math's in-place flvector + iteration. There is no n where it accidentally wins. + +For the existing single-threaded one-shot dense workload, math is +the architectural floor. Network's value isn't beating math on +this workload — it's amortizing the bookkeeping to ≤ 1–2 % at +algorithmic scale, while delivering the propagator network's +infrastructure (incremental computation, parallel firing, +worldview tagging, partial recomputation) that math can't +express at all. + +## What about parallel BSP? + +The natural follow-up: the BSP scheduler can fire independent +propagators across cores. Why didn't the coarse variant exploit +that? Two reasons: + +1. **Default executor is sequential.** `run-to-quiescence-bsp` + defaults to `sequential-fire-all` unless + `current-parallel-executor` or `current-worker-pool` is set. + The benches above don't set either, so all data is + single-threaded. + +2. **Coarse topology has no parallel work to dispatch.** The + coarse variant is K propagators in a strict chain: t₀ → t₁ → + t₂ → … → t_K. Each fires only after its predecessor's cell + is written. One propagator per BSP round. Parallel BSP + needs ≥ 2 independent propagators per round. + +To actually exercise parallel BSP, we built a fine-grained +variant — `eigentrust-propagators-fine-float.rkt` — with K·n +per-peer propagators (n peer-step functions per BSP round, each +computing one element of the new trust vector independently). +The bench `eigentrust-parallel-bench.rkt` runs it with three +executors: sequential, `make-parallel-thread-fire-all` (Racket-9 +`thread #:pool 'own`), and a persistent worker-pool dispatcher. + +K=4, NUM-RUNS=3, 4-core host, median of 3 measured runs: + +| n | math (1 core) | coarse net (1) | fine-seq (1) | fine-thread (4) | fine-pool (4) | par speedup | fine-thread vs math | +| ---: | ------------: | -------------: | -----------: | --------------: | ------------: | ----------: | ------------------: | +| 16 | 0.06 ms | 0.27 ms | 5.67 ms | 5.24 ms | 5.70 ms | 1.08× | 87× | +| 32 | 0.08 ms | 0.25 ms | 19.21 ms | 13.59 ms | 12.25 ms | 1.41× | 170× | +| 64 | 0.16 ms | 0.47 ms | 82.61 ms | 52.10 ms | 55.30 ms | 1.59× | 326× | +| 128 | 0.49 ms | 1.15 ms | 307.25 ms | 159.97 ms | 173.11 ms | 1.92× | 326× | +| 256 | 2.09 ms | 4.26 ms | 1348.7 ms | 624.71 ms | 618.37 ms | 2.16× | 299× | +| 512 | 10.83 ms | 17.67 ms | 6796.4 ms | 3172.2 ms | 3154.4 ms | 2.14× | 293× | +| 1024 | 40.74 ms | 70.62 ms | 29240.9 ms | 16164.2 ms | 16302.3 ms | 1.81× | 397× | + +### Two clean findings + +* **BSP parallelism does work**: fine-seq → fine-thread shows up + to **2.16× speedup at n=256** on the 4-core box. Theoretical + max is 4×; the measured 2.16× reflects per-fire serialization + costs (snapshot taking, dependency walk, write merging) that + Amdahl-bound the speedup. Parallel-thread and worker-pool + executors are within noise of each other. + +* **The fine-grained topology's overhead swamps the parallelism**: + fine-thread is still **290–400× slower than math** at every n. + Per-peer decomposition pays K · n² cell reads (n peer + propagators × n cell reads each × K rounds) + K · n cell + writes through CHAMP (each O(log n)). At n=1024 that's ~4M + CHAMP lookups vs math's 4M flonum ops. The cell-access + overhead, not the math, dominates per-fire cost. + +### Why neither variant wins + +The architectural bind: + +| Variant | Parallel work? | Per-fire overhead | Net result | +| ------- | -------------- | ----------------- | ---------- | +| **coarse** | none (chain) | tiny (K cell writes total) | 1.4× math at n=16384 | +| **fine** | yes (n props/round) | K·n² CHAMP reads + K·n writes | 290–400× math at all sizes | + +To actually beat math via parallelism we'd need either: + +* **Coarse topology + parallelism inside the fire function** — + e.g., a fire function that internally splits its mat-vec-mul + across a worker pool. The propagator primitives don't directly + enable this: `fire-fn` is a run-to-completion thunk; the + scheduler doesn't decompose individual fire calls. Adding it + would mean the fire function manually dispatches via futures / + threads, paying its own coordination cost outside the BSP + framework. + +* **Much higher arithmetic intensity per fire** — e.g., dense + tensor ops where one fire does millions of flonum ops, so + per-fire BSP overhead amortizes over real work. Mat-vec-mul + at the per-peer grain is too small (n flonum mul-adds per + fire); at the whole-vector grain (coarse) there's only one + propagator to fire. + +The propagator network's parallelism lever — many independent +propagators per BSP round — only pays off when each propagator's +fire-fn does substantial work AND there are many of them. Dense +linear algebra naturally factors the wrong way for this: either +you split into many tiny independent ops (fine, too much +bookkeeping) or you have one huge op (coarse, no parallelism). +This isn't a propagator-network indictment — it's the same +reason GPUs don't accelerate `BLAS-1` (vector-vector) code +proportionally to compute throughput, and why `BLAS-3` +(matrix-matrix) is where they shine. EigenTrust per iteration +is a `BLAS-2` kernel (matrix-vector), squarely in the regime +where coordination overhead vs flop count is unfavorable. + +For workloads that DO match the propagator-parallelism profile — +many independent moderately-expensive sub-computations — the +infrastructure works as designed. EigenTrust just isn't one of +them. + +* **At n=4096 the network is 619 ms, math is 442 ms, Prologos + math is intractable** — the surface variant's elaborator + + reducer plus the O(k²) blowup means even a single 4096-peer + iteration would take many minutes (extrapolating from the + W3 numbers and the chain-depth scaling, the reducer time + alone would be hours). + +## What this tells us about the layered design + +* **The propagator network is a thin wrapper over plain math.** + +40 % overhead at moderate n is the price of cell-based + incremental computation, BSP scheduling, partial-recomputation + potential, and parallel-fire potential. For applications that + need any of those, it's a reasonable cost. For one-shot + numerical pipelines it's pure tax — but only ~40–75 % tax, not + orders of magnitude. +* **The Prologos surface language is the expensive layer right now.** + The ~600 000× gap between **math** and **Prologos math** at W3 is + almost entirely Prologos elaboration + reduction overhead. The + reducer is the bottleneck; the propagator network is not. +* **The "Prologos network" axis is the interesting future direction.** + A surface Prologos program that compiles to a propagator network + (instead of being evaluated by the reducer) would inherit the + network's +40–75 % overhead profile, not the reducer's 6-order + multiplier. The path from "Prologos math" to "Prologos network" + is the Prologos compiler maturity story — and based on these + numbers, where the algorithmic-cost wins live. + +## How to reproduce + +``` +# W3 reference — variants 5–7 (rat-coarse, rat-fine, float) +racket benchmarks/comparative/eigentrust-propagators-bench.rkt + +# Scaling across n=8..4096 — variants 5–9 (3 propagator + 2 plain) +racket benchmarks/comparative/eigentrust-propagators-scaling.rkt + +# Extended scaling n=4096..16384 — math + network only +racket benchmarks/comparative/eigentrust-scaling-large.rkt + +# Parallel BSP exploration — math + coarse + fine × {seq, thread, pool} +racket benchmarks/comparative/eigentrust-parallel-bench.rkt + +# Prologos surface (4 surface variants, fixed n=4 W3) +racket tools/bench-phases.rkt --runs 2 \ + benchmarks/comparative/eigentrust-list-rat.prologos \ + benchmarks/comparative/eigentrust-list-posit.prologos \ + benchmarks/comparative/eigentrust-pvec-rat.prologos \ + benchmarks/comparative/eigentrust-pvec-posit.prologos + +# Tests across all variants +raco test tests/test-eigentrust.rkt # 13 tests (List+Rat, P) +raco test tests/test-eigentrust-posit.rkt # 6 tests (List+Posit32, P) +raco test tests/test-eigentrust-pvec.rkt # 6 tests (PVec+Rat, P) +raco test tests/test-eigentrust-pvec-posit.rkt # 6 tests (PVec+Posit32, P) +raco test tests/test-eigentrust-propagators.rkt # 13 tests (rat-coarse) +raco test tests/test-eigentrust-propagators-fine.rkt # 6 tests (rat-fine) +raco test tests/test-eigentrust-propagators-float.rkt # 9 tests (float) +raco test tests/test-eigentrust-plain.rkt # 9 tests (plain rat + plain fl) +``` + +## Appendix: rat-flavored variants + +Five exact-rational variants exist as correctness-verification tools +for small n, **not as comparison data points** (exact-rat is +intractable beyond n ≈ 64; denominators grow as O(n^k) and bigint +arithmetic is O(d log d) in digit count): + +| Axis | Variant | Status at W3 (n=4) | Status at scale | +| --- | --- | --- | --- | +| math | `plain-rat` | 2.74 ms | times out at n=128 (>30 s) | +| network | `rat-coarse`, `rat-fine` | 3.36 ms / 4.43 ms | times out at n=128 | +| Prologos math | `List + Rat`, `PVec + Rat` (P) | 18 372 ms / 33 006 ms | not measured | + +The rat variants share the four axes but were dropped from the +main analysis because float-typed answers are what real EigenTrust +deployments need. The rat code is retained for golden-equality +testing at small n (the 13-test rackunit suite for +`test-eigentrust.rkt` checks every primitive against an exact +expected rational). + +## Pitfalls surfaced + +* **Surface-language pitfalls**: `2026-04-23_eigentrust_pitfalls.md` + (16 entries — closure capture, multi-line def, posit literal + parsing, etc.). +* **Propagator-track pitfalls**: + `2026-04-29_eigentrust_propagators_pitfalls.md` + (5 entries — fire-once vs chain, cell-merge during initial reads, + float `equal?`, `for/sum` exact 0 vs 0.0, fine-grained cell-id + lookup). + +## Reproducing the W3 Prologos-math number specifically + +``` +# Pure reduce_ms for ONE W3 evaluation (no W1/W2 bundled, no startup): +racket tools/bench-phases.rkt --runs 2 \ + benchmarks/comparative/eigentrust-list-posit-w3only.prologos +``` + +This file is identical to `eigentrust-list-posit.prologos` for the +defns + fixtures, but its only top-level expression is the W3 +workload (`eigentrust m-ring-4 p-seed-0 ~0.3 ~0.0 3`). The reported +`reduce_ms` is therefore the cost of evaluating exactly that one +expression through the Prologos reducer, with all earlier phases +(elaborate, type-check, qtt, zonk) reported separately and summing +to ~1.5 s on this workload (≈100× smaller than reduce). diff --git a/docs/tracking/2026-04-23_eigentrust_pitfalls.md b/docs/tracking/2026-04-23_eigentrust_pitfalls.md new file mode 100644 index 000000000..72c9cb175 --- /dev/null +++ b/docs/tracking/2026-04-23_eigentrust_pitfalls.md @@ -0,0 +1,410 @@ +# EigenTrust in Prologos — Surprising Language Gotchas + +_Session: 2026-04-23. Implementing the EigenTrust reputation algorithm +(Kamvar-Schlosser-Garcia-Molina 2003) exposed several Prologos +language/elaboration behaviors that felt like they should have worked +but did not. These notes are for future readers who hit the same walls, +and as potential backlog items._ + +## 1. Primitive operators are not first-class values in `zip-with` / `map` + +**Expected:** +```prologos +[zip-with rat+ xs ys] ;; elementwise add — looks obvious +[map rat-abs xs] ;; elementwise abs — looks obvious +``` + +**Actual:** `Unbound variable rat+`, `Multiplicity violation` in the +elaborator. `rat+` (and its siblings `rat*`, `rat-`, `rat-abs`, +`int+`, `int*`, …) are preparser keywords backed by `expr-rat-add`-style +AST structs, not regular functions. They are only legal in call +position with the right arity; they cannot be bound to a variable, +stored in a data structure, or passed to a higher-order combinator. + +**Notable asymmetry:** `[foldr int+ 0 '[1 2 3 4 5]]` **does** work (used +in `2026-04-17-ppn-track4c-adversarial.prologos` and +`2026-03-25-track10b.prologos`). So `foldr` somehow accepts these +tokens even though `zip-with` and `map` do not. Either `foldr` has a +special-case in the preparser or the test coverage gap is larger than +we think. + +**Workaround:** explicit recursion or a named `fn` wrapper — +```prologos +spec add-vec [List Rat] [List Rat] -> [List Rat] +defn add-vec [xs ys] + match xs + | nil -> nil + | cons x as -> match ys + | nil -> nil + | cons y bs -> cons [rat+ x y] [add-vec as bs] +``` + +**Suggested fix:** make every `expr-*-add`/etc. elaborate into a +concrete lambda when used in non-application position, or at least +surface a consistent "not first-class" error across all call sites. + +## 2. Closure capture in `map` triggers QTT multiplicity violations + +**Expected:** +```prologos +spec scale-vec Rat [List Rat] -> [List Rat] +defn scale-vec [s xs] + [map (fn [x : Rat] [rat* s x]) xs] +``` + +**Actual:** QTT multiplicity error — `s` is being captured at +multiplicity 1 (linear) but used once per element of `xs`. The +elaborator does not auto-infer an unrestricted multiplicity for the +closure argument. + +**Workaround:** explicit recursion that threads `s` through directly. +Same pattern as workaround #1. + +**Suggested fix:** default captured type variables to `mw` when the +closure is the argument to a generic combinator whose spec is +polymorphic in multiplicity. Documented hint in CLAUDE.md +("Dict params use mw not m0") already covers dict params — extend the +same logic to ordinary closures. + +## 3. `0/1` and `1/1` inside nested list literals silently become `Int` + +**Expected:** +```prologos +def C : [List [List Rat]] + := '['[0/1 1/2 1/2] '[1/2 0/1 1/2] '[1/2 1/2 0/1]] +``` + +**Actual:** `Type mismatch [List [List Rat]] +'['[0 1/2 1/2] …]`. The WS reader (or a preparse pass) canonicalises +`0/1` → `0` and `1/1` → `1` *before* list-literal type inference, +so the outer `'[…]` sees a mix of `Int` and `Rat` tokens and picks +`Int` as the element type, against the outer type annotation. + +**Workaround:** bind `def rz : Rat := 0/1` at top level and splice +the reference in: +```prologos +def rz : Rat := 0/1 +def C : [List [List Rat]] := '['[rz 1/2 1/2] '[1/2 rz 1/2] '[1/2 1/2 rz]] +``` + +The reference is opaque to the simplifier, so the type stays `Rat`. + +**Suggested fix:** preserve the `Rat` type of a literal when the +surrounding context (either annotation or enclosing list element +type) demands it. This is the mirror image of the "don't coerce +across number types" principle — here the parser is effectively +coercing `Rat → Int` silently. + +## 4. Mutual recursion between top-level `defn`s is unsupported + +**Expected:** two `defn`s that reference each other (a two-state +iterator with an advance/check split) elaborate like mutually +recursive functions in every other ML-family language. + +**Actual:** forward references fail with `Unbound variable ` +for whichever of the pair comes first in the file. Noted as a known +issue in `examples/2026-03-16-track4-acceptance.prologos`: + +> `;; BUG (L3): forward reference to odd? at file level — mutual +> recursion not supported.` + +**Workaround:** collapse both functions into one, pass extra +arguments to avoid the cross-call. For EigenTrust I carry both +`t` (previous iterate) and `tnew` (current iterate) so each round +computes `eigentrust-step` exactly once without a second `defn`. + +**Suggested fix:** a `defns` (plural) block or an inference pass +that gathers all mutually-referencing `defn`s in a module before +binding them. + +## 5. `let` with a multi-line indented body confuses the WS reader + +**Expected:** +```prologos +(let [tnew := [eigentrust-step c p alpha t]] + match [rat-lt [linf-norm [sub-vec tnew t]] eps] + | true -> tnew + | false -> [eigentrust-iterate c p alpha eps [int- budget 1] tnew]) +``` + +**Actual:** `let: let with bracket bindings requires: (let [bindings…] body)`. +Working `let` examples in the codebase have either (a) the body on the +same physical line as `let ...]`, or (b) a very specific two-space +indentation with a pure expression body — nested `match` inside a +`let` body doesn't parse under WS. + +**Workaround:** lift the `let` out into an extra parameter passed +through the recursive call (option #4 above). + +**Suggested fix:** align the WS `let` parser with the usual +"continuation lines are indented further than `let`" rule that +`defn`/`match` already honour. + +## 6. Multi-line `spec` with line-comments between tokens breaks + +**Expected:** +```prologos +spec eigentrust-step + [List [List Rat]] ;; matrix C + [List Rat] ;; pre-trust p + Rat ;; damping + [List Rat] ;; current t + -> [List Rat] ;; next t +defn eigentrust-step [c p alpha t] … +``` + +**Actual:** `spec: spec type for eigentrust-step has no arrow but defn +has 4 params`. The preparser's token-scan doesn't cross comment-end +lines consistently, so it sees only some of the continuation tokens +and thinks the spec is missing its `->`. + +**Workaround:** collapse to one line. + +**Suggested fix:** ignore line comments in spec-type scanning the same +way they are ignored elsewhere. + +## 7. Multi-arity `defn | pat -> body | pat -> body` is single-argument only + +**Expected:** +```prologos +defn sum-rows + | nil -> nil + | cons r nil -> r + | cons r rest -> [add-vec r [sum-rows rest]] +``` + +The three-clause form, with a literal `nil` in the second position of +the second clause, looks like a standard ML/Haskell pattern. + +**Actual:** `Unbound variable sum-rows::1` — the compiler generates a +per-clause helper and then fails to resolve it. The issue might be the +nested literal pattern; it might also be that multi-clause `defn` only +works for single-argument cases (cf. `is-zero | zero -> true | suc _ -> +false`). + +**Workaround:** nested `match` inside a `defn [args] body`. + +**Suggested fix:** either support general pattern matrices in +multi-clause `defn`, or raise a clear error that says "multi-clause +defn accepts only a single argument pattern." + +## 8. Exact-Rat arithmetic slows power iteration by many orders of magnitude + +**Observation:** a 3×3 damped power iteration with `alpha = 1/10` and +40 steps did not terminate within 5 minutes; the same problem with 15 +steps completed in ~2 minutes; switching to matrices that avoid `rz` +(no `0/1 → Int 0` workaround) cut the same 20-step workload to ~47 +seconds. + +The asymptotic cause is that each Rat multiplication can grow the +numerator and denominator by a small factor, so after *k* steps +denominators can reach ≈ (10 · max-matrix-denom)^k. The simplification +pass runs on every operation but doesn't prevent the growth when the +iterates don't share a common denominator. + +**Consequence:** EigenTrust is great for correctness tests (exact +arithmetic means golden-output testing is trivial) but a poor fit for +deep-iteration benchmarks. A future `Float` / `Posit32` variant of the +same algorithm would let us benchmark at larger n and deeper iteration +counts. + +## Summary of the implementation-shape constraints (List + Rat) + +After all of the above, the List + Rat EigenTrust implementation obeys +these rules: +* Every vector/matrix helper is explicit structural recursion; no + `map`, `zip-with`, or `foldr` over Rat lists. +* Every `fn` closure avoids capturing by inlining the captured value + as a function argument. +* Every Rat constant that lives inside a nested `'[…]` literal is + either a proper fraction like `1/2` or a top-level `def` of Rat + type. +* Every `spec` fits on one line, with arrow and all argument types + flush. +* Every iteration is one `defn` — the `(t, tnew)` pair trick avoids + mutual recursion and the broken WS `let`. +* Benchmark matrices are small (3×3, 4×4) with modest iteration + counts (≤ 15) so exact-Rat arithmetic stays tractable. + + +## Addendum (2026-04-23, same day): Posit32 and PVec variants + +After the initial List+Rat version shipped, the user asked for Posit +variants and PVec variants to compare performance across +{List, PVec} × {Rat, Posit32}. That surfaced several more behaviors. + +### 9. Posit32 literals survive nested list/PVec literals — `Rat` quirks do not apply + +**Observation:** where `'[0/1 1/2]` fails (`0/1` reads as Int `0`) and +`@[0/1 1/2]` succeeds (PVec preserves Rat type), `'[~0.0 ~0.5]` and +`@[~0.0 ~0.5]` *both* preserve `Posit32`. The `~0.0` / `~1.0` literal +form does not have a bare-Int alias, so the preparser cannot silently +reinterpret it. + +**Consequence:** the Posit variants can write matrices directly +without the `rz : Rat := 0/1` splice workaround. One less line of +ceremony. + +### 10. PVec `@[...]` literals preserve element types where `'[...]` does not + +**Observation:** `@[0/1 1/2]` elaborates as `PVec Rat` with `0/1` +stored as the Rat value whose numerator is 0. The List literal +`'[0/1 1/2]` silently coerces the same `0/1` to Int. Either a reader +difference or a type-propagation difference in elaboration; either +way the PVec path is more permissive. + +**Consequence:** for Rat-typed matrices, PVec is more ergonomic than +List — no splice workaround is needed. For Posit-typed matrices both +containers are equally ergonomic. + +### 11. Lazy argument reduction makes deep iteration scale as O(k²) for + non-fixed-point iterates + +**Observation:** `eigentrust-iterate` passes its recursive call the +expression `[eigentrust-step c p alpha tnew]` *unreduced* as the new +`tnew`. Each subsequent iteration forces reduction of the whole +argument chain. When every intermediate value differs bit-for-bit +(Posit32, or non-fixed-point Rat), the reducer redoes O(n) levels on +every iteration — total ~O(k²) terms expanded across k rounds — plus +whatever constant the pattern-match compiler adds on top. + +Concretely: +- List+Rat, 10 iters on 4×4 uniform (starting *at* the fixed point + where every step returns exactly the same Rat): ~36 s total (fine, + because the term tree collapses to a single repeated value). +- List+Posit, 10 iters on the same workload (starting at the fixed + point, but Posit32 step introduces tiny rounding so the tree does + *not* collapse): does not terminate within 2 min. +- List+Posit, 5 iters on an asymmetric 3×3 matrix: does not terminate + within 3 min. + +**Workaround:** use converging workloads (eps > 0) where the iterator +exits after 1–2 rounds, or use exact-Rat fixed-point matrices where +every step returns the identical value. Deep iter-budget-driven +workloads blow up regardless of matrix size. + +**Suggested fix:** force reduction of the `tnew` argument before the +recursive tail call — either via a primitive `seq`/`force` form, or +by making the reducer aggressively normalise `eigentrust-step` results +to `[posit32 bitpattern]` / `'[rat literals]` WHNF. + +### 12. PVec indexing is Nat-only; Int indices and `from-int` don't bridge + +**Observation:** `pvec-nth : PVec A → Nat → A`. There is no +`pvec-nth-int` (unlike `nth-int` for Lists). There is `from-nat : +Nat → Int` but no `from-int : Int → Nat`. So an algorithm that already +has Int counters (for `int-le budget 0` termination checks) has to +carry a second Nat counter in parallel when it wants to index a PVec. + +**Workaround:** use `zero`/`suc i` Nat counters for PVec indexing and +keep `Int` only for the iteration budget. The PVec variants of this +algorithm do exactly this and it doubles the arity of every inner +helper (`-go` functions now take both `i : Nat` and `n : Nat`). + +**Suggested fix:** add `pvec-nth-int`, `pvec-length-int`, and friends +— mirror the `nth-int` / `length-int` / `take-int` / `drop-int` +quartet that already exists for List. + +### 13. Index-based recursion over PVec is forced because `pvec-map` + and `pvec-fold` have the same closure-capture issue as `map` + +**Observation:** every `pvec-map`/`pvec-fold` call that wants to +capture a scalar (e.g. `pvec-map (fn [x] [rat* s x]) v`) would hit +the QTT multiplicity issue from gotcha #2. Since there's no +`zip-with` equivalent for PVec (i.e. no `pvec-zip-with`), elementwise +operations on two PVecs must be written as explicit +index-threaded-accumulator recursion: `acc` grows via `pvec-push`, +inputs are read via `pvec-nth`. + +**Consequence:** the PVec variants each have one extra `*-go` +function per primitive (scale-vec-go, add-vec-go, sub-vec-go, +linf-norm-go, col-dot-go, ct-times-vec-go) that the List variants +don't need. That's 6 extra top-level defns, roughly doubling the +file size. + +**Suggested fix:** either fix closure multiplicity inference, add +a `pvec-zip-with` primitive, or both. + +### 14. PVec does not print with the same syntax it reads + +**Observation:** the reader accepts `@[1/4 1/4 1/4 1/4]` and the +type-checker reports the value as `(PVec Rat)` — but the output +pretty-printer writes `(PVec Rat)` in parens where the type signature +of a `spec` would say `[PVec Rat]`. Not a correctness issue, but a +cosmetic divergence between how types are *read* (`[...]`) and how +they're *printed* (`(...)`). + +### 16. EigenTrust wants column-stochastic, not row-stochastic + +**Observation:** the original 2003 paper defines `c_{ij}` as +"peer i's normalized trust in peer j" — this gives a **row-stochastic** +matrix `C`. The global-trust update is then `t_{k+1} = C^T · t_k`, +i.e. the matrix that actually gets multiplied by `t` is `C^T`, which +is **column-stochastic**. + +In my first implementation I took `C` (row-stochastic) as input and +computed `(C^T · t)` internally via "sum of row-scaled rows" (a trick +to avoid an explicit transpose). This worked but made the invariant +awkward to state: users had to pass a row-stochastic matrix and +trust the algorithm to do the right dance. + +**Fix:** take the column-stochastic matrix `M` directly as input. The +update is plain matrix-vector multiply `y[i] = dot(M[i], t)`. The +invariant is clear: each column of M sums to 1. `col-stochastic?` +checks it in O(n²) by summing rows (columns-sums-of-M = sum of M's +rows element-wise). `eigentrust` panics if the invariant fails. + +This also simplifies the implementation: no more `sum-rows` / +`scale-rows` helpers, just `dot` + standard `mat-vec-mul`. + +**Ring fixture:** a column-stochastic ring of size n is the +permutation matrix where column j has a single 1 in row (j+1) mod n. +It's doubly stochastic so uniform is stationary, but if pre-trust is +concentrated on one node, damping settles the distribution slowly +(rate ≈ 1-α). This is a better "slow iteration" fixture than an +asymmetric row-stochastic matrix whose geometry happens to produce +interesting dynamics under the transpose trick. + +### 15. Multi-line `def X : T := body` silently suppresses evaluation of downstream top-level expressions + +**Observation:** when a `def` with a type annotation is split across +two lines — +```prologos +def c-asym-3 : [List [List Rat]] + := '['[rz 1/2 1/2] '[rz rz ro] '[ro rz rz]] +``` +— subsequent top-level expressions in the file that depend on the +bound name appear to execute (they type-check, they return values) but +the reducer never fires. `PHASE-TIMINGS` reports `reduce_ms = 0`, and +the computed value is never printed to stdout by `driver.rkt`. The +same file with the def collapsed to one line — +```prologos +def c-asym-3 : [List [List Rat]] := '['[rz 1/2 1/2] '[rz rz ro] '[ro rz rz]] +``` +— reduces normally (`reduce_ms = 38_855` for a 3-iter benchmark). + +**How it was surfaced:** `bench-phases.rkt` reported `reduce_ms = 0` +for a benchmark file whose algorithm was clearly expensive. Running +the same expressions via `process-string-ws` (the test-support path) +reduced correctly. Diffing a working scratch file against the benchmark +file narrowed the trigger to the two-line `def` form, specifically the +form +``` +def NAME : TYPE + := BODY +``` + +**Consequence:** benchmarks that use multi-line `def`s for fixtures +silently measure zero reduce time, regardless of workload. This is +load-bearing for benchmark validity. + +**Workaround:** collapse every `def NAME : TYPE := BODY` to one +physical line. All four EigenTrust benchmark files have been updated +accordingly; see the bench-phases numbers go from "~250 ms reduce_ms" +(silent bug) to "20–40 s reduce_ms" (real) after the change. + +**Suggested fix:** either make the WS reader produce identical surf +trees for the one-line and two-line forms, OR have the +elaborator/preparser reject the broken form with a clear error. The +current failure mode (silent suppression of evaluation) is the worst +of both worlds. diff --git a/docs/tracking/2026-04-29_0650_eigentrust-on-propagators.md b/docs/tracking/2026-04-29_0650_eigentrust-on-propagators.md new file mode 100644 index 000000000..db4d157e2 --- /dev/null +++ b/docs/tracking/2026-04-29_0650_eigentrust-on-propagators.md @@ -0,0 +1,129 @@ +# EigenTrust on Propagators (Racket-direct, 5th comparison variant) + +**Track**: PR #2 follow-up — direct Racket-on-propagators eigentrust to +compare against the four Prologos surface variants. + +## Summary + +The four existing variants (List + Rat, List + Posit32, PVec + Rat, +PVec + Posit32 — see `2026-04-23_eigentrust_comparison.md`) are all +Prologos surface programs whose `reduce_ms` we measure through +`process-file`. They share the Prologos elaboration / type-check +overhead and the exact-Rat denominator dynamics. + +This track adds a 5th variant: a direct Racket implementation of +EigenTrust that uses Prologos's underlying propagator network +infrastructure (`make-prop-network`, `net-new-cell`, +`net-add-propagator`, `run-to-quiescence-bsp`) but bypasses the +Prologos surface language entirely. The measurement isolates "how +fast is the propagator infrastructure on this workload" from "how +fast is Prologos elaboration + reduction on this workload". + +## Design + +### Architecture: chain-of-cells, one per iteration + +``` ++---+ step-1 +---+ step-2 +---+ step-3 +---+ +|t_0| ---------> |t_1| ---------> |t_2| ---------> |t_3| ++---+ +---+ +---+ +---+ +``` + +- **Cells**: `t_0`, `t_1`, …, `t_K` — each holds the entire trust + vector at iteration k, as a Racket value. The `t_0` cell is + pre-loaded with the pre-trust vector p. +- **Constants on-network**: `m-cid` (matrix), `p-cid` (pre-trust), + `alpha-cid` (damping). Each is a fire-once-write cell with + last-write-wins merge. +- **Propagators**: K fire-once propagators. `step-k` reads + (`alpha`, `m`, `p`, `t_{k-1}`) and writes `t_k = (1-α)·M·t + α·p`. + Fire-once because each step happens exactly once (no re-fire). +- **Final read**: after BSP quiescence, `net-cell-read net t_K-cid` + returns the result vector. + +### Cell merge + +Trust vectors are not naturally monotone (each step replaces). +Merge is **last-write-wins**: `(λ (old new) new)`. Combined with +fire-once, this gives "exactly one write" semantics — the cell value +is the propagator's output. (For a true monotone formulation we +would need iteration-indexed lattice values, but that adds +complexity without illuminating the comparison; documented as a +follow-up.) + +### Workload + +Match the Prologos benchmarks' W3 (the dominant workload): +**4-peer ring matrix, pre-trust concentrated on peer 0, α = 3/10, +3 forced iterations** (eps = 0/1, budget = 3). The expected +result after 3 iterations is + +``` +[5401/10000, 21/100, 147/1000, 1029/10000] +``` + +— same hand-computed trajectory as the Prologos versions, derivable +from `t_{k+1} = 7/10·M·t_k + 3/10·p`. + +## Network Reality Check + +Per `.claude/rules/workflow.md` § "Network Reality Check": + +1. **`net-add-propagator` calls?** K calls (one per step) → installation phase. +2. **`net-cell-write` calls produce the result?** K writes (one per `t_k`); final answer is read from `t_K`'s cell. +3. **cell creation → prop installation → cell write → cell read = result?** Yes — direct chain. + +The matrix-vector multiply inside the fire function is off-network Racket compute. This is acknowledged debt: the on-network cell flow carries trust vectors between iterations; the per-step computation is opaque to the network. A finer-grained variant (one cell per peer per iteration, K·n propagators each doing one row's dot product) would be more on-network but at higher constant cost — out of scope for the comparison-vs-Prologos goal. + +## Progress Tracker + +| Phase | Description | Status | Notes | +|---|---|---|---| +| 0 | Tracking doc (this file) | ✅ | – | +| 1 | Implementation: chain-of-cells eigentrust on propagators | ✅ | `benchmarks/comparative/eigentrust-propagators.rkt` — plain propagators (not fire-once); ring-4 W3 returns matching `[5401/10000, 21/100, 147/1000, 1029/10000]` | +| 2 | rackunit test: verify ring-4 result matches the Prologos variants | ✅ | `tests/test-eigentrust-propagators.rkt` — 13 tests (col-stochastic check, off-network kernel, end-to-end, mass preservation), all pass | +| 3 | Benchmark runner integrated with `tools/bench-micro.rkt`-style timing | ✅ | `benchmarks/comparative/eigentrust-propagators-bench.rkt` — W1/W2/W3/W3-deep, 5-run median | +| 4 | 5-way comparison numbers; update `2026-04-23_eigentrust_comparison.md` | ✅ | W3 reduce_ms = 0.16 ms vs Prologos List+Rat 18 372 ms (~115 000× faster); k=10 tractable where surface variants O(k²) blow up | + +## Headline result + +| Variant | reduce_ms (W3, ring k=4) | +|--------------------|--------------------------| +| List + Rat (P) | 18 372 | +| List + Posit32 (P) | 17 990 | +| PVec + Rat (P) | 33 006 | +| PVec + Posit32 (P) | 33 198 | +| **propagators** | **0.16** | + +The propagator-direct version is ~115 000× faster because it +bypasses Prologos elaboration + reduction — both versions perform +identical Racket-level rational arithmetic. The gap measures the +cost of the Prologos surface language + reducer on this workload. + +## Network Reality Check (final) + +1. **`net-add-propagator` calls?** K (= 4 for W3) plain propagators installed. +2. **`net-cell-write` calls produce the result?** K writes to t_1..t_K cells; the final answer is read from t_K. +3. **cell creation → prop installation → cell write → cell read = result?** Yes. After construction, BSP runs; after K rounds the chain has settled; final cell is read. + +The matrix-vector multiply inside each fire function is off-network +Racket compute (Racket's vector + rational ops). The on-network +flow is the trust-vector cell sequence between iterations. A +fine-grained variant (one cell per peer per iteration, K·n +propagators each doing one row's dot product) is documented in +the design as out-of-scope; the K-step chain is the simplest +on-network expression of the iteration that satisfies the Reality +Check. + +## Files + +- `racket/prologos/benchmarks/comparative/eigentrust-propagators.rkt` — implementation + main entry +- `racket/prologos/tests/test-eigentrust-propagators.rkt` — rackunit +- `docs/tracking/2026-04-23_eigentrust_comparison.md` — updated with 5th-variant numbers (Phase 4) + +## Out of scope + +- Fine-grained per-peer cells (K·n propagators) +- Convergence-driven version (eps-based) — only forced-iteration here, matches W3 +- Iteration-indexed lattice values for true monotone semantics +- Rewriting the Prologos surface variants to use the same fixture set (already done in PR #2) diff --git a/docs/tracking/2026-04-29_eigentrust_propagators_pitfalls.md b/docs/tracking/2026-04-29_eigentrust_propagators_pitfalls.md new file mode 100644 index 000000000..f6bbbe258 --- /dev/null +++ b/docs/tracking/2026-04-29_eigentrust_propagators_pitfalls.md @@ -0,0 +1,145 @@ +# EigenTrust on Propagators — Pitfalls + +_Track-specific landmines surfaced while implementing EigenTrust +directly on Prologos's propagator network infrastructure +(2026-04-29). For the surface-language pitfalls see +`2026-04-23_eigentrust_pitfalls.md`._ + +## 1. Fire-once + initial scheduling breaks chained propagators + +**Expected:** +```racket +;; Chain: t_0 → step-1 → t_1 → step-2 → t_2 → ... → t_K. +;; Use net-add-fire-once-propagator because each step fires exactly once. +(net-add-fire-once-propagator net inputs outputs fire-fn) +``` + +**Actual:** all K fire-once propagators get scheduled at install +time (since their inputs already have values — t_0 was pre-loaded +with p, the constants are pre-loaded), then BSP fires them ALL +in round 1 on the same initial snapshot. Only step-1's read of +its input (t_0) sees a useful value (p); step-2..step-K read their +input cells which are still at their initial value (zeros). +Each step writes `α·p` (the formula's other term) and that's the +final result — every iteration cell ends up holding `α·p`, not the +correct trajectory. + +**Why fire-once doesn't compose with chains:** fire-once means +"fire AT MOST ONCE per propagator". Once a propagator fires in +round 1, it never fires again — even if its input cell changes +later via a chain-upstream write. A chain depends on +re-firing-on-input-change, which is what _plain_ propagators do. + +**Fix:** use `net-add-propagator` (no fire-once flag). Plain +propagators re-fire on input changes. After the first BSP round +all step-k cells have wrong values, but only t_1 (= step-1's output +from a correct read of t_0) is useful. In round 2, step-2 sees +t_1's correct value and re-fires (its other inputs — M, p, α — +haven't changed but cell-change detection only needs ONE input to +have changed). After K BSP rounds the chain has fully settled. + +**Suggested doc fix:** the propagator-design checklist in +`.claude/rules/propagator-design.md` already says +> "If the propagator writes a result and never needs to fire again +> (narrower, contradiction detector, type-write, usage-write, +> constraint-creation), use `net-add-fire-once-propagator`." + +Add a counter-example: "If the propagator is part of a chain +where it must fire when an upstream sibling produces output, use +plain `net-add-propagator`. Fire-once + chain = wrong result on +the first round." Or, equivalently: "fire-once is for +single-direction writes from inputs that are SET BEFORE the +propagator is installed — not for chains where some upstream input +is written by another propagator after install." + +**Diagnostic clue:** if a chained propagator network produces the +"residual term only" (in EigenTrust: `α·p` instead of +`(1-α)·M·t_K + α·p`), it's the fire-once-on-uninitialised-input +pattern. + +## 2. Cell merge function semantics during initial-value reads vs writes + +**Observation:** `net-new-cell net initial-value merge-fn` stores +`initial-value` directly. The merge function is invoked only on +subsequent `net-cell-write` calls (`new = merge-fn(old, new-from-write)`). +A propagator that reads a cell BEFORE any write sees the initial +value untransformed — `merge-fn` plays no role. + +**Why this matters:** in the chain implementation, the trust-vector +cells `t_1, …, t_K` are initialised to a zero vector (placeholder) +and overwritten by their step propagator. The first write goes +through `merge-fn`, but with `initial = zeros` and `new = step-result` +the merge result must be `step-result` regardless of `initial`. +We use `(λ (old new) new)` (last-write-wins). If we'd used a +"pointwise max" or "set-union" merge on a value-typed cell, the +zero-vector initial would survive the first merge in some +positions and silently corrupt the iterate. + +**Suggested practice:** for value-typed (non-monotone) cells, use +last-write-wins explicitly. Combined with plain (not fire-once) +propagators, this gives "the cell holds the latest computed value" +semantics — which is what most numerical iterations want. + +## 3. Vector equality of inexact (float) values fails `equal?` + +**Observation:** Racket's `equal?` on two `(vector 0.1 0.2 0.3)` +values is `#t` only if the byte-identical floats are stored. After +arithmetic (e.g. `(+ 0.1 0.2)` vs `(+ 0.2 0.1)` — which can produce +different bit patterns due to non-commutative round-off) `equal?` +returns `#f` even though the values are mathematically equal. + +**Consequence for tests:** rackunit's `check-equal?` against an +expected float vector is brittle. Use `check-=` with an explicit +tolerance, or compare each component within an epsilon, or check +properties (mass-preservation, magnitude bounds) rather than exact +equality. + +**Workaround in this work:** the float variant's tests check that +the result is within ε of the exact-rational result. We compute +the rational version (which has `equal?`-stable output) and then +verify `|float-result[i] - rational-result[i]| < eps` for each i. + +## 4. `for/sum` over an empty vector returns 0 (an exact integer), not 0.0 + +**Observation:** `(for/sum ([x (in-vector (vector))]) x)` returns +`0`, the exact integer. When the rest of the algorithm expects +flonums, this propagates an exact 0 through subsequent arithmetic. +Mixed exact/inexact arithmetic in Racket promotes to inexact, so +correctness is preserved, but type predicates like `flonum?` or +`(real? x)` checks may give surprising answers. + +**Workaround:** seed `for/sum` with `0.0` explicitly: +```racket +(for/sum ([x (in-vector v)] #:when #t) x) ;; 0 for empty +;; vs +(for/fold ([acc 0.0]) ([x (in-vector v)]) (+ acc x)) ;; 0.0 for empty +``` + +This is a Racket library hiccup (not a Prologos one), but it bites +when constructing the initial accumulator for a flonum dot product +on an empty vector — though for non-empty matrices we never hit +the empty case. + +## 5. Fine-grained per-peer variant: cell-id arrays vs lookup overhead + +**Observation:** the fine-grained variant allocates `K·n` cells +(one per peer per iteration) plus 4 constant cells. For W3 +(K=4, n=4) that's 20 cells. Each fire function reads n+3 inputs +(`t_{k-1, 0..n-1}`, `M[i,:]` constant, `p[i]`, `alpha`). + +**Naive implementation pitfall:** if you store cell-ids in a list +and `list-ref` in the fire function, you pay O(n) lookup per access +times O(n) access count = O(n²) per fire. For small n this is +swamped by `net-cell-read` costs but at scale it matters. + +**Workaround:** store cell-ids in a vector of vectors +(`(vector-ref (vector-ref t-cids k) i)`) for O(1) access. Closures +capture the vector once at install time. + +**Bonus:** the per-peer variant is more "on-network" (each peer's +trust is its own cell) but also has higher constant overhead. For +small n (≤ 4) the coarse variant is faster; for larger n the +fine-grained version's parallel-fire potential MIGHT win, but at +the matrix sizes EigenTrust is typically benchmarked on (n ≤ 100), +the constant-factor overhead per cell ($cell-id allocation + CHAMP +insert + dependent registration) dominates. diff --git a/racket/prologos/benchmarks/comparative/eigentrust-list-posit-w3only.prologos b/racket/prologos/benchmarks/comparative/eigentrust-list-posit-w3only.prologos new file mode 100644 index 000000000..f72de7f1b --- /dev/null +++ b/racket/prologos/benchmarks/comparative/eigentrust-list-posit-w3only.prologos @@ -0,0 +1,116 @@ +ns benchmarks::comparative::eigentrust-list-posit-w3only + +;; ============================================================ +;; Benchmark: EigenTrust — List + Posit32 variant +;; See eigentrust-list-rat.prologos for the workload specification. +;; ============================================================ + +spec dot [List Posit32] [List Posit32] -> Posit32 +defn dot [xs ys] + match xs + | nil -> ~0.0 + | cons x as -> match ys + | nil -> ~0.0 + | cons y bs -> p32+ [p32* x y] [dot as bs] + +spec scale-vec Posit32 [List Posit32] -> [List Posit32] +defn scale-vec [s xs] + match xs + | nil -> nil + | cons x as -> cons [p32* s x] [scale-vec s as] + +spec add-vec [List Posit32] [List Posit32] -> [List Posit32] +defn add-vec [xs ys] + match xs + | nil -> nil + | cons x as -> match ys + | nil -> nil + | cons y bs -> cons [p32+ x y] [add-vec as bs] + +spec sub-vec [List Posit32] [List Posit32] -> [List Posit32] +defn sub-vec [xs ys] + match xs + | nil -> nil + | cons x as -> match ys + | nil -> nil + | cons y bs -> cons [p32- x y] [sub-vec as bs] + +spec p32-max Posit32 Posit32 -> Posit32 +defn p32-max [a b] + match [p32-lt a b] + | true -> b + | false -> a + +spec linf-norm [List Posit32] -> Posit32 +defn linf-norm [xs] + match xs + | nil -> ~0.0 + | cons x as -> p32-max [p32-abs x] [linf-norm as] + +spec mat-vec-mul [List [List Posit32]] [List Posit32] -> [List Posit32] +defn mat-vec-mul [m t] + match m + | nil -> nil + | cons r rs -> cons [dot r t] [mat-vec-mul rs t] + +spec col-sums [List [List Posit32]] -> [List Posit32] +defn col-sums [m] + match m + | nil -> nil + | cons r rest -> match rest + | nil -> r + | cons _ _ -> add-vec r [col-sums rest] + +spec all-ones? [List Posit32] -> Bool +defn all-ones? [xs] + match xs + | nil -> true + | cons x as -> match [p32-eq x ~1.0] + | false -> false + | true -> all-ones? as + +spec col-stochastic? [List [List Posit32]] -> Bool +defn col-stochastic? [m] + all-ones? [col-sums m] + +spec eigentrust-step [List [List Posit32]] [List Posit32] Posit32 [List Posit32] -> [List Posit32] +defn eigentrust-step [m p alpha t] + add-vec [scale-vec [p32- ~1.0 alpha] [mat-vec-mul m t]] [scale-vec alpha p] + +spec eigentrust-iterate [List [List Posit32]] [List Posit32] Posit32 Posit32 Int [List Posit32] [List Posit32] -> [List Posit32] +defn eigentrust-iterate [m p alpha eps budget t tnew] + match [int-le budget 0] + | true -> tnew + | false -> match [p32-lt [linf-norm [sub-vec tnew t]] eps] + | true -> tnew + | false -> eigentrust-iterate m p alpha eps [int- budget 1] tnew [eigentrust-step m p alpha tnew] + +spec eigentrust [List [List Posit32]] [List Posit32] Posit32 Posit32 Int -> [List Posit32] +defn eigentrust [m p alpha eps max-iter] + match [col-stochastic? m] + | false -> (the [List Posit32] [panic "eigentrust: M must be column-stochastic"]) + | true -> eigentrust-iterate m p alpha eps max-iter p [eigentrust-step m p alpha p] + + +;; ============================================================ +;; Fixtures +;; ============================================================ + +def p-uniform-4 : [List Posit32] := '[~0.25 ~0.25 ~0.25 ~0.25] +def p-uniform-3 : [List Posit32] := '[~0.33333 ~0.33333 ~0.33333] +def p-seed-0 : [List Posit32] := '[~1.0 ~0.0 ~0.0 ~0.0] + +def m-uniform-4 : [List [List Posit32]] := '['[~0.25 ~0.25 ~0.25 ~0.25] '[~0.25 ~0.25 ~0.25 ~0.25] '[~0.25 ~0.25 ~0.25 ~0.25] '[~0.25 ~0.25 ~0.25 ~0.25]] + +def m-others-3 : [List [List Posit32]] := '['[~0.0 ~0.5 ~0.5] '[~0.5 ~0.0 ~0.5] '[~0.5 ~0.5 ~0.0]] + +def m-ring-4 : [List [List Posit32]] := '['[~0.0 ~0.0 ~0.0 ~1.0] '[~1.0 ~0.0 ~0.0 ~0.0] '[~0.0 ~1.0 ~0.0 ~0.0] '[~0.0 ~0.0 ~1.0 ~0.0]] + + +;; ============================================================ +;; W3 ONLY — for isolating reduce_ms attributable to the iteration. +;; Same workload as the W3 line in eigentrust-list-posit.prologos. +;; max-iter=3 ⇒ k=4 step calls (entry pre-computes step 1). +;; ============================================================ + +eigentrust m-ring-4 p-seed-0 ~0.3 ~0.0 3 diff --git a/racket/prologos/benchmarks/comparative/eigentrust-list-posit.prologos b/racket/prologos/benchmarks/comparative/eigentrust-list-posit.prologos new file mode 100644 index 000000000..79fb9011b --- /dev/null +++ b/racket/prologos/benchmarks/comparative/eigentrust-list-posit.prologos @@ -0,0 +1,122 @@ +ns benchmarks::comparative::eigentrust-list-posit + +;; ============================================================ +;; Benchmark: EigenTrust — List + Posit32 variant +;; See eigentrust-list-rat.prologos for the workload specification. +;; ============================================================ + +spec dot [List Posit32] [List Posit32] -> Posit32 +defn dot [xs ys] + match xs + | nil -> ~0.0 + | cons x as -> match ys + | nil -> ~0.0 + | cons y bs -> p32+ [p32* x y] [dot as bs] + +spec scale-vec Posit32 [List Posit32] -> [List Posit32] +defn scale-vec [s xs] + match xs + | nil -> nil + | cons x as -> cons [p32* s x] [scale-vec s as] + +spec add-vec [List Posit32] [List Posit32] -> [List Posit32] +defn add-vec [xs ys] + match xs + | nil -> nil + | cons x as -> match ys + | nil -> nil + | cons y bs -> cons [p32+ x y] [add-vec as bs] + +spec sub-vec [List Posit32] [List Posit32] -> [List Posit32] +defn sub-vec [xs ys] + match xs + | nil -> nil + | cons x as -> match ys + | nil -> nil + | cons y bs -> cons [p32- x y] [sub-vec as bs] + +spec p32-max Posit32 Posit32 -> Posit32 +defn p32-max [a b] + match [p32-lt a b] + | true -> b + | false -> a + +spec linf-norm [List Posit32] -> Posit32 +defn linf-norm [xs] + match xs + | nil -> ~0.0 + | cons x as -> p32-max [p32-abs x] [linf-norm as] + +spec mat-vec-mul [List [List Posit32]] [List Posit32] -> [List Posit32] +defn mat-vec-mul [m t] + match m + | nil -> nil + | cons r rs -> cons [dot r t] [mat-vec-mul rs t] + +spec col-sums [List [List Posit32]] -> [List Posit32] +defn col-sums [m] + match m + | nil -> nil + | cons r rest -> match rest + | nil -> r + | cons _ _ -> add-vec r [col-sums rest] + +spec all-ones? [List Posit32] -> Bool +defn all-ones? [xs] + match xs + | nil -> true + | cons x as -> match [p32-eq x ~1.0] + | false -> false + | true -> all-ones? as + +spec col-stochastic? [List [List Posit32]] -> Bool +defn col-stochastic? [m] + all-ones? [col-sums m] + +spec eigentrust-step [List [List Posit32]] [List Posit32] Posit32 [List Posit32] -> [List Posit32] +defn eigentrust-step [m p alpha t] + add-vec [scale-vec [p32- ~1.0 alpha] [mat-vec-mul m t]] [scale-vec alpha p] + +spec eigentrust-iterate [List [List Posit32]] [List Posit32] Posit32 Posit32 Int [List Posit32] [List Posit32] -> [List Posit32] +defn eigentrust-iterate [m p alpha eps budget t tnew] + match [int-le budget 0] + | true -> tnew + | false -> match [p32-lt [linf-norm [sub-vec tnew t]] eps] + | true -> tnew + | false -> eigentrust-iterate m p alpha eps [int- budget 1] tnew [eigentrust-step m p alpha tnew] + +spec eigentrust [List [List Posit32]] [List Posit32] Posit32 Posit32 Int -> [List Posit32] +defn eigentrust [m p alpha eps max-iter] + match [col-stochastic? m] + | false -> (the [List Posit32] [panic "eigentrust: M must be column-stochastic"]) + | true -> eigentrust-iterate m p alpha eps max-iter p [eigentrust-step m p alpha p] + + +;; ============================================================ +;; Fixtures +;; ============================================================ + +def p-uniform-4 : [List Posit32] := '[~0.25 ~0.25 ~0.25 ~0.25] +def p-uniform-3 : [List Posit32] := '[~0.33333 ~0.33333 ~0.33333] +def p-seed-0 : [List Posit32] := '[~1.0 ~0.0 ~0.0 ~0.0] + +def m-uniform-4 : [List [List Posit32]] := '['[~0.25 ~0.25 ~0.25 ~0.25] '[~0.25 ~0.25 ~0.25 ~0.25] '[~0.25 ~0.25 ~0.25 ~0.25] '[~0.25 ~0.25 ~0.25 ~0.25]] + +def m-others-3 : [List [List Posit32]] := '['[~0.0 ~0.5 ~0.5] '[~0.5 ~0.0 ~0.5] '[~0.5 ~0.5 ~0.0]] + +def m-ring-4 : [List [List Posit32]] := '['[~0.0 ~0.0 ~0.0 ~1.0] '[~1.0 ~0.0 ~0.0 ~0.0] '[~0.0 ~1.0 ~0.0 ~0.0] '[~0.0 ~0.0 ~1.0 ~0.0]] + + +;; ============================================================ +;; Workloads +;; ============================================================ + +eigentrust m-uniform-4 p-uniform-4 ~0.1 ~0.001 50 +eigentrust m-others-3 p-uniform-3 ~0.1 ~0.001 50 +eigentrust m-ring-4 p-seed-0 ~0.3 ~0.0 3 +col-stochastic? m-uniform-4 +eigentrust-step m-uniform-4 p-uniform-4 ~0.1 p-uniform-4 +mat-vec-mul m-uniform-4 p-uniform-4 +scale-vec ~0.5 '[~0.5 ~0.33333 ~0.25] +add-vec '[~0.25 ~0.25] '[~0.25 ~0.25] +linf-norm [sub-vec '[~0.5 ~0.33333] '[~0.25 ~0.33333]] diff --git a/racket/prologos/benchmarks/comparative/eigentrust-list-rat.prologos b/racket/prologos/benchmarks/comparative/eigentrust-list-rat.prologos new file mode 100644 index 000000000..48e9a6474 --- /dev/null +++ b/racket/prologos/benchmarks/comparative/eigentrust-list-rat.prologos @@ -0,0 +1,146 @@ +ns benchmarks::comparative::eigentrust-list-rat + +;; ============================================================ +;; Benchmark: EigenTrust — List + Rat variant +;; +;; Part of the 4-way {List, PVec} × {Rat, Posit32} comparison. +;; The matrix is column-stochastic: each column sums to 1. The +;; `eigentrust` entry point enforces the invariant via `col-stochastic?` +;; and panics if it fails. +;; +;; Shared workload across all four variants: +;; W1. eigentrust on 4x4 uniform (converges in 1 step). +;; W2. eigentrust on 3x3 symmetric (converges in 1 step). +;; W3. eigentrust on 4x4 ring with concentrated pre-trust, 3 FORCED +;; iters (alpha=3/10, eps=0 — never converges, budget exhausts). +;; This is the only workload producing non-trivial reduce_ms. +;; The ring permutation has |eigenvalue| = 1 on the unit circle; +;; only damping pulls toward uniform. Starting all trust on +;; peer 0 amplifies the settling pattern. +;; W4. col-stochastic? m-uniform-4 (invariant check). +;; W5. one eigentrust-step on 4x4 uniform. +;; W6. one mat-vec-mul on 4x4 uniform. +;; W7-W9. a handful of primitive vector ops. +;; ============================================================ + +spec dot [List Rat] [List Rat] -> Rat +defn dot [xs ys] + match xs + | nil -> 0/1 + | cons x as -> match ys + | nil -> 0/1 + | cons y bs -> rat+ [rat* x y] [dot as bs] + +spec scale-vec Rat [List Rat] -> [List Rat] +defn scale-vec [s xs] + match xs + | nil -> nil + | cons x as -> cons [rat* s x] [scale-vec s as] + +spec add-vec [List Rat] [List Rat] -> [List Rat] +defn add-vec [xs ys] + match xs + | nil -> nil + | cons x as -> match ys + | nil -> nil + | cons y bs -> cons [rat+ x y] [add-vec as bs] + +spec sub-vec [List Rat] [List Rat] -> [List Rat] +defn sub-vec [xs ys] + match xs + | nil -> nil + | cons x as -> match ys + | nil -> nil + | cons y bs -> cons [rat- x y] [sub-vec as bs] + +spec rat-max Rat Rat -> Rat +defn rat-max [a b] + match [rat-lt a b] + | true -> b + | false -> a + +spec linf-norm [List Rat] -> Rat +defn linf-norm [xs] + match xs + | nil -> 0/1 + | cons x as -> rat-max [rat-abs x] [linf-norm as] + +spec mat-vec-mul [List [List Rat]] [List Rat] -> [List Rat] +defn mat-vec-mul [m t] + match m + | nil -> nil + | cons r rs -> cons [dot r t] [mat-vec-mul rs t] + +spec col-sums [List [List Rat]] -> [List Rat] +defn col-sums [m] + match m + | nil -> nil + | cons r rest -> match rest + | nil -> r + | cons _ _ -> add-vec r [col-sums rest] + +spec all-ones? [List Rat] -> Bool +defn all-ones? [xs] + match xs + | nil -> true + | cons x as -> match [rat-eq x 1/1] + | false -> false + | true -> all-ones? as + +spec col-stochastic? [List [List Rat]] -> Bool +defn col-stochastic? [m] + all-ones? [col-sums m] + +spec eigentrust-step [List [List Rat]] [List Rat] Rat [List Rat] -> [List Rat] +defn eigentrust-step [m p alpha t] + add-vec [scale-vec [rat- 1/1 alpha] [mat-vec-mul m t]] [scale-vec alpha p] + +spec eigentrust-iterate [List [List Rat]] [List Rat] Rat Rat Int [List Rat] [List Rat] -> [List Rat] +defn eigentrust-iterate [m p alpha eps budget t tnew] + match [int-le budget 0] + | true -> tnew + | false -> match [rat-lt [linf-norm [sub-vec tnew t]] eps] + | true -> tnew + | false -> eigentrust-iterate m p alpha eps [int- budget 1] tnew [eigentrust-step m p alpha tnew] + +spec eigentrust [List [List Rat]] [List Rat] Rat Rat Int -> [List Rat] +defn eigentrust [m p alpha eps max-iter] + match [col-stochastic? m] + | false -> (the [List Rat] [panic "eigentrust: M must be column-stochastic"]) + | true -> eigentrust-iterate m p alpha eps max-iter p [eigentrust-step m p alpha p] + + +;; ============================================================ +;; Fixtures +;; ============================================================ + +def rz : Rat := 0/1 +def ro : Rat := 1/1 + +def p-uniform-4 : [List Rat] := '[1/4 1/4 1/4 1/4] +def p-uniform-3 : [List Rat] := '[1/3 1/3 1/3] +def p-seed-0 : [List Rat] := '[ro rz rz rz] + +def m-uniform-4 : [List [List Rat]] := '['[1/4 1/4 1/4 1/4] '[1/4 1/4 1/4 1/4] '[1/4 1/4 1/4 1/4] '[1/4 1/4 1/4 1/4]] + +def m-others-3 : [List [List Rat]] := '['[rz 1/2 1/2] '[1/2 rz 1/2] '[1/2 1/2 rz]] + +;; 4-peer ring: column j has a single 1 in row (j+1) mod 4. +;; Column-stochastic (each column sums to 1); row 0 receives from peer 3, etc. +def m-ring-4 : [List [List Rat]] := '['[rz rz rz ro] '[ro rz rz rz] '[rz ro rz rz] '[rz rz ro rz]] + + +;; ============================================================ +;; Workloads (W1..W9 — identical across all 4 comparison variants) +;; ============================================================ + +eigentrust m-uniform-4 p-uniform-4 1/10 1/1000 50 +eigentrust m-others-3 p-uniform-3 1/10 1/1000 50 +;; W3: 3 FORCED iters on ring, alpha=3/10, eps=0 — slow settling. +eigentrust m-ring-4 p-seed-0 3/10 0/1 3 +col-stochastic? m-uniform-4 +eigentrust-step m-uniform-4 p-uniform-4 1/10 p-uniform-4 +mat-vec-mul m-uniform-4 p-uniform-4 +scale-vec 1/2 '[1/2 1/3 1/4] +add-vec '[1/4 1/4] '[1/4 1/4] +linf-norm [sub-vec '[1/2 1/3] '[1/4 1/3]] diff --git a/racket/prologos/benchmarks/comparative/eigentrust-parallel-bench.rkt b/racket/prologos/benchmarks/comparative/eigentrust-parallel-bench.rkt new file mode 100644 index 000000000..9f384e420 --- /dev/null +++ b/racket/prologos/benchmarks/comparative/eigentrust-parallel-bench.rkt @@ -0,0 +1,186 @@ +#lang racket/base + +;;; eigentrust-parallel-bench.rkt — does parallel BSP help EigenTrust? +;;; +;;; Compares 5 implementations at sizes large enough for parallel +;;; dispatch to kick in (n ≥ 64 — comfortably above min-parallel=8): +;;; +;;; 1. math (plain-fl) — direct Racket flvector, 1 core +;;; 2. coarse net (sequential) — 1 propagator per BSP round, 1 core +;;; 3. fine net (sequential) — n propagators per BSP round, 1 core +;;; 4. fine net (parallel-thread) — n propagators / round, K cores via Racket-9 threads +;;; 5. fine net (worker-pool) — n propagators / round, K cores via persistent pool +;;; +;;; The thesis under test: at large n, fine-grained network with +;;; parallel BSP CAN beat single-threaded math because the n +;;; per-iteration peer propagators fire across cores. Sequential +;;; fine cannot win (more bookkeeping than coarse for the same +;;; total work). Parallel fine MIGHT win once n ≫ ncores · +;;; (per-fire constant cost). +;;; +;;; This is the "where parallelism could live" experiment named in +;;; docs/tracking/2026-04-23_eigentrust_comparison.md § Trend / +;;; breakeven analysis. + +(require "../../propagator.rkt" + "eigentrust-propagators-float.rkt" + "eigentrust-propagators-fine-float.rkt" + "eigentrust-plain.rkt" + racket/flonum + racket/list + racket/future) + +(define K 4) +(define NUM-RUNS 3) +(define WARMUP-RUNS 1) +(define TIMEOUT-MS 60000) + +;; n=4 is below min-parallel=8 (sequential fallback even with parallel exec). +;; Real signal starts at n ≥ 16; we sample up through n=1024 to keep wall +;; time tractable on the fine variant (K·n propagator installs + reads). +(define SIZES '(16 32 64 128 256 512 1024)) + + +;; ============================================================ +;; Fixture generation (cache-friendly: row-major, no scatter). +;; ============================================================ + +(define (gen-fl-col-stochastic n) + (define rows (for/vector #:length n ([i (in-range n)]) (make-flvector n))) + (define col (make-flvector n)) + (for ([j (in-range n)]) + (define s 0.0) + (for ([i (in-range n)]) + (define v (+ 1.0 (random))) + (flvector-set! col i v) + (set! s (+ s v))) + (for ([i (in-range n)]) + (flvector-set! (vector-ref rows i) j + (/ (flvector-ref col i) s)))) + rows) + +(define (uniform-fl n) + (define v (make-flvector n)) + (for ([i (in-range n)]) (flvector-set! v i (/ 1.0 n))) + v) + + +;; ============================================================ +;; Timing harness +;; ============================================================ + +(define (timed-thunk-or-timeout thunk timeout-ms) + (define result-box (box #f)) + (define t0 (current-inexact-monotonic-milliseconds)) + (define t (thread (lambda () + (thunk) + (set-box! result-box + (- (current-inexact-monotonic-milliseconds) t0))))) + (define done? (sync/timeout (/ timeout-ms 1000.0) t)) + (cond [done? (unbox result-box)] + [else (kill-thread t) 'timeout])) + +(define (sample-with-timeout fn) + (collect-garbage 'major) + (define first (timed-thunk-or-timeout fn TIMEOUT-MS)) + (cond + [(eq? first 'timeout) 'timeout] + [else + (let loop ([acc '()] [remaining NUM-RUNS]) + (cond [(zero? remaining) (reverse acc)] + [else + (collect-garbage 'major) + (define s (timed-thunk-or-timeout fn TIMEOUT-MS)) + (cond [(eq? s 'timeout) 'timeout] + [else (loop (cons s acc) (sub1 remaining))])]))])) + +(define (median xs) + (define s (sort xs <)) + (define n (length s)) + (cond [(zero? n) 0] + [(odd? n) (list-ref s (quotient n 2))] + [else (/ (+ (list-ref s (sub1 (quotient n 2))) + (list-ref s (quotient n 2))) 2)])) + +(define (round-2 x) (/ (round (* 100 x)) 100)) + + +;; ============================================================ +;; Variants — each closed over the fixture; takes no args. +;; ============================================================ + +(define (run-math m p) (run-eigentrust-plain-fl m p 0.3 K)) +(define (run-coarse m p) (run-eigentrust-propagators-fl m p 0.3 K)) +(define (run-fine m p) (run-eigentrust-propagators-fine-fl m p 0.3 K)) + +(define (run-fine-with-executor m p executor) + (parameterize ([current-parallel-executor executor]) + (run-eigentrust-propagators-fine-fl m p 0.3 K))) + + +;; ============================================================ +;; Per-row sweep +;; ============================================================ + +(define (bench-row n parallel-thread-exec pool-exec) + (printf "n=~a — generating fixture..." n) (flush-output) + (define m (gen-fl-col-stochastic n)) + (define p (uniform-fl n)) + (printf " done.~n") (flush-output) + + (define (variant label fn) + (printf " ~a : " label) (flush-output) + (define s (sample-with-timeout fn)) + (cond + [(eq? s 'timeout) (printf "TIMEOUT~n") 'timeout] + [else (define r (round-2 (median s))) (printf "~a ms~n" r) r])) + + (define math-r (variant "math (plain-fl) " + (lambda () (run-math m p)))) + (define coarse-r (variant "coarse net (seq) " + (lambda () (run-coarse m p)))) + (define fine-seq-r (variant "fine net (seq) " + (lambda () (run-fine m p)))) + (define fine-thread-r + (variant "fine net (parallel-thread) " + (lambda () (run-fine-with-executor m p parallel-thread-exec)))) + (define fine-pool-r + (variant "fine net (worker-pool) " + (lambda () (run-fine-with-executor m p pool-exec)))) + + ;; Overheads / speedups vs math + (define (ratio r) + (cond [(eq? r 'timeout) "TIMEOUT"] + [(eq? math-r 'timeout) "n/a"] + [else (format "~ax" (round-2 (/ r math-r)))])) + (printf " vs math: coarse=~a fine-seq=~a fine-thread=~a fine-pool=~a~n~n" + (ratio coarse-r) (ratio fine-seq-r) + (ratio fine-thread-r) (ratio fine-pool-r))) + + +;; ============================================================ +;; Main +;; ============================================================ + +(module+ main + (define ncores (processor-count)) + (printf "═══ EigenTrust parallel bench (cores=~a) ═══~n" ncores) + (printf "K=~a, ~a measured runs/cell (median), ~a warmup, ~a ms timeout~n" + K NUM-RUNS WARMUP-RUNS TIMEOUT-MS) + (printf "Variants:~n") + (printf " - math : plain Racket flvector, single-threaded~n") + (printf " - coarse net : 1 propagator per BSP round (chain), single-threaded~n") + (printf " - fine net : n propagators per BSP round (per-peer)~n") + (printf " (seq / parallel-thread / worker-pool)~n~n") + (random-seed 42) + + ;; Build pool + thread executors once; both reused across all rows. + (define pool (make-worker-pool ncores)) + (define pool-exec (make-pool-executor pool 8)) + (define thread-exec (make-parallel-thread-fire-all 8)) + + (for ([n (in-list SIZES)]) + (bench-row n thread-exec pool-exec)) + + ;; Cleanup + (worker-pool-shutdown! pool)) diff --git a/racket/prologos/benchmarks/comparative/eigentrust-plain.rkt b/racket/prologos/benchmarks/comparative/eigentrust-plain.rkt new file mode 100644 index 000000000..dd6aa6b90 --- /dev/null +++ b/racket/prologos/benchmarks/comparative/eigentrust-plain.rkt @@ -0,0 +1,75 @@ +#lang racket/base + +;;; eigentrust-plain.rkt — plain Racket EigenTrust implementation. +;;; +;;; No propagator network. Just iterates the off-network step kernel +;;; K times in a tail loop with parameter-passing. The point is to +;;; isolate "propagator infrastructure overhead" from "the algorithm +;;; in plain Racket" — both versions call the SAME `eigentrust-step` +;;; / `eigentrust-step-fl` kernel; only the orchestration differs. +;;; +;;; Compared against `eigentrust-propagators.rkt` (rat-coarse) and +;;; `eigentrust-propagators-float.rkt` (float), this gives: +;;; +;;; propagator overhead = (propagator time) − (plain time) +;;; +;;; for the same workload. + +(require "eigentrust-propagators.rkt" + "eigentrust-propagators-float.rkt" + racket/flonum) + +(provide + run-eigentrust-plain + run-eigentrust-plain-fl) + +;; ============================================================ +;; Plain rational implementation +;; ============================================================ + +;; t_K = step^K(p), starting from t_0 = p. +;; Same convention as the propagator version (see semantic note in +;; eigentrust-propagators.rkt): k = max-iter + 1 to match the Prologos +;; surface `eigentrust m p α 0/1 max-iter` semantics. +(define (run-eigentrust-plain m p alpha k) + (unless (col-stochastic? m) + (error 'run-eigentrust-plain "M must be column-stochastic")) + (let loop ([t p] [remaining k]) + (cond + [(zero? remaining) t] + [else (loop (eigentrust-step m p alpha t) (sub1 remaining))]))) + +;; ============================================================ +;; Plain float implementation +;; ============================================================ + +(define (run-eigentrust-plain-fl m p alpha k) + (unless (col-stochastic-fl? m) + (error 'run-eigentrust-plain-fl "M must be column-stochastic")) + (let loop ([t p] [remaining k]) + (cond + [(zero? remaining) t] + [else (loop (eigentrust-step-fl m p alpha t) (sub1 remaining))]))) + + +;; ============================================================ +;; Smoke +;; ============================================================ + +(module+ main + (define rat-result (run-eigentrust-plain m-ring-4 p-seed-0 3/10 4)) + (printf "plain rat ring-4 / α=3/10 / k=4:~n ~s~n" rat-result) + (unless (equal? rat-result (vector 5401/10000 21/100 147/1000 1029/10000)) + (error 'main "rat result mismatch")) + (printf " matches Prologos surface + propagator versions ✓~n~n") + + (define fl-result (run-eigentrust-plain-fl m-ring-4-fl p-seed-0-fl 0.3 4)) + (printf "plain float ring-4 / α=0.3 / k=4:~n ~s~n" fl-result) + ;; Expected ~ #fl(0.5401 0.21 0.147 0.1029); float round-off makes + ;; this approximate. + (define ok? + (for/and ([i (in-range 4)]) + (define expected (vector-ref (vector 0.5401 0.21 0.147 0.1029) i)) + (< (abs (- (flvector-ref fl-result i) expected)) 1e-9))) + (unless ok? (error 'main "float result mismatch")) + (printf " matches rational version within 1e-9 ✓~n")) diff --git a/racket/prologos/benchmarks/comparative/eigentrust-propagators-bench.rkt b/racket/prologos/benchmarks/comparative/eigentrust-propagators-bench.rkt new file mode 100644 index 000000000..76e8586ae --- /dev/null +++ b/racket/prologos/benchmarks/comparative/eigentrust-propagators-bench.rkt @@ -0,0 +1,162 @@ +#lang racket/base + +;;; eigentrust-propagators-bench.rkt — timing harness for the +;;; Racket-direct propagator EigenTrust variants. +;;; +;;; Three propagator variants compared head-to-head, plus their +;;; relationship to the Prologos surface benchmarks: +;;; - rat (coarse) : one cell per iteration, exact rationals +;;; - rat (fine) : one cell per (iteration, peer) — K·n cells +;;; - float : one cell per iteration, hardware flonum +;;; +;;; Reports wall_ms, reduce_ms (algorithm time, comparable to +;;; PHASE-TIMINGS reduce_ms in the Prologos benchmarks), and the +;;; sub-breakdowns build_ms (network construction) and bsp_ms (run +;;; to quiescence). +;;; +;;; Run via: +;;; racket benchmarks/comparative/eigentrust-propagators-bench.rkt + +(require "../../propagator.rkt" + "eigentrust-propagators.rkt" + "eigentrust-propagators-fine.rkt" + "eigentrust-propagators-float.rkt" + racket/list + racket/flonum) + +(define NUM-RUNS 5) +(define WARMUP-RUNS 1) + +;; ============================================================ +;; Per-run timing — variant-agnostic. +;; +;; `build-fn` is a thunk-like procedure: (build-fn m p alpha k) → +;; (values net result-cid-or-vector). For coarse/float variants, +;; result-cid is a single cell-id; for fine, it's a vector of +;; cell-ids. +;; `read-fn` is (read-fn net result-cid-or-vector) → result. +;; ============================================================ + +(define (time-one-run build-fn read-fn m p alpha k) + (define t0 (current-inexact-monotonic-milliseconds)) + (define-values (net result-handle) (build-fn m p alpha k)) + (define t1 (current-inexact-monotonic-milliseconds)) + (define net* (run-to-quiescence-bsp net)) + (define t2 (current-inexact-monotonic-milliseconds)) + (define result (read-fn net* result-handle)) + (define t3 (current-inexact-monotonic-milliseconds)) + (values (- t1 t0) (- t2 t1) (- t3 t2) (- t3 t0) result)) + +;; Coarse variants: result-handle is a single cell-id, read once. +(define (read-single net cid) (net-cell-read net cid)) + +;; Fine variant: result-handle is a vector of cell-ids; read each. +(define (read-many net cids) + (define n (vector-length cids)) + (for/vector #:length n ([j (in-range n)]) + (net-cell-read net (vector-ref cids j)))) + + +;; ============================================================ +;; Aggregation +;; ============================================================ + +(define (median xs) + (define sorted (sort xs <)) + (define n (length sorted)) + (cond [(zero? n) 0] + [(odd? n) (list-ref sorted (quotient n 2))] + [else (/ (+ (list-ref sorted (sub1 (quotient n 2))) + (list-ref sorted (quotient n 2))) + 2)])) + +(define (round-2 x) + (/ (round (* 100 x)) 100)) + +(define (run-bench label build-fn read-fn m p alpha k) + ;; Warmup + (for ([_ (in-range WARMUP-RUNS)]) + (time-one-run build-fn read-fn m p alpha k)) + ;; Measured runs + (define samples + (for/list ([_ (in-range NUM-RUNS)]) + (collect-garbage 'major) + (define-values (b r d t result) (time-one-run build-fn read-fn m p alpha k)) + (list b r d t result))) + (define builds (map car samples)) + (define bsps (map cadr samples)) + (define totals (map cadddr samples)) + (define result (last (last samples))) + (printf "── ~a ──~n" label) + (printf " result : ~s~n" result) + (printf " build_ms : median ~a (min ~a, max ~a)~n" + (round-2 (median builds)) + (round-2 (apply min builds)) + (round-2 (apply max builds))) + (printf " bsp_ms : median ~a (min ~a, max ~a)~n" + (round-2 (median bsps)) + (round-2 (apply min bsps)) + (round-2 (apply max bsps))) + (printf " reduce_ms : median ~a (min ~a, max ~a, n=~a)~n" + (round-2 (median totals)) + (round-2 (apply min totals)) + (round-2 (apply max totals)) + NUM-RUNS) + (newline)) + + +;; ============================================================ +;; Workloads +;; ============================================================ + +(module+ main + (define wall-t0 (current-inexact-monotonic-milliseconds)) + + (printf "═══ EigenTrust on Propagators — 3 variants × workloads ═══~n") + (printf "Runs per workload: ~a (+ ~a warmup)~n~n" NUM-RUNS WARMUP-RUNS) + + ;; ============================================================ + ;; W3 — the canonical workload: ring-4, α=3/10, k=4. + ;; All three variants must produce the same answer (within tol + ;; for float). + ;; ============================================================ + (run-bench "rat-coarse W3 ring-4 / α=3/10 / k=4" + build-eigentrust-network read-single + m-ring-4 p-seed-0 3/10 4) + (run-bench "rat-fine W3 ring-4 / α=3/10 / k=4" + build-eigentrust-network-fine read-many + m-ring-4 p-seed-0 3/10 4) + (run-bench "float W3 ring-4 / α=0.3 / k=4" + build-eigentrust-network-fl read-single + m-ring-4-fl p-seed-0-fl 0.3 4) + + ;; ============================================================ + ;; W3-deep — ring with k=10, exercises chain depth. + ;; The Prologos surface variants O(k²) blow up here; propagator + ;; variants stay linear in K. + ;; ============================================================ + (run-bench "rat-coarse W3-deep ring-4 / α=3/10 / k=10" + build-eigentrust-network read-single + m-ring-4 p-seed-0 3/10 10) + (run-bench "rat-fine W3-deep ring-4 / α=3/10 / k=10" + build-eigentrust-network-fine read-many + m-ring-4 p-seed-0 3/10 10) + (run-bench "float W3-deep ring-4 / α=0.3 / k=10" + build-eigentrust-network-fl read-single + m-ring-4-fl p-seed-0-fl 0.3 10) + + ;; ============================================================ + ;; W1 — uniform fixed point. + ;; ============================================================ + (run-bench "rat-coarse W1 uniform-4 / α=1/10 / k=2" + build-eigentrust-network read-single + m-uniform-4 p-uniform-4 1/10 2) + (run-bench "rat-fine W1 uniform-4 / α=1/10 / k=2" + build-eigentrust-network-fine read-many + m-uniform-4 p-uniform-4 1/10 2) + (run-bench "float W1 uniform-4 / α=0.1 / k=2" + build-eigentrust-network-fl read-single + m-uniform-4-fl p-uniform-4-fl 0.1 2) + + (define wall-t1 (current-inexact-monotonic-milliseconds)) + (printf "Total benchmark wall: ~a ms~n" (round-2 (- wall-t1 wall-t0)))) diff --git a/racket/prologos/benchmarks/comparative/eigentrust-propagators-fine-float.rkt b/racket/prologos/benchmarks/comparative/eigentrust-propagators-fine-float.rkt new file mode 100644 index 000000000..3aede6342 --- /dev/null +++ b/racket/prologos/benchmarks/comparative/eigentrust-propagators-fine-float.rkt @@ -0,0 +1,162 @@ +#lang racket/base + +;;; eigentrust-propagators-fine-float.rkt — fine-grained per-peer +;;; FLOAT variant. Like `eigentrust-propagators-fine.rkt` but uses +;;; Racket flonums (flvector) instead of exact rationals. +;;; +;;; Topology: K·n propagators total. At iteration k, n independent +;;; per-peer propagators each compute one element of the new trust +;;; vector. WITHIN a BSP round, those n propagators are independent +;;; — the BSP scheduler can fire them in parallel via a pool / +;;; parallel-thread executor. This is the parallel-friendly variant. +;;; +;;; Cell layout: (K+1) × n trust cells, each holding a single +;;; flonum. m-cid (vector of flvectors), p-cid (flvector), +;;; alpha-cid (flonum) constants on top. + +(require "../../propagator.rkt" + "eigentrust-propagators-float.rkt" + racket/flonum) + +(provide + build-eigentrust-network-fine-fl + run-eigentrust-propagators-fine-fl) + + +;; ============================================================ +;; Per-peer kernel (off-network; called from each peer-fire fn). +;; t_{k,i} = (1 − α) · sum_j M[i,j] · t_prev[j] + α · p[i] +;; m-row : flvector (length n), p-i : flonum, alpha : flonum, +;; t-prev : flvector. Returns flonum. +;; ============================================================ + +(define (peer-step-kernel-fl m-row p-i alpha t-prev) + (define n (flvector-length m-row)) + (define dot + (for/fold ([acc 0.0]) ([j (in-range n)]) + (fl+ acc (fl* (flvector-ref m-row j) + (flvector-ref t-prev j))))) + (fl+ (fl* (fl- 1.0 alpha) dot) + (fl* alpha p-i))) + + +;; ============================================================ +;; Network construction +;; ============================================================ + +(define (lww old new) new) + +(define (build-eigentrust-network-fine-fl m p alpha k) + (unless (col-stochastic-fl? m) + (error 'build-eigentrust-network-fine-fl + "M must be column-stochastic (within tolerance)")) + (define n (flvector-length p)) + (define net0 (make-prop-network)) + (define-values (net1 m-cid) (net-new-cell net0 m lww)) + (define-values (net2 p-cid) (net-new-cell net1 p lww)) + (define-values (net3 alpha-cid) (net-new-cell net2 alpha lww)) + + ;; Allocate (K+1) × n trust cells. Row 0 holds p[i] for each i; + ;; rows 1..K hold 0.0 and are written by their step propagator. + (define t-cids (make-vector (add1 k) #f)) + (define-values (net4 row0) + (let alloc ([net net3] [i 0] [acc '()]) + (cond + [(>= i n) (values net (list->vector (reverse acc)))] + [else + (define-values (net* cid) + (net-new-cell net (flvector-ref p i) lww)) + (alloc net* (add1 i) (cons cid acc))]))) + (vector-set! t-cids 0 row0) + + (define-values (net5 _ignore) + (let row-alloc ([net net4] [step 1]) + (cond + [(> step k) (values net 'done)] + [else + (define-values (net* row) + (let alloc ([net net] [i 0] [acc '()]) + (cond + [(>= i n) (values net (list->vector (reverse acc)))] + [else + (define-values (net** cid) (net-new-cell net 0.0 lww)) + (alloc net** (add1 i) (cons cid acc))]))) + (vector-set! t-cids step row) + (row-alloc net* (add1 step))]))) + + ;; Install K · n peer-step propagators. + (define-values (net-final last-row) + (let step-loop ([net net5] [step 1]) + (cond + [(> step k) (values net (vector-ref t-cids k))] + [else + (define prev-row (vector-ref t-cids (sub1 step))) + (define next-row (vector-ref t-cids step)) + (define net* + (let peer-loop ([net net] [i 0]) + (cond + [(>= i n) net] + [else + ;; Capture i, prev-row, next-row by value via let binding. + (define peer-idx i) + (define prev-cids prev-row) + (define next-cid (vector-ref next-row peer-idx)) + (define (fire net-param) + (define m-val (net-cell-read net-param m-cid)) + (define p-val (net-cell-read net-param p-cid)) + (define alpha-val (net-cell-read net-param alpha-cid)) + ;; Build the previous trust vector by reading each + ;; per-peer cell (n cell reads). + (define t-prev (make-flvector n)) + (for ([j (in-range n)]) + (flvector-set! t-prev j + (net-cell-read net-param (vector-ref prev-cids j)))) + (define m-row (vector-ref m-val peer-idx)) + (define p-i (flvector-ref p-val peer-idx)) + (define t-i (peer-step-kernel-fl m-row p-i alpha-val t-prev)) + (net-cell-write net-param next-cid t-i)) + (define inputs + (cons m-cid + (cons p-cid + (cons alpha-cid + (for/list ([j (in-range n)]) + (vector-ref prev-cids j)))))) + (define-values (net** _pid) + (net-add-propagator net inputs (list next-cid) fire)) + (peer-loop net** (add1 i))]))) + (step-loop net* (add1 step))]))) + + (values net-final last-row)) + + +;; Read result row into an flvector of length n. +(define (read-row net cids) + (define n (vector-length cids)) + (define out (make-flvector n)) + (for ([i (in-range n)]) + (flvector-set! out i (net-cell-read net (vector-ref cids i)))) + out) + +(define (run-eigentrust-propagators-fine-fl m p alpha k) + (define-values (net last-row) (build-eigentrust-network-fine-fl m p alpha k)) + (define net* (run-to-quiescence-bsp net)) + (read-row net* last-row)) + + +;; ============================================================ +;; Smoke +;; ============================================================ + +(module+ main + (define result (run-eigentrust-propagators-fine-fl + m-ring-4-fl p-seed-0-fl 0.3 4)) + (printf "ring-4 / α=0.3 / k=4 (fine-flonum):~n ~s~n" result) + (define expected (flvector 0.5401 0.21 0.147 0.1029)) + (define tol 1e-9) + (define ok? + (for/and ([i (in-range 4)]) + (< (abs (- (flvector-ref result i) (flvector-ref expected i))) tol))) + (unless ok? + (error 'main "result not within ~e of expected~n expected: ~s~n got: ~s" + tol expected result)) + (printf "matches coarse-float W3 within ~e ✓~n" tol)) diff --git a/racket/prologos/benchmarks/comparative/eigentrust-propagators-fine.rkt b/racket/prologos/benchmarks/comparative/eigentrust-propagators-fine.rkt new file mode 100644 index 000000000..3eeff1fca --- /dev/null +++ b/racket/prologos/benchmarks/comparative/eigentrust-propagators-fine.rkt @@ -0,0 +1,163 @@ +#lang racket/base + +;;; eigentrust-propagators-fine.rkt — fine-grained per-peer variant. +;;; +;;; Whereas the coarse variant (eigentrust-propagators.rkt) holds an +;;; entire trust vector in one cell per iteration, the fine-grained +;;; variant holds ONE cell PER PEER PER ITERATION. For W3 (n=4 peers, +;;; K=4 steps) that's 16 trust cells + 4 constant cells = 20 cells, +;;; and 16 propagators (one per (k, i) pair). Each peer-i propagator +;;; at step k reads (M[i,:], p[i], alpha, t_{k-1, 0..n-1}) — n+3 +;;; inputs — and writes one peer-trust value `t_{k, i}`. +;;; +;;; This is "more on-network": each peer's evolving trust value is +;;; its own cell with its own propagator. Within a single iteration +;;; round, the n peer propagators are independent and can be fired +;;; in parallel by the BSP scheduler. +;;; +;;; Trade-off vs coarse: more cell allocations and propagator-fire +;;; bookkeeping; smaller per-fire work. For small n the coarse +;;; variant wins on constants; for larger n (or with parallel BSP +;;; execution) the fine variant should pull ahead. + +(require "../../propagator.rkt" + "eigentrust-propagators.rkt" + (only-in racket/list last)) + +(provide + build-eigentrust-network-fine + run-eigentrust-propagators-fine) + +;; ============================================================ +;; Per-peer kernel: t_{k, i} = (1-α) · sum_j M[i,j] · t_{k-1, j} + α · p[i] +;; ============================================================ + +;; Compute t_{k, i} given the previous full trust vector, +;; the matrix row M[i,:], pre-trust scalar p[i], and alpha. +;; This is the off-network kernel each per-peer propagator runs. +(define (peer-step-kernel m-row p-i alpha t-prev) + (define n (vector-length m-row)) + (define dot + (for/fold ([acc 0]) ([j (in-range n)]) + (+ acc (* (vector-ref m-row j) (vector-ref t-prev j))))) + (+ (* (- 1 alpha) dot) + (* alpha p-i))) + +;; ============================================================ +;; Network construction +;; +;; Layout: +;; t-cids: vector of (vector cell-id) of shape K+1 × n +;; t-cids[0][i] = cell holding t_{0, i} = p[i] +;; t-cids[k][i] = cell holding t_{k, i} (computed by step-(k,i)) +;; m-cid, p-cid, alpha-cid: constant cells (lww merge). +;; +;; Each step-(k,i) propagator reads: +;; - t-cids[k-1][0..n-1] (n inputs) +;; - m-cid, p-cid, alpha-cid (3 inputs) +;; And writes: +;; - t-cids[k][i] (1 output) +;; ============================================================ + +(define (lww old new) new) + +(define (build-eigentrust-network-fine m p alpha k) + (unless (col-stochastic? m) + (error 'build-eigentrust-network-fine "M must be column-stochastic")) + (define n (vector-length p)) + ;; Constant cells + (define net0 (make-prop-network)) + (define-values (net1 m-cid) (net-new-cell net0 m lww)) + (define-values (net2 p-cid) (net-new-cell net1 p lww)) + (define-values (net3 alpha-cid) (net-new-cell net2 alpha lww)) + ;; Allocate (K+1) × n trust cells. + ;; Row 0: pre-loaded with p[i]. + ;; Rows 1..K: pre-loaded with 0; written by their step propagator. + (define t-cids (make-vector (add1 k) #f)) + (let-values ([(net4 row0) + (let alloc ([net net3] [i 0] [acc '()]) + (if (>= i n) + (values net (list->vector (reverse acc))) + (let-values ([(net* cid) (net-new-cell net (vector-ref p i) lww)]) + (alloc net* (add1 i) (cons cid acc)))))]) + (vector-set! t-cids 0 row0) + ;; Allocate t-cids[1..K] + (let row-alloc ([net net4] [step 1]) + (if (> step k) + (build-prop-chain net t-cids m-cid p-cid alpha-cid k n) + (let-values ([(net* row) + (let alloc ([net net] [i 0] [acc '()]) + (if (>= i n) + (values net (list->vector (reverse acc))) + (let-values ([(net* cid) (net-new-cell net 0 lww)]) + (alloc net* (add1 i) (cons cid acc)))))]) + (vector-set! t-cids step row) + (row-alloc net* (add1 step))))))) + +;; Install the K·n peer-step propagators. Returns (values net last-row-cids). +(define (build-prop-chain net t-cids m-cid p-cid alpha-cid k n) + (let step-loop ([net net] [step 1]) + (if (> step k) + (values net (vector-ref t-cids k)) + (let ([prev-row (vector-ref t-cids (sub1 step))] + [next-row (vector-ref t-cids step)]) + ;; For each peer i, install propagator step-(k=step, i). + (let peer-loop ([net net] [i 0]) + (if (>= i n) + (step-loop net (add1 step)) + (let () + ;; Fire function reads M (full matrix) once and indexes + ;; row i; reads p[i]; reads alpha; reads each t_{step-1, j}. + ;; CRITICAL: capture i (and prev-row, next-row) by value + ;; via the let binding here, NOT by reference to step-loop's + ;; mutable index. + (define peer-idx i) + (define prev-cids prev-row) + (define next-cid (vector-ref next-row peer-idx)) + (define (fire net-param) + (define m-val (net-cell-read net-param m-cid)) + (define p-val (net-cell-read net-param p-cid)) + (define alpha-val (net-cell-read net-param alpha-cid)) + ;; Build the previous trust vector by reading each + ;; per-peer cell (n cell reads). + (define t-prev + (for/vector #:length n ([j (in-range n)]) + (net-cell-read net-param (vector-ref prev-cids j)))) + (define m-row (vector-ref m-val peer-idx)) + (define p-i (vector-ref p-val peer-idx)) + (define t-i (peer-step-kernel m-row p-i alpha-val t-prev)) + (net-cell-write net-param next-cid t-i)) + ;; Inputs: ALL n cells of the previous row + 3 constants. + (define inputs + (cons m-cid (cons p-cid (cons alpha-cid + (for/list ([j (in-range n)]) + (vector-ref prev-cids j)))))) + (define-values (net** _pid) + (net-add-propagator + net inputs (list next-cid) fire)) + (peer-loop net** (add1 i))))))))) + + +;; End-to-end: build, run BSP, gather final per-peer cells into a +;; vector. Result has shape (vector Rat₀ Rat₁ ... Ratₙ₋₁) — same +;; shape as the coarse variant for direct comparison. +(define (run-eigentrust-propagators-fine m p alpha k) + (define-values (net last-row-cids) (build-eigentrust-network-fine m p alpha k)) + (define net* (run-to-quiescence-bsp net)) + (define n (vector-length p)) + (for/vector #:length n ([j (in-range n)]) + (net-cell-read net* (vector-ref last-row-cids j)))) + + +;; ============================================================ +;; Module main: smoke test against the coarse variant's golden output +;; ============================================================ + +(module+ main + (define result (run-eigentrust-propagators-fine m-ring-4 p-seed-0 3/10 4)) + (printf "fine ring-4 / α=3/10 / k=4:~n ~s~n" result) + (define expected (vector 5401/10000 21/100 147/1000 1029/10000)) + (unless (equal? result expected) + (error 'main "fine variant mismatch~n expected: ~s~n got: ~s" + expected result)) + (printf "fine result matches coarse + Prologos surface variants ✓~n")) diff --git a/racket/prologos/benchmarks/comparative/eigentrust-propagators-float.rkt b/racket/prologos/benchmarks/comparative/eigentrust-propagators-float.rkt new file mode 100644 index 000000000..248ade27f --- /dev/null +++ b/racket/prologos/benchmarks/comparative/eigentrust-propagators-float.rkt @@ -0,0 +1,166 @@ +#lang racket/base + +;;; eigentrust-propagators-float.rkt — float (flonum) variant of the +;;; Racket-direct EigenTrust on propagators. +;;; +;;; Same chain-of-cells architecture as eigentrust-propagators.rkt +;;; (one cell per iteration, K plain propagators) but uses Racket +;;; flonums instead of exact rationals. Lets us measure the cost +;;; of exact-Rat arithmetic vs hardware float on the same propagator +;;; topology. + +(require "../../propagator.rkt" + racket/flonum) + +(provide + vec-zeros-fl + col-stochastic-fl? + eigentrust-step-fl + build-eigentrust-network-fl + run-eigentrust-propagators-fl + ;; Fixtures + m-ring-4-fl + p-seed-0-fl + m-uniform-4-fl + p-uniform-4-fl + m-others-3-fl + p-uniform-3-fl) + +;; ============================================================ +;; Vector-of-flonum primitives +;; ============================================================ + +(define (vec-zeros-fl n) + (make-flvector n 0.0)) + +(define (vec-add-fl a b) + (define n (flvector-length a)) + (define out (make-flvector n)) + (for ([i (in-range n)]) + (flvector-set! out i (fl+ (flvector-ref a i) (flvector-ref b i)))) + out) + +(define (vec-scale-fl s v) + (define n (flvector-length v)) + (define out (make-flvector n)) + (for ([i (in-range n)]) + (flvector-set! out i (fl* s (flvector-ref v i)))) + out) + +;; (M*t)[i] = dot(M[i], t). +;; M is a vector of flvectors (rows), t is an flvector. +(define (mat-vec-mul-fl m t) + (define n (vector-length m)) + (define out (make-flvector n)) + (for ([i (in-range n)]) + (define row (vector-ref m i)) + (define dim (flvector-length row)) + ;; Seed at 0.0 to keep the accumulator a flonum (per pitfall #4). + (flvector-set! out i + (for/fold ([acc 0.0]) ([j (in-range dim)]) + (fl+ acc (fl* (flvector-ref row j) (flvector-ref t j)))))) + out) + +;; Column-stochastic check (with a small tolerance for float round-off). +(define COL-SUM-TOLERANCE 1e-9) + +(define (col-stochastic-fl? m) + (define n (vector-length m)) + (define n-cols (flvector-length (vector-ref m 0))) + (for/and ([j (in-range n-cols)]) + (define s (for/fold ([acc 0.0]) ([row (in-vector m)]) + (fl+ acc (flvector-ref row j)))) + (< (abs (- s 1.0)) COL-SUM-TOLERANCE))) + +;; ============================================================ +;; Off-network kernel +;; ============================================================ + +(define (eigentrust-step-fl m p alpha t) + (vec-add-fl (vec-scale-fl (fl- 1.0 alpha) (mat-vec-mul-fl m t)) + (vec-scale-fl alpha p))) + +;; ============================================================ +;; Propagator-net assembly +;; ============================================================ + +(define (lww old new) new) + +(define (build-eigentrust-network-fl m p alpha k) + (unless (col-stochastic-fl? m) + (error 'build-eigentrust-network-fl + "M must be column-stochastic (within tolerance)")) + (define net0 (make-prop-network)) + (define-values (net1 m-cid) (net-new-cell net0 m lww)) + (define-values (net2 p-cid) (net-new-cell net1 p lww)) + (define-values (net3 alpha-cid) (net-new-cell net2 alpha lww)) + (define-values (net4 t0-cid) (net-new-cell net3 p lww)) + (let loop ([net net4] [prev-cid t0-cid] [step 1]) + (if (> step k) + (values net prev-cid) + (let-values ([(net* next-cid) + (net-new-cell net (vec-zeros-fl (flvector-length p)) lww)]) + (define (fire net-param) + (define t-prev (net-cell-read net-param prev-cid)) + (define m-val (net-cell-read net-param m-cid)) + (define p-val (net-cell-read net-param p-cid)) + (define alpha-val (net-cell-read net-param alpha-cid)) + (net-cell-write net-param next-cid + (eigentrust-step-fl m-val p-val alpha-val t-prev))) + (define-values (net** _pid) + (net-add-propagator + net* + (list prev-cid m-cid p-cid alpha-cid) + (list next-cid) + fire)) + (loop net** next-cid (add1 step)))))) + +(define (run-eigentrust-propagators-fl m p alpha k) + (define-values (net t-final-cid) (build-eigentrust-network-fl m p alpha k)) + (define net* (run-to-quiescence-bsp net)) + (net-cell-read net* t-final-cid)) + +;; ============================================================ +;; Fixtures (flonum equivalents) +;; ============================================================ + +(define m-ring-4-fl + (vector + (flvector 0.0 0.0 0.0 1.0) + (flvector 1.0 0.0 0.0 0.0) + (flvector 0.0 1.0 0.0 0.0) + (flvector 0.0 0.0 1.0 0.0))) + +(define p-seed-0-fl (flvector 1.0 0.0 0.0 0.0)) + +(define m-uniform-4-fl + (let ([row (flvector 0.25 0.25 0.25 0.25)]) + (vector row row row row))) + +(define p-uniform-4-fl (flvector 0.25 0.25 0.25 0.25)) + +(define m-others-3-fl + (vector + (flvector 0.0 0.5 0.5) + (flvector 0.5 0.0 0.5) + (flvector 0.5 0.5 0.0))) + +(define p-uniform-3-fl (flvector (/ 1.0 3.0) (/ 1.0 3.0) (/ 1.0 3.0))) + +;; ============================================================ +;; Module main: smoke +;; ============================================================ + +(module+ main + (define result (run-eigentrust-propagators-fl m-ring-4-fl p-seed-0-fl 0.3 4)) + (printf "ring-4 / α=0.3 / k=4 (flonum):~n ~s~n" result) + ;; Expected (rat) ~ #(0.5401 0.21 0.147 0.1029). + (define expected (flvector 0.5401 0.21 0.147 0.1029)) + (define tol 1e-9) + (define ok? + (for/and ([i (in-range 4)]) + (< (abs (- (flvector-ref result i) (flvector-ref expected i))) tol))) + (unless ok? + (error 'main "result not within ~e of expected~n expected: ~s~n got: ~s" + tol expected result)) + (printf "result matches rational version within ~e ✓~n" tol)) diff --git a/racket/prologos/benchmarks/comparative/eigentrust-propagators-scaling.rkt b/racket/prologos/benchmarks/comparative/eigentrust-propagators-scaling.rkt new file mode 100644 index 000000000..3796a0003 --- /dev/null +++ b/racket/prologos/benchmarks/comparative/eigentrust-propagators-scaling.rkt @@ -0,0 +1,210 @@ +#lang racket/base + +;;; eigentrust-propagators-scaling.rkt — scaling benchmark across n. +;;; +;;; Generates random column-stochastic matrices at various sizes and +;;; times all three propagator variants. Each (variant, n) sample +;;; gets a wall-clock timeout (TIMEOUT-MS); samples that exceed it +;;; are killed and reported as TIMEOUT. Once a variant times out at +;;; some n, larger n are skipped (also TIMEOUT) — exact-rat scaling +;;; is monotone in n so this is safe and avoids wasting hours on +;;; intractable cells. +;;; +;;; Fixed K=4 (matches the W3 reference workload). + +(require "../../propagator.rkt" + "eigentrust-propagators.rkt" + "eigentrust-propagators-fine.rkt" + "eigentrust-propagators-float.rkt" + "eigentrust-plain.rkt" + racket/list + racket/flonum) + +(define K 4) +(define NUM-RUNS 3) +(define WARMUP-RUNS 1) +(define TIMEOUT-MS 30000) ;; per-sample timeout — anything slower is "intractable" + +(define SIZES '(8 16 32 64 128 256 512 1024 2048 4096)) + + +;; ============================================================ +;; Random column-stochastic matrix generation +;; ============================================================ + +(define (random-positive) + (+ 1 (/ (random 1 1000000) 1000000))) + +(define (gen-rat-col-stochastic n) + (define cols + (for/list ([j (in-range n)]) + (define raw (for/list ([i (in-range n)]) (random-positive))) + (define s (apply + raw)) + (map (lambda (x) (/ x s)) raw))) + (for/vector #:length n ([i (in-range n)]) + (for/vector #:length n ([j (in-range n)]) + (list-ref (list-ref cols j) i)))) + +(define (gen-fl-col-stochastic n) + (define cols + (for/list ([j (in-range n)]) + (define raw (for/list ([i (in-range n)]) (+ 1.0 (random)))) + (define s (apply + raw)) + (map (lambda (x) (/ x s)) raw))) + (for/vector #:length n ([i (in-range n)]) + (define out (make-flvector n)) + (for ([j (in-range n)]) + (flvector-set! out j (list-ref (list-ref cols j) i))) + out)) + +(define (uniform-rat n) + (for/vector #:length n ([i (in-range n)]) (/ 1 n))) + +(define (uniform-fl n) + (define v (make-flvector n)) + (for ([i (in-range n)]) (flvector-set! v i (/ 1.0 n))) + v) + + +;; ============================================================ +;; Per-sample timeout +;; +;; Run thunk in a thread; if it doesn't finish within TIMEOUT-MS, +;; kill the thread and return 'timeout. Otherwise return the elapsed +;; ms (the thunk's value is discarded — we just want the timing). +;; ============================================================ + +(define (timed-thunk-or-timeout thunk timeout-ms) + (define result-box (box #f)) + (define t0 (current-inexact-monotonic-milliseconds)) + (define t (thread (lambda () + (thunk) + (set-box! result-box + (- (current-inexact-monotonic-milliseconds) t0))))) + (define done? (sync/timeout (/ timeout-ms 1000.0) t)) + (cond + [done? (unbox result-box)] + [else (kill-thread t) 'timeout])) + +(define (sample-with-timeout fn) + ;; Returns either a list of timing samples (length NUM-RUNS) or 'timeout. + ;; Bails on the first timeout. + ;; Skip warmup if first measured run already times out. + (collect-garbage 'major) + (define first (timed-thunk-or-timeout fn TIMEOUT-MS)) + (cond + [(eq? first 'timeout) 'timeout] + [else + ;; First sample succeeded; do warmup ALREADY happened (the + ;; first call) → use it as warmup, take NUM-RUNS more. + (let loop ([acc '()] [remaining NUM-RUNS]) + (cond + [(zero? remaining) (reverse acc)] + [else + (collect-garbage 'major) + (define s (timed-thunk-or-timeout fn TIMEOUT-MS)) + (cond + [(eq? s 'timeout) 'timeout] + [else (loop (cons s acc) (sub1 remaining))])]))])) + + +;; ============================================================ +;; Aggregation +;; ============================================================ + +(define (median xs) + (define s (sort xs <)) + (define n (length s)) + (cond [(zero? n) 0] + [(odd? n) (list-ref s (quotient n 2))] + [else (/ (+ (list-ref s (sub1 (quotient n 2))) + (list-ref s (quotient n 2))) 2)])) + +(define (round-2 x) (/ (round (* 100 x)) 100)) + +(define (~a v [w 5]) + (define s (format "~a" v)) + (define pad (max 0 (- w (string-length s)))) + (string-append s (make-string pad #\space))) + +(define (~r v [w 11]) + (define s (cond [(eq? v 'timeout) "TIMEOUT"] + [(eq? v 'skipped) "(skip)"] + [else (format "~a ms" v)])) + (define pad (max 0 (- w (string-length s)))) + (string-append (make-string pad #\space) s)) + + +;; ============================================================ +;; Per-size benchmark — accepts a "skip" flag per variant +;; ============================================================ + +(define (run-or-skip skip? thunk) + (cond [skip? 'skipped] + [else (define samples (sample-with-timeout thunk)) + (cond [(eq? samples 'timeout) 'timeout] + [else (round-2 (median samples))])])) + +(define (bench-row n + skip-plain-rat? skip-rat-coarse? skip-rat-fine? + skip-plain-fl? skip-float?) + (define need-rat? (not (and skip-plain-rat? skip-rat-coarse? skip-rat-fine?))) + (define need-fl? (not (and skip-plain-fl? skip-float?))) + (define m-rat (if need-rat? (gen-rat-col-stochastic n) #f)) + (define p-rat (if need-rat? (uniform-rat n) #f)) + (define m-fl (if need-fl? (gen-fl-col-stochastic n) #f)) + (define p-fl (if need-fl? (uniform-fl n) #f)) + + (define plain-rat-r + (run-or-skip skip-plain-rat? + (lambda () (run-eigentrust-plain m-rat p-rat 3/10 K)))) + (define rat-coarse-r + (run-or-skip skip-rat-coarse? + (lambda () (run-eigentrust-propagators m-rat p-rat 3/10 K)))) + (define rat-fine-r + (run-or-skip skip-rat-fine? + (lambda () (run-eigentrust-propagators-fine m-rat p-rat 3/10 K)))) + (define plain-fl-r + (run-or-skip skip-plain-fl? + (lambda () (run-eigentrust-plain-fl m-fl p-fl 0.3 K)))) + (define float-r + (run-or-skip skip-float? + (lambda () (run-eigentrust-propagators-fl m-fl p-fl 0.3 K)))) + + (printf " n=~a plain-rat=~a rat-coarse=~a rat-fine=~a ‖ plain-fl=~a float=~a~n" + (~a n) + (~r plain-rat-r) (~r rat-coarse-r) (~r rat-fine-r) + (~r plain-fl-r) (~r float-r)) + (values + (or skip-plain-rat? (eq? plain-rat-r 'timeout)) + (or skip-rat-coarse? (eq? rat-coarse-r 'timeout)) + (or skip-rat-fine? (eq? rat-fine-r 'timeout)) + (or skip-plain-fl? (eq? plain-fl-r 'timeout)) + (or skip-float? (eq? float-r 'timeout)))) + + +;; ============================================================ +;; Main: sweep +;; ============================================================ + +(module+ main + (printf "═══ Scaling: 3 propagator variants × matrix size n ═══~n") + (printf "Fixed K=~a (W3 reference). ~a measured runs per cell, median.~n" + K NUM-RUNS) + (printf "Per-sample timeout: ~a ms. Once a variant times out at n,~n" TIMEOUT-MS) + (printf "larger n are skipped (TIMEOUT propagates monotonically in n).~n~n") + (random-seed 42) + (let loop ([sizes SIZES] + [skip-plain-rat? #f] + [skip-rat-coarse? #f] + [skip-rat-fine? #f] + [skip-plain-fl? #f] + [skip-float? #f]) + (cond + [(null? sizes) (void)] + [else + (define-values (spr skc skf spf sf) + (bench-row (car sizes) + skip-plain-rat? skip-rat-coarse? skip-rat-fine? + skip-plain-fl? skip-float?)) + (loop (cdr sizes) spr skc skf spf sf)]))) diff --git a/racket/prologos/benchmarks/comparative/eigentrust-propagators.rkt b/racket/prologos/benchmarks/comparative/eigentrust-propagators.rkt new file mode 100644 index 000000000..7230a07e7 --- /dev/null +++ b/racket/prologos/benchmarks/comparative/eigentrust-propagators.rkt @@ -0,0 +1,221 @@ +#lang racket/base + +;;; eigentrust-propagators.rkt — direct Racket-on-propagators eigentrust. +;;; +;;; Fifth variant for the EigenTrust comparison (vs the four Prologos +;;; surface variants in this directory: list-rat, list-posit, pvec-rat, +;;; pvec-posit). This file bypasses the Prologos surface language and +;;; uses the propagator network primitives (make-prop-network, +;;; net-new-cell, net-add-propagator, run-to-quiescence-bsp) directly +;;; from Racket. It isolates "how fast is the propagator infrastructure +;;; on this workload" from "how fast is Prologos elaboration + reduction +;;; on this workload" — the surface variants pay both. +;;; +;;; Architecture: chain-of-cells (one cell per iteration, holding the +;;; entire trust vector). K propagators (one per step) flow trust from +;;; t_{k-1} to t_k. Constants (matrix M, pre-trust p, alpha) live in +;;; their own cells with last-write-wins merge. See +;;; docs/tracking/2026-04-29_0650_eigentrust-on-propagators.md. +;;; +;;; The matrix-vector multiply inside each step's fire function is +;;; off-network Racket compute. The on-network flow is the trust-vector +;;; cell sequence; the per-step kernel is opaque to the network. A +;;; finer-grained variant (one cell per peer per iteration) would be +;;; more on-network at higher constant cost — out of scope for this +;;; comparison. + +(require "../../propagator.rkt") + +(provide + ;; Vector-of-rationals helpers + vec-zeros + col-stochastic? + ;; The eigentrust kernel (off-network, for reuse in tests + bench) + eigentrust-step + ;; The propagator-net assembly + run + build-eigentrust-network + run-eigentrust-propagators + ;; Pre-built fixtures (match the Prologos benchmarks) + m-ring-4 + p-seed-0 + m-uniform-4 + p-uniform-4 + m-others-3 + p-uniform-3) + +;; ============================================================ +;; Vector-of-rationals primitives (Racket exact rationals) +;; ============================================================ + +(define (vec-zeros n) + (make-vector n 0)) + +(define (vec-add a b) + (for/vector #:length (vector-length a) ([x (in-vector a)] [y (in-vector b)]) + (+ x y))) + +(define (vec-scale s v) + (for/vector #:length (vector-length v) ([x (in-vector v)]) + (* s x))) + +;; Standard matrix-vector multiply: (M*t)[i] = dot(M[i], t). +;; M is a vector-of-vectors (row-major). +(define (mat-vec-mul m t) + (for/vector #:length (vector-length m) ([row (in-vector m)]) + (for/sum ([x (in-vector row)] [y (in-vector t)]) + (* x y)))) + +;; Column-stochastic invariant: every column sums to 1. +(define (col-stochastic? m) + (define n (vector-length m)) + (define n-cols (vector-length (vector-ref m 0))) + (for/and ([j (in-range n-cols)]) + (= 1 (for/sum ([row (in-vector m)]) (vector-ref row j))))) + +;; ============================================================ +;; The EigenTrust kernel — off-network +;; ============================================================ + +;; t_new = (1 - alpha) * M * t + alpha * p +(define (eigentrust-step m p alpha t) + (vec-add (vec-scale (- 1 alpha) (mat-vec-mul m t)) + (vec-scale alpha p))) + + +;; ============================================================ +;; The propagator-net assembly: build + run +;; ============================================================ + +;; last-write-wins merge — used for cells that get exactly one write. +;; Treats #f as "no value yet"; first non-#f wins, second non-#f +;; replaces (we use it under fire-once so this never happens in +;; practice). Combined with PROP-FIRE-ONCE on the producer, the cell +;; sees exactly one write. +(define (lww old new) new) + +;; Build a propagator network for K iterations of EigenTrust on +;; matrix M, pre-trust p, alpha. Pre-loads t_0 with p (matches the +;; Prologos `eigentrust` entry: t0 = p convention). Returns +;; (values network t-final-cid) — t-final-cid is the cell holding +;; t_K after BSP quiescence. +(define (build-eigentrust-network m p alpha k) + ;; Enforce the invariant up front, mirroring the Prologos + ;; `eigentrust` entry. + (unless (col-stochastic? m) + (error 'build-eigentrust-network + "M must be column-stochastic")) + ;; Constant cells: last-write-wins, pre-loaded. + (define net0 (make-prop-network)) + (define-values (net1 m-cid) (net-new-cell net0 m lww)) + (define-values (net2 p-cid) (net-new-cell net1 p lww)) + (define-values (net3 alpha-cid) (net-new-cell net2 alpha lww)) + ;; Iteration cells. Pre-load t_0 with p. + (define-values (net4 t0-cid) (net-new-cell net3 p lww)) + ;; Build the chain: t_0 → t_1 → ... → t_K. + ;; Each step is a fire-once propagator. + (let loop ([net net4] [prev-cid t0-cid] [step 1]) + (if (> step k) + (values net prev-cid) + (let-values ([(net* next-cid) (net-new-cell net (vec-zeros (vector-length p)) lww)]) + ;; Fire function: read prev, M, p, alpha; write next. + ;; CRITICAL (per propagator-design rule): use the lambda's + ;; net parameter, never close over the outer net. + (define (fire net-param) + (define t-prev (net-cell-read net-param prev-cid)) + (define m-val (net-cell-read net-param m-cid)) + (define p-val (net-cell-read net-param p-cid)) + (define alpha-val (net-cell-read net-param alpha-cid)) + (define t-new (eigentrust-step m-val p-val alpha-val t-prev)) + (net-cell-write net-param next-cid t-new)) + ;; Plain propagator (NOT fire-once). All K propagators are + ;; scheduled at install time; in BSP round 1 they all fire on + ;; the initial cell snapshot, but only step-1's read sees its + ;; pre-loaded input (t_0 = p). The other steps see [0,0,0,0] + ;; for their input and write α·p. In round 2, step-2 sees its + ;; correct (round-1) t_1 input and re-fires; but step-3, + ;; step-4 still see stale (round-1) inputs. After K rounds + ;; the chain has converged. Plain propagators (no fire-once) + ;; re-fire on input changes — that's what makes the chain + ;; settle. (Fire-once would fail at round 1.) + (define-values (net** _pid) + (net-add-propagator + net* + (list prev-cid m-cid p-cid alpha-cid) ;; inputs + (list next-cid) ;; outputs + fire)) + (loop net** next-cid (add1 step)))))) + +;; End-to-end: build the network, run BSP to quiescence, return the +;; final trust vector. K = number of forced iterations. +(define (run-eigentrust-propagators m p alpha k) + (define-values (net t-final-cid) (build-eigentrust-network m p alpha k)) + (define net* (run-to-quiescence-bsp net)) + (net-cell-read net* t-final-cid)) + + +;; ============================================================ +;; Fixtures — exact same matrices and vectors as the Prologos +;; benchmarks (rationals = Prologos Rat). +;; ============================================================ + +;; 4-peer ring: column j has a single 1 in row (j+1) mod 4. +;; Column-stochastic; sparse. The W3 fixture in the Prologos +;; benchmarks. +(define m-ring-4 + (vector + (vector 0 0 0 1) + (vector 1 0 0 0) + (vector 0 1 0 0) + (vector 0 0 1 0))) + +;; All trust on peer 0. +(define p-seed-0 (vector 1 0 0 0)) + +;; 4-peer uniform — every entry 1/4. Doubly stochastic; uniform is +;; a fixed point. +(define m-uniform-4 + (let ([row (vector 1/4 1/4 1/4 1/4)]) + (vector row row row row))) + +(define p-uniform-4 (vector 1/4 1/4 1/4 1/4)) + +;; 3-peer symmetric "uniform-on-others". +(define m-others-3 + (vector + (vector 0 1/2 1/2) + (vector 1/2 0 1/2) + (vector 1/2 1/2 0))) + +(define p-uniform-3 (vector 1/3 1/3 1/3)) + + +;; ============================================================ +;; Semantic note on iteration counts: +;; +;; The Prologos top-level `eigentrust` PRE-COMPUTES step 1 in its +;; entry expression and then runs `eigentrust-iterate` for max-iter +;; more steps. So `eigentrust m p α 0/1 3` performs 4 total step +;; calls. To match the W3 ring-4 fixture's expected result +;; [5401/10000, 21/100, 147/1000, 1029/10000], call this Racket +;; version with k = max-iter + 1 = 4. +;; +;; (We could embed this convention in the entry, but the K-step +;; semantics is what matters for benchmarking parity with the +;; Prologos `reduce_ms` measurements; documenting the offset is +;; less surprising than building it in.) +;; ============================================================ + +(module+ main + ;; Smoke test on the ring-4 W3 workload. + ;; Run via `racket benchmarks/comparative/eigentrust-propagators.rkt`. + ;; k = max-iter + 1 = 4 to match the Prologos benchmark fixture. + (define result (run-eigentrust-propagators m-ring-4 p-seed-0 3/10 4)) + (printf "ring-4 / α=3/10 / k=4 (max-iter+1):~n ~s~n" result) + ;; Sanity check — should equal the Prologos answer. + (define expected + (vector 5401/10000 21/100 147/1000 1029/10000)) + (unless (equal? result expected) + (error 'main + "result mismatch~n expected: ~s~n got: ~s" + expected result)) + (printf "result matches Prologos surface variants ✓~n")) diff --git a/racket/prologos/benchmarks/comparative/eigentrust-pvec-posit.prologos b/racket/prologos/benchmarks/comparative/eigentrust-pvec-posit.prologos new file mode 100644 index 000000000..82ee7a0c2 --- /dev/null +++ b/racket/prologos/benchmarks/comparative/eigentrust-pvec-posit.prologos @@ -0,0 +1,166 @@ +ns benchmarks::comparative::eigentrust-pvec-posit + +;; ============================================================ +;; Benchmark: EigenTrust — PVec + Posit32 variant +;; See eigentrust-list-rat.prologos for the workload specification. +;; ============================================================ + +spec dot-go Nat Nat [PVec Posit32] [PVec Posit32] Posit32 -> Posit32 +defn dot-go [i n xs ys acc] + match [nat-eq? i n] + | true -> acc + | false -> dot-go [suc i] n xs ys [p32+ acc [p32* [pvec-nth xs i] [pvec-nth ys i]]] + +spec dot [PVec Posit32] [PVec Posit32] -> Posit32 +defn dot [xs ys] + dot-go zero [pvec-length xs] xs ys ~0.0 + + +spec scale-vec-go Nat Nat Posit32 [PVec Posit32] [PVec Posit32] -> [PVec Posit32] +defn scale-vec-go [i n s xs acc] + match [nat-eq? i n] + | true -> acc + | false -> scale-vec-go [suc i] n s xs [pvec-push acc [p32* s [pvec-nth xs i]]] + +spec scale-vec Posit32 [PVec Posit32] -> [PVec Posit32] +defn scale-vec [s xs] + scale-vec-go zero [pvec-length xs] s xs (pvec-empty Posit32) + + +spec add-vec-go Nat Nat [PVec Posit32] [PVec Posit32] [PVec Posit32] -> [PVec Posit32] +defn add-vec-go [i n xs ys acc] + match [nat-eq? i n] + | true -> acc + | false -> add-vec-go [suc i] n xs ys [pvec-push acc [p32+ [pvec-nth xs i] [pvec-nth ys i]]] + +spec add-vec [PVec Posit32] [PVec Posit32] -> [PVec Posit32] +defn add-vec [xs ys] + add-vec-go zero [pvec-length xs] xs ys (pvec-empty Posit32) + + +spec sub-vec-go Nat Nat [PVec Posit32] [PVec Posit32] [PVec Posit32] -> [PVec Posit32] +defn sub-vec-go [i n xs ys acc] + match [nat-eq? i n] + | true -> acc + | false -> sub-vec-go [suc i] n xs ys [pvec-push acc [p32- [pvec-nth xs i] [pvec-nth ys i]]] + +spec sub-vec [PVec Posit32] [PVec Posit32] -> [PVec Posit32] +defn sub-vec [xs ys] + sub-vec-go zero [pvec-length xs] xs ys (pvec-empty Posit32) + + +spec p32-max Posit32 Posit32 -> Posit32 +defn p32-max [a b] + match [p32-lt a b] + | true -> b + | false -> a + +spec linf-norm-go Nat Nat [PVec Posit32] Posit32 -> Posit32 +defn linf-norm-go [i n xs acc] + match [nat-eq? i n] + | true -> acc + | false -> linf-norm-go [suc i] n xs [p32-max acc [p32-abs [pvec-nth xs i]]] + +spec linf-norm [PVec Posit32] -> Posit32 +defn linf-norm [xs] + linf-norm-go zero [pvec-length xs] xs ~0.0 + + +spec mat-vec-mul-go Nat Nat [PVec [PVec Posit32]] [PVec Posit32] [PVec Posit32] -> [PVec Posit32] +defn mat-vec-mul-go [i n m t acc] + match [nat-eq? i n] + | true -> acc + | false -> mat-vec-mul-go [suc i] n m t [pvec-push acc [dot [pvec-nth m i] t]] + +spec mat-vec-mul [PVec [PVec Posit32]] [PVec Posit32] -> [PVec Posit32] +defn mat-vec-mul [m t] + mat-vec-mul-go zero [pvec-length m] m t (pvec-empty Posit32) + + +;; --- Column-stochastic invariant check --- +spec col-sums-fold Nat Nat [PVec [PVec Posit32]] [PVec Posit32] -> [PVec Posit32] +defn col-sums-fold [i n m acc] + match [nat-eq? i n] + | true -> acc + | false -> col-sums-fold [suc i] n m [add-vec acc [pvec-nth m i]] + +spec zeros-go Nat Nat [PVec Posit32] -> [PVec Posit32] +defn zeros-go [i n acc] + match [nat-eq? i n] + | true -> acc + | false -> zeros-go [suc i] n [pvec-push acc ~0.0] + +spec zeros Nat -> [PVec Posit32] +defn zeros [n] + zeros-go zero n (pvec-empty Posit32) + +spec col-sums [PVec [PVec Posit32]] -> [PVec Posit32] +defn col-sums [m] + match [nat-eq? [pvec-length m] zero] + | true -> (pvec-empty Posit32) + | false -> col-sums-fold zero [pvec-length m] m [zeros [pvec-length [pvec-nth m zero]]] + +spec all-ones?-go Nat Nat [PVec Posit32] -> Bool +defn all-ones?-go [i n xs] + match [nat-eq? i n] + | true -> true + | false -> match [p32-eq [pvec-nth xs i] ~1.0] + | false -> false + | true -> all-ones?-go [suc i] n xs + +spec all-ones? [PVec Posit32] -> Bool +defn all-ones? [xs] + all-ones?-go zero [pvec-length xs] xs + +spec col-stochastic? [PVec [PVec Posit32]] -> Bool +defn col-stochastic? [m] + all-ones? [col-sums m] + + +spec eigentrust-step [PVec [PVec Posit32]] [PVec Posit32] Posit32 [PVec Posit32] -> [PVec Posit32] +defn eigentrust-step [m p alpha t] + add-vec [scale-vec [p32- ~1.0 alpha] [mat-vec-mul m t]] [scale-vec alpha p] + +spec eigentrust-iterate [PVec [PVec Posit32]] [PVec Posit32] Posit32 Posit32 Int [PVec Posit32] [PVec Posit32] -> [PVec Posit32] +defn eigentrust-iterate [m p alpha eps budget t tnew] + match [int-le budget 0] + | true -> tnew + | false -> match [p32-lt [linf-norm [sub-vec tnew t]] eps] + | true -> tnew + | false -> eigentrust-iterate m p alpha eps [int- budget 1] tnew [eigentrust-step m p alpha tnew] + +spec eigentrust [PVec [PVec Posit32]] [PVec Posit32] Posit32 Posit32 Int -> [PVec Posit32] +defn eigentrust [m p alpha eps max-iter] + match [col-stochastic? m] + | false -> (the [PVec Posit32] [panic "eigentrust: M must be column-stochastic"]) + | true -> eigentrust-iterate m p alpha eps max-iter p [eigentrust-step m p alpha p] + + +;; ============================================================ +;; Fixtures +;; ============================================================ + +def p-uniform-4 : [PVec Posit32] := @[~0.25 ~0.25 ~0.25 ~0.25] +def p-uniform-3 : [PVec Posit32] := @[~0.33333 ~0.33333 ~0.33333] +def p-seed-0 : [PVec Posit32] := @[~1.0 ~0.0 ~0.0 ~0.0] + +def m-uniform-4 : [PVec [PVec Posit32]] := @[@[~0.25 ~0.25 ~0.25 ~0.25] @[~0.25 ~0.25 ~0.25 ~0.25] @[~0.25 ~0.25 ~0.25 ~0.25] @[~0.25 ~0.25 ~0.25 ~0.25]] + +def m-others-3 : [PVec [PVec Posit32]] := @[@[~0.0 ~0.5 ~0.5] @[~0.5 ~0.0 ~0.5] @[~0.5 ~0.5 ~0.0]] + +def m-ring-4 : [PVec [PVec Posit32]] := @[@[~0.0 ~0.0 ~0.0 ~1.0] @[~1.0 ~0.0 ~0.0 ~0.0] @[~0.0 ~1.0 ~0.0 ~0.0] @[~0.0 ~0.0 ~1.0 ~0.0]] + + +;; ============================================================ +;; Workloads +;; ============================================================ + +eigentrust m-uniform-4 p-uniform-4 ~0.1 ~0.001 50 +eigentrust m-others-3 p-uniform-3 ~0.1 ~0.001 50 +eigentrust m-ring-4 p-seed-0 ~0.3 ~0.0 3 +col-stochastic? m-uniform-4 +eigentrust-step m-uniform-4 p-uniform-4 ~0.1 p-uniform-4 +mat-vec-mul m-uniform-4 p-uniform-4 +scale-vec ~0.5 @[~0.5 ~0.33333 ~0.25] +add-vec @[~0.25 ~0.25] @[~0.25 ~0.25] +linf-norm [sub-vec @[~0.5 ~0.33333] @[~0.25 ~0.33333]] diff --git a/racket/prologos/benchmarks/comparative/eigentrust-pvec-rat.prologos b/racket/prologos/benchmarks/comparative/eigentrust-pvec-rat.prologos new file mode 100644 index 000000000..c2e9a9f46 --- /dev/null +++ b/racket/prologos/benchmarks/comparative/eigentrust-pvec-rat.prologos @@ -0,0 +1,166 @@ +ns benchmarks::comparative::eigentrust-pvec-rat + +;; ============================================================ +;; Benchmark: EigenTrust — PVec + Rat variant +;; See eigentrust-list-rat.prologos for the workload specification. +;; ============================================================ + +spec dot-go Nat Nat [PVec Rat] [PVec Rat] Rat -> Rat +defn dot-go [i n xs ys acc] + match [nat-eq? i n] + | true -> acc + | false -> dot-go [suc i] n xs ys [rat+ acc [rat* [pvec-nth xs i] [pvec-nth ys i]]] + +spec dot [PVec Rat] [PVec Rat] -> Rat +defn dot [xs ys] + dot-go zero [pvec-length xs] xs ys 0/1 + + +spec scale-vec-go Nat Nat Rat [PVec Rat] [PVec Rat] -> [PVec Rat] +defn scale-vec-go [i n s xs acc] + match [nat-eq? i n] + | true -> acc + | false -> scale-vec-go [suc i] n s xs [pvec-push acc [rat* s [pvec-nth xs i]]] + +spec scale-vec Rat [PVec Rat] -> [PVec Rat] +defn scale-vec [s xs] + scale-vec-go zero [pvec-length xs] s xs (pvec-empty Rat) + + +spec add-vec-go Nat Nat [PVec Rat] [PVec Rat] [PVec Rat] -> [PVec Rat] +defn add-vec-go [i n xs ys acc] + match [nat-eq? i n] + | true -> acc + | false -> add-vec-go [suc i] n xs ys [pvec-push acc [rat+ [pvec-nth xs i] [pvec-nth ys i]]] + +spec add-vec [PVec Rat] [PVec Rat] -> [PVec Rat] +defn add-vec [xs ys] + add-vec-go zero [pvec-length xs] xs ys (pvec-empty Rat) + + +spec sub-vec-go Nat Nat [PVec Rat] [PVec Rat] [PVec Rat] -> [PVec Rat] +defn sub-vec-go [i n xs ys acc] + match [nat-eq? i n] + | true -> acc + | false -> sub-vec-go [suc i] n xs ys [pvec-push acc [rat- [pvec-nth xs i] [pvec-nth ys i]]] + +spec sub-vec [PVec Rat] [PVec Rat] -> [PVec Rat] +defn sub-vec [xs ys] + sub-vec-go zero [pvec-length xs] xs ys (pvec-empty Rat) + + +spec rat-max Rat Rat -> Rat +defn rat-max [a b] + match [rat-lt a b] + | true -> b + | false -> a + +spec linf-norm-go Nat Nat [PVec Rat] Rat -> Rat +defn linf-norm-go [i n xs acc] + match [nat-eq? i n] + | true -> acc + | false -> linf-norm-go [suc i] n xs [rat-max acc [rat-abs [pvec-nth xs i]]] + +spec linf-norm [PVec Rat] -> Rat +defn linf-norm [xs] + linf-norm-go zero [pvec-length xs] xs 0/1 + + +spec mat-vec-mul-go Nat Nat [PVec [PVec Rat]] [PVec Rat] [PVec Rat] -> [PVec Rat] +defn mat-vec-mul-go [i n m t acc] + match [nat-eq? i n] + | true -> acc + | false -> mat-vec-mul-go [suc i] n m t [pvec-push acc [dot [pvec-nth m i] t]] + +spec mat-vec-mul [PVec [PVec Rat]] [PVec Rat] -> [PVec Rat] +defn mat-vec-mul [m t] + mat-vec-mul-go zero [pvec-length m] m t (pvec-empty Rat) + + +;; --- Column-stochastic invariant check --- +spec col-sums-fold Nat Nat [PVec [PVec Rat]] [PVec Rat] -> [PVec Rat] +defn col-sums-fold [i n m acc] + match [nat-eq? i n] + | true -> acc + | false -> col-sums-fold [suc i] n m [add-vec acc [pvec-nth m i]] + +spec zeros-go Nat Nat [PVec Rat] -> [PVec Rat] +defn zeros-go [i n acc] + match [nat-eq? i n] + | true -> acc + | false -> zeros-go [suc i] n [pvec-push acc 0/1] + +spec zeros Nat -> [PVec Rat] +defn zeros [n] + zeros-go zero n (pvec-empty Rat) + +spec col-sums [PVec [PVec Rat]] -> [PVec Rat] +defn col-sums [m] + match [nat-eq? [pvec-length m] zero] + | true -> (pvec-empty Rat) + | false -> col-sums-fold zero [pvec-length m] m [zeros [pvec-length [pvec-nth m zero]]] + +spec all-ones?-go Nat Nat [PVec Rat] -> Bool +defn all-ones?-go [i n xs] + match [nat-eq? i n] + | true -> true + | false -> match [rat-eq [pvec-nth xs i] 1/1] + | false -> false + | true -> all-ones?-go [suc i] n xs + +spec all-ones? [PVec Rat] -> Bool +defn all-ones? [xs] + all-ones?-go zero [pvec-length xs] xs + +spec col-stochastic? [PVec [PVec Rat]] -> Bool +defn col-stochastic? [m] + all-ones? [col-sums m] + + +spec eigentrust-step [PVec [PVec Rat]] [PVec Rat] Rat [PVec Rat] -> [PVec Rat] +defn eigentrust-step [m p alpha t] + add-vec [scale-vec [rat- 1/1 alpha] [mat-vec-mul m t]] [scale-vec alpha p] + +spec eigentrust-iterate [PVec [PVec Rat]] [PVec Rat] Rat Rat Int [PVec Rat] [PVec Rat] -> [PVec Rat] +defn eigentrust-iterate [m p alpha eps budget t tnew] + match [int-le budget 0] + | true -> tnew + | false -> match [rat-lt [linf-norm [sub-vec tnew t]] eps] + | true -> tnew + | false -> eigentrust-iterate m p alpha eps [int- budget 1] tnew [eigentrust-step m p alpha tnew] + +spec eigentrust [PVec [PVec Rat]] [PVec Rat] Rat Rat Int -> [PVec Rat] +defn eigentrust [m p alpha eps max-iter] + match [col-stochastic? m] + | false -> (the [PVec Rat] [panic "eigentrust: M must be column-stochastic"]) + | true -> eigentrust-iterate m p alpha eps max-iter p [eigentrust-step m p alpha p] + + +;; ============================================================ +;; Fixtures +;; ============================================================ + +def p-uniform-4 : [PVec Rat] := @[1/4 1/4 1/4 1/4] +def p-uniform-3 : [PVec Rat] := @[1/3 1/3 1/3] +def p-seed-0 : [PVec Rat] := @[1/1 0/1 0/1 0/1] + +def m-uniform-4 : [PVec [PVec Rat]] := @[@[1/4 1/4 1/4 1/4] @[1/4 1/4 1/4 1/4] @[1/4 1/4 1/4 1/4] @[1/4 1/4 1/4 1/4]] + +def m-others-3 : [PVec [PVec Rat]] := @[@[0/1 1/2 1/2] @[1/2 0/1 1/2] @[1/2 1/2 0/1]] + +def m-ring-4 : [PVec [PVec Rat]] := @[@[0/1 0/1 0/1 1/1] @[1/1 0/1 0/1 0/1] @[0/1 1/1 0/1 0/1] @[0/1 0/1 1/1 0/1]] + + +;; ============================================================ +;; Workloads +;; ============================================================ + +eigentrust m-uniform-4 p-uniform-4 1/10 1/1000 50 +eigentrust m-others-3 p-uniform-3 1/10 1/1000 50 +eigentrust m-ring-4 p-seed-0 3/10 0/1 3 +col-stochastic? m-uniform-4 +eigentrust-step m-uniform-4 p-uniform-4 1/10 p-uniform-4 +mat-vec-mul m-uniform-4 p-uniform-4 +scale-vec 1/2 @[1/2 1/3 1/4] +add-vec @[1/4 1/4] @[1/4 1/4] +linf-norm [sub-vec @[1/2 1/3] @[1/4 1/3]] diff --git a/racket/prologos/benchmarks/comparative/eigentrust-scaling-large.rkt b/racket/prologos/benchmarks/comparative/eigentrust-scaling-large.rkt new file mode 100644 index 000000000..9e4bd5f6b --- /dev/null +++ b/racket/prologos/benchmarks/comparative/eigentrust-scaling-large.rkt @@ -0,0 +1,118 @@ +#lang racket/base + +;;; eigentrust-scaling-large.rkt — extended scaling math vs network. +;;; +;;; Companion to eigentrust-propagators-scaling.rkt. Pushes math +;;; (plain-fl) + network (float) past n=4096 to look for a +;;; breakeven / breakaway point. K=4 (matches the W3 reference +;;; workload). Per-sample timeout 180s — enough that math's O(n²) +;;; can reach n=32768 (~30 s/run). + +(require "../../propagator.rkt" + "eigentrust-propagators-float.rkt" + "eigentrust-plain.rkt" + racket/list + racket/flonum) + +(define K 4) +(define NUM-RUNS 2) +(define TIMEOUT-MS 180000) + +;; n=32768 needs ~8 GB for the matrix alone; on a 16 GB host this OOMs +;; once Racket's previous-iteration heap is still rooted. Stop at 16384. +;; If you have a 32+ GB host, append 32768 here. +(define SIZES '(4096 8192 16384)) + + +;; Build column-stochastic n×n matrix as a vector of flvectors (rows). +;; Uses only flvector ops — O(n²) flonum operations, no list allocation. +;; Layout: result[i] is row i, an flvector of length n; result[i][j] = M_{ij}. +;; Build column-by-column: draw n positives, sum, divide by sum, scatter into rows. +(define (gen-fl-col-stochastic n) + (define rows + (for/vector #:length n ([i (in-range n)]) + (make-flvector n))) + (define col (make-flvector n)) + (for ([j (in-range n)]) + (define s 0.0) + (for ([i (in-range n)]) + (define v (+ 1.0 (random))) + (flvector-set! col i v) + (set! s (+ s v))) + (for ([i (in-range n)]) + (flvector-set! (vector-ref rows i) j + (/ (flvector-ref col i) s)))) + rows) + +(define (uniform-fl n) + (define v (make-flvector n)) + (for ([i (in-range n)]) (flvector-set! v i (/ 1.0 n))) + v) + +(define (timed-thunk-or-timeout thunk timeout-ms) + (define result-box (box #f)) + (define t0 (current-inexact-monotonic-milliseconds)) + (define t (thread (lambda () + (thunk) + (set-box! result-box + (- (current-inexact-monotonic-milliseconds) t0))))) + (define done? (sync/timeout (/ timeout-ms 1000.0) t)) + (cond [done? (unbox result-box)] + [else (kill-thread t) 'timeout])) + +(define (sample-with-timeout fn) + (collect-garbage 'major) + (define first (timed-thunk-or-timeout fn TIMEOUT-MS)) + (cond + [(eq? first 'timeout) 'timeout] + [else + (let loop ([acc '()] [remaining NUM-RUNS]) + (cond [(zero? remaining) (reverse acc)] + [else + (collect-garbage 'major) + (define s (timed-thunk-or-timeout fn TIMEOUT-MS)) + (cond [(eq? s 'timeout) 'timeout] + [else (loop (cons s acc) (sub1 remaining))])]))])) + +(define (median xs) + (define s (sort xs <)) + (define n (length s)) + (cond [(zero? n) 0] + [(odd? n) (list-ref s (quotient n 2))] + [else (/ (+ (list-ref s (sub1 (quotient n 2))) + (list-ref s (quotient n 2))) 2)])) + +(define (round-2 x) (/ (round (* 100 x)) 100)) + +(module+ main + (printf "═══ Extended scaling: math vs network at large n ═══~n") + (printf "K=~a, ~a runs/cell, TIMEOUT ~a ms~n~n" K NUM-RUNS TIMEOUT-MS) + (random-seed 42) + (for ([n (in-list SIZES)]) + (printf "n=~a — generating fixture..." n) + (flush-output) + (define m (gen-fl-col-stochastic n)) + (define p (uniform-fl n)) + (printf " done.~n") + (printf " math (plain-fl) : ") + (flush-output) + (define plain-samples + (sample-with-timeout + (lambda () (run-eigentrust-plain-fl m p 0.3 K)))) + (define plain-r + (cond [(eq? plain-samples 'timeout) 'timeout] + [else (round-2 (median plain-samples))])) + (printf "~a ms~n" plain-r) + (printf " network (float) : ") + (flush-output) + (define net-samples + (sample-with-timeout + (lambda () (run-eigentrust-propagators-fl m p 0.3 K)))) + (define net-r + (cond [(eq? net-samples 'timeout) 'timeout] + [else (round-2 (median net-samples))])) + (printf "~a ms~n" net-r) + (when (and (number? plain-r) (number? net-r)) + (define ovh (round-2 (* 100.0 (/ (- net-r plain-r) plain-r)))) + (printf " overhead : +~a%%~n" ovh)) + (newline))) diff --git a/racket/prologos/benchmarks/micro/info.rkt b/racket/prologos/benchmarks/micro/info.rkt new file mode 100644 index 000000000..d969060f5 --- /dev/null +++ b/racket/prologos/benchmarks/micro/info.rkt @@ -0,0 +1,12 @@ +#lang info + +;; Pre-0 micro-benchmarks for tracks that were implemented after this +;; file was last updated. `bench-bsp-le-track2.rkt` references the +;; pre-D.5b TMS API (`tms-write`, `tms-cell-value`, `tms-read`, +;; `tms-commit`) that no longer exists; since the file is a historical +;; baseline-measurement artifact (not code that runs in CI or +;; regression suites), we simply skip it during `raco setup` compilation +;; so it does not fail the build. To re-enable the benchmark, migrate +;; its calls to the current `tms-cell` / `atms-write-cell` API in +;; `atms.rkt` and remove this omit directive. +(define compile-omit-paths '("bench-bsp-le-track2.rkt")) diff --git a/racket/prologos/data/cache/pnet/prologos/data/nat.pnet b/racket/prologos/data/cache/pnet/prologos/data/nat.pnet index bbb45dc37..a4cdb387a 100644 --- a/racket/prologos/data/cache/pnet/prologos/data/nat.pnet +++ b/racket/prologos/data/cache/pnet/prologos/data/nat.pnet @@ -1 +1 @@ -(1 "/home/user/prologos/racket/prologos/lib/prologos/data/nat.prologos:1776910266" #hasheq((add . (#(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Nat))) . #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-reduce #(struct:expr-bvar 0) (#(struct:expr-reduce-arm zero 0 #(struct:expr-bvar 1)) #(struct:expr-reduce-arm suc 1 #(struct:expr-suc #(struct:expr-app #(struct:expr-app #(struct:expr-fvar prologos::data::nat::add) #(struct:expr-bvar 2)) #(struct:expr-bvar 0))))) #t))))) (apply . (#(struct:expr-Pi m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-Pi m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-Pi mw #(struct:expr-Pi mw #(struct:expr-bvar 1) #(struct:expr-bvar 1)) #(struct:expr-Pi mw #(struct:expr-bvar 2) #(struct:expr-bvar 2))))) . #(struct:expr-lam m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-lam m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-lam mw #(struct:expr-Pi mw #(struct:expr-bvar 1) #(struct:expr-bvar 1)) #(struct:expr-lam mw #(struct:expr-bvar 2) #(struct:expr-app #(struct:expr-bvar 1) #(struct:expr-bvar 0)))))))) (bool-to-nat . (#(struct:expr-Pi mw #(struct:expr-Bool) #(struct:expr-Nat)) . #(struct:expr-lam mw #(struct:expr-Bool) #(struct:expr-reduce #(struct:expr-bvar 0) (#(struct:expr-reduce-arm true 0 #(struct:expr-nat-val 1)) #(struct:expr-reduce-arm false 0 #(struct:expr-nat-val 0))) #t)))) (clamp . (#(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Nat)))) . #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-lam mw #(struct:expr-hole) #(struct:expr-app #(struct:expr-app #(struct:expr-fvar prologos::data::nat::max) #(struct:expr-bvar 2)) #(struct:expr-app #(struct:expr-app #(struct:expr-fvar prologos::data::nat::min) #(struct:expr-bvar 0)) #(struct:expr-bvar 1)))))))) (compose . (#(struct:expr-Pi m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-Pi m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-Pi m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-Pi mw #(struct:expr-Pi mw #(struct:expr-bvar 2) #(struct:expr-bvar 2)) #(struct:expr-Pi mw #(struct:expr-Pi mw #(struct:expr-bvar 1) #(struct:expr-bvar 4)) #(struct:expr-Pi mw #(struct:expr-bvar 2) #(struct:expr-bvar 4))))))) . #(struct:expr-lam m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-lam m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-lam m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-lam mw #(struct:expr-Pi mw #(struct:expr-bvar 2) #(struct:expr-bvar 2)) #(struct:expr-lam mw #(struct:expr-Pi mw #(struct:expr-bvar 1) #(struct:expr-bvar 4)) #(struct:expr-lam mw #(struct:expr-bvar 2) #(struct:expr-app #(struct:expr-bvar 2) #(struct:expr-app #(struct:expr-bvar 1) #(struct:expr-bvar 0))))))))))) (const . (#(struct:expr-Pi m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-Pi m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-Pi mw #(struct:expr-bvar 1) #(struct:expr-Pi mw #(struct:expr-bvar 1) #(struct:expr-bvar 3))))) . #(struct:expr-lam m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-lam m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-lam mw #(struct:expr-bvar 1) #(struct:expr-lam mw #(struct:expr-bvar 1) #(struct:expr-bvar 1))))))) (double . (#(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Nat)) . #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-reduce #(struct:expr-bvar 0) (#(struct:expr-reduce-arm zero 0 #(struct:expr-nat-val 0)) #(struct:expr-reduce-arm suc 1 #(struct:expr-suc #(struct:expr-suc #(struct:expr-app #(struct:expr-fvar prologos::data::nat::double) #(struct:expr-bvar 0)))))) #t)))) (flip . (#(struct:expr-Pi m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-Pi m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-Pi m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-Pi mw #(struct:expr-Pi mw #(struct:expr-bvar 2) #(struct:expr-Pi mw #(struct:expr-bvar 2) #(struct:expr-bvar 2))) #(struct:expr-Pi mw #(struct:expr-bvar 2) #(struct:expr-Pi mw #(struct:expr-bvar 4) #(struct:expr-bvar 3))))))) . #(struct:expr-lam m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-lam m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-lam m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-lam mw #(struct:expr-Pi mw #(struct:expr-bvar 2) #(struct:expr-Pi mw #(struct:expr-bvar 2) #(struct:expr-bvar 2))) #(struct:expr-lam mw #(struct:expr-bvar 2) #(struct:expr-lam mw #(struct:expr-bvar 4) #(struct:expr-app #(struct:expr-app #(struct:expr-bvar 2) #(struct:expr-bvar 0)) #(struct:expr-bvar 1)))))))))) (ge? . (#(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Bool))) . #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-app #(struct:expr-app #(struct:expr-fvar prologos::data::nat::le?) #(struct:expr-bvar 0)) #(struct:expr-bvar 1)))))) (gt? . (#(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Bool))) . #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-app #(struct:expr-app #(struct:expr-fvar prologos::data::nat::lt?) #(struct:expr-bvar 0)) #(struct:expr-bvar 1)))))) (id . (#(struct:expr-Pi m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-Pi mw #(struct:expr-bvar 0) #(struct:expr-bvar 1))) . #(struct:expr-lam m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-lam mw #(struct:expr-bvar 0) #(struct:expr-bvar 0))))) (le? . (#(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Bool))) . #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-app #(struct:expr-fvar prologos::data::nat::zero?) #(struct:expr-app #(struct:expr-app #(struct:expr-fvar prologos::data::nat::sub) #(struct:expr-bvar 1)) #(struct:expr-bvar 0))))))) (lt? . (#(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Bool))) . #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-reduce #(struct:expr-app #(struct:expr-app #(struct:expr-fvar prologos::data::nat::le?) #(struct:expr-bvar 0)) #(struct:expr-bvar 1)) (#(struct:expr-reduce-arm true 0 #(struct:expr-false)) #(struct:expr-reduce-arm false 0 #(struct:expr-true))) #t))))) (max . (#(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Nat))) . #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-reduce #(struct:expr-app #(struct:expr-app #(struct:expr-fvar prologos::data::nat::le?) #(struct:expr-bvar 1)) #(struct:expr-bvar 0)) (#(struct:expr-reduce-arm true 0 #(struct:expr-bvar 0)) #(struct:expr-reduce-arm false 0 #(struct:expr-bvar 1))) #t))))) (min . (#(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Nat))) . #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-reduce #(struct:expr-app #(struct:expr-app #(struct:expr-fvar prologos::data::nat::le?) #(struct:expr-bvar 1)) #(struct:expr-bvar 0)) (#(struct:expr-reduce-arm true 0 #(struct:expr-bvar 1)) #(struct:expr-reduce-arm false 0 #(struct:expr-bvar 0))) #t))))) (mult . (#(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Nat))) . #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-reduce #(struct:expr-bvar 0) (#(struct:expr-reduce-arm zero 0 #(struct:expr-nat-val 0)) #(struct:expr-reduce-arm suc 1 #(struct:expr-app #(struct:expr-app #(struct:expr-fvar prologos::data::nat::add) #(struct:expr-bvar 2)) #(struct:expr-app #(struct:expr-app #(struct:expr-fvar prologos::data::nat::mult) #(struct:expr-bvar 2)) #(struct:expr-bvar 0))))) #t))))) (nat-eq? . (#(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Bool))) . #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-reduce #(struct:expr-app #(struct:expr-app #(struct:expr-fvar prologos::data::nat::le?) #(struct:expr-bvar 1)) #(struct:expr-bvar 0)) (#(struct:expr-reduce-arm true 0 #(struct:expr-app #(struct:expr-app #(struct:expr-fvar prologos::data::nat::le?) #(struct:expr-bvar 0)) #(struct:expr-bvar 1))) #(struct:expr-reduce-arm false 0 #(struct:expr-false))) #t))))) (on . (#(struct:expr-Pi m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-Pi m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-Pi m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-Pi mw #(struct:expr-Pi mw #(struct:expr-bvar 2) #(struct:expr-Pi mw #(struct:expr-bvar 3) #(struct:expr-bvar 3))) #(struct:expr-Pi mw #(struct:expr-Pi mw #(struct:expr-bvar 1) #(struct:expr-bvar 4)) #(struct:expr-Pi mw #(struct:expr-bvar 2) #(struct:expr-Pi mw #(struct:expr-bvar 3) #(struct:expr-bvar 5)))))))) . #(struct:expr-lam m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-lam m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-lam m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-lam mw #(struct:expr-Pi mw #(struct:expr-bvar 2) #(struct:expr-Pi mw #(struct:expr-bvar 3) #(struct:expr-bvar 3))) #(struct:expr-lam mw #(struct:expr-Pi mw #(struct:expr-bvar 1) #(struct:expr-bvar 4)) #(struct:expr-lam mw #(struct:expr-bvar 2) #(struct:expr-lam mw #(struct:expr-bvar 3) #(struct:expr-app #(struct:expr-app #(struct:expr-bvar 3) #(struct:expr-app #(struct:expr-bvar 2) #(struct:expr-bvar 1))) #(struct:expr-app #(struct:expr-bvar 2) #(struct:expr-bvar 0)))))))))))) (pow . (#(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Nat))) . #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-reduce #(struct:expr-bvar 0) (#(struct:expr-reduce-arm zero 0 #(struct:expr-nat-val 1)) #(struct:expr-reduce-arm suc 1 #(struct:expr-app #(struct:expr-app #(struct:expr-fvar prologos::data::nat::mult) #(struct:expr-bvar 2)) #(struct:expr-app #(struct:expr-app #(struct:expr-fvar prologos::data::nat::pow) #(struct:expr-bvar 2)) #(struct:expr-bvar 0))))) #t))))) (pred . (#(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Nat)) . #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-reduce #(struct:expr-bvar 0) (#(struct:expr-reduce-arm zero 0 #(struct:expr-nat-val 0)) #(struct:expr-reduce-arm suc 1 #(struct:expr-bvar 0))) #t)))) (prologos::core::apply . (#(struct:expr-Pi m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-Pi m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-Pi mw #(struct:expr-Pi mw #(struct:expr-bvar 1) #(struct:expr-bvar 1)) #(struct:expr-Pi mw #(struct:expr-bvar 2) #(struct:expr-bvar 2))))) . #(struct:expr-lam m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-lam m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-lam mw #(struct:expr-Pi mw #(struct:expr-bvar 1) #(struct:expr-bvar 1)) #(struct:expr-lam mw #(struct:expr-bvar 2) #(struct:expr-app #(struct:expr-bvar 1) #(struct:expr-bvar 0)))))))) (prologos::core::compose . (#(struct:expr-Pi m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-Pi m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-Pi m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-Pi mw #(struct:expr-Pi mw #(struct:expr-bvar 2) #(struct:expr-bvar 2)) #(struct:expr-Pi mw #(struct:expr-Pi mw #(struct:expr-bvar 1) #(struct:expr-bvar 4)) #(struct:expr-Pi mw #(struct:expr-bvar 2) #(struct:expr-bvar 4))))))) . #(struct:expr-lam m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-lam m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-lam m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-lam mw #(struct:expr-Pi mw #(struct:expr-bvar 2) #(struct:expr-bvar 2)) #(struct:expr-lam mw #(struct:expr-Pi mw #(struct:expr-bvar 1) #(struct:expr-bvar 4)) #(struct:expr-lam mw #(struct:expr-bvar 2) #(struct:expr-app #(struct:expr-bvar 2) #(struct:expr-app #(struct:expr-bvar 1) #(struct:expr-bvar 0))))))))))) (prologos::core::const . (#(struct:expr-Pi m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-Pi m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-Pi mw #(struct:expr-bvar 1) #(struct:expr-Pi mw #(struct:expr-bvar 1) #(struct:expr-bvar 3))))) . #(struct:expr-lam m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-lam m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-lam mw #(struct:expr-bvar 1) #(struct:expr-lam mw #(struct:expr-bvar 1) #(struct:expr-bvar 1))))))) (prologos::core::flip . (#(struct:expr-Pi m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-Pi m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-Pi m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-Pi mw #(struct:expr-Pi mw #(struct:expr-bvar 2) #(struct:expr-Pi mw #(struct:expr-bvar 2) #(struct:expr-bvar 2))) #(struct:expr-Pi mw #(struct:expr-bvar 2) #(struct:expr-Pi mw #(struct:expr-bvar 4) #(struct:expr-bvar 3))))))) . #(struct:expr-lam m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-lam m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-lam m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-lam mw #(struct:expr-Pi mw #(struct:expr-bvar 2) #(struct:expr-Pi mw #(struct:expr-bvar 2) #(struct:expr-bvar 2))) #(struct:expr-lam mw #(struct:expr-bvar 2) #(struct:expr-lam mw #(struct:expr-bvar 4) #(struct:expr-app #(struct:expr-app #(struct:expr-bvar 2) #(struct:expr-bvar 0)) #(struct:expr-bvar 1)))))))))) (prologos::core::id . (#(struct:expr-Pi m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-Pi mw #(struct:expr-bvar 0) #(struct:expr-bvar 1))) . #(struct:expr-lam m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-lam mw #(struct:expr-bvar 0) #(struct:expr-bvar 0))))) (prologos::core::on . (#(struct:expr-Pi m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-Pi m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-Pi m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-Pi mw #(struct:expr-Pi mw #(struct:expr-bvar 2) #(struct:expr-Pi mw #(struct:expr-bvar 3) #(struct:expr-bvar 3))) #(struct:expr-Pi mw #(struct:expr-Pi mw #(struct:expr-bvar 1) #(struct:expr-bvar 4)) #(struct:expr-Pi mw #(struct:expr-bvar 2) #(struct:expr-Pi mw #(struct:expr-bvar 3) #(struct:expr-bvar 5)))))))) . #(struct:expr-lam m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-lam m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-lam m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-lam mw #(struct:expr-Pi mw #(struct:expr-bvar 2) #(struct:expr-Pi mw #(struct:expr-bvar 3) #(struct:expr-bvar 3))) #(struct:expr-lam mw #(struct:expr-Pi mw #(struct:expr-bvar 1) #(struct:expr-bvar 4)) #(struct:expr-lam mw #(struct:expr-bvar 2) #(struct:expr-lam mw #(struct:expr-bvar 3) #(struct:expr-app #(struct:expr-app #(struct:expr-bvar 3) #(struct:expr-app #(struct:expr-bvar 2) #(struct:expr-bvar 1))) #(struct:expr-app #(struct:expr-bvar 2) #(struct:expr-bvar 0)))))))))))) (prologos::data::nat::add . (#(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Nat))) . #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-reduce #(struct:expr-bvar 0) (#(struct:expr-reduce-arm zero 0 #(struct:expr-bvar 1)) #(struct:expr-reduce-arm suc 1 #(struct:expr-suc #(struct:expr-app #(struct:expr-app #(struct:expr-fvar prologos::data::nat::add) #(struct:expr-bvar 2)) #(struct:expr-bvar 0))))) #t))))) (prologos::data::nat::bool-to-nat . (#(struct:expr-Pi mw #(struct:expr-Bool) #(struct:expr-Nat)) . #(struct:expr-lam mw #(struct:expr-Bool) #(struct:expr-reduce #(struct:expr-bvar 0) (#(struct:expr-reduce-arm true 0 #(struct:expr-nat-val 1)) #(struct:expr-reduce-arm false 0 #(struct:expr-nat-val 0))) #t)))) (prologos::data::nat::clamp . (#(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Nat)))) . #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-lam mw #(struct:expr-hole) #(struct:expr-app #(struct:expr-app #(struct:expr-fvar prologos::data::nat::max) #(struct:expr-bvar 2)) #(struct:expr-app #(struct:expr-app #(struct:expr-fvar prologos::data::nat::min) #(struct:expr-bvar 0)) #(struct:expr-bvar 1)))))))) (prologos::data::nat::double . (#(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Nat)) . #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-reduce #(struct:expr-bvar 0) (#(struct:expr-reduce-arm zero 0 #(struct:expr-nat-val 0)) #(struct:expr-reduce-arm suc 1 #(struct:expr-suc #(struct:expr-suc #(struct:expr-app #(struct:expr-fvar prologos::data::nat::double) #(struct:expr-bvar 0)))))) #t)))) (prologos::data::nat::ge? . (#(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Bool))) . #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-app #(struct:expr-app #(struct:expr-fvar prologos::data::nat::le?) #(struct:expr-bvar 0)) #(struct:expr-bvar 1)))))) (prologos::data::nat::gt? . (#(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Bool))) . #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-app #(struct:expr-app #(struct:expr-fvar prologos::data::nat::lt?) #(struct:expr-bvar 0)) #(struct:expr-bvar 1)))))) (prologos::data::nat::le? . (#(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Bool))) . #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-app #(struct:expr-fvar prologos::data::nat::zero?) #(struct:expr-app #(struct:expr-app #(struct:expr-fvar prologos::data::nat::sub) #(struct:expr-bvar 1)) #(struct:expr-bvar 0))))))) (prologos::data::nat::lt? . (#(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Bool))) . #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-reduce #(struct:expr-app #(struct:expr-app #(struct:expr-fvar prologos::data::nat::le?) #(struct:expr-bvar 0)) #(struct:expr-bvar 1)) (#(struct:expr-reduce-arm true 0 #(struct:expr-false)) #(struct:expr-reduce-arm false 0 #(struct:expr-true))) #t))))) (prologos::data::nat::max . (#(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Nat))) . #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-reduce #(struct:expr-app #(struct:expr-app #(struct:expr-fvar prologos::data::nat::le?) #(struct:expr-bvar 1)) #(struct:expr-bvar 0)) (#(struct:expr-reduce-arm true 0 #(struct:expr-bvar 0)) #(struct:expr-reduce-arm false 0 #(struct:expr-bvar 1))) #t))))) (prologos::data::nat::min . (#(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Nat))) . #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-reduce #(struct:expr-app #(struct:expr-app #(struct:expr-fvar prologos::data::nat::le?) #(struct:expr-bvar 1)) #(struct:expr-bvar 0)) (#(struct:expr-reduce-arm true 0 #(struct:expr-bvar 1)) #(struct:expr-reduce-arm false 0 #(struct:expr-bvar 0))) #t))))) (prologos::data::nat::mult . (#(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Nat))) . #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-reduce #(struct:expr-bvar 0) (#(struct:expr-reduce-arm zero 0 #(struct:expr-nat-val 0)) #(struct:expr-reduce-arm suc 1 #(struct:expr-app #(struct:expr-app #(struct:expr-fvar prologos::data::nat::add) #(struct:expr-bvar 2)) #(struct:expr-app #(struct:expr-app #(struct:expr-fvar prologos::data::nat::mult) #(struct:expr-bvar 2)) #(struct:expr-bvar 0))))) #t))))) (prologos::data::nat::nat-eq? . (#(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Bool))) . #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-reduce #(struct:expr-app #(struct:expr-app #(struct:expr-fvar prologos::data::nat::le?) #(struct:expr-bvar 1)) #(struct:expr-bvar 0)) (#(struct:expr-reduce-arm true 0 #(struct:expr-app #(struct:expr-app #(struct:expr-fvar prologos::data::nat::le?) #(struct:expr-bvar 0)) #(struct:expr-bvar 1))) #(struct:expr-reduce-arm false 0 #(struct:expr-false))) #t))))) (prologos::data::nat::pow . (#(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Nat))) . #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-reduce #(struct:expr-bvar 0) (#(struct:expr-reduce-arm zero 0 #(struct:expr-nat-val 1)) #(struct:expr-reduce-arm suc 1 #(struct:expr-app #(struct:expr-app #(struct:expr-fvar prologos::data::nat::mult) #(struct:expr-bvar 2)) #(struct:expr-app #(struct:expr-app #(struct:expr-fvar prologos::data::nat::pow) #(struct:expr-bvar 2)) #(struct:expr-bvar 0))))) #t))))) (prologos::data::nat::pred . (#(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Nat)) . #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-reduce #(struct:expr-bvar 0) (#(struct:expr-reduce-arm zero 0 #(struct:expr-nat-val 0)) #(struct:expr-reduce-arm suc 1 #(struct:expr-bvar 0))) #t)))) (prologos::data::nat::sub . (#(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Nat))) . #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-reduce #(struct:expr-bvar 0) (#(struct:expr-reduce-arm zero 0 #(struct:expr-bvar 1)) #(struct:expr-reduce-arm suc 1 #(struct:expr-app #(struct:expr-fvar prologos::data::nat::pred) #(struct:expr-app #(struct:expr-app #(struct:expr-fvar prologos::data::nat::sub) #(struct:expr-bvar 2)) #(struct:expr-bvar 0))))) #t))))) (prologos::data::nat::zero? . (#(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Bool)) . #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-reduce #(struct:expr-bvar 0) (#(struct:expr-reduce-arm zero 0 #(struct:expr-true)) #(struct:expr-reduce-arm suc 1 #(struct:expr-false))) #t)))) (sub . (#(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Nat))) . #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-reduce #(struct:expr-bvar 0) (#(struct:expr-reduce-arm zero 0 #(struct:expr-bvar 1)) #(struct:expr-reduce-arm suc 1 #(struct:expr-app #(struct:expr-fvar prologos::data::nat::pred) #(struct:expr-app #(struct:expr-app #(struct:expr-fvar prologos::data::nat::sub) #(struct:expr-bvar 2)) #(struct:expr-bvar 0))))) #t))))) (zero? . (#(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Bool)) . #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-reduce #(struct:expr-bvar 0) (#(struct:expr-reduce-arm zero 0 #(struct:expr-true)) #(struct:expr-reduce-arm suc 1 #(struct:expr-false))) #t))))) #hasheq((add . #(struct:spec-entry ((Nat Nat -> Nat)) #f #f #(struct:srcloc "" 0 0 0) () () #f #f)) (apply . #(struct:spec-entry (((A -> B) A -> B)) #f #f #(struct:srcloc "" 0 0 0) () ((A Type 0) (B Type 0)) #f #f)) (bool-to-nat . #(struct:spec-entry ((Bool -> Nat)) #f #f #(struct:srcloc "" 0 0 0) () () #f #f)) (clamp . #(struct:spec-entry ((Nat Nat -> Nat -> Nat)) #f #f #(struct:srcloc "" 0 0 0) () () #f #f)) (compose . #(struct:spec-entry (((B -> C) (A -> B) A -> C)) #f #f #(struct:srcloc "" 0 0 0) () ((B Type 0) (C Type 0) (A Type 0)) #f #f)) (const . #(struct:spec-entry ((A B -> A)) #f #f #(struct:srcloc "" 0 0 0) () ((A Type 0) (B Type 0)) #f #f)) (double . #(struct:spec-entry ((Nat -> Nat)) #f #f #(struct:srcloc "" 0 0 0) () () #f #f)) (flip . #(struct:spec-entry (((A -> B -> C) B A -> C)) #f #f #(struct:srcloc "" 0 0 0) () ((A Type 0) (B Type 0) (C Type 0)) #f #f)) (ge? . #(struct:spec-entry ((Nat Nat -> Bool)) #f #f #(struct:srcloc "" 0 0 0) () () #f #f)) (gt? . #(struct:spec-entry ((Nat Nat -> Bool)) #f #f #(struct:srcloc "" 0 0 0) () () #f #f)) (id . #(struct:spec-entry ((A -> A)) #f #f #(struct:srcloc "" 0 0 0) () ((A Type 0)) #f #f)) (le? . #(struct:spec-entry ((Nat Nat -> Bool)) #f #f #(struct:srcloc "" 0 0 0) () () #f #f)) (lt? . #(struct:spec-entry ((Nat Nat -> Bool)) #f #f #(struct:srcloc "" 0 0 0) () () #f #f)) (max . #(struct:spec-entry ((Nat Nat -> Nat)) #f #f #(struct:srcloc "" 0 0 0) () () #f #f)) (min . #(struct:spec-entry ((Nat Nat -> Nat)) #f #f #(struct:srcloc "" 0 0 0) () () #f #f)) (mult . #(struct:spec-entry ((Nat Nat -> Nat)) #f #f #(struct:srcloc "" 0 0 0) () () #f #f)) (nat-eq? . #(struct:spec-entry ((Nat Nat -> Bool)) #f #f #(struct:srcloc "" 0 0 0) () () #f #f)) (on . #(struct:spec-entry (((B -> B -> C) (A -> B) A A -> C)) #f #f #(struct:srcloc "" 0 0 0) () ((B Type 0) (C Type 0) (A Type 0)) #f #f)) (pow . #(struct:spec-entry ((Nat Nat -> Nat)) #f #f #(struct:srcloc "" 0 0 0) () () #f #f)) (pred . #(struct:spec-entry ((Nat -> Nat)) #f #f #(struct:srcloc "" 0 0 0) () () #f #f)) (sub . #(struct:spec-entry ((Nat Nat -> Nat)) #f #f #(struct:srcloc "" 0 0 0) () () #f #f)) (zero? . #(struct:spec-entry ((Nat -> Bool)) #f #f #(struct:srcloc "" 0 0 0) () () #f #f))) #hasheq((Ordering . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/data/ordering.prologos" 8 0 40)) (add . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/data/nat.prologos" 10 0 69)) (and . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/data/bool.prologos" 17 0 62)) (apply . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/core.prologos" 25 0 23)) (bool-eq . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/data/bool.prologos" 38 0 66)) (bool-to-nat . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/data/nat.prologos" 119 0 74)) (clamp . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/data/nat.prologos" 126 0 54)) (compose . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/core.prologos" 20 0 29)) (const . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/core.prologos" 15 0 20)) (double . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/data/nat.prologos" 24 0 79)) (eq-ord . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/data/ordering.prologos" 8 0 40)) (flip . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/core.prologos" 30 0 26)) (ge? . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/data/nat.prologos" 85 0 24)) (gt-ord . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/data/ordering.prologos" 8 0 40)) (gt? . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/data/nat.prologos" 80 0 24)) (id . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/core.prologos" 10 0 15)) (implies . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/data/bool.prologos" 59 0 33)) (le? . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/data/nat.prologos" 68 0 31)) (lt-ord . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/data/ordering.prologos" 8 0 40)) (lt? . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/data/nat.prologos" 73 0 73)) (max . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/data/nat.prologos" 108 0 66)) (min . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/data/nat.prologos" 101 0 66)) (mult . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/data/nat.prologos" 17 0 76)) (nand . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/data/bool.prologos" 49 0 30)) (nat-eq? . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/data/nat.prologos" 90 0 80)) (nor . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/data/bool.prologos" 54 0 28)) (not . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/data/bool.prologos" 10 0 63)) (on . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/core.prologos" 36 0 32)) (or . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/data/bool.prologos" 24 0 60)) (pow . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/data/nat.prologos" 57 0 92)) (pred . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/data/nat.prologos" 31 0 60)) (prologos::core::apply . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/core.prologos" 25 0 23)) (prologos::core::compose . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/core.prologos" 20 0 29)) (prologos::core::const . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/core.prologos" 15 0 20)) (prologos::core::flip . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/core.prologos" 30 0 26)) (prologos::core::id . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/core.prologos" 10 0 15)) (prologos::core::on . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/core.prologos" 36 0 32)) (prologos::data::bool::and . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/data/bool.prologos" 17 0 62)) (prologos::data::bool::bool-eq . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/data/bool.prologos" 38 0 66)) (prologos::data::bool::implies . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/data/bool.prologos" 59 0 33)) (prologos::data::bool::nand . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/data/bool.prologos" 49 0 30)) (prologos::data::bool::nor . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/data/bool.prologos" 54 0 28)) (prologos::data::bool::not . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/data/bool.prologos" 10 0 63)) (prologos::data::bool::or . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/data/bool.prologos" 24 0 60)) (prologos::data::bool::xor . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/data/bool.prologos" 31 0 62)) (prologos::data::nat::add . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/data/nat.prologos" 10 0 69)) (prologos::data::nat::bool-to-nat . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/data/nat.prologos" 119 0 74)) (prologos::data::nat::clamp . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/data/nat.prologos" 126 0 54)) (prologos::data::nat::double . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/data/nat.prologos" 24 0 79)) (prologos::data::nat::ge? . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/data/nat.prologos" 85 0 24)) (prologos::data::nat::gt? . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/data/nat.prologos" 80 0 24)) (prologos::data::nat::le? . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/data/nat.prologos" 68 0 31)) (prologos::data::nat::lt? . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/data/nat.prologos" 73 0 73)) (prologos::data::nat::max . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/data/nat.prologos" 108 0 66)) (prologos::data::nat::min . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/data/nat.prologos" 101 0 66)) (prologos::data::nat::mult . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/data/nat.prologos" 17 0 76)) (prologos::data::nat::nat-eq? . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/data/nat.prologos" 90 0 80)) (prologos::data::nat::pow . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/data/nat.prologos" 57 0 92)) (prologos::data::nat::pred . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/data/nat.prologos" 31 0 60)) (prologos::data::nat::sub . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/data/nat.prologos" 50 0 70)) (prologos::data::nat::zero? . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/data/nat.prologos" 38 0 65)) (prologos::data::ordering::Ordering . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/data/ordering.prologos" 8 0 40)) (prologos::data::ordering::eq-ord . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/data/ordering.prologos" 8 0 40)) (prologos::data::ordering::gt-ord . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/data/ordering.prologos" 8 0 40)) (prologos::data::ordering::lt-ord . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/data/ordering.prologos" 8 0 40)) (sub . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/data/nat.prologos" 50 0 70)) (xor . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/data/bool.prologos" 31 0 62)) (zero? . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/data/nat.prologos" 38 0 65))) (add mult double pred zero? sub pow le? lt? gt? ge? nat-eq? min max bool-to-nat clamp) "prologos::data::nat" #hasheq(($compose . expand-compose-sexp) ($list-literal . expand-list-literal) ($lseq-literal . expand-lseq-literal) ($mixfix . expand-mixfix-form) ($pipe-gt . expand-pipe-block) ($quasiquote . expand-quasiquote) ($quote . expand-quote) (cond . expand-cond) (do . expand-do) (if . expand-if) (let . expand-let) (pipe2 . #(struct:preparse-macro pipe2 (pipe2 $x $f $g) ($g ($f $x)))) (pipe3 . #(struct:preparse-macro pipe3 (pipe3 $x $f $g $h) ($h ($g ($f $x))))) (twice . #(struct:preparse-macro twice (twice $f $x) ($f ($f $x)))) (unless . #(struct:preparse-macro unless (unless $cond $body) (if $cond unit $body))) (when . #(struct:preparse-macro when (when $cond $body) (if $cond $body unit))) (with-transient . expand-with-transient)) #hasheq((eq-ord . #(struct:ctor-meta Ordering () () () 1)) (false . #(struct:ctor-meta Bool () () () 1)) (gt-ord . #(struct:ctor-meta Ordering () () () 2)) (lt-ord . #(struct:ctor-meta Ordering () () () 0)) (suc . #(struct:ctor-meta Nat () (Nat) (#t) 1)) (true . #(struct:ctor-meta Bool () () () 0)) (unit . #(struct:ctor-meta Unit () () () 0)) (zero . #(struct:ctor-meta Nat () () () 0))) #hasheq((Bool . (true false)) (Nat . (zero suc)) (Ordering . (lt-ord eq-ord gt-ord)) (Unit . (unit))) #hasheq() #hasheq(((Posit16 . Posit32) . #t) ((Nat . Int) . #t) ((Posit8 . Posit64) . #t) ((Posit8 . Posit32) . #t) ((Posit32 . Posit64) . #t) ((Nat . Rat) . #t) ((Posit8 . Posit16) . #t) ((Posit16 . Posit64) . #t) ((Int . Rat) . #t)) #hasheq() #hasheq() #hasheq() #hasheq() #hasheq() #hasheq() #hasheq() #hasheq() #hasheq((add . (x y)) (and . (a b)) (apply . (A B f x)) (bool-eq . (a b)) (bool-to-nat . (b)) (clamp . (low high)) (compose . (B C A g f x)) (const . (A B x _)) (double . (n)) (flip . (A B C f b a)) (ge? . (x y)) (gt? . (x y)) (id . (A x)) (implies . (a b)) (le? . (x y)) (lt? . (x y)) (max . (x y)) (min . (x y)) (mult . (x y)) (nand . (a b)) (nat-eq? . (x y)) (nor . (a b)) (not . (b)) (on . (B C A f g x y)) (or . (a b)) (pow . (base exp)) (pred . (n)) (sub . (x y)) (xor . (a b)) (zero? . (n))) #hasheq() #hasheq() #hasheq()) \ No newline at end of file +(1 "/home/user/prologos/racket/prologos/lib/prologos/data/nat.prologos:1776910265" #hasheq((add . (#(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Nat))) . #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-reduce #(struct:expr-bvar 0) (#(struct:expr-reduce-arm zero 0 #(struct:expr-bvar 1)) #(struct:expr-reduce-arm suc 1 #(struct:expr-suc #(struct:expr-app #(struct:expr-app #(struct:expr-fvar prologos::data::nat::add) #(struct:expr-bvar 2)) #(struct:expr-bvar 0))))) #t))))) (apply . (#(struct:expr-Pi m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-Pi m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-Pi mw #(struct:expr-Pi mw #(struct:expr-bvar 1) #(struct:expr-bvar 1)) #(struct:expr-Pi mw #(struct:expr-bvar 2) #(struct:expr-bvar 2))))) . #(struct:expr-lam m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-lam m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-lam mw #(struct:expr-Pi mw #(struct:expr-bvar 1) #(struct:expr-bvar 1)) #(struct:expr-lam mw #(struct:expr-bvar 2) #(struct:expr-app #(struct:expr-bvar 1) #(struct:expr-bvar 0)))))))) (bool-to-nat . (#(struct:expr-Pi mw #(struct:expr-Bool) #(struct:expr-Nat)) . #(struct:expr-lam mw #(struct:expr-Bool) #(struct:expr-reduce #(struct:expr-bvar 0) (#(struct:expr-reduce-arm true 0 #(struct:expr-nat-val 1)) #(struct:expr-reduce-arm false 0 #(struct:expr-nat-val 0))) #t)))) (clamp . (#(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Nat)))) . #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-lam mw #(struct:expr-hole) #(struct:expr-app #(struct:expr-app #(struct:expr-fvar prologos::data::nat::max) #(struct:expr-bvar 2)) #(struct:expr-app #(struct:expr-app #(struct:expr-fvar prologos::data::nat::min) #(struct:expr-bvar 0)) #(struct:expr-bvar 1)))))))) (compose . (#(struct:expr-Pi m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-Pi m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-Pi m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-Pi mw #(struct:expr-Pi mw #(struct:expr-bvar 2) #(struct:expr-bvar 2)) #(struct:expr-Pi mw #(struct:expr-Pi mw #(struct:expr-bvar 1) #(struct:expr-bvar 4)) #(struct:expr-Pi mw #(struct:expr-bvar 2) #(struct:expr-bvar 4))))))) . #(struct:expr-lam m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-lam m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-lam m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-lam mw #(struct:expr-Pi mw #(struct:expr-bvar 2) #(struct:expr-bvar 2)) #(struct:expr-lam mw #(struct:expr-Pi mw #(struct:expr-bvar 1) #(struct:expr-bvar 4)) #(struct:expr-lam mw #(struct:expr-bvar 2) #(struct:expr-app #(struct:expr-bvar 2) #(struct:expr-app #(struct:expr-bvar 1) #(struct:expr-bvar 0))))))))))) (const . (#(struct:expr-Pi m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-Pi m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-Pi mw #(struct:expr-bvar 1) #(struct:expr-Pi mw #(struct:expr-bvar 1) #(struct:expr-bvar 3))))) . #(struct:expr-lam m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-lam m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-lam mw #(struct:expr-bvar 1) #(struct:expr-lam mw #(struct:expr-bvar 1) #(struct:expr-bvar 1))))))) (double . (#(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Nat)) . #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-reduce #(struct:expr-bvar 0) (#(struct:expr-reduce-arm zero 0 #(struct:expr-nat-val 0)) #(struct:expr-reduce-arm suc 1 #(struct:expr-suc #(struct:expr-suc #(struct:expr-app #(struct:expr-fvar prologos::data::nat::double) #(struct:expr-bvar 0)))))) #t)))) (flip . (#(struct:expr-Pi m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-Pi m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-Pi m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-Pi mw #(struct:expr-Pi mw #(struct:expr-bvar 2) #(struct:expr-Pi mw #(struct:expr-bvar 2) #(struct:expr-bvar 2))) #(struct:expr-Pi mw #(struct:expr-bvar 2) #(struct:expr-Pi mw #(struct:expr-bvar 4) #(struct:expr-bvar 3))))))) . #(struct:expr-lam m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-lam m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-lam m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-lam mw #(struct:expr-Pi mw #(struct:expr-bvar 2) #(struct:expr-Pi mw #(struct:expr-bvar 2) #(struct:expr-bvar 2))) #(struct:expr-lam mw #(struct:expr-bvar 2) #(struct:expr-lam mw #(struct:expr-bvar 4) #(struct:expr-app #(struct:expr-app #(struct:expr-bvar 2) #(struct:expr-bvar 0)) #(struct:expr-bvar 1)))))))))) (ge? . (#(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Bool))) . #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-app #(struct:expr-app #(struct:expr-fvar prologos::data::nat::le?) #(struct:expr-bvar 0)) #(struct:expr-bvar 1)))))) (gt? . (#(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Bool))) . #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-app #(struct:expr-app #(struct:expr-fvar prologos::data::nat::lt?) #(struct:expr-bvar 0)) #(struct:expr-bvar 1)))))) (id . (#(struct:expr-Pi m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-Pi mw #(struct:expr-bvar 0) #(struct:expr-bvar 1))) . #(struct:expr-lam m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-lam mw #(struct:expr-bvar 0) #(struct:expr-bvar 0))))) (le? . (#(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Bool))) . #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-app #(struct:expr-fvar prologos::data::nat::zero?) #(struct:expr-app #(struct:expr-app #(struct:expr-fvar prologos::data::nat::sub) #(struct:expr-bvar 1)) #(struct:expr-bvar 0))))))) (lt? . (#(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Bool))) . #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-reduce #(struct:expr-app #(struct:expr-app #(struct:expr-fvar prologos::data::nat::le?) #(struct:expr-bvar 0)) #(struct:expr-bvar 1)) (#(struct:expr-reduce-arm true 0 #(struct:expr-false)) #(struct:expr-reduce-arm false 0 #(struct:expr-true))) #t))))) (max . (#(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Nat))) . #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-reduce #(struct:expr-app #(struct:expr-app #(struct:expr-fvar prologos::data::nat::le?) #(struct:expr-bvar 1)) #(struct:expr-bvar 0)) (#(struct:expr-reduce-arm true 0 #(struct:expr-bvar 0)) #(struct:expr-reduce-arm false 0 #(struct:expr-bvar 1))) #t))))) (min . (#(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Nat))) . #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-reduce #(struct:expr-app #(struct:expr-app #(struct:expr-fvar prologos::data::nat::le?) #(struct:expr-bvar 1)) #(struct:expr-bvar 0)) (#(struct:expr-reduce-arm true 0 #(struct:expr-bvar 1)) #(struct:expr-reduce-arm false 0 #(struct:expr-bvar 0))) #t))))) (mult . (#(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Nat))) . #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-reduce #(struct:expr-bvar 0) (#(struct:expr-reduce-arm zero 0 #(struct:expr-nat-val 0)) #(struct:expr-reduce-arm suc 1 #(struct:expr-app #(struct:expr-app #(struct:expr-fvar prologos::data::nat::add) #(struct:expr-bvar 2)) #(struct:expr-app #(struct:expr-app #(struct:expr-fvar prologos::data::nat::mult) #(struct:expr-bvar 2)) #(struct:expr-bvar 0))))) #t))))) (nat-eq? . (#(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Bool))) . #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-reduce #(struct:expr-app #(struct:expr-app #(struct:expr-fvar prologos::data::nat::le?) #(struct:expr-bvar 1)) #(struct:expr-bvar 0)) (#(struct:expr-reduce-arm true 0 #(struct:expr-app #(struct:expr-app #(struct:expr-fvar prologos::data::nat::le?) #(struct:expr-bvar 0)) #(struct:expr-bvar 1))) #(struct:expr-reduce-arm false 0 #(struct:expr-false))) #t))))) (on . (#(struct:expr-Pi m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-Pi m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-Pi m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-Pi mw #(struct:expr-Pi mw #(struct:expr-bvar 2) #(struct:expr-Pi mw #(struct:expr-bvar 3) #(struct:expr-bvar 3))) #(struct:expr-Pi mw #(struct:expr-Pi mw #(struct:expr-bvar 1) #(struct:expr-bvar 4)) #(struct:expr-Pi mw #(struct:expr-bvar 2) #(struct:expr-Pi mw #(struct:expr-bvar 3) #(struct:expr-bvar 5)))))))) . #(struct:expr-lam m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-lam m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-lam m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-lam mw #(struct:expr-Pi mw #(struct:expr-bvar 2) #(struct:expr-Pi mw #(struct:expr-bvar 3) #(struct:expr-bvar 3))) #(struct:expr-lam mw #(struct:expr-Pi mw #(struct:expr-bvar 1) #(struct:expr-bvar 4)) #(struct:expr-lam mw #(struct:expr-bvar 2) #(struct:expr-lam mw #(struct:expr-bvar 3) #(struct:expr-app #(struct:expr-app #(struct:expr-bvar 3) #(struct:expr-app #(struct:expr-bvar 2) #(struct:expr-bvar 1))) #(struct:expr-app #(struct:expr-bvar 2) #(struct:expr-bvar 0)))))))))))) (pow . (#(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Nat))) . #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-reduce #(struct:expr-bvar 0) (#(struct:expr-reduce-arm zero 0 #(struct:expr-nat-val 1)) #(struct:expr-reduce-arm suc 1 #(struct:expr-app #(struct:expr-app #(struct:expr-fvar prologos::data::nat::mult) #(struct:expr-bvar 2)) #(struct:expr-app #(struct:expr-app #(struct:expr-fvar prologos::data::nat::pow) #(struct:expr-bvar 2)) #(struct:expr-bvar 0))))) #t))))) (pred . (#(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Nat)) . #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-reduce #(struct:expr-bvar 0) (#(struct:expr-reduce-arm zero 0 #(struct:expr-nat-val 0)) #(struct:expr-reduce-arm suc 1 #(struct:expr-bvar 0))) #t)))) (prologos::core::apply . (#(struct:expr-Pi m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-Pi m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-Pi mw #(struct:expr-Pi mw #(struct:expr-bvar 1) #(struct:expr-bvar 1)) #(struct:expr-Pi mw #(struct:expr-bvar 2) #(struct:expr-bvar 2))))) . #(struct:expr-lam m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-lam m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-lam mw #(struct:expr-Pi mw #(struct:expr-bvar 1) #(struct:expr-bvar 1)) #(struct:expr-lam mw #(struct:expr-bvar 2) #(struct:expr-app #(struct:expr-bvar 1) #(struct:expr-bvar 0)))))))) (prologos::core::compose . (#(struct:expr-Pi m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-Pi m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-Pi m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-Pi mw #(struct:expr-Pi mw #(struct:expr-bvar 2) #(struct:expr-bvar 2)) #(struct:expr-Pi mw #(struct:expr-Pi mw #(struct:expr-bvar 1) #(struct:expr-bvar 4)) #(struct:expr-Pi mw #(struct:expr-bvar 2) #(struct:expr-bvar 4))))))) . #(struct:expr-lam m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-lam m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-lam m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-lam mw #(struct:expr-Pi mw #(struct:expr-bvar 2) #(struct:expr-bvar 2)) #(struct:expr-lam mw #(struct:expr-Pi mw #(struct:expr-bvar 1) #(struct:expr-bvar 4)) #(struct:expr-lam mw #(struct:expr-bvar 2) #(struct:expr-app #(struct:expr-bvar 2) #(struct:expr-app #(struct:expr-bvar 1) #(struct:expr-bvar 0))))))))))) (prologos::core::const . (#(struct:expr-Pi m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-Pi m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-Pi mw #(struct:expr-bvar 1) #(struct:expr-Pi mw #(struct:expr-bvar 1) #(struct:expr-bvar 3))))) . #(struct:expr-lam m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-lam m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-lam mw #(struct:expr-bvar 1) #(struct:expr-lam mw #(struct:expr-bvar 1) #(struct:expr-bvar 1))))))) (prologos::core::flip . (#(struct:expr-Pi m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-Pi m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-Pi m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-Pi mw #(struct:expr-Pi mw #(struct:expr-bvar 2) #(struct:expr-Pi mw #(struct:expr-bvar 2) #(struct:expr-bvar 2))) #(struct:expr-Pi mw #(struct:expr-bvar 2) #(struct:expr-Pi mw #(struct:expr-bvar 4) #(struct:expr-bvar 3))))))) . #(struct:expr-lam m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-lam m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-lam m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-lam mw #(struct:expr-Pi mw #(struct:expr-bvar 2) #(struct:expr-Pi mw #(struct:expr-bvar 2) #(struct:expr-bvar 2))) #(struct:expr-lam mw #(struct:expr-bvar 2) #(struct:expr-lam mw #(struct:expr-bvar 4) #(struct:expr-app #(struct:expr-app #(struct:expr-bvar 2) #(struct:expr-bvar 0)) #(struct:expr-bvar 1)))))))))) (prologos::core::id . (#(struct:expr-Pi m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-Pi mw #(struct:expr-bvar 0) #(struct:expr-bvar 1))) . #(struct:expr-lam m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-lam mw #(struct:expr-bvar 0) #(struct:expr-bvar 0))))) (prologos::core::on . (#(struct:expr-Pi m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-Pi m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-Pi m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-Pi mw #(struct:expr-Pi mw #(struct:expr-bvar 2) #(struct:expr-Pi mw #(struct:expr-bvar 3) #(struct:expr-bvar 3))) #(struct:expr-Pi mw #(struct:expr-Pi mw #(struct:expr-bvar 1) #(struct:expr-bvar 4)) #(struct:expr-Pi mw #(struct:expr-bvar 2) #(struct:expr-Pi mw #(struct:expr-bvar 3) #(struct:expr-bvar 5)))))))) . #(struct:expr-lam m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-lam m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-lam m0 #(struct:expr-Type #(struct:lzero)) #(struct:expr-lam mw #(struct:expr-Pi mw #(struct:expr-bvar 2) #(struct:expr-Pi mw #(struct:expr-bvar 3) #(struct:expr-bvar 3))) #(struct:expr-lam mw #(struct:expr-Pi mw #(struct:expr-bvar 1) #(struct:expr-bvar 4)) #(struct:expr-lam mw #(struct:expr-bvar 2) #(struct:expr-lam mw #(struct:expr-bvar 3) #(struct:expr-app #(struct:expr-app #(struct:expr-bvar 3) #(struct:expr-app #(struct:expr-bvar 2) #(struct:expr-bvar 1))) #(struct:expr-app #(struct:expr-bvar 2) #(struct:expr-bvar 0)))))))))))) (prologos::data::nat::add . (#(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Nat))) . #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-reduce #(struct:expr-bvar 0) (#(struct:expr-reduce-arm zero 0 #(struct:expr-bvar 1)) #(struct:expr-reduce-arm suc 1 #(struct:expr-suc #(struct:expr-app #(struct:expr-app #(struct:expr-fvar prologos::data::nat::add) #(struct:expr-bvar 2)) #(struct:expr-bvar 0))))) #t))))) (prologos::data::nat::bool-to-nat . (#(struct:expr-Pi mw #(struct:expr-Bool) #(struct:expr-Nat)) . #(struct:expr-lam mw #(struct:expr-Bool) #(struct:expr-reduce #(struct:expr-bvar 0) (#(struct:expr-reduce-arm true 0 #(struct:expr-nat-val 1)) #(struct:expr-reduce-arm false 0 #(struct:expr-nat-val 0))) #t)))) (prologos::data::nat::clamp . (#(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Nat)))) . #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-lam mw #(struct:expr-hole) #(struct:expr-app #(struct:expr-app #(struct:expr-fvar prologos::data::nat::max) #(struct:expr-bvar 2)) #(struct:expr-app #(struct:expr-app #(struct:expr-fvar prologos::data::nat::min) #(struct:expr-bvar 0)) #(struct:expr-bvar 1)))))))) (prologos::data::nat::double . (#(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Nat)) . #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-reduce #(struct:expr-bvar 0) (#(struct:expr-reduce-arm zero 0 #(struct:expr-nat-val 0)) #(struct:expr-reduce-arm suc 1 #(struct:expr-suc #(struct:expr-suc #(struct:expr-app #(struct:expr-fvar prologos::data::nat::double) #(struct:expr-bvar 0)))))) #t)))) (prologos::data::nat::ge? . (#(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Bool))) . #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-app #(struct:expr-app #(struct:expr-fvar prologos::data::nat::le?) #(struct:expr-bvar 0)) #(struct:expr-bvar 1)))))) (prologos::data::nat::gt? . (#(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Bool))) . #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-app #(struct:expr-app #(struct:expr-fvar prologos::data::nat::lt?) #(struct:expr-bvar 0)) #(struct:expr-bvar 1)))))) (prologos::data::nat::le? . (#(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Bool))) . #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-app #(struct:expr-fvar prologos::data::nat::zero?) #(struct:expr-app #(struct:expr-app #(struct:expr-fvar prologos::data::nat::sub) #(struct:expr-bvar 1)) #(struct:expr-bvar 0))))))) (prologos::data::nat::lt? . (#(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Bool))) . #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-reduce #(struct:expr-app #(struct:expr-app #(struct:expr-fvar prologos::data::nat::le?) #(struct:expr-bvar 0)) #(struct:expr-bvar 1)) (#(struct:expr-reduce-arm true 0 #(struct:expr-false)) #(struct:expr-reduce-arm false 0 #(struct:expr-true))) #t))))) (prologos::data::nat::max . (#(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Nat))) . #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-reduce #(struct:expr-app #(struct:expr-app #(struct:expr-fvar prologos::data::nat::le?) #(struct:expr-bvar 1)) #(struct:expr-bvar 0)) (#(struct:expr-reduce-arm true 0 #(struct:expr-bvar 0)) #(struct:expr-reduce-arm false 0 #(struct:expr-bvar 1))) #t))))) (prologos::data::nat::min . (#(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Nat))) . #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-reduce #(struct:expr-app #(struct:expr-app #(struct:expr-fvar prologos::data::nat::le?) #(struct:expr-bvar 1)) #(struct:expr-bvar 0)) (#(struct:expr-reduce-arm true 0 #(struct:expr-bvar 1)) #(struct:expr-reduce-arm false 0 #(struct:expr-bvar 0))) #t))))) (prologos::data::nat::mult . (#(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Nat))) . #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-reduce #(struct:expr-bvar 0) (#(struct:expr-reduce-arm zero 0 #(struct:expr-nat-val 0)) #(struct:expr-reduce-arm suc 1 #(struct:expr-app #(struct:expr-app #(struct:expr-fvar prologos::data::nat::add) #(struct:expr-bvar 2)) #(struct:expr-app #(struct:expr-app #(struct:expr-fvar prologos::data::nat::mult) #(struct:expr-bvar 2)) #(struct:expr-bvar 0))))) #t))))) (prologos::data::nat::nat-eq? . (#(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Bool))) . #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-reduce #(struct:expr-app #(struct:expr-app #(struct:expr-fvar prologos::data::nat::le?) #(struct:expr-bvar 1)) #(struct:expr-bvar 0)) (#(struct:expr-reduce-arm true 0 #(struct:expr-app #(struct:expr-app #(struct:expr-fvar prologos::data::nat::le?) #(struct:expr-bvar 0)) #(struct:expr-bvar 1))) #(struct:expr-reduce-arm false 0 #(struct:expr-false))) #t))))) (prologos::data::nat::pow . (#(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Nat))) . #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-reduce #(struct:expr-bvar 0) (#(struct:expr-reduce-arm zero 0 #(struct:expr-nat-val 1)) #(struct:expr-reduce-arm suc 1 #(struct:expr-app #(struct:expr-app #(struct:expr-fvar prologos::data::nat::mult) #(struct:expr-bvar 2)) #(struct:expr-app #(struct:expr-app #(struct:expr-fvar prologos::data::nat::pow) #(struct:expr-bvar 2)) #(struct:expr-bvar 0))))) #t))))) (prologos::data::nat::pred . (#(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Nat)) . #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-reduce #(struct:expr-bvar 0) (#(struct:expr-reduce-arm zero 0 #(struct:expr-nat-val 0)) #(struct:expr-reduce-arm suc 1 #(struct:expr-bvar 0))) #t)))) (prologos::data::nat::sub . (#(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Nat))) . #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-reduce #(struct:expr-bvar 0) (#(struct:expr-reduce-arm zero 0 #(struct:expr-bvar 1)) #(struct:expr-reduce-arm suc 1 #(struct:expr-app #(struct:expr-fvar prologos::data::nat::pred) #(struct:expr-app #(struct:expr-app #(struct:expr-fvar prologos::data::nat::sub) #(struct:expr-bvar 2)) #(struct:expr-bvar 0))))) #t))))) (prologos::data::nat::zero? . (#(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Bool)) . #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-reduce #(struct:expr-bvar 0) (#(struct:expr-reduce-arm zero 0 #(struct:expr-true)) #(struct:expr-reduce-arm suc 1 #(struct:expr-false))) #t)))) (sub . (#(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Nat))) . #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-reduce #(struct:expr-bvar 0) (#(struct:expr-reduce-arm zero 0 #(struct:expr-bvar 1)) #(struct:expr-reduce-arm suc 1 #(struct:expr-app #(struct:expr-fvar prologos::data::nat::pred) #(struct:expr-app #(struct:expr-app #(struct:expr-fvar prologos::data::nat::sub) #(struct:expr-bvar 2)) #(struct:expr-bvar 0))))) #t))))) (zero? . (#(struct:expr-Pi mw #(struct:expr-Nat) #(struct:expr-Bool)) . #(struct:expr-lam mw #(struct:expr-Nat) #(struct:expr-reduce #(struct:expr-bvar 0) (#(struct:expr-reduce-arm zero 0 #(struct:expr-true)) #(struct:expr-reduce-arm suc 1 #(struct:expr-false))) #t))))) #hasheq((add . #(struct:spec-entry ((Nat Nat -> Nat)) #f #f #(struct:srcloc "" 0 0 0) () () #f #f)) (apply . #(struct:spec-entry (((A -> B) A -> B)) #f #f #(struct:srcloc "" 0 0 0) () ((A Type 0) (B Type 0)) #f #f)) (bool-to-nat . #(struct:spec-entry ((Bool -> Nat)) #f #f #(struct:srcloc "" 0 0 0) () () #f #f)) (clamp . #(struct:spec-entry ((Nat Nat -> Nat -> Nat)) #f #f #(struct:srcloc "" 0 0 0) () () #f #f)) (compose . #(struct:spec-entry (((B -> C) (A -> B) A -> C)) #f #f #(struct:srcloc "" 0 0 0) () ((B Type 0) (C Type 0) (A Type 0)) #f #f)) (const . #(struct:spec-entry ((A B -> A)) #f #f #(struct:srcloc "" 0 0 0) () ((A Type 0) (B Type 0)) #f #f)) (double . #(struct:spec-entry ((Nat -> Nat)) #f #f #(struct:srcloc "" 0 0 0) () () #f #f)) (flip . #(struct:spec-entry (((A -> B -> C) B A -> C)) #f #f #(struct:srcloc "" 0 0 0) () ((A Type 0) (B Type 0) (C Type 0)) #f #f)) (ge? . #(struct:spec-entry ((Nat Nat -> Bool)) #f #f #(struct:srcloc "" 0 0 0) () () #f #f)) (gt? . #(struct:spec-entry ((Nat Nat -> Bool)) #f #f #(struct:srcloc "" 0 0 0) () () #f #f)) (id . #(struct:spec-entry ((A -> A)) #f #f #(struct:srcloc "" 0 0 0) () ((A Type 0)) #f #f)) (le? . #(struct:spec-entry ((Nat Nat -> Bool)) #f #f #(struct:srcloc "" 0 0 0) () () #f #f)) (lt? . #(struct:spec-entry ((Nat Nat -> Bool)) #f #f #(struct:srcloc "" 0 0 0) () () #f #f)) (max . #(struct:spec-entry ((Nat Nat -> Nat)) #f #f #(struct:srcloc "" 0 0 0) () () #f #f)) (min . #(struct:spec-entry ((Nat Nat -> Nat)) #f #f #(struct:srcloc "" 0 0 0) () () #f #f)) (mult . #(struct:spec-entry ((Nat Nat -> Nat)) #f #f #(struct:srcloc "" 0 0 0) () () #f #f)) (nat-eq? . #(struct:spec-entry ((Nat Nat -> Bool)) #f #f #(struct:srcloc "" 0 0 0) () () #f #f)) (on . #(struct:spec-entry (((B -> B -> C) (A -> B) A A -> C)) #f #f #(struct:srcloc "" 0 0 0) () ((B Type 0) (C Type 0) (A Type 0)) #f #f)) (pow . #(struct:spec-entry ((Nat Nat -> Nat)) #f #f #(struct:srcloc "" 0 0 0) () () #f #f)) (pred . #(struct:spec-entry ((Nat -> Nat)) #f #f #(struct:srcloc "" 0 0 0) () () #f #f)) (sub . #(struct:spec-entry ((Nat Nat -> Nat)) #f #f #(struct:srcloc "" 0 0 0) () () #f #f)) (zero? . #(struct:spec-entry ((Nat -> Bool)) #f #f #(struct:srcloc "" 0 0 0) () () #f #f))) #hasheq((add . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/data/nat.prologos" 10 0 69)) (bool-to-nat . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/data/nat.prologos" 119 0 74)) (clamp . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/data/nat.prologos" 126 0 54)) (double . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/data/nat.prologos" 24 0 79)) (ge? . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/data/nat.prologos" 85 0 24)) (gt? . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/data/nat.prologos" 80 0 24)) (le? . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/data/nat.prologos" 68 0 31)) (lt? . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/data/nat.prologos" 73 0 73)) (max . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/data/nat.prologos" 108 0 66)) (min . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/data/nat.prologos" 101 0 66)) (mult . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/data/nat.prologos" 17 0 76)) (nat-eq? . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/data/nat.prologos" 90 0 80)) (pow . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/data/nat.prologos" 57 0 92)) (pred . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/data/nat.prologos" 31 0 60)) (prologos::data::nat::add . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/data/nat.prologos" 10 0 69)) (prologos::data::nat::bool-to-nat . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/data/nat.prologos" 119 0 74)) (prologos::data::nat::clamp . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/data/nat.prologos" 126 0 54)) (prologos::data::nat::double . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/data/nat.prologos" 24 0 79)) (prologos::data::nat::ge? . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/data/nat.prologos" 85 0 24)) (prologos::data::nat::gt? . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/data/nat.prologos" 80 0 24)) (prologos::data::nat::le? . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/data/nat.prologos" 68 0 31)) (prologos::data::nat::lt? . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/data/nat.prologos" 73 0 73)) (prologos::data::nat::max . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/data/nat.prologos" 108 0 66)) (prologos::data::nat::min . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/data/nat.prologos" 101 0 66)) (prologos::data::nat::mult . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/data/nat.prologos" 17 0 76)) (prologos::data::nat::nat-eq? . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/data/nat.prologos" 90 0 80)) (prologos::data::nat::pow . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/data/nat.prologos" 57 0 92)) (prologos::data::nat::pred . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/data/nat.prologos" 31 0 60)) (prologos::data::nat::sub . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/data/nat.prologos" 50 0 70)) (prologos::data::nat::zero? . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/data/nat.prologos" 38 0 65)) (sub . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/data/nat.prologos" 50 0 70)) (zero? . #(struct:srcloc "/home/user/prologos/racket/prologos/lib/prologos/data/nat.prologos" 38 0 65))) (add mult double pred zero? sub pow le? lt? gt? ge? nat-eq? min max bool-to-nat clamp) "prologos::data::nat" #hasheq(($compose . expand-compose-sexp) ($list-literal . expand-list-literal) ($lseq-literal . expand-lseq-literal) ($mixfix . expand-mixfix-form) ($pipe-gt . expand-pipe-block) ($quasiquote . expand-quasiquote) ($quote . expand-quote) (cond . expand-cond) (do . expand-do) (if . expand-if) (let . expand-let) (pipe2 . #(struct:preparse-macro pipe2 (pipe2 $x $f $g) ($g ($f $x)))) (pipe3 . #(struct:preparse-macro pipe3 (pipe3 $x $f $g $h) ($h ($g ($f $x))))) (twice . #(struct:preparse-macro twice (twice $f $x) ($f ($f $x)))) (unless . #(struct:preparse-macro unless (unless $cond $body) (if $cond unit $body))) (when . #(struct:preparse-macro when (when $cond $body) (if $cond $body unit))) (with-transient . expand-with-transient)) #hasheq((eq-ord . #(struct:ctor-meta Ordering () () () 1)) (false . #(struct:ctor-meta Bool () () () 1)) (gt-ord . #(struct:ctor-meta Ordering () () () 2)) (lt-ord . #(struct:ctor-meta Ordering () () () 0)) (suc . #(struct:ctor-meta Nat () (Nat) (#t) 1)) (true . #(struct:ctor-meta Bool () () () 0)) (unit . #(struct:ctor-meta Unit () () () 0)) (zero . #(struct:ctor-meta Nat () () () 0))) #hasheq((Bool . (true false)) (Nat . (zero suc)) (Ordering . (lt-ord eq-ord gt-ord)) (Unit . (unit))) #hasheq() #hasheq(((Posit16 . Posit64) . #t) ((Int . Rat) . #t) ((Posit16 . Posit32) . #t) ((Nat . Int) . #t) ((Posit8 . Posit64) . #t) ((Posit8 . Posit32) . #t) ((Posit32 . Posit64) . #t) ((Nat . Rat) . #t) ((Posit8 . Posit16) . #t)) #hasheq() #hasheq() #hasheq() #hasheq() #hasheq() #hasheq() #hasheq() #hasheq() #hasheq((add . (x y)) (and . (a b)) (apply . (A B f x)) (bool-eq . (a b)) (bool-to-nat . (b)) (clamp . (low high)) (compose . (B C A g f x)) (const . (A B x _)) (double . (n)) (flip . (A B C f b a)) (ge? . (x y)) (gt? . (x y)) (id . (A x)) (implies . (a b)) (le? . (x y)) (lt? . (x y)) (max . (x y)) (min . (x y)) (mult . (x y)) (nand . (a b)) (nat-eq? . (x y)) (nor . (a b)) (not . (b)) (on . (B C A f g x y)) (or . (a b)) (pow . (base exp)) (pred . (n)) (sub . (x y)) (xor . (a b)) (zero? . (n))) #hasheq() #hasheq() #hasheq()) \ No newline at end of file diff --git a/racket/prologos/examples/eigentrust-posit.prologos b/racket/prologos/examples/eigentrust-posit.prologos new file mode 100644 index 000000000..1f2175bf0 --- /dev/null +++ b/racket/prologos/examples/eigentrust-posit.prologos @@ -0,0 +1,139 @@ +ns examples.eigentrust-posit + +;; ======================================================================== +;; +;; E I G E N T R U S T ( L I S T + P O S I T 3 2 ) +;; ------------------------------------------------------ +;; +;; Column-stochastic convention (see examples/eigentrust.prologos): +;; t_{k+1} = (1 - alpha) * M * t_k + alpha * p +;; where M is column-stochastic (every column sums to 1) and p is +;; a probability distribution. +;; +;; This variant swaps Rat for Posit32 (hardware floating point). +;; Posit32 is inexact, so the column-sum check uses exact equality +;; to `~1.0` — small round-off in the matrix literals would fail it. +;; For the fixtures here every entry is representable exactly +;; (powers of 2 or 0), so the check succeeds. +;; +;; ======================================================================== + + +spec dot [List Posit32] [List Posit32] -> Posit32 +defn dot [xs ys] + match xs + | nil -> ~0.0 + | cons x as -> match ys + | nil -> ~0.0 + | cons y bs -> p32+ [p32* x y] [dot as bs] + +spec vec-sum [List Posit32] -> Posit32 +defn vec-sum [xs] + match xs + | nil -> ~0.0 + | cons x as -> p32+ x [vec-sum as] + +spec scale-vec Posit32 [List Posit32] -> [List Posit32] +defn scale-vec [s xs] + match xs + | nil -> nil + | cons x as -> cons [p32* s x] [scale-vec s as] + +spec add-vec [List Posit32] [List Posit32] -> [List Posit32] +defn add-vec [xs ys] + match xs + | nil -> nil + | cons x as -> match ys + | nil -> nil + | cons y bs -> cons [p32+ x y] [add-vec as bs] + +spec sub-vec [List Posit32] [List Posit32] -> [List Posit32] +defn sub-vec [xs ys] + match xs + | nil -> nil + | cons x as -> match ys + | nil -> nil + | cons y bs -> cons [p32- x y] [sub-vec as bs] + +spec p32-max Posit32 Posit32 -> Posit32 +defn p32-max [a b] + match [p32-lt a b] + | true -> b + | false -> a + +spec linf-norm [List Posit32] -> Posit32 +defn linf-norm [xs] + match xs + | nil -> ~0.0 + | cons x as -> p32-max [p32-abs x] [linf-norm as] + +spec mat-vec-mul [List [List Posit32]] [List Posit32] -> [List Posit32] +defn mat-vec-mul [m t] + match m + | nil -> nil + | cons r rs -> cons [dot r t] [mat-vec-mul rs t] + + +;; Column-stochastic invariant check. +spec col-sums [List [List Posit32]] -> [List Posit32] +defn col-sums [m] + match m + | nil -> nil + | cons r rest -> match rest + | nil -> r + | cons _ _ -> add-vec r [col-sums rest] + +spec all-ones? [List Posit32] -> Bool +defn all-ones? [xs] + match xs + | nil -> true + | cons x as -> match [p32-eq x ~1.0] + | false -> false + | true -> all-ones? as + +spec col-stochastic? [List [List Posit32]] -> Bool +defn col-stochastic? [m] + all-ones? [col-sums m] + + +spec eigentrust-step [List [List Posit32]] [List Posit32] Posit32 [List Posit32] -> [List Posit32] +defn eigentrust-step [m p alpha t] + add-vec [scale-vec [p32- ~1.0 alpha] [mat-vec-mul m t]] [scale-vec alpha p] + +spec eigentrust-iterate [List [List Posit32]] [List Posit32] Posit32 Posit32 Int [List Posit32] [List Posit32] -> [List Posit32] +defn eigentrust-iterate [m p alpha eps budget t tnew] + match [int-le budget 0] + | true -> tnew + | false -> match [p32-lt [linf-norm [sub-vec tnew t]] eps] + | true -> tnew + | false -> eigentrust-iterate m p alpha eps [int- budget 1] tnew [eigentrust-step m p alpha tnew] + +spec eigentrust [List [List Posit32]] [List Posit32] Posit32 Posit32 Int -> [List Posit32] +defn eigentrust [m p alpha eps max-iter] + match [col-stochastic? m] + | false -> (the [List Posit32] [panic "eigentrust: M must be column-stochastic"]) + | true -> eigentrust-iterate m p alpha eps max-iter p [eigentrust-step m p alpha p] + + +;; ======================================================================== +;; Worked examples +;; ======================================================================== + +def m-uniform-4 : [List [List Posit32]] := '['[~0.25 ~0.25 ~0.25 ~0.25] '[~0.25 ~0.25 ~0.25 ~0.25] '[~0.25 ~0.25 ~0.25 ~0.25] '[~0.25 ~0.25 ~0.25 ~0.25]] +def p-uniform-4 : [List Posit32] := '[~0.25 ~0.25 ~0.25 ~0.25] + +[col-stochastic? m-uniform-4] +[eigentrust m-uniform-4 p-uniform-4 ~0.1 ~0.001 50] + +def m-others-3 : [List [List Posit32]] := '['[~0.0 ~0.5 ~0.5] '[~0.5 ~0.0 ~0.5] '[~0.5 ~0.5 ~0.0]] +def p-uniform-3 : [List Posit32] := '[~0.33333 ~0.33333 ~0.33333] + +[col-stochastic? m-others-3] +[eigentrust m-others-3 p-uniform-3 ~0.1 ~0.001 50] + +;; Ring (already column-stochastic: each column has a single 1). +def m-ring-4 : [List [List Posit32]] := '['[~0.0 ~0.0 ~0.0 ~1.0] '[~1.0 ~0.0 ~0.0 ~0.0] '[~0.0 ~1.0 ~0.0 ~0.0] '[~0.0 ~0.0 ~1.0 ~0.0]] +def p-seed-0 : [List Posit32] := '[~1.0 ~0.0 ~0.0 ~0.0] + +[col-stochastic? m-ring-4] +[eigentrust m-ring-4 p-seed-0 ~0.3 ~0.001 3] diff --git a/racket/prologos/examples/eigentrust-pvec-posit.prologos b/racket/prologos/examples/eigentrust-pvec-posit.prologos new file mode 100644 index 000000000..b6fc95b28 --- /dev/null +++ b/racket/prologos/examples/eigentrust-pvec-posit.prologos @@ -0,0 +1,174 @@ +ns examples.eigentrust-pvec-posit + +;; ======================================================================== +;; +;; E I G E N T R U S T ( P V E C + P O S I T 3 2 ) +;; ------------------------------------------------------ +;; +;; Column-stochastic convention; see examples/eigentrust.prologos. +;; PVec + hardware-posit combination. +;; +;; ======================================================================== + + +spec dot-go Nat Nat [PVec Posit32] [PVec Posit32] Posit32 -> Posit32 +defn dot-go [i n xs ys acc] + match [nat-eq? i n] + | true -> acc + | false -> dot-go [suc i] n xs ys [p32+ acc [p32* [pvec-nth xs i] [pvec-nth ys i]]] + +spec dot [PVec Posit32] [PVec Posit32] -> Posit32 +defn dot [xs ys] + dot-go zero [pvec-length xs] xs ys ~0.0 + + +spec vec-sum-go Nat Nat [PVec Posit32] Posit32 -> Posit32 +defn vec-sum-go [i n xs acc] + match [nat-eq? i n] + | true -> acc + | false -> vec-sum-go [suc i] n xs [p32+ acc [pvec-nth xs i]] + +spec vec-sum [PVec Posit32] -> Posit32 +defn vec-sum [xs] + vec-sum-go zero [pvec-length xs] xs ~0.0 + + +spec scale-vec-go Nat Nat Posit32 [PVec Posit32] [PVec Posit32] -> [PVec Posit32] +defn scale-vec-go [i n s xs acc] + match [nat-eq? i n] + | true -> acc + | false -> scale-vec-go [suc i] n s xs [pvec-push acc [p32* s [pvec-nth xs i]]] + +spec scale-vec Posit32 [PVec Posit32] -> [PVec Posit32] +defn scale-vec [s xs] + scale-vec-go zero [pvec-length xs] s xs (pvec-empty Posit32) + +spec add-vec-go Nat Nat [PVec Posit32] [PVec Posit32] [PVec Posit32] -> [PVec Posit32] +defn add-vec-go [i n xs ys acc] + match [nat-eq? i n] + | true -> acc + | false -> add-vec-go [suc i] n xs ys [pvec-push acc [p32+ [pvec-nth xs i] [pvec-nth ys i]]] + +spec add-vec [PVec Posit32] [PVec Posit32] -> [PVec Posit32] +defn add-vec [xs ys] + add-vec-go zero [pvec-length xs] xs ys (pvec-empty Posit32) + +spec sub-vec-go Nat Nat [PVec Posit32] [PVec Posit32] [PVec Posit32] -> [PVec Posit32] +defn sub-vec-go [i n xs ys acc] + match [nat-eq? i n] + | true -> acc + | false -> sub-vec-go [suc i] n xs ys [pvec-push acc [p32- [pvec-nth xs i] [pvec-nth ys i]]] + +spec sub-vec [PVec Posit32] [PVec Posit32] -> [PVec Posit32] +defn sub-vec [xs ys] + sub-vec-go zero [pvec-length xs] xs ys (pvec-empty Posit32) + +spec p32-max Posit32 Posit32 -> Posit32 +defn p32-max [a b] + match [p32-lt a b] + | true -> b + | false -> a + +spec linf-norm-go Nat Nat [PVec Posit32] Posit32 -> Posit32 +defn linf-norm-go [i n xs acc] + match [nat-eq? i n] + | true -> acc + | false -> linf-norm-go [suc i] n xs [p32-max acc [p32-abs [pvec-nth xs i]]] + +spec linf-norm [PVec Posit32] -> Posit32 +defn linf-norm [xs] + linf-norm-go zero [pvec-length xs] xs ~0.0 + + +spec mat-vec-mul-go Nat Nat [PVec [PVec Posit32]] [PVec Posit32] [PVec Posit32] -> [PVec Posit32] +defn mat-vec-mul-go [i n m t acc] + match [nat-eq? i n] + | true -> acc + | false -> mat-vec-mul-go [suc i] n m t [pvec-push acc [dot [pvec-nth m i] t]] + +spec mat-vec-mul [PVec [PVec Posit32]] [PVec Posit32] -> [PVec Posit32] +defn mat-vec-mul [m t] + mat-vec-mul-go zero [pvec-length m] m t (pvec-empty Posit32) + + +;; --- Column-stochastic invariant check --- + +spec col-sums-fold Nat Nat [PVec [PVec Posit32]] [PVec Posit32] -> [PVec Posit32] +defn col-sums-fold [i n m acc] + match [nat-eq? i n] + | true -> acc + | false -> col-sums-fold [suc i] n m [add-vec acc [pvec-nth m i]] + +spec zeros-go Nat Nat [PVec Posit32] -> [PVec Posit32] +defn zeros-go [i n acc] + match [nat-eq? i n] + | true -> acc + | false -> zeros-go [suc i] n [pvec-push acc ~0.0] + +spec zeros Nat -> [PVec Posit32] +defn zeros [n] + zeros-go zero n (pvec-empty Posit32) + +spec col-sums [PVec [PVec Posit32]] -> [PVec Posit32] +defn col-sums [m] + match [nat-eq? [pvec-length m] zero] + | true -> (pvec-empty Posit32) + | false -> col-sums-fold zero [pvec-length m] m [zeros [pvec-length [pvec-nth m zero]]] + +spec all-ones?-go Nat Nat [PVec Posit32] -> Bool +defn all-ones?-go [i n xs] + match [nat-eq? i n] + | true -> true + | false -> match [p32-eq [pvec-nth xs i] ~1.0] + | false -> false + | true -> all-ones?-go [suc i] n xs + +spec all-ones? [PVec Posit32] -> Bool +defn all-ones? [xs] + all-ones?-go zero [pvec-length xs] xs + +spec col-stochastic? [PVec [PVec Posit32]] -> Bool +defn col-stochastic? [m] + all-ones? [col-sums m] + + +spec eigentrust-step [PVec [PVec Posit32]] [PVec Posit32] Posit32 [PVec Posit32] -> [PVec Posit32] +defn eigentrust-step [m p alpha t] + add-vec [scale-vec [p32- ~1.0 alpha] [mat-vec-mul m t]] [scale-vec alpha p] + +spec eigentrust-iterate [PVec [PVec Posit32]] [PVec Posit32] Posit32 Posit32 Int [PVec Posit32] [PVec Posit32] -> [PVec Posit32] +defn eigentrust-iterate [m p alpha eps budget t tnew] + match [int-le budget 0] + | true -> tnew + | false -> match [p32-lt [linf-norm [sub-vec tnew t]] eps] + | true -> tnew + | false -> eigentrust-iterate m p alpha eps [int- budget 1] tnew [eigentrust-step m p alpha tnew] + +spec eigentrust [PVec [PVec Posit32]] [PVec Posit32] Posit32 Posit32 Int -> [PVec Posit32] +defn eigentrust [m p alpha eps max-iter] + match [col-stochastic? m] + | false -> (the [PVec Posit32] [panic "eigentrust: M must be column-stochastic"]) + | true -> eigentrust-iterate m p alpha eps max-iter p [eigentrust-step m p alpha p] + + +;; ======================================================================== +;; Worked examples +;; ======================================================================== + +def m-uniform-4 : [PVec [PVec Posit32]] := @[@[~0.25 ~0.25 ~0.25 ~0.25] @[~0.25 ~0.25 ~0.25 ~0.25] @[~0.25 ~0.25 ~0.25 ~0.25] @[~0.25 ~0.25 ~0.25 ~0.25]] +def p-uniform-4 : [PVec Posit32] := @[~0.25 ~0.25 ~0.25 ~0.25] + +[col-stochastic? m-uniform-4] +[eigentrust m-uniform-4 p-uniform-4 ~0.1 ~0.001 50] + +def m-others-3 : [PVec [PVec Posit32]] := @[@[~0.0 ~0.5 ~0.5] @[~0.5 ~0.0 ~0.5] @[~0.5 ~0.5 ~0.0]] +def p-uniform-3 : [PVec Posit32] := @[~0.33333 ~0.33333 ~0.33333] + +[col-stochastic? m-others-3] +[eigentrust m-others-3 p-uniform-3 ~0.1 ~0.001 50] + +def m-ring-4 : [PVec [PVec Posit32]] := @[@[~0.0 ~0.0 ~0.0 ~1.0] @[~1.0 ~0.0 ~0.0 ~0.0] @[~0.0 ~1.0 ~0.0 ~0.0] @[~0.0 ~0.0 ~1.0 ~0.0]] +def p-seed-0 : [PVec Posit32] := @[~1.0 ~0.0 ~0.0 ~0.0] + +[col-stochastic? m-ring-4] +[eigentrust m-ring-4 p-seed-0 ~0.3 ~0.001 3] diff --git a/racket/prologos/examples/eigentrust-pvec.prologos b/racket/prologos/examples/eigentrust-pvec.prologos new file mode 100644 index 000000000..28b48c6d2 --- /dev/null +++ b/racket/prologos/examples/eigentrust-pvec.prologos @@ -0,0 +1,190 @@ +ns examples.eigentrust-pvec + +;; ======================================================================== +;; +;; E I G E N T R U S T ( P V E C + R A T ) +;; ----------------------------------------------- +;; +;; Column-stochastic convention (see examples/eigentrust.prologos). +;; This variant uses PVec instead of List. PVec indexing is Nat-only, +;; so every helper carries an `i : Nat` counter and builds its result +;; by `pvec-push` onto an accumulator. +;; +;; ======================================================================== + + +;; --- Dot product --- + +spec dot-go Nat Nat [PVec Rat] [PVec Rat] Rat -> Rat +defn dot-go [i n xs ys acc] + match [nat-eq? i n] + | true -> acc + | false -> dot-go [suc i] n xs ys [rat+ acc [rat* [pvec-nth xs i] [pvec-nth ys i]]] + +spec dot [PVec Rat] [PVec Rat] -> Rat +defn dot [xs ys] + dot-go zero [pvec-length xs] xs ys 0/1 + + +;; --- Sum of a vector --- + +spec vec-sum-go Nat Nat [PVec Rat] Rat -> Rat +defn vec-sum-go [i n xs acc] + match [nat-eq? i n] + | true -> acc + | false -> vec-sum-go [suc i] n xs [rat+ acc [pvec-nth xs i]] + +spec vec-sum [PVec Rat] -> Rat +defn vec-sum [xs] + vec-sum-go zero [pvec-length xs] xs 0/1 + + +;; --- Elementwise operations --- + +spec scale-vec-go Nat Nat Rat [PVec Rat] [PVec Rat] -> [PVec Rat] +defn scale-vec-go [i n s xs acc] + match [nat-eq? i n] + | true -> acc + | false -> scale-vec-go [suc i] n s xs [pvec-push acc [rat* s [pvec-nth xs i]]] + +spec scale-vec Rat [PVec Rat] -> [PVec Rat] +defn scale-vec [s xs] + scale-vec-go zero [pvec-length xs] s xs (pvec-empty Rat) + +spec add-vec-go Nat Nat [PVec Rat] [PVec Rat] [PVec Rat] -> [PVec Rat] +defn add-vec-go [i n xs ys acc] + match [nat-eq? i n] + | true -> acc + | false -> add-vec-go [suc i] n xs ys [pvec-push acc [rat+ [pvec-nth xs i] [pvec-nth ys i]]] + +spec add-vec [PVec Rat] [PVec Rat] -> [PVec Rat] +defn add-vec [xs ys] + add-vec-go zero [pvec-length xs] xs ys (pvec-empty Rat) + +spec sub-vec-go Nat Nat [PVec Rat] [PVec Rat] [PVec Rat] -> [PVec Rat] +defn sub-vec-go [i n xs ys acc] + match [nat-eq? i n] + | true -> acc + | false -> sub-vec-go [suc i] n xs ys [pvec-push acc [rat- [pvec-nth xs i] [pvec-nth ys i]]] + +spec sub-vec [PVec Rat] [PVec Rat] -> [PVec Rat] +defn sub-vec [xs ys] + sub-vec-go zero [pvec-length xs] xs ys (pvec-empty Rat) + +spec rat-max Rat Rat -> Rat +defn rat-max [a b] + match [rat-lt a b] + | true -> b + | false -> a + +spec linf-norm-go Nat Nat [PVec Rat] Rat -> Rat +defn linf-norm-go [i n xs acc] + match [nat-eq? i n] + | true -> acc + | false -> linf-norm-go [suc i] n xs [rat-max acc [rat-abs [pvec-nth xs i]]] + +spec linf-norm [PVec Rat] -> Rat +defn linf-norm [xs] + linf-norm-go zero [pvec-length xs] xs 0/1 + + +;; --- Matrix-vector multiply: (M * t)[i] = dot(M[i], t) --- + +spec mat-vec-mul-go Nat Nat [PVec [PVec Rat]] [PVec Rat] [PVec Rat] -> [PVec Rat] +defn mat-vec-mul-go [i n m t acc] + match [nat-eq? i n] + | true -> acc + | false -> mat-vec-mul-go [suc i] n m t [pvec-push acc [dot [pvec-nth m i] t]] + +spec mat-vec-mul [PVec [PVec Rat]] [PVec Rat] -> [PVec Rat] +defn mat-vec-mul [m t] + mat-vec-mul-go zero [pvec-length m] m t (pvec-empty Rat) + + +;; --- Column-stochastic invariant check --- +;; col-sums = for each column j, sum over rows i of M[i][j]. +;; We build a result of length n (= row length) by accumulating across +;; rows. + +spec col-sums-fold Nat Nat [PVec [PVec Rat]] [PVec Rat] -> [PVec Rat] +defn col-sums-fold [i n m acc] + match [nat-eq? i n] + | true -> acc + | false -> col-sums-fold [suc i] n m [add-vec acc [pvec-nth m i]] + +;; Returns a PVec Rat of length (pvec-length of a row) with zero seeds. +spec zeros-go Nat Nat [PVec Rat] -> [PVec Rat] +defn zeros-go [i n acc] + match [nat-eq? i n] + | true -> acc + | false -> zeros-go [suc i] n [pvec-push acc 0/1] + +spec zeros Nat -> [PVec Rat] +defn zeros [n] + zeros-go zero n (pvec-empty Rat) + +spec col-sums [PVec [PVec Rat]] -> [PVec Rat] +defn col-sums [m] + match [nat-eq? [pvec-length m] zero] + | true -> (pvec-empty Rat) + | false -> col-sums-fold zero [pvec-length m] m [zeros [pvec-length [pvec-nth m zero]]] + +spec all-ones?-go Nat Nat [PVec Rat] -> Bool +defn all-ones?-go [i n xs] + match [nat-eq? i n] + | true -> true + | false -> match [rat-eq [pvec-nth xs i] 1/1] + | false -> false + | true -> all-ones?-go [suc i] n xs + +spec all-ones? [PVec Rat] -> Bool +defn all-ones? [xs] + all-ones?-go zero [pvec-length xs] xs + +spec col-stochastic? [PVec [PVec Rat]] -> Bool +defn col-stochastic? [m] + all-ones? [col-sums m] + + +;; --- Power iteration --- + +spec eigentrust-step [PVec [PVec Rat]] [PVec Rat] Rat [PVec Rat] -> [PVec Rat] +defn eigentrust-step [m p alpha t] + add-vec [scale-vec [rat- 1/1 alpha] [mat-vec-mul m t]] [scale-vec alpha p] + +spec eigentrust-iterate [PVec [PVec Rat]] [PVec Rat] Rat Rat Int [PVec Rat] [PVec Rat] -> [PVec Rat] +defn eigentrust-iterate [m p alpha eps budget t tnew] + match [int-le budget 0] + | true -> tnew + | false -> match [rat-lt [linf-norm [sub-vec tnew t]] eps] + | true -> tnew + | false -> eigentrust-iterate m p alpha eps [int- budget 1] tnew [eigentrust-step m p alpha tnew] + +spec eigentrust [PVec [PVec Rat]] [PVec Rat] Rat Rat Int -> [PVec Rat] +defn eigentrust [m p alpha eps max-iter] + match [col-stochastic? m] + | false -> (the [PVec Rat] [panic "eigentrust: M must be column-stochastic"]) + | true -> eigentrust-iterate m p alpha eps max-iter p [eigentrust-step m p alpha p] + + +;; ======================================================================== +;; Worked examples +;; ======================================================================== + +def m-uniform-4 : [PVec [PVec Rat]] := @[@[1/4 1/4 1/4 1/4] @[1/4 1/4 1/4 1/4] @[1/4 1/4 1/4 1/4] @[1/4 1/4 1/4 1/4]] +def p-uniform-4 : [PVec Rat] := @[1/4 1/4 1/4 1/4] + +[col-stochastic? m-uniform-4] +[eigentrust m-uniform-4 p-uniform-4 1/10 1/1000 50] + +def m-others-3 : [PVec [PVec Rat]] := @[@[0/1 1/2 1/2] @[1/2 0/1 1/2] @[1/2 1/2 0/1]] +def p-uniform-3 : [PVec Rat] := @[1/3 1/3 1/3] + +[col-stochastic? m-others-3] +[eigentrust m-others-3 p-uniform-3 1/10 1/1000 50] + +def m-ring-4 : [PVec [PVec Rat]] := @[@[0/1 0/1 0/1 1/1] @[1/1 0/1 0/1 0/1] @[0/1 1/1 0/1 0/1] @[0/1 0/1 1/1 0/1]] +def p-seed-0 : [PVec Rat] := @[1/1 0/1 0/1 0/1] + +[col-stochastic? m-ring-4] +[eigentrust m-ring-4 p-seed-0 3/10 1/1000 3] diff --git a/racket/prologos/examples/eigentrust.prologos b/racket/prologos/examples/eigentrust.prologos new file mode 100644 index 000000000..181d7db53 --- /dev/null +++ b/racket/prologos/examples/eigentrust.prologos @@ -0,0 +1,285 @@ +ns examples.eigentrust + +;; ======================================================================== +;; +;; E I G E N T R U S T I N P R O L O G O S +;; -------------------------------------------- +;; +;; Kamvar, Schlosser, Garcia-Molina (2003): +;; "The EigenTrust Algorithm for Reputation Management in P2P Networks" +;; +;; EigenTrust computes a global trust vector t over n peers from a +;; trust-flow matrix M (column-stochastic: each column sums to 1). +;; M[i][j] is the fraction of peer j's outgoing trust that flows TO +;; peer i. A pre-trust vector p anchors the computation. +;; +;; The update is a damped power iteration: +;; +;; t_{k+1} = (1 - alpha) * M * t_k + alpha * p +;; +;; (Equivalently, if C is the row-stochastic "i trusts j" matrix, +;; M = C^T. This file works directly with the column-stochastic M +;; so the iteration is a plain matrix-vector multiply.) +;; +;; Invariants (enforceable via `col-stochastic?`): +;; - Every column of M sums to 1. Rows have no such requirement. +;; - p is a probability distribution over peers: non-negative, sums to 1. +;; +;; Implementation notes: +;; - Uses exact Rat arithmetic (no float rounding). +;; - Matrix is [List [List Rat]], row-major. Row i of M is the +;; incoming-trust vector for peer i. +;; - `mat-vec-mul M t` is the standard matrix-vector multiply: +;; (M*t)[i] = dot(M[i], t). +;; - Convergence: L-infinity norm of (t_{k+1} - t_k) < epsilon. +;; - All vector/matrix helpers use explicit structural recursion +;; (no closures over map/zip-with) to stay inside QTT linearity. +;; +;; ======================================================================== + + +;; ============================================================ +;; Vector primitives (explicit recursion over [List Rat]) +;; ============================================================ + +;; Dot product of two Rat vectors. Truncates to the shorter input. +spec dot [List Rat] [List Rat] -> Rat +defn dot [xs ys] + match xs + | nil -> 0/1 + | cons x as -> match ys + | nil -> 0/1 + | cons y bs -> rat+ [rat* x y] [dot as bs] + +;; Sum of a vector's entries — useful for the column-stochastic check. +spec vec-sum [List Rat] -> Rat +defn vec-sum [xs] + match xs + | nil -> 0/1 + | cons x as -> rat+ x [vec-sum as] + +;; Scale every element of a vector by a Rat scalar. +spec scale-vec Rat [List Rat] -> [List Rat] +defn scale-vec [s xs] + match xs + | nil -> nil + | cons x as -> cons [rat* s x] [scale-vec s as] + +;; Elementwise addition of two Rat vectors. +spec add-vec [List Rat] [List Rat] -> [List Rat] +defn add-vec [xs ys] + match xs + | nil -> nil + | cons x as -> match ys + | nil -> nil + | cons y bs -> cons [rat+ x y] [add-vec as bs] + +;; Elementwise subtraction. +spec sub-vec [List Rat] [List Rat] -> [List Rat] +defn sub-vec [xs ys] + match xs + | nil -> nil + | cons x as -> match ys + | nil -> nil + | cons y bs -> cons [rat- x y] [sub-vec as bs] + +;; Pointwise max of two Rats. +spec rat-max Rat Rat -> Rat +defn rat-max [a b] + match [rat-lt a b] + | true -> b + | false -> a + +;; L-infinity norm (max absolute value). Seeded at 0/1. +spec linf-norm [List Rat] -> Rat +defn linf-norm [xs] + match xs + | nil -> 0/1 + | cons x as -> rat-max [rat-abs x] [linf-norm as] + + +;; ============================================================ +;; Standard matrix-vector multiply: (M * t)[i] = dot(M[i], t) +;; ============================================================ + +spec mat-vec-mul [List [List Rat]] [List Rat] -> [List Rat] +defn mat-vec-mul [m t] + match m + | nil -> nil + | cons r rs -> cons [dot r t] [mat-vec-mul rs t] + + +;; ============================================================ +;; Column-stochastic invariant check. +;; +;; A matrix M is column-stochastic iff for every column j, +;; sum_i M[i][j] = 1. Equivalently, M^T * 1 = 1 (where 1 is the +;; all-ones vector). We compute M^T * 1 via one pass and compare +;; every entry to 1/1. +;; +;; M^T * 1: for each column j, sum over i of M[i][j]. This is +;; "sum of rows": add-vec-fold across rows of M. +;; ============================================================ + +;; Elementwise sum of all rows of a matrix. +;; Returns nil on the empty matrix (0-dimensional has no columns). +spec col-sums [List [List Rat]] -> [List Rat] +defn col-sums [m] + match m + | nil -> nil + | cons r rest -> match rest + | nil -> r + | cons _ _ -> add-vec r [col-sums rest] + +;; All entries of a vector equal to 1/1? +spec all-ones? [List Rat] -> Bool +defn all-ones? [xs] + match xs + | nil -> true + | cons x as -> match [rat-eq x 1/1] + | false -> false + | true -> all-ones? as + +;; Main predicate: does every column of M sum to 1? +spec col-stochastic? [List [List Rat]] -> Bool +defn col-stochastic? [m] + all-ones? [col-sums m] + + +;; ============================================================ +;; Damped EigenTrust update: +;; t_new = (1 - alpha) * M * t + alpha * p +;; ============================================================ + +spec eigentrust-step [List [List Rat]] [List Rat] Rat [List Rat] -> [List Rat] +defn eigentrust-step [m p alpha t] + add-vec [scale-vec [rat- 1/1 alpha] [mat-vec-mul m t]] [scale-vec alpha p] + + +;; ============================================================ +;; Power iteration. Carries both the previous iterate `t` and the +;; current iterate `tnew` so each round computes `eigentrust-step` +;; exactly once. +;; ============================================================ + +spec eigentrust-iterate [List [List Rat]] [List Rat] Rat Rat Int [List Rat] [List Rat] -> [List Rat] +defn eigentrust-iterate [m p alpha eps budget t tnew] + match [int-le budget 0] + | true -> tnew + | false -> match [rat-lt [linf-norm [sub-vec tnew t]] eps] + | true -> tnew + | false -> eigentrust-iterate m p alpha eps [int- budget 1] tnew [eigentrust-step m p alpha tnew] + + +;; ============================================================ +;; Top-level entry point. Enforces the column-stochastic invariant +;; on M; panics if violated. +;; +;; Initial iterate is the pre-trust vector p (convention: any +;; distribution summing to 1 works; starting at p is simplest). +;; ============================================================ + +spec eigentrust [List [List Rat]] [List Rat] Rat Rat Int -> [List Rat] +defn eigentrust [m p alpha eps max-iter] + match [col-stochastic? m] + | false -> (the [List Rat] [panic "eigentrust: M must be column-stochastic"]) + | true -> eigentrust-iterate m p alpha eps max-iter p [eigentrust-step m p alpha p] + + +;; ======================================================================== +;; Worked examples +;; ======================================================================== + +;; --- Example 1: uniform matrix (doubly stochastic; uniform is fixed point) --- + +def m-uniform-4 : [List [List Rat]] := '['[1/4 1/4 1/4 1/4] '[1/4 1/4 1/4 1/4] '[1/4 1/4 1/4 1/4] '[1/4 1/4 1/4 1/4]] + +def p-uniform-4 : [List Rat] := '[1/4 1/4 1/4 1/4] + +[col-stochastic? m-uniform-4] +;; => true + +[eigentrust m-uniform-4 p-uniform-4 1/10 1/1000 50] +;; => '[1/4 1/4 1/4 1/4] + + +;; --- Example 2: 3-peer symmetric matrix --- + +def rz : Rat := 0/1 + +def m-others-3 : [List [List Rat]] := '['[rz 1/2 1/2] '[1/2 rz 1/2] '[1/2 1/2 rz]] + +def p-uniform-3 : [List Rat] := '[1/3 1/3 1/3] + +[col-stochastic? m-others-3] +;; => true + +[eigentrust m-others-3 p-uniform-3 1/10 1/1000 50] +;; => '[1/3 1/3 1/3] + + +;; --- Example 3: asymmetric 3-peer (column-stochastic form) --- +;; +;; The column-stochastic form of the "peer 0 splits trust equally +;; between 1 and 2; peer 1 trusts only 2; peer 2 trusts only 0" graph +;; has each column summing to 1. Columns are "outgoing trust from j", +;; rows are "incoming trust to i". + +def ro : Rat := 1/1 + +def m-asym-3 : [List [List Rat]] := '['[rz rz ro] '[1/2 rz rz] '[1/2 ro rz]] + +[col-stochastic? m-asym-3] +;; => true + +[eigentrust-step m-asym-3 p-uniform-3 3/10 p-uniform-3] + + +;; --- Example 4: 4-peer ring with concentrated pre-trust --- +;; +;; Ring: peer j sends all their trust to peer (j+1) mod 4. +;; Column j has a single 1 in row (j+1) mod 4. +;; Pre-trust concentrates on peer 0. +;; Convergence is slow (the ring permutation has |eigenvalue| = 1 +;; on the unit circle; only damping pulls us toward uniform). + +def m-ring-4 : [List [List Rat]] := '['[rz rz rz ro] '[ro rz rz rz] '[rz ro rz rz] '[rz rz ro rz]] + +def p-seed-0 : [List Rat] := '[ro rz rz rz] + +[col-stochastic? m-ring-4] +;; => true + +[vec-sum p-seed-0] +;; => 1 : Rat (valid probability distribution) + +[eigentrust m-ring-4 p-seed-0 3/10 1/1000 3] + + +;; --- Example 5: a handful of primitives --- + +[dot '[1/2 1/3] '[1/4 1/3]] +;; => 1/8 + 1/9 = 17/72 + +[scale-vec 1/2 '[1/2 1/3 1/4]] +;; => '[1/4 1/6 1/8] + +[add-vec '[1/4 1/4] '[1/4 1/4]] +;; => '[1/2 1/2] + +[linf-norm [sub-vec '[1/2 1/3] '[1/4 1/3]]] +;; => 1/4 + + +;; --- Example 6: invariant enforcement --- +;; +;; A non-column-stochastic matrix panics. + +def m-bad : [List [List Rat]] := '['[1/2 1/2] '[1/2 rz]] + +[col-stochastic? m-bad] +;; => false (col 1 sums to 1/2) + +;; Calling eigentrust on m-bad panics (error value returned, not evaluated): +;; [eigentrust m-bad '[1/2 1/2] 1/10 1/1000 10] +;; => panic: eigentrust: M must be column-stochastic diff --git a/racket/prologos/tests/test-eigentrust-plain.rkt b/racket/prologos/tests/test-eigentrust-plain.rkt new file mode 100644 index 000000000..a0499c9a0 --- /dev/null +++ b/racket/prologos/tests/test-eigentrust-plain.rkt @@ -0,0 +1,75 @@ +#lang racket/base + +;;; Tests for the plain (non-propagator) Racket EigenTrust +;;; implementations (`benchmarks/comparative/eigentrust-plain.rkt`). +;;; They use the same off-network kernel as the propagator variants; +;;; we just verify the plain orchestration produces the same result. + +(require rackunit + racket/flonum + "../benchmarks/comparative/eigentrust-plain.rkt" + "../benchmarks/comparative/eigentrust-propagators.rkt" + "../benchmarks/comparative/eigentrust-propagators-float.rkt") + +(define TOL 1e-9) + +(define (flv-close? a b [tol TOL]) + (and (= (flvector-length a) (flvector-length b)) + (for/and ([x (in-flvector a)] [y (in-flvector b)]) + (< (abs (- x y)) tol)))) + +;; ============================================================ +;; Plain rational +;; ============================================================ + +(test-case "plain rat ring-4 k=4 matches golden" + (define result (run-eigentrust-plain m-ring-4 p-seed-0 3/10 4)) + (check-equal? result + (vector 5401/10000 21/100 147/1000 1029/10000))) + +(test-case "plain rat uniform-4 stays uniform" + (check-equal? (run-eigentrust-plain m-uniform-4 p-uniform-4 1/10 2) + p-uniform-4)) + +(test-case "plain rat symmetric-3 stays uniform" + (check-equal? (run-eigentrust-plain m-others-3 p-uniform-3 1/10 2) + p-uniform-3)) + +;; Plain and propagator variants must agree exactly on rats. +(test-case "plain rat == propagator rat-coarse on ring k=1..6" + (for ([k (in-range 1 7)]) + (check-equal? (run-eigentrust-plain m-ring-4 p-seed-0 3/10 k) + (run-eigentrust-propagators m-ring-4 p-seed-0 3/10 k) + (format "k=~a" k)))) + +(test-case "plain rat panics on non-column-stochastic" + (define m-bad (vector (vector 1/2 1/2) (vector 1/2 0))) + (check-exn exn:fail? + (lambda () + (run-eigentrust-plain m-bad (vector 1/2 1/2) 1/10 1)))) + +;; ============================================================ +;; Plain float +;; ============================================================ + +(test-case "plain float ring-4 k=4 matches golden within tol" + (define result (run-eigentrust-plain-fl m-ring-4-fl p-seed-0-fl 0.3 4)) + (define expected (flvector 0.5401 0.21 0.147 0.1029)) + (check-true (flv-close? result expected))) + +(test-case "plain float uniform-4 stays uniform" + (check-true (flv-close? + (run-eigentrust-plain-fl m-uniform-4-fl p-uniform-4-fl 0.1 2) + p-uniform-4-fl))) + +(test-case "plain float symmetric-3 stays uniform" + (check-true (flv-close? + (run-eigentrust-plain-fl m-others-3-fl p-uniform-3-fl 0.1 2) + p-uniform-3-fl))) + +(test-case "plain float == propagator float on ring k=1..6 (within tol)" + (for ([k (in-range 1 7)]) + (check-true (flv-close? + (run-eigentrust-plain-fl m-ring-4-fl p-seed-0-fl 0.3 k) + (run-eigentrust-propagators-fl m-ring-4-fl p-seed-0-fl 0.3 k)) + (format "k=~a" k)))) diff --git a/racket/prologos/tests/test-eigentrust-posit.rkt b/racket/prologos/tests/test-eigentrust-posit.rkt new file mode 100644 index 000000000..15ffac79e --- /dev/null +++ b/racket/prologos/tests/test-eigentrust-posit.rkt @@ -0,0 +1,159 @@ +#lang racket/base + +;;; Unit tests for EigenTrust — List + Posit32 variant +;;; (column-stochastic convention). + +(require rackunit + racket/list + racket/string + "test-support.rkt") + +(define preamble + #< Posit32 +defn dot [xs ys] + match xs + | nil -> ~0.0 + | cons x as -> match ys + | nil -> ~0.0 + | cons y bs -> p32+ [p32* x y] [dot as bs] + +spec scale-vec Posit32 [List Posit32] -> [List Posit32] +defn scale-vec [s xs] + match xs + | nil -> nil + | cons x as -> cons [p32* s x] [scale-vec s as] + +spec add-vec [List Posit32] [List Posit32] -> [List Posit32] +defn add-vec [xs ys] + match xs + | nil -> nil + | cons x as -> match ys + | nil -> nil + | cons y bs -> cons [p32+ x y] [add-vec as bs] + +spec sub-vec [List Posit32] [List Posit32] -> [List Posit32] +defn sub-vec [xs ys] + match xs + | nil -> nil + | cons x as -> match ys + | nil -> nil + | cons y bs -> cons [p32- x y] [sub-vec as bs] + +spec p32-max Posit32 Posit32 -> Posit32 +defn p32-max [a b] + match [p32-lt a b] + | true -> b + | false -> a + +spec linf-norm [List Posit32] -> Posit32 +defn linf-norm [xs] + match xs + | nil -> ~0.0 + | cons x as -> p32-max [p32-abs x] [linf-norm as] + +spec mat-vec-mul [List [List Posit32]] [List Posit32] -> [List Posit32] +defn mat-vec-mul [m t] + match m + | nil -> nil + | cons r rs -> cons [dot r t] [mat-vec-mul rs t] + +spec col-sums [List [List Posit32]] -> [List Posit32] +defn col-sums [m] + match m + | nil -> nil + | cons r rest -> match rest + | nil -> r + | cons _ _ -> add-vec r [col-sums rest] + +spec all-ones? [List Posit32] -> Bool +defn all-ones? [xs] + match xs + | nil -> true + | cons x as -> match [p32-eq x ~1.0] + | false -> false + | true -> all-ones? as + +spec col-stochastic? [List [List Posit32]] -> Bool +defn col-stochastic? [m] + all-ones? [col-sums m] + +spec eigentrust-step [List [List Posit32]] [List Posit32] Posit32 [List Posit32] -> [List Posit32] +defn eigentrust-step [m p alpha t] + add-vec [scale-vec [p32- ~1.0 alpha] [mat-vec-mul m t]] [scale-vec alpha p] + +spec eigentrust-iterate [List [List Posit32]] [List Posit32] Posit32 Posit32 Int [List Posit32] [List Posit32] -> [List Posit32] +defn eigentrust-iterate [m p alpha eps budget t tnew] + match [int-le budget 0] + | true -> tnew + | false -> match [p32-lt [linf-norm [sub-vec tnew t]] eps] + | true -> tnew + | false -> eigentrust-iterate m p alpha eps [int- budget 1] tnew [eigentrust-step m p alpha tnew] + +spec eigentrust [List [List Posit32]] [List Posit32] Posit32 Posit32 Int -> [List Posit32] +defn eigentrust [m p alpha eps max-iter] + match [col-stochastic? m] + | false -> (the [List Posit32] [panic "eigentrust: M must be column-stochastic"]) + | true -> eigentrust-iterate m p alpha eps max-iter p [eigentrust-step m p alpha p] + +def p-uniform-4 : [List Posit32] := '[~0.25 ~0.25 ~0.25 ~0.25] +def p-uniform-3 : [List Posit32] := '[~0.33333 ~0.33333 ~0.33333] +def p-seed-0 : [List Posit32] := '[~1.0 ~0.0 ~0.0 ~0.0] + +def m-uniform-4 : [List [List Posit32]] := '['[~0.25 ~0.25 ~0.25 ~0.25] '[~0.25 ~0.25 ~0.25 ~0.25] '[~0.25 ~0.25 ~0.25 ~0.25] '[~0.25 ~0.25 ~0.25 ~0.25]] + +def m-others-3 : [List [List Posit32]] := '['[~0.0 ~0.5 ~0.5] '[~0.5 ~0.0 ~0.5] '[~0.5 ~0.5 ~0.0]] + +def m-ring-4 : [List [List Posit32]] := '['[~0.0 ~0.0 ~0.0 ~1.0] '[~1.0 ~0.0 ~0.0 ~0.0] '[~0.0 ~1.0 ~0.0 ~0.0] '[~0.0 ~0.0 ~1.0 ~0.0]] + +PROLOGOS + ) + +(define test-expressions + #< uniform" + (check-printed (res 3) "posit32 805306368")) + +;; 1/3 ≈ [posit32 850043821] +(test-case "eigentrust-posit/converge: symmetric 3x3 -> uniform" + (define s (res 4)) + (check-true (regexp-match? #rx"posit32 850043[0-9]+" s) + (format "expected ~~1/3 result, got ~s" s))) + +;; After 3 ring iters from concentrated start, values should differ +;; (slow settling). Just verify it's a 4-element posit vector. +(test-case "eigentrust-posit/ring: 3-iter result is a 4-element posit vec" + (define s (res 5)) + (check-true (regexp-match? #rx"posit32.*posit32.*posit32.*posit32" s) + (format "expected 4-element result, got ~s" s))) diff --git a/racket/prologos/tests/test-eigentrust-propagators-fine.rkt b/racket/prologos/tests/test-eigentrust-propagators-fine.rkt new file mode 100644 index 000000000..8b7061690 --- /dev/null +++ b/racket/prologos/tests/test-eigentrust-propagators-fine.rkt @@ -0,0 +1,45 @@ +#lang racket/base + +;;; Tests for the fine-grained per-peer variant of EigenTrust +;;; (benchmarks/comparative/eigentrust-propagators-fine.rkt). +;;; +;;; The fine variant returns the SAME exact-rational result as the +;;; coarse variant; we check exact equality. + +(require rackunit + "../benchmarks/comparative/eigentrust-propagators-fine.rkt" + "../benchmarks/comparative/eigentrust-propagators.rkt") + +;; End-to-end golden equality with the coarse variant. +(test-case "fine ring-4 k=4 matches coarse golden" + (define result (run-eigentrust-propagators-fine m-ring-4 p-seed-0 3/10 4)) + (check-equal? result + (vector 5401/10000 21/100 147/1000 1029/10000))) + +(test-case "fine uniform-4 k=2 stays uniform" + (define result (run-eigentrust-propagators-fine m-uniform-4 p-uniform-4 1/10 2)) + (check-equal? result p-uniform-4)) + +(test-case "fine symmetric-3 k=2 stays uniform" + (define result (run-eigentrust-propagators-fine m-others-3 p-uniform-3 1/10 2)) + (check-equal? result p-uniform-3)) + +;; The fine and coarse variants must agree on every fixture. +(test-case "fine == coarse on ring k=1..6" + (for ([k (in-range 1 7)]) + (define coarse (run-eigentrust-propagators m-ring-4 p-seed-0 3/10 k)) + (define fine (run-eigentrust-propagators-fine m-ring-4 p-seed-0 3/10 k)) + (check-equal? fine coarse + (format "k=~a: fine and coarse should match" k)))) + +;; Mass preservation. +(test-case "fine ring-4 mass preservation" + (define result (run-eigentrust-propagators-fine m-ring-4 p-seed-0 3/10 4)) + (define total (for/sum ([x (in-vector result)]) x)) + (check-equal? total 1)) + +(test-case "fine panics on non-column-stochastic" + (define m-bad (vector (vector 1/2 1/2) (vector 1/2 0))) + (check-exn exn:fail? + (lambda () + (run-eigentrust-propagators-fine m-bad (vector 1/2 1/2) 1/10 1)))) diff --git a/racket/prologos/tests/test-eigentrust-propagators-float.rkt b/racket/prologos/tests/test-eigentrust-propagators-float.rkt new file mode 100644 index 000000000..22c099ef5 --- /dev/null +++ b/racket/prologos/tests/test-eigentrust-propagators-float.rkt @@ -0,0 +1,86 @@ +#lang racket/base + +;;; Tests for the float (flonum) propagator variant of EigenTrust +;;; (benchmarks/comparative/eigentrust-propagators-float.rkt). +;;; +;;; Floats are inexact; tests check approximate equality against the +;;; exact-rational answer rather than `equal?` (see pitfalls doc §3). + +(require rackunit + racket/flonum + "../benchmarks/comparative/eigentrust-propagators-float.rkt" + "../benchmarks/comparative/eigentrust-propagators.rkt") + +(define TOL 1e-9) + +(define (flv-close? a b [tol TOL]) + (and (= (flvector-length a) (flvector-length b)) + (for/and ([x (in-flvector a)] [y (in-flvector b)]) + (< (abs (- x y)) tol)))) + +(define (rat-vec->flvector v) + (define n (vector-length v)) + (define out (make-flvector n)) + (for ([i (in-range n)]) + (flvector-set! out i (exact->inexact (vector-ref v i)))) + out) + +;; ============================================================ +;; Column-stochastic check +;; ============================================================ + +(test-case "float col-stochastic? on uniform" + (check-true (col-stochastic-fl? m-uniform-4-fl))) + +(test-case "float col-stochastic? on ring" + (check-true (col-stochastic-fl? m-ring-4-fl))) + +(test-case "float col-stochastic? rejects bad matrix" + (define m-bad + (vector (flvector 0.5 0.5) + (flvector 0.5 0.0))) + (check-false (col-stochastic-fl? m-bad))) + +;; ============================================================ +;; Off-network kernel matches the rational version (within tolerance) +;; ============================================================ + +(test-case "float kernel matches rational ring step from p-seed-0" + (define rat-result (eigentrust-step m-ring-4 p-seed-0 3/10 p-seed-0)) + (define fl-result (eigentrust-step-fl m-ring-4-fl p-seed-0-fl 0.3 p-seed-0-fl)) + (check-true (flv-close? fl-result (rat-vec->flvector rat-result)))) + +;; ============================================================ +;; End-to-end matches the rational version +;; ============================================================ + +(test-case "float ring-4 k=4 matches rational golden" + (define result (run-eigentrust-propagators-fl m-ring-4-fl p-seed-0-fl 0.3 4)) + (define expected + (rat-vec->flvector + (vector 5401/10000 21/100 147/1000 1029/10000))) + (check-true (flv-close? result expected))) + +(test-case "float uniform converges to itself" + (define result (run-eigentrust-propagators-fl m-uniform-4-fl p-uniform-4-fl 0.1 2)) + (check-true (flv-close? result p-uniform-4-fl))) + +(test-case "float symmetric converges to uniform" + (define result (run-eigentrust-propagators-fl m-others-3-fl p-uniform-3-fl 0.1 2)) + (check-true (flv-close? result p-uniform-3-fl))) + +;; Mass preservation: result should sum to ~1 (probability distribution). +(test-case "float ring-4 mass preservation" + (define result (run-eigentrust-propagators-fl m-ring-4-fl p-seed-0-fl 0.3 4)) + (define total (for/fold ([acc 0.0]) ([x (in-flvector result)]) (fl+ acc x))) + (check-true (< (abs (- total 1.0)) 1e-12))) + +(test-case "float panics on non-column-stochastic" + (define m-bad + (vector (flvector 0.5 0.5) + (flvector 0.5 0.0))) + (check-exn exn:fail? + (lambda () + (run-eigentrust-propagators-fl m-bad + (flvector 0.5 0.5) + 0.1 1)))) diff --git a/racket/prologos/tests/test-eigentrust-propagators.rkt b/racket/prologos/tests/test-eigentrust-propagators.rkt new file mode 100644 index 000000000..29b661c47 --- /dev/null +++ b/racket/prologos/tests/test-eigentrust-propagators.rkt @@ -0,0 +1,101 @@ +#lang racket/base + +;;; Unit tests for the Racket-on-propagators EigenTrust implementation +;;; (benchmarks/comparative/eigentrust-propagators.rkt). +;;; +;;; Verifies that the propagator-based implementation produces the +;;; SAME results as the four Prologos surface variants (List+Rat, +;;; List+Posit32, PVec+Rat, PVec+Posit32) on the same fixtures. The +;;; column-stochastic invariant check, ring-4 slow-settling +;;; trajectory, and uniform fixed-point are all asserted with exact +;;; rational equality (the Racket version uses Racket's exact +;;; rationals, equivalent to the Prologos List+Rat variant). + +(require rackunit + "../benchmarks/comparative/eigentrust-propagators.rkt") + +;; ============================================================ +;; Column-stochastic invariant +;; ============================================================ + +(test-case "col-stochastic? on uniform 4x4" + (check-true (col-stochastic? m-uniform-4))) + +(test-case "col-stochastic? on symmetric 3x3" + (check-true (col-stochastic? m-others-3))) + +(test-case "col-stochastic? on ring 4" + (check-true (col-stochastic? m-ring-4))) + +(test-case "col-stochastic? rejects bad matrix" + (define m-bad (vector (vector 1/2 1/2) + (vector 1/2 0))) ;; col 1 sums to 1/2 + (check-false (col-stochastic? m-bad))) + +;; ============================================================ +;; Off-network kernel: eigentrust-step +;; ============================================================ + +(test-case "eigentrust-step on uniform 4x4 is fixed point" + (define t (vector 1/4 1/4 1/4 1/4)) + (check-equal? (eigentrust-step m-uniform-4 p-uniform-4 1/10 t) t)) + +(test-case "eigentrust-step on symmetric 3x3 with uniform" + (define t (vector 1/3 1/3 1/3)) + (check-equal? (eigentrust-step m-others-3 p-uniform-3 1/10 t) t)) + +(test-case "eigentrust-step on ring with concentrated pre-trust" + ;; t0 = p = [1, 0, 0, 0], M*t0 = [0, 1, 0, 0] + ;; step = 7/10 * [0, 1, 0, 0] + 3/10 * [1, 0, 0, 0] + ;; = [3/10, 7/10, 0, 0] + (check-equal? (eigentrust-step m-ring-4 p-seed-0 3/10 p-seed-0) + (vector 3/10 7/10 0 0))) + +;; ============================================================ +;; End-to-end via the propagator network +;; ============================================================ + +(test-case "run-eigentrust-propagators: uniform converges to itself (k=2)" + (define result (run-eigentrust-propagators m-uniform-4 p-uniform-4 1/10 2)) + (check-equal? result p-uniform-4)) + +(test-case "run-eigentrust-propagators: symmetric converges to uniform (k=2)" + (define result (run-eigentrust-propagators m-others-3 p-uniform-3 1/10 2)) + (check-equal? result p-uniform-3)) + +(test-case "run-eigentrust-propagators: ring slow settling (k=4 = W3 fixture)" + ;; This is the W3 benchmark workload, identical to the four Prologos + ;; surface variants' W3 result. + ;; Hand calc: + ;; t0 = [1, 0, 0, 0] + ;; step-1 = [3/10, 7/10, 0, 0] + ;; step-2 = [3/10, 21/100, 49/100, 0] + ;; step-3 = [3/10, 21/100, 147/1000, 343/1000] + ;; step-4 = [5401/10000, 21/100, 147/1000, 1029/10000] + (define result (run-eigentrust-propagators m-ring-4 p-seed-0 3/10 4)) + (check-equal? result + (vector 5401/10000 21/100 147/1000 1029/10000))) + +(test-case "run-eigentrust-propagators: panics on non-column-stochastic" + (define m-bad (vector (vector 1/2 1/2) + (vector 1/2 0))) + (check-exn exn:fail? (lambda () + (run-eigentrust-propagators m-bad + (vector 1/2 1/2) + 1/10 + 1)))) + +;; ============================================================ +;; Mass preservation across iterations (the result must remain a +;; probability distribution: sum = 1). +;; ============================================================ + +(test-case "ring-4 mass preservation across k=4" + (define result (run-eigentrust-propagators m-ring-4 p-seed-0 3/10 4)) + (define total (for/sum ([x (in-vector result)]) x)) + (check-equal? total 1)) + +(test-case "symmetric-3 mass preservation across k=10" + (define result (run-eigentrust-propagators m-others-3 p-uniform-3 1/10 10)) + (define total (for/sum ([x (in-vector result)]) x)) + (check-equal? total 1)) diff --git a/racket/prologos/tests/test-eigentrust-pvec-posit.rkt b/racket/prologos/tests/test-eigentrust-pvec-posit.rkt new file mode 100644 index 000000000..d764d6c83 --- /dev/null +++ b/racket/prologos/tests/test-eigentrust-pvec-posit.rkt @@ -0,0 +1,191 @@ +#lang racket/base + +;;; Unit tests for EigenTrust — PVec + Posit32 variant +;;; (column-stochastic convention). + +(require rackunit + racket/list + racket/string + "test-support.rkt") + +(define preamble + #< Posit32 +defn dot-go [i n xs ys acc] + match [nat-eq? i n] + | true -> acc + | false -> dot-go [suc i] n xs ys [p32+ acc [p32* [pvec-nth xs i] [pvec-nth ys i]]] + +spec dot [PVec Posit32] [PVec Posit32] -> Posit32 +defn dot [xs ys] + dot-go zero [pvec-length xs] xs ys ~0.0 + +spec scale-vec-go Nat Nat Posit32 [PVec Posit32] [PVec Posit32] -> [PVec Posit32] +defn scale-vec-go [i n s xs acc] + match [nat-eq? i n] + | true -> acc + | false -> scale-vec-go [suc i] n s xs [pvec-push acc [p32* s [pvec-nth xs i]]] + +spec scale-vec Posit32 [PVec Posit32] -> [PVec Posit32] +defn scale-vec [s xs] + scale-vec-go zero [pvec-length xs] s xs (pvec-empty Posit32) + +spec add-vec-go Nat Nat [PVec Posit32] [PVec Posit32] [PVec Posit32] -> [PVec Posit32] +defn add-vec-go [i n xs ys acc] + match [nat-eq? i n] + | true -> acc + | false -> add-vec-go [suc i] n xs ys [pvec-push acc [p32+ [pvec-nth xs i] [pvec-nth ys i]]] + +spec add-vec [PVec Posit32] [PVec Posit32] -> [PVec Posit32] +defn add-vec [xs ys] + add-vec-go zero [pvec-length xs] xs ys (pvec-empty Posit32) + +spec sub-vec-go Nat Nat [PVec Posit32] [PVec Posit32] [PVec Posit32] -> [PVec Posit32] +defn sub-vec-go [i n xs ys acc] + match [nat-eq? i n] + | true -> acc + | false -> sub-vec-go [suc i] n xs ys [pvec-push acc [p32- [pvec-nth xs i] [pvec-nth ys i]]] + +spec sub-vec [PVec Posit32] [PVec Posit32] -> [PVec Posit32] +defn sub-vec [xs ys] + sub-vec-go zero [pvec-length xs] xs ys (pvec-empty Posit32) + +spec p32-max Posit32 Posit32 -> Posit32 +defn p32-max [a b] + match [p32-lt a b] + | true -> b + | false -> a + +spec linf-norm-go Nat Nat [PVec Posit32] Posit32 -> Posit32 +defn linf-norm-go [i n xs acc] + match [nat-eq? i n] + | true -> acc + | false -> linf-norm-go [suc i] n xs [p32-max acc [p32-abs [pvec-nth xs i]]] + +spec linf-norm [PVec Posit32] -> Posit32 +defn linf-norm [xs] + linf-norm-go zero [pvec-length xs] xs ~0.0 + +spec mat-vec-mul-go Nat Nat [PVec [PVec Posit32]] [PVec Posit32] [PVec Posit32] -> [PVec Posit32] +defn mat-vec-mul-go [i n m t acc] + match [nat-eq? i n] + | true -> acc + | false -> mat-vec-mul-go [suc i] n m t [pvec-push acc [dot [pvec-nth m i] t]] + +spec mat-vec-mul [PVec [PVec Posit32]] [PVec Posit32] -> [PVec Posit32] +defn mat-vec-mul [m t] + mat-vec-mul-go zero [pvec-length m] m t (pvec-empty Posit32) + +spec col-sums-fold Nat Nat [PVec [PVec Posit32]] [PVec Posit32] -> [PVec Posit32] +defn col-sums-fold [i n m acc] + match [nat-eq? i n] + | true -> acc + | false -> col-sums-fold [suc i] n m [add-vec acc [pvec-nth m i]] + +spec zeros-go Nat Nat [PVec Posit32] -> [PVec Posit32] +defn zeros-go [i n acc] + match [nat-eq? i n] + | true -> acc + | false -> zeros-go [suc i] n [pvec-push acc ~0.0] + +spec zeros Nat -> [PVec Posit32] +defn zeros [n] + zeros-go zero n (pvec-empty Posit32) + +spec col-sums [PVec [PVec Posit32]] -> [PVec Posit32] +defn col-sums [m] + match [nat-eq? [pvec-length m] zero] + | true -> (pvec-empty Posit32) + | false -> col-sums-fold zero [pvec-length m] m [zeros [pvec-length [pvec-nth m zero]]] + +spec all-ones?-go Nat Nat [PVec Posit32] -> Bool +defn all-ones?-go [i n xs] + match [nat-eq? i n] + | true -> true + | false -> match [p32-eq [pvec-nth xs i] ~1.0] + | false -> false + | true -> all-ones?-go [suc i] n xs + +spec all-ones? [PVec Posit32] -> Bool +defn all-ones? [xs] + all-ones?-go zero [pvec-length xs] xs + +spec col-stochastic? [PVec [PVec Posit32]] -> Bool +defn col-stochastic? [m] + all-ones? [col-sums m] + +spec eigentrust-step [PVec [PVec Posit32]] [PVec Posit32] Posit32 [PVec Posit32] -> [PVec Posit32] +defn eigentrust-step [m p alpha t] + add-vec [scale-vec [p32- ~1.0 alpha] [mat-vec-mul m t]] [scale-vec alpha p] + +spec eigentrust-iterate [PVec [PVec Posit32]] [PVec Posit32] Posit32 Posit32 Int [PVec Posit32] [PVec Posit32] -> [PVec Posit32] +defn eigentrust-iterate [m p alpha eps budget t tnew] + match [int-le budget 0] + | true -> tnew + | false -> match [p32-lt [linf-norm [sub-vec tnew t]] eps] + | true -> tnew + | false -> eigentrust-iterate m p alpha eps [int- budget 1] tnew [eigentrust-step m p alpha tnew] + +spec eigentrust [PVec [PVec Posit32]] [PVec Posit32] Posit32 Posit32 Int -> [PVec Posit32] +defn eigentrust [m p alpha eps max-iter] + match [col-stochastic? m] + | false -> (the [PVec Posit32] [panic "eigentrust: M must be column-stochastic"]) + | true -> eigentrust-iterate m p alpha eps max-iter p [eigentrust-step m p alpha p] + +def p-uniform-4 : [PVec Posit32] := @[~0.25 ~0.25 ~0.25 ~0.25] +def p-uniform-3 : [PVec Posit32] := @[~0.33333 ~0.33333 ~0.33333] +def p-seed-0 : [PVec Posit32] := @[~1.0 ~0.0 ~0.0 ~0.0] + +def m-uniform-4 : [PVec [PVec Posit32]] := @[@[~0.25 ~0.25 ~0.25 ~0.25] @[~0.25 ~0.25 ~0.25 ~0.25] @[~0.25 ~0.25 ~0.25 ~0.25] @[~0.25 ~0.25 ~0.25 ~0.25]] + +def m-others-3 : [PVec [PVec Posit32]] := @[@[~0.0 ~0.5 ~0.5] @[~0.5 ~0.0 ~0.5] @[~0.5 ~0.5 ~0.0]] + +def m-ring-4 : [PVec [PVec Posit32]] := @[@[~0.0 ~0.0 ~0.0 ~1.0] @[~1.0 ~0.0 ~0.0 ~0.0] @[~0.0 ~1.0 ~0.0 ~0.0] @[~0.0 ~0.0 ~1.0 ~0.0]] + +PROLOGOS + ) + +(define test-expressions + #< uniform" + (check-printed (res 3) "posit32 805306368")) + +(test-case "eigentrust-pvec-posit/converge: symmetric -> uniform" + (define s (res 4)) + (check-true (regexp-match? #rx"posit32 850043[0-9]+" s) + (format "expected ~~1/3, got ~s" s))) + +(test-case "eigentrust-pvec-posit/ring: 3 iters produces 4-element posit vec" + (define s (res 5)) + (check-true (regexp-match? #rx"posit32.*posit32.*posit32.*posit32" s) + (format "expected 4-element posit vec, got ~s" s))) diff --git a/racket/prologos/tests/test-eigentrust-pvec.rkt b/racket/prologos/tests/test-eigentrust-pvec.rkt new file mode 100644 index 000000000..8b46a7fe4 --- /dev/null +++ b/racket/prologos/tests/test-eigentrust-pvec.rkt @@ -0,0 +1,187 @@ +#lang racket/base + +;;; Unit tests for EigenTrust — PVec + Rat variant +;;; (column-stochastic convention). + +(require rackunit + racket/list + racket/string + "test-support.rkt") + +(define preamble + #< Rat +defn dot-go [i n xs ys acc] + match [nat-eq? i n] + | true -> acc + | false -> dot-go [suc i] n xs ys [rat+ acc [rat* [pvec-nth xs i] [pvec-nth ys i]]] + +spec dot [PVec Rat] [PVec Rat] -> Rat +defn dot [xs ys] + dot-go zero [pvec-length xs] xs ys 0/1 + +spec scale-vec-go Nat Nat Rat [PVec Rat] [PVec Rat] -> [PVec Rat] +defn scale-vec-go [i n s xs acc] + match [nat-eq? i n] + | true -> acc + | false -> scale-vec-go [suc i] n s xs [pvec-push acc [rat* s [pvec-nth xs i]]] + +spec scale-vec Rat [PVec Rat] -> [PVec Rat] +defn scale-vec [s xs] + scale-vec-go zero [pvec-length xs] s xs (pvec-empty Rat) + +spec add-vec-go Nat Nat [PVec Rat] [PVec Rat] [PVec Rat] -> [PVec Rat] +defn add-vec-go [i n xs ys acc] + match [nat-eq? i n] + | true -> acc + | false -> add-vec-go [suc i] n xs ys [pvec-push acc [rat+ [pvec-nth xs i] [pvec-nth ys i]]] + +spec add-vec [PVec Rat] [PVec Rat] -> [PVec Rat] +defn add-vec [xs ys] + add-vec-go zero [pvec-length xs] xs ys (pvec-empty Rat) + +spec sub-vec-go Nat Nat [PVec Rat] [PVec Rat] [PVec Rat] -> [PVec Rat] +defn sub-vec-go [i n xs ys acc] + match [nat-eq? i n] + | true -> acc + | false -> sub-vec-go [suc i] n xs ys [pvec-push acc [rat- [pvec-nth xs i] [pvec-nth ys i]]] + +spec sub-vec [PVec Rat] [PVec Rat] -> [PVec Rat] +defn sub-vec [xs ys] + sub-vec-go zero [pvec-length xs] xs ys (pvec-empty Rat) + +spec rat-max Rat Rat -> Rat +defn rat-max [a b] + match [rat-lt a b] + | true -> b + | false -> a + +spec linf-norm-go Nat Nat [PVec Rat] Rat -> Rat +defn linf-norm-go [i n xs acc] + match [nat-eq? i n] + | true -> acc + | false -> linf-norm-go [suc i] n xs [rat-max acc [rat-abs [pvec-nth xs i]]] + +spec linf-norm [PVec Rat] -> Rat +defn linf-norm [xs] + linf-norm-go zero [pvec-length xs] xs 0/1 + +spec mat-vec-mul-go Nat Nat [PVec [PVec Rat]] [PVec Rat] [PVec Rat] -> [PVec Rat] +defn mat-vec-mul-go [i n m t acc] + match [nat-eq? i n] + | true -> acc + | false -> mat-vec-mul-go [suc i] n m t [pvec-push acc [dot [pvec-nth m i] t]] + +spec mat-vec-mul [PVec [PVec Rat]] [PVec Rat] -> [PVec Rat] +defn mat-vec-mul [m t] + mat-vec-mul-go zero [pvec-length m] m t (pvec-empty Rat) + +spec col-sums-fold Nat Nat [PVec [PVec Rat]] [PVec Rat] -> [PVec Rat] +defn col-sums-fold [i n m acc] + match [nat-eq? i n] + | true -> acc + | false -> col-sums-fold [suc i] n m [add-vec acc [pvec-nth m i]] + +spec zeros-go Nat Nat [PVec Rat] -> [PVec Rat] +defn zeros-go [i n acc] + match [nat-eq? i n] + | true -> acc + | false -> zeros-go [suc i] n [pvec-push acc 0/1] + +spec zeros Nat -> [PVec Rat] +defn zeros [n] + zeros-go zero n (pvec-empty Rat) + +spec col-sums [PVec [PVec Rat]] -> [PVec Rat] +defn col-sums [m] + match [nat-eq? [pvec-length m] zero] + | true -> (pvec-empty Rat) + | false -> col-sums-fold zero [pvec-length m] m [zeros [pvec-length [pvec-nth m zero]]] + +spec all-ones?-go Nat Nat [PVec Rat] -> Bool +defn all-ones?-go [i n xs] + match [nat-eq? i n] + | true -> true + | false -> match [rat-eq [pvec-nth xs i] 1/1] + | false -> false + | true -> all-ones?-go [suc i] n xs + +spec all-ones? [PVec Rat] -> Bool +defn all-ones? [xs] + all-ones?-go zero [pvec-length xs] xs + +spec col-stochastic? [PVec [PVec Rat]] -> Bool +defn col-stochastic? [m] + all-ones? [col-sums m] + +spec eigentrust-step [PVec [PVec Rat]] [PVec Rat] Rat [PVec Rat] -> [PVec Rat] +defn eigentrust-step [m p alpha t] + add-vec [scale-vec [rat- 1/1 alpha] [mat-vec-mul m t]] [scale-vec alpha p] + +spec eigentrust-iterate [PVec [PVec Rat]] [PVec Rat] Rat Rat Int [PVec Rat] [PVec Rat] -> [PVec Rat] +defn eigentrust-iterate [m p alpha eps budget t tnew] + match [int-le budget 0] + | true -> tnew + | false -> match [rat-lt [linf-norm [sub-vec tnew t]] eps] + | true -> tnew + | false -> eigentrust-iterate m p alpha eps [int- budget 1] tnew [eigentrust-step m p alpha tnew] + +spec eigentrust [PVec [PVec Rat]] [PVec Rat] Rat Rat Int -> [PVec Rat] +defn eigentrust [m p alpha eps max-iter] + match [col-stochastic? m] + | false -> (the [PVec Rat] [panic "eigentrust: M must be column-stochastic"]) + | true -> eigentrust-iterate m p alpha eps max-iter p [eigentrust-step m p alpha p] + +def p-uniform-4 : [PVec Rat] := @[1/4 1/4 1/4 1/4] +def p-uniform-3 : [PVec Rat] := @[1/3 1/3 1/3] +def p-seed-0 : [PVec Rat] := @[1/1 0/1 0/1 0/1] + +def m-uniform-4 : [PVec [PVec Rat]] := @[@[1/4 1/4 1/4 1/4] @[1/4 1/4 1/4 1/4] @[1/4 1/4 1/4 1/4] @[1/4 1/4 1/4 1/4]] + +def m-others-3 : [PVec [PVec Rat]] := @[@[0/1 1/2 1/2] @[1/2 0/1 1/2] @[1/2 1/2 0/1]] + +def m-ring-4 : [PVec [PVec Rat]] := @[@[0/1 0/1 0/1 1/1] @[1/1 0/1 0/1 0/1] @[0/1 1/1 0/1 0/1] @[0/1 0/1 1/1 0/1]] + +PROLOGOS + ) + +(define test-expressions + #< uniform" + (check-printed (res 3) "@[1/4 1/4 1/4 1/4]")) + +(test-case "eigentrust-pvec/converge: symmetric -> uniform" + (check-printed (res 4) "@[1/3 1/3 1/3]")) + +(test-case "eigentrust-pvec/ring slow settling" + (check-printed (res 5) "@[5401/10000 21/100 147/1000 1029/10000]")) diff --git a/racket/prologos/tests/test-eigentrust.rkt b/racket/prologos/tests/test-eigentrust.rkt new file mode 100644 index 000000000..e878f6c79 --- /dev/null +++ b/racket/prologos/tests/test-eigentrust.rkt @@ -0,0 +1,244 @@ +#lang racket/base + +;;; +;;; Unit tests for EigenTrust (List + Rat variant, +;;; column-stochastic convention — see examples/eigentrust.prologos). +;;; + +(require rackunit + racket/list + racket/string + "test-support.rkt") + +(define eigentrust-preamble + #< Rat +defn dot [xs ys] + match xs + | nil -> 0/1 + | cons x as -> match ys + | nil -> 0/1 + | cons y bs -> rat+ [rat* x y] [dot as bs] + +spec scale-vec Rat [List Rat] -> [List Rat] +defn scale-vec [s xs] + match xs + | nil -> nil + | cons x as -> cons [rat* s x] [scale-vec s as] + +spec add-vec [List Rat] [List Rat] -> [List Rat] +defn add-vec [xs ys] + match xs + | nil -> nil + | cons x as -> match ys + | nil -> nil + | cons y bs -> cons [rat+ x y] [add-vec as bs] + +spec sub-vec [List Rat] [List Rat] -> [List Rat] +defn sub-vec [xs ys] + match xs + | nil -> nil + | cons x as -> match ys + | nil -> nil + | cons y bs -> cons [rat- x y] [sub-vec as bs] + +spec rat-max Rat Rat -> Rat +defn rat-max [a b] + match [rat-lt a b] + | true -> b + | false -> a + +spec linf-norm [List Rat] -> Rat +defn linf-norm [xs] + match xs + | nil -> 0/1 + | cons x as -> rat-max [rat-abs x] [linf-norm as] + +spec mat-vec-mul [List [List Rat]] [List Rat] -> [List Rat] +defn mat-vec-mul [m t] + match m + | nil -> nil + | cons r rs -> cons [dot r t] [mat-vec-mul rs t] + +spec col-sums [List [List Rat]] -> [List Rat] +defn col-sums [m] + match m + | nil -> nil + | cons r rest -> match rest + | nil -> r + | cons _ _ -> add-vec r [col-sums rest] + +spec all-ones? [List Rat] -> Bool +defn all-ones? [xs] + match xs + | nil -> true + | cons x as -> match [rat-eq x 1/1] + | false -> false + | true -> all-ones? as + +spec col-stochastic? [List [List Rat]] -> Bool +defn col-stochastic? [m] + all-ones? [col-sums m] + +spec eigentrust-step [List [List Rat]] [List Rat] Rat [List Rat] -> [List Rat] +defn eigentrust-step [m p alpha t] + add-vec [scale-vec [rat- 1/1 alpha] [mat-vec-mul m t]] [scale-vec alpha p] + +spec eigentrust-iterate [List [List Rat]] [List Rat] Rat Rat Int [List Rat] [List Rat] -> [List Rat] +defn eigentrust-iterate [m p alpha eps budget t tnew] + match [int-le budget 0] + | true -> tnew + | false -> match [rat-lt [linf-norm [sub-vec tnew t]] eps] + | true -> tnew + | false -> eigentrust-iterate m p alpha eps [int- budget 1] tnew [eigentrust-step m p alpha tnew] + +spec eigentrust [List [List Rat]] [List Rat] Rat Rat Int -> [List Rat] +defn eigentrust [m p alpha eps max-iter] + match [col-stochastic? m] + | false -> (the [List Rat] [panic "eigentrust: M must be column-stochastic"]) + | true -> eigentrust-iterate m p alpha eps max-iter p [eigentrust-step m p alpha p] + +def rz : Rat := 0/1 +def ro : Rat := 1/1 + +def p-uniform-4 : [List Rat] := '[1/4 1/4 1/4 1/4] +def p-uniform-3 : [List Rat] := '[1/3 1/3 1/3] +def p-seed-0 : [List Rat] := '[ro rz rz rz] + +def m-uniform-4 : [List [List Rat]] := '['[1/4 1/4 1/4 1/4] '[1/4 1/4 1/4 1/4] '[1/4 1/4 1/4 1/4] '[1/4 1/4 1/4 1/4]] + +def m-others-3 : [List [List Rat]] := '['[rz 1/2 1/2] '[1/2 rz 1/2] '[1/2 1/2 rz]] + +def m-ring-4 : [List [List Rat]] := '['[rz rz rz ro] '[ro rz rz rz] '[rz ro rz rz] '[rz rz ro rz]] + +;; A non-column-stochastic matrix: column 1 sums to 1/2. +def m-bad : [List [List Rat]] := '['[1/2 1/2] '[1/2 rz]] + +PROLOGOS + ) + +(define test-expressions + #<1) +;; t1 = 0.7 * [0,1,0,0] + 0.3 * [1,0,0,0] = [3/10, 7/10, 0, 0] +;; M*t1 = [0, 3/10, 7/10, 0] +;; t2 = 0.7 * [0, 3/10, 7/10, 0] + 0.3 * [1, 0, 0, 0] +;; = [3/10, 21/100, 49/100, 0] +;; M*t2 = [0, 3/10, 21/100, 49/100] +;; t3 = 0.7 * [0, 3/10, 21/100, 49/100] + 0.3 * [1, 0, 0, 0] +;; = [3/10, 21/100, 147/1000, 343/1000] +;; = [300/1000, 210/1000, 147/1000, 343/1000] +;; After 3 forced iters at eps=0 the iterator primes with t0=p and +;; returns t3; check gives [5401/10000 21/100 147/1000 1029/10000] +;; (one extra step from the seed pair). +eigentrust m-ring-4 p-seed-0 3/10 0/1 3 + +;; T13: one step on asymmetric 3x3 (from example, cross-check) +eigentrust-step m-others-3 p-uniform-3 1/10 p-uniform-3 + +PROLOGOS + ) + +(define full-program + (string-append eigentrust-preamble "\n" test-expressions)) + +(define all-results (run-ns-ws-all full-program)) + +(define (last-n xs n) (drop xs (- (length xs) n))) +(define test-results (last-n all-results 13)) +(define (res i) (list-ref test-results i)) + +(define (check-printed actual expected-substr [msg #f]) + (check-true (string-contains? actual expected-substr) + (or msg (format "Expected ~s to contain ~s" actual expected-substr)))) + +;; T1: 1/2 + 1/3 + 1/4 = 6/12 + 4/12 + 3/12 = 13/12 +(test-case "eigentrust/dot: basic" + (check-printed (res 0) "13/12 : Rat")) + +;; T2 +(test-case "eigentrust/scale-vec" + (check-printed (res 1) "'[1/4 1/6 1/8]")) + +;; T3 +(test-case "eigentrust/add-vec" + (check-printed (res 2) "'[1/2 1/2]")) + +;; T4: 1/2-1/4=1/4, 1/3-1/3=0 +(test-case "eigentrust/sub-vec" + (check-printed (res 3) "'[1/4 0]")) + +;; T5: max(1/8, 1/4, 1/2, 1/10) = 1/2 +(test-case "eigentrust/linf-norm" + (check-printed (res 4) "1/2 : Rat")) + +;; T6: M*p where M is uniform and p is uniform → uniform +(test-case "eigentrust/mat-vec-mul: uniform preserves uniform" + (check-printed (res 5) "'[1/4 1/4 1/4 1/4]")) + +;; T7 +(test-case "eigentrust/col-stochastic? on good uniform" + (check-printed (res 6) "true : Bool")) + +;; T8 +(test-case "eigentrust/col-stochastic? on ring" + (check-printed (res 7) "true : Bool")) + +;; T9 +(test-case "eigentrust/col-stochastic? rejects bad matrix" + (check-printed (res 8) "false : Bool")) + +;; T10 +(test-case "eigentrust/converge: uniform 4x4 -> uniform" + (check-printed (res 9) "'[1/4 1/4 1/4 1/4]")) + +;; T11 +(test-case "eigentrust/converge: symmetric 3x3 -> uniform" + (check-printed (res 10) "'[1/3 1/3 1/3]")) + +;; T12: ring slow settling, 3 forced iters from concentrated pre-trust +(test-case "eigentrust/ring: slow settling from concentrated pre-trust" + (check-printed (res 11) "'[5401/10000 21/100 147/1000 1029/10000]")) + +;; T13: single step on symmetric is a no-op under uniform pre-trust +(test-case "eigentrust/step on symmetric with uniform" + (check-printed (res 12) "'[1/3 1/3 1/3]")) diff --git a/racket/prologos/tools/bench-phases.rkt b/racket/prologos/tools/bench-phases.rkt new file mode 100644 index 000000000..47e0fec2e --- /dev/null +++ b/racket/prologos/tools/bench-phases.rkt @@ -0,0 +1,183 @@ +#lang racket/base + +;;; tools/bench-phases.rkt --- aggregate PHASE-TIMINGS across N runs +;;; +;;; For each given .prologos file, run `racket driver.rkt FILE` N times, +;;; extract the PHASE-TIMINGS line from stderr, and report the median +;;; per phase plus the leftover "prelude + overhead" (wall - sum-of-phases). +;;; +;;; Usage: +;;; racket tools/bench-phases.rkt [--runs N] FILE.prologos [FILE.prologos ...] +;;; +;;; Example: +;;; racket tools/bench-phases.rkt --runs 3 \ +;;; benchmarks/comparative/eigentrust-list-rat.prologos \ +;;; benchmarks/comparative/eigentrust-pvec-posit.prologos + +(require racket/cmdline + racket/list + racket/port + racket/path + racket/system + json) + +;; ============================================================ +;; Path anchoring (mirrors bench-ab.rkt) +;; ============================================================ + +(define tools-dir + (let ([src (resolved-module-path-name + (variable-reference->resolved-module-path + (#%variable-reference)))]) + (simplify-path (path-only src)))) + +(define project-root + (path->string (simplify-path (build-path tools-dir "..")))) + +(define driver-path + (path->string (build-path project-root "driver.rkt"))) + +(define racket-path + (or (find-executable-path "racket") "racket")) + +;; ============================================================ +;; One-shot run +;; ============================================================ + +;; Run driver.rkt PATH once. Capture stderr. Parse PHASE-TIMINGS. +;; Return (values wall-ms phases-hash), where phases-hash has keys: +;; 'parse_ms 'elaborate_ms 'type_check_ms 'trait_resolve_ms +;; 'qtt_ms 'zonk_ms 'reduce_ms +(define (run-once program-path) + (define t0 (current-inexact-monotonic-milliseconds)) + (define-values (proc stdout-port stdin-port stderr-port) + (subprocess #f #f #f racket-path driver-path program-path)) + (close-output-port stdin-port) + (subprocess-wait proc) + (define t1 (current-inexact-monotonic-milliseconds)) + (define wall-ms (- t1 t0)) + (define err-text (port->string stderr-port)) + (close-input-port stdout-port) + (close-input-port stderr-port) + (values wall-ms (extract-phases err-text))) + +(define phase-re + #rx"PHASE-TIMINGS:(\\{[^}]*\\})") + +;; Extract the PHASE-TIMINGS JSON from the stderr text. Because process-file +;; emits one cumulative PHASE-TIMINGS per file, we take the LAST match (the +;; summary) and ignore earlier ones (which are per-preload zeroes). +(define (extract-phases err-text) + (define matches (regexp-match* phase-re err-text #:match-select cadr)) + (cond + [(null? matches) (hasheq)] + [else + (define last-json (last matches)) + (string->jsexpr last-json)])) + +;; ============================================================ +;; Median over N runs +;; ============================================================ + +(define (median xs) + (define sorted (sort xs <)) + (define n (length sorted)) + (cond + [(zero? n) 0] + [(odd? n) (list-ref sorted (quotient n 2))] + [else (/ (+ (list-ref sorted (sub1 (quotient n 2))) + (list-ref sorted (quotient n 2))) + 2)])) + +(define (run-n program-path n) + ;; Warmup (not counted). + (run-once program-path) + (for/list ([_ (in-range n)]) + (collect-garbage 'major) + (define-values (wall phases) (run-once program-path)) + (hasheq 'wall_ms wall 'phases phases))) + +;; ============================================================ +;; Reporting +;; ============================================================ + +(define phase-keys + '(parse_ms elaborate_ms type_check_ms trait_resolve_ms qtt_ms zonk_ms reduce_ms)) + +(define (report-one program-path runs) + (define walls (map (λ (r) (hash-ref r 'wall_ms 0)) runs)) + (define walls-median (median walls)) + (define walls-min (apply min walls)) + (define walls-max (apply max walls)) + (printf "\n── ~a ──\n" program-path) + (printf " wall_ms : median ~a (min ~a, max ~a, n=~a)\n" + (exact-round walls-median) + (exact-round walls-min) + (exact-round walls-max) + (length runs)) + ;; Per-phase medians. + (define phase-samples + (for/hasheq ([k (in-list phase-keys)]) + (values k + (for/list ([r (in-list runs)]) + (hash-ref (hash-ref r 'phases (hasheq)) k 0))))) + (define phase-medians + (for/hasheq ([k (in-list phase-keys)]) + (values k (median (hash-ref phase-samples k))))) + (define phase-sum + (for/sum ([k (in-list phase-keys)]) + (hash-ref phase-medians k 0))) + (printf " Phase totals (median ms of ~a runs):\n" (length runs)) + (for ([k (in-list phase-keys)]) + (define v (hash-ref phase-medians k 0)) + (define pct (if (zero? walls-median) 0 (* 100.0 (/ v walls-median)))) + (printf " ~a : ~a ms (~a%% of wall)\n" + (~a k #:width 18) + (~a (exact-round v) #:width 6 #:align 'right) + (real->decimal-string pct 1))) + (printf " ~a : ~a ms (~a%% of wall) -- sum of phases above\n" + (~a "[all user phases]" #:width 18) + (~a (exact-round phase-sum) #:width 6 #:align 'right) + (real->decimal-string + (if (zero? walls-median) 0 (* 100.0 (/ phase-sum walls-median))) 1)) + (define overhead (max 0 (- walls-median phase-sum))) + (printf " ~a : ~a ms (~a%% of wall) -- prelude load + Racket startup + I/O\n" + (~a "[outside phases]" #:width 18) + (~a (exact-round overhead) #:width 6 #:align 'right) + (real->decimal-string + (if (zero? walls-median) 0 (* 100.0 (/ overhead walls-median))) 1))) + +(define (~a v #:width [w 0] #:align [align 'left]) + (define s (format "~a" v)) + (define pad (max 0 (- w (string-length s)))) + (case align + [(right) (string-append (make-string pad #\space) s)] + [else (string-append s (make-string pad #\space))])) + +(define (exact-round v) + (inexact->exact (round v))) + +;; ============================================================ +;; CLI +;; ============================================================ + +(define num-runs (make-parameter 3)) + +(define files + (command-line + #:program "bench-phases" + #:once-each + [("--runs") N "Measured runs per program (default: 3, plus 1 warmup)" + (num-runs (string->number N))] + #:args program-paths + program-paths)) + +(when (null? files) + (eprintf "usage: racket tools/bench-phases.rkt [--runs N] FILE.prologos ...\n") + (exit 1)) + +(printf "═══ Phase-timing benchmark (runs=~a per file) ═══\n" (num-runs)) +(for ([f (in-list files)]) + (define runs (run-n f (num-runs))) + (report-one f runs)) +(newline)