aboutsummaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
authoreunhhu <[email protected]>2026-10-08 16:15:25 +0100
committereunhhu <[email protected]>2026-10-08 16:15:25 +0100
commite62a8e3af75f9099a64380398ff581fb03c5bed8 (patch)
treed84e5a8f2d80d3f0e678eb0a9ec0b42b318714a9 /tests
parentea79694ede16a9204d71a8effe638cf580666ed0 (diff)
downloadpure-e62a8e3af75f9099a64380398ff581fb03c5bed8.tar.gz
pure-e62a8e3af75f9099a64380398ff581fb03c5bed8.zip
check.py: REDUCE_SOURCE, reductions that start with no lanes (a count, uniform max and min, a fraction, a sum past 2^53, a count in a nested loop, the same e then a masked or a lane's own and the other way), against the rounds one by one and the compiled code; it makes lanes from the one number 56 times and folds the one number 77 times
Diffstat (limited to 'tests')
-rw-r--r--tests/check.py68
1 files changed, 68 insertions, 0 deletions
diff --git a/tests/check.py b/tests/check.py
index ddb4a7a..2314c36 100644
--- a/tests/check.py
+++ b/tests/check.py
@@ -1227,6 +1227,61 @@ on go {
"""
+# Reductions that start with no lanes (REDUCE_SOURCE, docs/design/025): the
+# same e in every lane (a count, a uniform max and min, a fraction, a sum
+# past 2^53 one a lane), a count in a nested loop of the same rounds in
+# every lane, and the same e first, then a masked one or a lane's own (the
+# lanes made from the one number), in both orders.
+REDUCE_SOURCE = """state count = 0
+state mixed = 0
+state other = 0
+state low = 0
+state high = 0
+state frac = 0
+state far = 0
+state inner = 0
+input go = message {
+ n: u32
+ q: u32
+}
+on go {
+ let c = 0
+ let m = 0
+ let o = 0
+ let lo = 1000000
+ let hi = 0 - 1000000
+ let f = 0
+ let x = 0
+ let t = 0
+ for i in 0 .. go.n {
+ c = c + 1
+ m = m + go.q
+ if i - floor(i / go.q) * go.q == 0 { m = m + i }
+ o = o + i
+ o = o + 3
+ lo = min(lo, go.q)
+ hi = max(hi, go.q * 2)
+ hi = max(hi, i - 5)
+ f = f + 0.5
+ x = x + 1099511627777
+ }
+ for i in 0 .. go.n {
+ for k in 0 .. go.q {
+ t = t + 2
+ }
+ t = t + i
+ }
+ count = c
+ mixed = m
+ other = o
+ low = lo
+ high = hi
+ frac = f
+ far = x
+ inner = t
+}
+"""
+
# Nested loops a batch runs together, and reductions (NESTED_SOURCE): Newton
# steps breaking out lane by lane, a sum of fractions one a round, a nested
# loop of 0 to 6 rounds a lane whose division by 0 is a fault in some
@@ -1643,6 +1698,19 @@ def module_assumptions(aup, pure, work):
assert outputs[name] == outputs['batches'], ('nested', name, next((a, b) for a, b in zip(outputs[name].splitlines(), outputs['batches'].splitlines()) if a != b))
faults = [json.loads(line)['fault'] for line in outputs['batches'].splitlines() if line.startswith('{"states"')]
assert 'divide' in faults and 'none' in faults and 'work' in faults, faults
+ source, program = work / 'reduce.aup', work / 'reduce.pmod' # reductions with no lanes yet, against the rounds one by one
+ source.write_text(REDUCE_SOURCE)
+ compile_module(aup, source, program)
+ given = ''.join(f'message go {struct.pack("<II", n, q).hex()}\nwork\n' for n, q in
+ ((3000, 7), (3000, 1), (10000, 3), (40, 3), (2, 5), (1500, 2000)))
+ given += 'budget 30011\n' + given + 'budget 0\n'
+ outputs = {name: subprocess.run([str(pure / 'build/pure-run'), str(program)], input=given, capture_output=True, text=True,
+ timeout=120, env=dict(os.environ, **env)).stdout
+ for name, env in (('batches', {'PURE_JIT': '0'}), ('rounds', {'PURE_JIT': '0', 'PURE_BATCH': '0'}), ('all', {'PURE_JIT': 'all'}))}
+ for name in ('rounds', 'all'):
+ assert outputs[name] == outputs['batches'], ('reduce', name, next((a, b) for a, b in zip(outputs[name].splitlines(), outputs['batches'].splitlines()) if a != b))
+ faults = [json.loads(line)['fault'] for line in outputs['batches'].splitlines() if line.startswith('{"states"')]
+ assert 'none' in faults and 'work' in faults, faults
return len(runs)