Skip to content

Commit a276541

Browse files
demo + tests: LLM bootstrap workflow + 10 reverse-autograd extras
examples/demos/llm_bootstrap_workflow.omc: End-to-end demo of the canonical "an LLM driving OMC via MCP" workflow — surface overview, category listing, OMC-unique primitives, builtin lookup, code memory (omc_id, remember, recall_matches), change report, error explanation. test_reverse_autograd_extras.omc (10): Chain rule sin(x^2), polynomial 3x^3 + 2x + 1, neg composition, exp(2x) via squared exp(x), multi-variable distance squared gradients, sigmoid of sum (both partials), tanh at 1, sum reduction gradient, substrate metadata visible on tape_value for integer leaves AND after operations (8 = Fibonacci attractor preserved through tape_add). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent 1a15665 commit a276541

2 files changed

Lines changed: 228 additions & 0 deletions

File tree

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
# Demonstrates the canonical LLM bootstrap + iteration workflow.
2+
#
3+
# This is what an LLM driving OMC via MCP would do at the start of a
4+
# session: pull the bootstrap pack, learn the surface, then start
5+
# writing + iterating with introspection + memory.
6+
7+
fn show(label, v) {
8+
print(concat_many(label, " = ", to_string(v)));
9+
}
10+
11+
fn main() {
12+
print("=== LLM bootstrap workflow ===");
13+
print("");
14+
15+
# Step 1: How big is the surface?
16+
print("[1] Surface overview");
17+
show(" documented builtins ", omc_builtin_count());
18+
show(" OMC-unique ", omc_unique_count());
19+
show(" categories ", omc_categories_count());
20+
show(" error patterns ", omc_error_count());
21+
print("");
22+
23+
# Step 2: What's available?
24+
print("[2] Category sample");
25+
h cats = omc_categories();
26+
h i = 0;
27+
while i < arr_len(cats) {
28+
h c = arr_get(cats, i);
29+
h items = omc_list_builtins(c);
30+
print(concat_many(" ", c, ": ", to_string(arr_len(items)), " builtins"));
31+
i = i + 1;
32+
if i >= 8 { i = arr_len(cats); } # limit output
33+
}
34+
print("");
35+
36+
# Step 3: What's the canonical OMC-only surface?
37+
print("[3] OMC-unique sample (Python has no equivalent)");
38+
h unique = omc_unique_builtins();
39+
h k = 0;
40+
while k < arr_len(unique) {
41+
if k < 8 {
42+
print(concat_many(" - ", arr_get(unique, k)));
43+
}
44+
k = k + 1;
45+
}
46+
if k > 8 { print(concat_many(" ... +", to_string(k - 8), " more")); }
47+
print("");
48+
49+
# Step 4: Help on a specific builtin (the "look it up" loop)
50+
print("[4] Looking up arr_softmax");
51+
h help = omc_help("arr_softmax");
52+
show(" signature ", dict_get(help, "signature"));
53+
show(" description", dict_get(help, "description"));
54+
show(" example ", dict_get(help, "example"));
55+
print("");
56+
57+
# Step 5: Write some code, hash it, remember
58+
print("[5] Code memory: remember, then recall");
59+
omc_memory_clear();
60+
h code_v1 = "fn loss(p, t) { return (p - t) * (p - t); }";
61+
h id = omc_id(code_v1);
62+
show(" v1 omc_id ", id);
63+
omc_remember("loss", code_v1);
64+
h code_v2 = "fn loss(pred, target) { return (pred - target) * (pred - target); }";
65+
show(" v2 omc_id ", omc_id(code_v2));
66+
show(" matches? ", omc_recall_matches("loss", code_v2)); # 1 (alpha-eq)
67+
h code_v3 = "fn loss(p, t) { return (p - t) * (p - t) + 0.01; }";
68+
show(" v3 omc_id ", omc_id(code_v3));
69+
show(" matches? ", omc_recall_matches("loss", code_v3)); # 0 (changed)
70+
print("");
71+
72+
# Step 6: Change report
73+
print("[6] What changed?");
74+
h r = omc_change_report(code_v1, code_v3);
75+
show(" modified ", dict_get(r, "modified"));
76+
show(" suggestion", dict_get(r, "suggested_action"));
77+
print("");
78+
79+
# Step 7: If error, explain it
80+
print("[7] Catching + explaining a typo");
81+
h err_msg = "";
82+
try {
83+
h _ = arr_softmx([1.0, 2.0]); # typo
84+
} catch e {
85+
err_msg = e;
86+
}
87+
h ex = omc_explain_error(err_msg);
88+
show(" matched ", dict_get(ex, "matched"));
89+
show(" fix ", dict_get(ex, "fix"));
90+
print("");
91+
92+
print("=== End: this is the workflow LLMs run via MCP ===");
93+
}
94+
95+
main();
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
# More reverse-mode autograd tests.
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+
# Scalar chain: y = sin(x^2)
18+
fn test_chain_sin_square() {
19+
tape_reset();
20+
h x = tape_var(1.0);
21+
h x2 = tape_mul(x, x);
22+
h y = tape_sin(x2);
23+
tape_backward(y);
24+
# d/dx sin(x^2) = 2x * cos(x^2). At x=1: 2 * cos(1) ≈ 1.0806
25+
assert_true(approx_eq(tape_grad(x), 1.0806, 0.001), "chain sin(x^2)");
26+
}
27+
28+
# Mixed ops
29+
fn test_polynomial_grad() {
30+
tape_reset();
31+
h x = tape_var(2.0);
32+
# y = x^3 + 2*x + 1
33+
h x3 = tape_pow_int(x, 3);
34+
h two = tape_const(2.0);
35+
h two_x = tape_mul(two, x);
36+
h sum_1 = tape_add(x3, two_x);
37+
h one = tape_const(1.0);
38+
h y = tape_add(sum_1, one);
39+
tape_backward(y);
40+
# dy/dx = 3x^2 + 2 = 14 at x=2
41+
assert_true(approx_eq(tape_grad(x), 14.0, 0.001), "3x^2 + 2 at 2");
42+
}
43+
44+
# Negation
45+
fn test_neg_in_chain() {
46+
tape_reset();
47+
h x = tape_var(5.0);
48+
h n = tape_neg(x); # = -5
49+
h y = tape_mul(n, n); # = 25
50+
tape_backward(y);
51+
# d(-x*-x)/dx = d(x^2)/dx = 2x = 10
52+
assert_true(approx_eq(tape_grad(x), 10.0, 0.001), "(-x)^2 grad");
53+
}
54+
55+
# Exp chain
56+
fn test_exp_squared() {
57+
tape_reset();
58+
h x = tape_var(1.0);
59+
h ex = tape_exp(x);
60+
h y = tape_mul(ex, ex); # = exp(2x)
61+
tape_backward(y);
62+
# d/dx exp(2x) = 2*exp(2x) at x=1 = 2 * e^2 ≈ 14.778
63+
assert_true(approx_eq(tape_grad(x), 14.778, 0.01), "d/dx exp(2x)");
64+
}
65+
66+
# Multi-variable: gradient of distance squared
67+
fn test_distance_squared_grad() {
68+
tape_reset();
69+
h x = tape_var(3.0);
70+
h y = tape_var(4.0);
71+
h x2 = tape_mul(x, x);
72+
h y2 = tape_mul(y, y);
73+
h d2 = tape_add(x2, y2);
74+
tape_backward(d2);
75+
# d/dx (x^2 + y^2) = 2x = 6
76+
# d/dy = 8
77+
assert_true(approx_eq(tape_grad(x), 6.0, 0.001), "dx");
78+
assert_true(approx_eq(tape_grad(y), 8.0, 0.001), "dy");
79+
}
80+
81+
# Sigmoid through addition
82+
fn test_sigmoid_of_sum() {
83+
tape_reset();
84+
h a = tape_var(1.0);
85+
h b = tape_var(0.0);
86+
h s = tape_add(a, b);
87+
h y = tape_sigmoid(s);
88+
tape_backward(y);
89+
# sigmoid(1)*(1-sigmoid(1)) ≈ 0.196
90+
assert_true(approx_eq(tape_grad(a), 0.196, 0.01), "dy/da");
91+
assert_true(approx_eq(tape_grad(b), 0.196, 0.01), "dy/db");
92+
}
93+
94+
# Tanh
95+
fn test_tanh_grad_at_one() {
96+
tape_reset();
97+
h x = tape_var(1.0);
98+
h y = tape_tanh(x);
99+
tape_backward(y);
100+
# d/dx tanh(x) = 1 - tanh^2(x). tanh(1) ≈ 0.7616, so 1 - 0.5801 ≈ 0.42
101+
assert_true(approx_eq(tape_grad(x), 0.42, 0.01), "tanh' at 1");
102+
}
103+
104+
# tape_sum reduces a vector
105+
fn test_tape_sum_grad() {
106+
tape_reset();
107+
h X = tape_var([1.0, 2.0, 3.0]);
108+
h s = tape_sum(X);
109+
tape_backward(s);
110+
h gX = tape_grad(X);
111+
# d(sum)/dx_i = 1 for each i
112+
assert_true(approx_eq(arr_get(gX, 0), 1.0, 0.001), "g[0]=1");
113+
assert_true(approx_eq(arr_get(gX, 2), 1.0, 0.001), "g[2]=1");
114+
}
115+
116+
# Substrate metadata visible on forward values
117+
fn test_substrate_visible_forward() {
118+
tape_reset();
119+
h x = tape_var(8); # Fibonacci attractor
120+
h v = tape_value(x);
121+
# Value comes back as substrate-annotated HInt for integers.
122+
# We can't introspect HInt fields directly, but is_attractor works.
123+
assert_eq(is_attractor(v), 1, "8 attractor preserved");
124+
}
125+
126+
fn test_substrate_visible_after_op() {
127+
tape_reset();
128+
h x = tape_var(3);
129+
h y = tape_var(5);
130+
h s = tape_add(x, y); # = 8
131+
h v = tape_value(s);
132+
assert_eq(is_attractor(v), 1, "8 attractor after sum");
133+
}

0 commit comments

Comments
 (0)