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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Ix/IxVM/Blake3.lean
Original file line number Diff line number Diff line change
Expand Up @@ -301,11 +301,11 @@ def blake3 := ⟦
x: [U8; 4],
y: [U8; 4]
) -> [[U8; 4]; 4] {
let a = @u32_add(@u32_add(a, b), x);
let a = @u32_add3(a, b, x);
let d = @u32_rotr16(@u32_xor(d, a));
let c = @u32_add(c, d);
let b = @u32_rotr12(@u32_xor(b, c));
let a = @u32_add(@u32_add(a, b), y);
let a = @u32_add3(a, b, y);
let d = @u32_rotr8(@u32_xor(d, a));
let c = @u32_add(c, d);
let b = @u32_rotr7(@u32_xor(b, c));
Expand Down
75 changes: 71 additions & 4 deletions Ix/IxVM/ByteStream.lean
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,10 @@ def byteStream := ⟦
}
}

fn u32_add(a: [U8; 4], b: [U8; 4]) -> [U8; 4] {
-- Witness the wrapping sum and its carry. This function is called only as
-- an unconstrained hint by `u32_add`; none of these byte operations enter
-- the proof. The caller range-checks and pins every returned value.
fn u32_add_hint(a: [U8; 4], b: [U8; 4]) -> ([G; 4], G) {
let [a0, a1, a2, a3] = a;
let [b0, b1, b2, b3] = b;

Expand All @@ -64,10 +67,74 @@ def byteStream := ⟦
let carry3 = u8_from_field_unsafe(to_field(overflow2) + to_field(carry2a));

-- Byte 3
let (sum3, _x) = u8_add(a3, b3);
let (sum3_with_carry, _x) = u8_add(sum3, carry3);
let (sum3, overflow3) = u8_add(a3, b3);
let (sum3_with_carry, carry3a) = u8_add(sum3, carry3);
let carry4 = to_field(overflow3) + to_field(carry3a);

([to_field(sum0), to_field(sum1_with_carry),
to_field(sum2_with_carry), to_field(sum3_with_carry)], carry4)
}

-- Wrapping little-endian u32 addition. The expensive bytewise addition is
-- advice only; this circuit verifies its five witnesses directly:
-- four range-checked result bytes, a boolean carry, and the packed integer
-- identity `a + b = result + carry * 2^32`. Since both sides are < 2^33,
-- the Goldilocks field equality is the intended integer equality (no field
-- wrap), and the checked decomposition is unique.
fn u32_add(a: [U8; 4], b: [U8; 4]) -> [U8; 4] {
let (raw, carry) = #u32_add_hint(a, b);
let (z0, z1) = u8_range_check(raw[0], raw[1]);
let (z2, z3) = u8_range_check(raw[2], raw[3]);

assert_eq!(carry * carry, carry, "u32_add: carry is not boolean");

let av = to_field(a[0]) + 0x100 * to_field(a[1])
+ 0x10000 * to_field(a[2]) + 0x1000000 * to_field(a[3]);
let bv = to_field(b[0]) + 0x100 * to_field(b[1])
+ 0x10000 * to_field(b[2]) + 0x1000000 * to_field(b[3]);
let zv = to_field(z0) + 0x100 * to_field(z1)
+ 0x10000 * to_field(z2) + 0x1000000 * to_field(z3);
assert_eq!(av + bv, zv + 0x100000000 * carry,
"u32_add: witnessed sum does not match its inputs");

[z0, z1, z2, z3]
}

-- Witness a wrapping three-word sum. Both nested additions execute under
-- the caller's unconstrained mode, so their byte operations are advice
-- only. The two binary overflow bits add to the total carry in {0,1,2}.
fn u32_add3_hint(a: [U8; 4], b: [U8; 4], c: [U8; 4]) -> ([G; 4], G) {
let (ab, carry1) = u32_add_hint(a, b);
let ab = [u8_from_field_unsafe(ab[0]), u8_from_field_unsafe(ab[1]),
u8_from_field_unsafe(ab[2]), u8_from_field_unsafe(ab[3])];
let (z, carry2) = u32_add_hint(ab, c);
(z, carry1 + carry2)
}

[sum0, sum1_with_carry, sum2_with_carry, sum3_with_carry]
-- Wrapping sum of three little-endian u32s, pinned directly rather than as
-- two binary additions. The output costs two paired range-check lookups;
-- the cubic carry constraint admits exactly 0, 1, or 2. The packed integer
-- identity cannot wrap in Goldilocks because its values are below 2^34.
fn u32_add3(a: [U8; 4], b: [U8; 4], c: [U8; 4]) -> [U8; 4] {
let (raw, carry) = #u32_add3_hint(a, b, c);
let (z0, z1) = u8_range_check(raw[0], raw[1]);
let (z2, z3) = u8_range_check(raw[2], raw[3]);

assert_eq!(carry * (carry - 1) * (carry - 2), 0,
"u32_add3: carry is not in {0, 1, 2}");

let av = to_field(a[0]) + 0x100 * to_field(a[1])
+ 0x10000 * to_field(a[2]) + 0x1000000 * to_field(a[3]);
let bv = to_field(b[0]) + 0x100 * to_field(b[1])
+ 0x10000 * to_field(b[2]) + 0x1000000 * to_field(b[3]);
let cv = to_field(c[0]) + 0x100 * to_field(c[1])
+ 0x10000 * to_field(c[2]) + 0x1000000 * to_field(c[3]);
let zv = to_field(z0) + 0x100 * to_field(z1)
+ 0x10000 * to_field(z2) + 0x1000000 * to_field(z3);
assert_eq!(av + bv + cv, zv + 0x100000000 * carry,
"u32_add3: witnessed sum does not match its inputs");

[z0, z1, z2, z3]
}

fn u32_xor(a: [U8; 4], b: [U8; 4]) -> [U8; 4] {
Expand Down
142 changes: 71 additions & 71 deletions Tests/Ix/IxVM.lean
Original file line number Diff line number Diff line change
Expand Up @@ -276,77 +276,77 @@ private def nameOfString (str : String) : Lean.Name :=
listed constant fails the suite, so a regression cannot land quietly
and an improvement has to be acknowledged by re-pinning. -/
private def kernelCheckEntries : List (String × Nat) := [
("HEq", 129_468_104),
("HEq.rec", 132_906_026),
("Eq.rec", 132_478_828),
("Nat", 129_465_234),
("Nat.add", 165_101_768),
("Nat.add_comm", 292_233_964),
("Nat.decEq", 341_688_609),
("Nat.decLe", 732_649_143),
("Nat.sub_le_of_le_add", 1_793_179_373),
("Nat.shiftRight_succ", 1_331_066_612),
("Trans.mk", 134_728_178),
("Array.append_assoc", 8_622_658_358),
("Vector.append", 8_832_851_736),
("IxVMPrim.nat_add_lit", 205_724_339),
("IxVMPrim.nat_sub_lit", 222_270_629),
("IxVMPrim.nat_mul_lit", 197_332_156),
("IxVMPrim.nat_mul_big", 195_705_843),
("IxVMPrim.nat_div_lit", 1_300_339_885),
("IxVMPrim.nat_mod_lit", 1_325_741_257),
("IxVMPrim.nat_succ_lit", 143_973_320),
("IxVMPrim.nat_pred_lit", 165_092_837),
("IxVMPrim.nat_gcd_lit", 2_077_835_172),
("IxVMPrim.nat_land_lit", 3_392_593_650),
("IxVMPrim.nat_lor_lit", 3_394_992_980),
("IxVMPrim.nat_xor_lit", 3_414_869_484),
("IxVMPrim.nat_shl_lit", 225_040_363),
("IxVMPrim.nat_shr_lit", 1_315_348_247),
("IxVMPrim.nat_pow_big", 386_936_789),
("IxVMPrim.nat_beq_lit", 195_521_462),
("IxVMPrim.nat_ble_lit", 190_279_372),
("IxVMPrim.nat_cases_big", 164_636_470),
("IxVMPrim.nat_dec_le", 750_956_369),
("IxVMPrim.nat_dec_lt", 762_189_408),
("IxVMPrim.nat_dec_eq", 380_458_982),
("IxVMPrim.str_size_lit", 2_388_927_980),
("IxVMPrim.bv_to_nat_lit", 1_978_174_861),
("IxVMInd.Even", 199_959_069),
("IxVMInd.Odd", 199_961_684),
("IxVMInd.Even.rec", 217_262_251),
("IxVMInd.Odd.rec", 217_263_174),
("IxVMInd.Tree", 130_857_345),
("IxVMInd.Tree.rec", 139_889_786),
("IxVMInd.DedupM", 134_173_839),
("IxVMInd.DedupM.rec", 146_300_644),
("IxVMInd.DepthM", 132_567_341),
("IxVMInd.DepthM.rec", 142_699_828),
("String.Internal.append", 2_360_689_108),
("_private.Init.Prelude.0.Lean.extractMainModule._unsafe_rec", 3_499_089_916),
("Lean.Syntax.rec", 2_415_335_613),
("IxVMInd.AuxTie", 315_165_566),
("IxVMInd.AuxTie.rec", 353_876_276),
("IxVMInd.HiddenIdx", 130_050_254),
("IxVMInd.HiddenIdx.rec", 132_543_931),
("IxVMInd.thmMajorUse", 488_836_722),
("IxVMInd.partialKRec", 149_911_108),
("IxVMInd.deepRebase", 208_423_967),
("String.Slice.Pattern.Model.NoPrefixForwardPatternModel.rec", 3_305_647_446),
("Lean.Widget.TaggedText.rec", 2_384_562_452),
("Lean.Doc.Part.rec", 2_425_540_913),
("Lean.Doc.Block.rec", 2_582_167_982),
("_private.Tests.Ix.Compile.Mutual.0.Tests.Ix.Compile.Mutual.AuxDedup1.A", 132_244_429),
("_private.Tests.Ix.Compile.Mutual.0.Tests.Ix.Compile.Mutual.AuxDedup1.A.rec", 135_209_335),
("_private.Tests.Ix.Compile.Mutual.0.Tests.Ix.Compile.Mutual.AuxDedup1.A.rec_1", 134_204_496),
("_private.Tests.Ix.Compile.Mutual.0.Tests.Ix.Compile.Mutual.AuxDedup1.A.rec_2", 134_204_496),
("_private.Tests.Ix.Compile.Mutual.0.Tests.Ix.Compile.Mutual.AuxDedup2.A.rec_1", 134_204_496),
("_private.Tests.Ix.Compile.Mutual.0.Tests.Ix.Compile.Mutual.AuxDedupMixed.M", 132_442_751),
("_private.Tests.Ix.Compile.Mutual.0.Tests.Ix.Compile.Mutual.AuxDedupMixed.M.rec", 142_670_108),
("_private.Tests.Ix.Compile.Mutual.0.Tests.Ix.Compile.Mutual.AuxDedupMixed.M.rec_1", 142_669_322),
("_private.Tests.Ix.Compile.Mutual.0.Tests.Ix.Compile.Mutual.AuxDedupMixed.M.rec_2", 134_204_496),
("strOfListFoldSize", 2_677_189_647),
("strOfListFoldSizeAscii", 2_678_115_796),
("HEq", 129_283_627),
("HEq.rec", 132_355_785),
("Eq.rec", 131_928_587),
("Nat", 129_280_757),
("Nat.add", 160_683_122),
("Nat.add_comm", 272_917_392),
("Nat.decEq", 319_807_459),
("Nat.decLe", 667_980_590),
("Nat.sub_le_of_le_add", 1_618_927_539),
("Nat.shiftRight_succ", 1_203_237_303),
("Trans.mk", 134_177_937),
("Array.append_assoc", 7_892_946_813),
("Vector.append", 8_077_261_126),
("IxVMPrim.nat_add_lit", 195_499_542),
("IxVMPrim.nat_sub_lit", 209_495_559),
("IxVMPrim.nat_mul_lit", 187_990_742),
("IxVMPrim.nat_mul_big", 186_656_745),
("IxVMPrim.nat_div_lit", 1_175_209_045),
("IxVMPrim.nat_mod_lit", 1_197_333_005),
("IxVMPrim.nat_succ_lit", 141_489_502),
("IxVMPrim.nat_pred_lit", 159_453_199),
("IxVMPrim.nat_gcd_lit", 1_869_344_980),
("IxVMPrim.nat_land_lit", 3_032_222_365),
("IxVMPrim.nat_lor_lit", 3_034_198_108),
("IxVMPrim.nat_xor_lit", 3_050_684_377),
("IxVMPrim.nat_shl_lit", 211_656_052),
("IxVMPrim.nat_shr_lit", 1_187_711_864),
("IxVMPrim.nat_pow_big", 366_822_593),
("IxVMPrim.nat_beq_lit", 186_618_103),
("IxVMPrim.nat_ble_lit", 182_100_356),
("IxVMPrim.nat_cases_big", 159_271_360),
("IxVMPrim.nat_dec_le", 683_753_642),
("IxVMPrim.nat_dec_lt", 693_352_535),
("IxVMPrim.nat_dec_eq", 353_351_557),
("IxVMPrim.str_size_lit", 2_132_351_314),
("IxVMPrim.bv_to_nat_lit", 1_777_943_887),
("IxVMInd.Even", 190_177_144),
("IxVMInd.Odd", 190_179_760),
("IxVMInd.Even.rec", 205_394_750),
("IxVMInd.Odd.rec", 205_395_673),
("IxVMInd.Tree", 130_498_296),
("IxVMInd.Tree.rec", 138_364_938),
("IxVMInd.DedupM", 133_208_754),
("IxVMInd.DedupM.rec", 143_816_825),
("IxVMInd.DepthM", 131_916_917),
("IxVMInd.DepthM.rec", 140_823_123),
("String.Internal.append", 2_108_221_220),
("_private.Init.Prelude.0.Lean.extractMainModule._unsafe_rec", 3_116_849_069),
("Lean.Syntax.rec", 2_155_056_055),
("IxVMInd.AuxTie", 287_231_017),
("IxVMInd.AuxTie.rec", 320_925_972),
("IxVMInd.HiddenIdx", 129_781_042),
("IxVMInd.HiddenIdx.rec", 131_993_689),
("IxVMInd.thmMajorUse", 450_274_623),
("IxVMInd.partialKRec", 146_926_142),
("IxVMInd.deepRebase", 197_902_654),
("String.Slice.Pattern.Model.NoPrefixForwardPatternModel.rec", 2_962_816_581),
("Lean.Widget.TaggedText.rec", 2_129_835_463),
("Lean.Doc.Part.rec", 2_165_878_829),
("Lean.Doc.Block.rec", 2_306_617_015),
("_private.Tests.Ix.Compile.Mutual.0.Tests.Ix.Compile.Mutual.AuxDedup1.A", 131_594_005),
("_private.Tests.Ix.Compile.Mutual.0.Tests.Ix.Compile.Mutual.AuxDedup1.A.rec", 134_135_426),
("_private.Tests.Ix.Compile.Mutual.0.Tests.Ix.Compile.Mutual.AuxDedup1.A.rec_1", 133_451_358),
("_private.Tests.Ix.Compile.Mutual.0.Tests.Ix.Compile.Mutual.AuxDedup1.A.rec_2", 133_451_358),
("_private.Tests.Ix.Compile.Mutual.0.Tests.Ix.Compile.Mutual.AuxDedup2.A.rec_1", 133_451_358),
("_private.Tests.Ix.Compile.Mutual.0.Tests.Ix.Compile.Mutual.AuxDedupMixed.M", 131_792_327),
("_private.Tests.Ix.Compile.Mutual.0.Tests.Ix.Compile.Mutual.AuxDedupMixed.M.rec", 140_673_909),
("_private.Tests.Ix.Compile.Mutual.0.Tests.Ix.Compile.Mutual.AuxDedupMixed.M.rec_1", 140_673_122),
("_private.Tests.Ix.Compile.Mutual.0.Tests.Ix.Compile.Mutual.AuxDedupMixed.M.rec_2", 133_451_358),
("strOfListFoldSize", 2_381_924_103),
("strOfListFoldSizeAscii", 2_382_642_101),
]

/-- Variant of `kernelChecks`, pinned to the baseline
Expand Down
4 changes: 2 additions & 2 deletions Tests/Main.lean
Original file line number Diff line number Diff line change
Expand Up @@ -223,8 +223,8 @@ def ignoredRunners (env : Lean.Environment) : List (String × IO UInt32) := [
let actual :=
(Aiur.computeStats v2Env.compiled qc v2Env.shapes).totalFftCost.round.toUInt64.toNat
pure (LSpec.test
s!"Shard pipeline FFT matches: expected 7332697739, got {actual}"
(actual = 7_332_697_739))
s!"Shard pipeline FFT matches: expected 6321805456, got {actual}"
(actual = 6_321_805_456))
LSpec.lspecIO
(.ofList [("ixvm",
[fullSeq, aiurSeq, arenaSeq, exploitSeq, paritySeq, shardSeq])]) []),
Expand Down
Loading
Loading