Skip to content

Commit 1f069ad

Browse files
tests: +61 across autograd / generators / substrate primitives
test_autograd_extras.omc (22): tape_sub/div/neg/pow_int/exp/sin/cos/mean grads, tape_update decreases loss, tape_reset clears state, dual constructor + read, dual_mul chain rule, dual_add/sub/div/neg, dual_pow_int, dual_exp/sin/cos at zero, dual_relu both branches. test_generators_extras.omc (14): eager count + single-yield, gen_count zero/100, gen_sum 5/empty, gen_take 0/exact/over-available, gen_stream early-stop/complete, gen_substrate_fib zero/first-7/huge-limit-with-stop. test_substrate_extras.omc (25): is_attractor for Fibonacci 0..144 (12 positive cases) + non-attractor 4/6/7/100, attractor_distance on/off/far, fibonacci_index, arr_resonance_vec all-attractors-→-1, arr_fold_all snaps every result to an attractor, crt_recover with 4 substrate moduli (zero + one cases). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent f2d6c02 commit 1f069ad

3 files changed

Lines changed: 420 additions & 0 deletions

File tree

Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
# Additional autograd / dual coverage.
2+
3+
fn assert_eq(actual, expected, msg) {
4+
if actual != expected {
5+
test_record_failure(msg + ": expected " + to_string(expected) + " got " + to_string(actual));
6+
}
7+
}
8+
9+
fn assert_true(cond, msg) { if !cond { test_record_failure(msg); } }
10+
11+
fn approx_eq(a, b, tol) {
12+
h d = a - b;
13+
if d < 0.0 { d = 0.0 - d; }
14+
return d <= tol;
15+
}
16+
17+
# ---- tape_sub ----
18+
fn test_tape_sub_grad() {
19+
tape_reset();
20+
h x = tape_var(5);
21+
h y = tape_var(3);
22+
h s = tape_sub(x, y);
23+
tape_backward(s);
24+
assert_true(approx_eq(tape_grad(x), 1.0, 0.001), "dx for x-y = 1");
25+
assert_true(approx_eq(tape_grad(y), 0 - 1.0, 0.001), "dy for x-y = -1");
26+
}
27+
28+
# ---- tape_div ----
29+
fn test_tape_div_grad() {
30+
tape_reset();
31+
h x = tape_var(6);
32+
h y = tape_var(2);
33+
h q = tape_div(x, y); # 3.0
34+
tape_backward(q);
35+
# d(x/y)/dx = 1/y = 0.5
36+
assert_true(approx_eq(tape_grad(x), 0.5, 0.001), "d(x/y)/dx");
37+
}
38+
39+
# ---- tape_neg ----
40+
fn test_tape_neg_grad() {
41+
tape_reset();
42+
h x = tape_var(7);
43+
h n = tape_neg(x);
44+
tape_backward(n);
45+
assert_true(approx_eq(tape_grad(x), 0 - 1.0, 0.001), "d(-x)/dx = -1");
46+
}
47+
48+
# ---- tape_pow_int ----
49+
fn test_tape_pow_int_cube() {
50+
tape_reset();
51+
h x = tape_var(2);
52+
h y = tape_pow_int(x, 3); # x^3 = 8
53+
tape_backward(y);
54+
# dy/dx = 3*x^2 = 12 at x=2
55+
assert_true(approx_eq(tape_grad(x), 12.0, 0.001), "d(x^3)/dx at 2");
56+
}
57+
58+
# ---- tape_exp ----
59+
fn test_tape_exp_at_zero() {
60+
tape_reset();
61+
h x = tape_var(0);
62+
h y = tape_exp(x);
63+
tape_backward(y);
64+
assert_true(approx_eq(tape_grad(x), 1.0, 0.001), "d(exp)/dx at 0 = 1");
65+
}
66+
67+
# ---- tape_sin / tape_cos ----
68+
fn test_tape_sin_at_zero() {
69+
tape_reset();
70+
h x = tape_var(0);
71+
h y = tape_sin(x);
72+
tape_backward(y);
73+
assert_true(approx_eq(tape_grad(x), 1.0, 0.001), "d(sin)/dx at 0 = cos(0) = 1");
74+
}
75+
76+
fn test_tape_cos_at_zero() {
77+
tape_reset();
78+
h x = tape_var(0);
79+
h y = tape_cos(x);
80+
tape_backward(y);
81+
assert_true(approx_eq(tape_grad(x), 0.0, 0.001), "d(cos)/dx at 0 = -sin(0) = 0");
82+
}
83+
84+
# ---- tape_mean ----
85+
fn test_tape_mean_grad() {
86+
tape_reset();
87+
h X = tape_var([1.0, 2.0, 3.0]);
88+
h m = tape_mean(X);
89+
tape_backward(m);
90+
h gX = tape_grad(X);
91+
h diff = arr_get(gX, 0) - 0.3333333;
92+
if diff < 0.0 { diff = 0.0 - diff; }
93+
assert_true(diff < 0.01, "d(mean)/dx_i = 1/n");
94+
}
95+
96+
# ---- tape_update SGD-step ----
97+
fn test_tape_update_decreases_loss() {
98+
tape_reset();
99+
h w = tape_var(5.0);
100+
h target = tape_const(0.0);
101+
h diff = tape_sub(w, target);
102+
h loss = tape_mul(diff, diff);
103+
tape_backward(loss);
104+
h before = tape_value(loss);
105+
tape_update(w, 0.1);
106+
# Verify value moved toward target.
107+
h new_w = tape_value(w);
108+
assert_true(new_w < 5.0, "w decreased toward target");
109+
}
110+
111+
# ---- tape_reset clears state ----
112+
fn test_tape_reset_clears() {
113+
tape_reset();
114+
h x = tape_var(1); # ID 0
115+
tape_reset();
116+
h y = tape_var(2); # also ID 0 after reset
117+
# No assertion needed beyond not crashing.
118+
assert_true(1, "no crash");
119+
}
120+
121+
# ---- Dual forward-mode coverage ----
122+
fn test_dual_construct_and_read() {
123+
h d = dual(3.0, 1.0);
124+
assert_true(approx_eq(dual_v(d), 3.0, 0.001), "value");
125+
assert_true(approx_eq(dual_d(d), 1.0, 0.001), "derivative");
126+
}
127+
128+
fn test_dual_mul_chain() {
129+
h x = dual(2.0, 1.0);
130+
h y = dual_mul(x, x);
131+
assert_true(approx_eq(dual_d(y), 4.0, 0.001), "d(x^2)/dx at 2 = 4");
132+
}
133+
134+
fn test_dual_add_combines() {
135+
h a = dual(3.0, 1.0);
136+
h b = dual(4.0, 0.0); # constant
137+
h c = dual_add(a, b);
138+
assert_true(approx_eq(dual_v(c), 7.0, 0.001), "value");
139+
assert_true(approx_eq(dual_d(c), 1.0, 0.001), "deriv passes through");
140+
}
141+
142+
fn test_dual_sub() {
143+
h a = dual(5.0, 1.0);
144+
h b = dual(3.0, 0.0);
145+
h c = dual_sub(a, b);
146+
assert_true(approx_eq(dual_v(c), 2.0, 0.001), "value");
147+
}
148+
149+
fn test_dual_div() {
150+
h a = dual(6.0, 1.0);
151+
h b = dual(2.0, 0.0);
152+
h c = dual_div(a, b);
153+
assert_true(approx_eq(dual_v(c), 3.0, 0.001), "value");
154+
assert_true(approx_eq(dual_d(c), 0.5, 0.001), "deriv 1/b");
155+
}
156+
157+
fn test_dual_neg() {
158+
h a = dual(3.0, 1.0);
159+
h n = dual_neg(a);
160+
assert_true(approx_eq(dual_v(n), 0 - 3.0, 0.001), "value negated");
161+
assert_true(approx_eq(dual_d(n), 0 - 1.0, 0.001), "deriv negated");
162+
}
163+
164+
fn test_dual_pow_int_cube() {
165+
h x = dual(2.0, 1.0);
166+
h y = dual_pow_int(x, 3);
167+
assert_true(approx_eq(dual_v(y), 8.0, 0.001), "2^3 = 8");
168+
assert_true(approx_eq(dual_d(y), 12.0, 0.001), "d/dx = 3x^2");
169+
}
170+
171+
fn test_dual_exp_at_zero() {
172+
h x = dual(0.0, 1.0);
173+
h y = dual_exp(x);
174+
assert_true(approx_eq(dual_v(y), 1.0, 0.001), "exp 0 = 1");
175+
assert_true(approx_eq(dual_d(y), 1.0, 0.001), "d/dx exp at 0 = 1");
176+
}
177+
178+
fn test_dual_sin_at_zero() {
179+
h x = dual(0.0, 1.0);
180+
h y = dual_sin(x);
181+
assert_true(approx_eq(dual_v(y), 0.0, 0.001), "sin 0 = 0");
182+
}
183+
184+
fn test_dual_cos_at_zero() {
185+
h x = dual(0.0, 1.0);
186+
h y = dual_cos(x);
187+
assert_true(approx_eq(dual_v(y), 1.0, 0.001), "cos 0 = 1");
188+
}
189+
190+
fn test_dual_relu_positive() {
191+
h x = dual(5.0, 1.0);
192+
h y = dual_relu(x);
193+
assert_true(approx_eq(dual_v(y), 5.0, 0.001), "relu+ keeps value");
194+
assert_true(approx_eq(dual_d(y), 1.0, 0.001), "relu+ keeps deriv");
195+
}
196+
197+
fn test_dual_relu_negative() {
198+
h x = dual(0 - 5.0, 1.0);
199+
h y = dual_relu(x);
200+
assert_true(approx_eq(dual_v(y), 0.0, 0.001), "relu- → 0");
201+
assert_true(approx_eq(dual_d(y), 0.0, 0.001), "relu- deriv = 0");
202+
}
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
# Additional generator coverage.
2+
3+
fn assert_eq(actual, expected, msg) {
4+
if actual != expected {
5+
test_record_failure(msg + ": expected " + to_string(expected) + " got " + to_string(actual));
6+
}
7+
}
8+
9+
fn assert_true(cond, msg) { if !cond { test_record_failure(msg); } }
10+
11+
fn count_to_n(n) {
12+
h i = 1;
13+
while i <= n {
14+
yield i;
15+
i = i + 1;
16+
}
17+
}
18+
19+
fn empty_gen() { return; }
20+
21+
fn yield_once(v) { yield v; }
22+
23+
# Basic eager.
24+
fn test_eager_count() {
25+
h xs = count_to_n(5);
26+
assert_eq(arr_len(xs), 5, "5 yields");
27+
}
28+
29+
fn test_eager_one() {
30+
h xs = yield_once(42);
31+
assert_eq(arr_len(xs), 1, "one yield");
32+
assert_eq(arr_get(xs, 0), 42, "value preserved");
33+
}
34+
35+
# Lazy gen_count.
36+
fn test_gen_count_zero() {
37+
h n = gen_count(fn() { return empty_gen(); });
38+
assert_eq(n, 0, "empty gen");
39+
}
40+
41+
fn test_gen_count_100() {
42+
h n = gen_count(fn() { return count_to_n(100); });
43+
assert_eq(n, 100, "100 yields");
44+
}
45+
46+
# Lazy gen_sum.
47+
fn test_gen_sum_5() {
48+
h s = gen_sum(fn() { return count_to_n(5); });
49+
assert_eq(s, 15, "1+2+3+4+5");
50+
}
51+
52+
fn test_gen_sum_zero_empty() {
53+
h s = gen_sum(fn() { return empty_gen(); });
54+
assert_eq(s, 0, "empty sums to 0");
55+
}
56+
57+
# Lazy gen_take.
58+
fn test_gen_take_zero() {
59+
h xs = gen_take(fn() { return count_to_n(100); }, 0);
60+
assert_eq(arr_len(xs), 0, "take 0");
61+
}
62+
63+
fn test_gen_take_exact() {
64+
h xs = gen_take(fn() { return count_to_n(5); }, 5);
65+
assert_eq(arr_len(xs), 5, "took all");
66+
}
67+
68+
fn test_gen_take_more_than_available() {
69+
h xs = gen_take(fn() { return count_to_n(3); }, 100);
70+
assert_eq(arr_len(xs), 3, "all 3 available");
71+
}
72+
73+
# gen_stream early stop.
74+
fn test_gen_stream_stops_early() {
75+
h count = [0];
76+
h done = gen_stream(
77+
fn() { return count_to_n(100); },
78+
fn(v) {
79+
arr_set(count, 0, arr_get(count, 0) + 1);
80+
if arr_get(count, 0) >= 3 { return 0; }
81+
return 1;
82+
}
83+
);
84+
assert_eq(arr_get(count, 0), 3, "stopped after 3");
85+
assert_eq(done, 0, "shorted = 0");
86+
}
87+
88+
fn test_gen_stream_completes() {
89+
h done = gen_stream(
90+
fn() { return count_to_n(5); },
91+
fn(v) { return 1; }
92+
);
93+
assert_eq(done, 1, "completed = 1");
94+
}
95+
96+
# Substrate fib.
97+
fn test_substrate_fib_zero() {
98+
h collected = [];
99+
gen_substrate_fib(
100+
fn(v) { arr_push(collected, v); return 1; },
101+
0
102+
);
103+
# 0 is the first Fibonacci, so we get [0].
104+
assert_eq(arr_len(collected), 1, "0 included");
105+
}
106+
107+
fn test_substrate_fib_first_few() {
108+
h collected = [];
109+
gen_substrate_fib(
110+
fn(v) { arr_push(collected, v); return 1; },
111+
10
112+
);
113+
# Fibs <= 10: 0, 1, 1, 2, 3, 5, 8.
114+
assert_eq(arr_len(collected), 7, "7 Fibs ≤ 10");
115+
}
116+
117+
fn test_substrate_fib_huge_limit() {
118+
h count = [0];
119+
gen_substrate_fib(
120+
fn(v) {
121+
arr_set(count, 0, arr_get(count, 0) + 1);
122+
if arr_get(count, 0) >= 30 { return 0; }
123+
return 1;
124+
},
125+
1000000000
126+
);
127+
assert_eq(arr_get(count, 0), 30, "stopped at 30");
128+
}

0 commit comments

Comments
 (0)