1use tang_expr::{ExprGraph, ExprId};
11
12struct Lcg(u64);
15
16impl Lcg {
17 fn new(seed: u64) -> Self {
18 Self(seed)
19 }
20 fn next(&mut self) -> u64 {
21 self.0 = self
22 .0
23 .wrapping_mul(6364136223846793005)
24 .wrapping_add(1442695040888963407);
25 self.0
26 }
27 fn uniform(&mut self) -> f64 {
28 (self.next() >> 11) as f64 / (1u64 << 53) as f64
29 }
30 fn range(&mut self, n: usize) -> usize {
31 (self.uniform() * n as f64) as usize % n
32 }
33}
34
35#[derive(Clone, Debug)]
38enum Expr {
39 X,
40 Lit(f64),
41 Add(Box<Expr>, Box<Expr>),
42 Mul(Box<Expr>, Box<Expr>),
43 Sin(Box<Expr>),
44 Neg(Box<Expr>),
45}
46
47impl Expr {
48 fn size(&self) -> usize {
50 match self {
51 Expr::X | Expr::Lit(_) => 1,
52 Expr::Sin(a) | Expr::Neg(a) => 1 + a.size(),
53 Expr::Add(a, b) | Expr::Mul(a, b) => 1 + a.size() + b.size(),
54 }
55 }
56
57 fn to_expr(&self, g: &mut ExprGraph) -> ExprId {
59 match self {
60 Expr::X => g.var(0),
61 Expr::Lit(v) => g.lit(*v),
62 Expr::Add(a, b) => {
63 let a = a.to_expr(g);
64 let b = b.to_expr(g);
65 g.add(a, b)
66 }
67 Expr::Mul(a, b) => {
68 let a = a.to_expr(g);
69 let b = b.to_expr(g);
70 g.mul(a, b)
71 }
72 Expr::Sin(a) => {
73 let a = a.to_expr(g);
74 g.sin(a)
75 }
76 Expr::Neg(a) => {
77 let a = a.to_expr(g);
78 g.neg(a)
79 }
80 }
81 }
82
83 fn format(&self) -> String {
85 let mut g = ExprGraph::new();
86 let root = self.to_expr(&mut g);
87 g.fmt_expr(root)
88 }
89
90 fn eval_at(&self, x: f64) -> f64 {
92 let mut g = ExprGraph::new();
93 let root = self.to_expr(&mut g);
94 g.eval(root, &[x])
95 }
96}
97
98fn random_expr(depth: usize, rng: &mut Lcg) -> Expr {
101 if depth == 0 || (depth < 3 && rng.uniform() < 0.3) {
102 return match rng.range(3) {
103 0 => Expr::X,
104 1 => Expr::Lit(
105 (rng.uniform() * 4.0 - 2.0) * 10.0_f64.powi(-((rng.uniform() * 2.0) as i32)),
106 ),
107 _ => Expr::X,
108 };
109 }
110
111 match rng.range(5) {
112 0 => Expr::Add(
113 Box::new(random_expr(depth - 1, rng)),
114 Box::new(random_expr(depth - 1, rng)),
115 ),
116 1 | 2 => Expr::Mul(
117 Box::new(random_expr(depth - 1, rng)),
118 Box::new(random_expr(depth - 1, rng)),
119 ),
120 3 => Expr::Sin(Box::new(random_expr(depth - 1, rng))),
121 _ => Expr::Neg(Box::new(random_expr(depth - 1, rng))),
122 }
123}
124
125fn mutate_grow(expr: &Expr, rng: &mut Lcg) -> Expr {
129 let size = expr.size();
130 let target = rng.range(size);
131 grow_at(expr, target, &mut 0, rng)
132}
133
134fn grow_at(expr: &Expr, target: usize, counter: &mut usize, rng: &mut Lcg) -> Expr {
135 if *counter == target {
136 *counter += expr.size(); return random_expr(3, rng);
138 }
139 *counter += 1;
140 match expr {
141 Expr::X => Expr::X,
142 Expr::Lit(v) => Expr::Lit(*v),
143 Expr::Add(a, b) => Expr::Add(
144 Box::new(grow_at(a, target, counter, rng)),
145 Box::new(grow_at(b, target, counter, rng)),
146 ),
147 Expr::Mul(a, b) => Expr::Mul(
148 Box::new(grow_at(a, target, counter, rng)),
149 Box::new(grow_at(b, target, counter, rng)),
150 ),
151 Expr::Sin(a) => Expr::Sin(Box::new(grow_at(a, target, counter, rng))),
152 Expr::Neg(a) => Expr::Neg(Box::new(grow_at(a, target, counter, rng))),
153 }
154}
155
156fn mutate_point(expr: &Expr, rng: &mut Lcg) -> Expr {
158 let size = expr.size();
159 let target = rng.range(size);
160 point_at(expr, target, &mut 0, rng)
161}
162
163fn point_at(expr: &Expr, target: usize, counter: &mut usize, rng: &mut Lcg) -> Expr {
164 if *counter == target {
165 *counter += 1;
166 return match expr {
167 Expr::X => Expr::Lit(rng.uniform() * 2.0 - 1.0),
168 Expr::Lit(_) => Expr::X,
169 Expr::Add(a, b) => Expr::Mul(a.clone(), b.clone()),
170 Expr::Mul(a, b) => Expr::Add(a.clone(), b.clone()),
171 Expr::Sin(a) => Expr::Neg(a.clone()),
172 Expr::Neg(a) => Expr::Sin(a.clone()),
173 };
174 }
175 *counter += 1;
176 match expr {
177 Expr::X => Expr::X,
178 Expr::Lit(v) => Expr::Lit(*v),
179 Expr::Add(a, b) => Expr::Add(
180 Box::new(point_at(a, target, counter, rng)),
181 Box::new(point_at(b, target, counter, rng)),
182 ),
183 Expr::Mul(a, b) => Expr::Mul(
184 Box::new(point_at(a, target, counter, rng)),
185 Box::new(point_at(b, target, counter, rng)),
186 ),
187 Expr::Sin(a) => Expr::Sin(Box::new(point_at(a, target, counter, rng))),
188 Expr::Neg(a) => Expr::Neg(Box::new(point_at(a, target, counter, rng))),
189 }
190}
191
192fn mutate_simplify(expr: &Expr) -> Expr {
195 let mut g = ExprGraph::new();
196 let root = expr.to_expr(&mut g);
197 let simplified = g.simplify(root);
198 let s = g.fmt_expr(simplified);
199
200 let orig = expr.format();
202 if s.len() < orig.len() {
203 let test = g.eval::<f64>(simplified, &[1.0]);
205 let orig_test = expr.eval_at(1.0);
206 if (test - orig_test).abs() < 1e-10 || (test.is_nan() && orig_test.is_nan()) {
207 return expr_from_graph(&g, simplified);
209 }
210 }
211 expr.clone()
212}
213
214fn expr_from_graph(g: &ExprGraph, id: ExprId) -> Expr {
216 match g.node(id) {
217 tang_expr::node::Node::Var(_) => Expr::X,
218 tang_expr::node::Node::Lit(bits) => {
219 let v = f64::from_bits(bits);
220 if v == 0.0 {
221 Expr::Lit(0.0)
222 } else {
223 Expr::Lit(v)
224 }
225 }
226 tang_expr::node::Node::Add(a, b) => Expr::Add(
227 Box::new(expr_from_graph(g, a)),
228 Box::new(expr_from_graph(g, b)),
229 ),
230 tang_expr::node::Node::Mul(a, b) => Expr::Mul(
231 Box::new(expr_from_graph(g, a)),
232 Box::new(expr_from_graph(g, b)),
233 ),
234 tang_expr::node::Node::Neg(a) => Expr::Neg(Box::new(expr_from_graph(g, a))),
235 tang_expr::node::Node::Sin(a) => Expr::Sin(Box::new(expr_from_graph(g, a))),
236 _ => {
238 let v = g.eval::<f64>(id, &[1.0]); Expr::Lit(v)
240 }
241 }
242}
243
244fn target(x: f64) -> f64 {
247 x * x * x.sin()
248}
249
250fn generate_data(n: usize, rng: &mut Lcg) -> Vec<(f64, f64)> {
251 (0..n)
252 .map(|i| {
253 let x = -3.0 + 6.0 * i as f64 / (n - 1) as f64;
254 let noise = (rng.uniform() - 0.5) * 0.01;
255 (x, target(x) + noise)
256 })
257 .collect()
258}
259
260fn fitness(expr: &Expr, data: &[(f64, f64)]) -> f64 {
262 let mut g = ExprGraph::new();
263 let root = expr.to_expr(&mut g);
264 let compiled = g.compile(root);
265
266 let mut mse = 0.0;
267 for &(x, y) in data {
268 let pred = compiled(&[x]);
269 if pred.is_nan() || pred.is_infinite() {
270 return f64::INFINITY;
271 }
272 let err = pred - y;
273 mse += err * err;
274 }
275 mse /= data.len() as f64;
276
277 let penalty = 0.001 * expr.size() as f64;
278 mse + penalty
279}
280
281fn tournament<'a>(pop: &'a [(Expr, f64)], k: usize, rng: &mut Lcg) -> &'a Expr {
284 let mut best_idx = rng.range(pop.len());
285 for _ in 1..k {
286 let idx = rng.range(pop.len());
287 if pop[idx].1 < pop[best_idx].1 {
288 best_idx = idx;
289 }
290 }
291 &pop[best_idx].0
292}
293
294fn main() {
297 println!("=== Symbolic Regression ===\n");
298 println!("target: f(x) = x^2 * sin(x)\n");
299
300 let mut rng = Lcg::new(42);
301 let data = generate_data(50, &mut rng);
302
303 const POP_SIZE: usize = 200;
304 const GENERATIONS: usize = 100;
305 const TOURNAMENT_K: usize = 5;
306
307 let mut population: Vec<(Expr, f64)> = (0..POP_SIZE)
309 .map(|_| {
310 let expr = random_expr(4, &mut rng);
311 let fit = fitness(&expr, &data);
312 (expr, fit)
313 })
314 .collect();
315
316 let mut best_expr: Option<Expr> = None;
317 let mut best_fitness = f64::INFINITY;
318
319 for gen in 0..GENERATIONS {
320 for (expr, fit) in &population {
322 if *fit < best_fitness {
323 best_fitness = *fit;
324 best_expr = Some(expr.clone());
325 }
326 }
327
328 if (gen + 1) % 10 == 0 || gen == 0 {
329 let b = best_expr.as_ref().unwrap();
330 println!(
331 "gen {:>3}: best fitness = {:.6} nodes = {:>3} expr = {}",
332 gen + 1,
333 best_fitness,
334 b.size(),
335 b.format(),
336 );
337 }
338
339 let mut next_pop = Vec::with_capacity(POP_SIZE);
341
342 let mut indices: Vec<usize> = (0..population.len()).collect();
344 indices.sort_by(|&a, &b| population[a].1.partial_cmp(&population[b].1).unwrap());
345 for &i in indices.iter().take(5) {
346 next_pop.push(population[i].clone());
347 }
348
349 while next_pop.len() < POP_SIZE {
351 let parent = tournament(&population, TOURNAMENT_K, &mut rng);
352 let child = match rng.range(10) {
353 0..=3 => mutate_grow(parent, &mut rng),
354 4..=6 => mutate_point(parent, &mut rng),
355 7..=8 => mutate_simplify(parent),
356 _ => random_expr(4, &mut rng), };
358
359 if child.size() > 50 {
361 continue;
362 }
363
364 let fit = fitness(&child, &data);
365 next_pop.push((child, fit));
366 }
367
368 population = next_pop;
369 }
370
371 println!();
373 let best = best_expr.unwrap();
374 let mut g = ExprGraph::new();
375 let root = best.to_expr(&mut g);
376 let simplified = g.simplify(root);
377 let expr_str = g.fmt_expr(simplified);
378 println!("best expression: {}", expr_str);
379 println!("best fitness: {:.6}", best_fitness);
380
381 println!("\nverification:");
383 let compiled = g.compile(simplified);
384 for &x in &[-2.0, -1.0, 0.0, 1.0, 2.0] {
385 let pred = compiled(&[x]);
386 let exact = target(x);
387 println!(
388 " f({:>5.1}) = {:>8.4} (predicted: {:>8.4}, error: {:>8.4})",
389 x,
390 exact,
391 pred,
392 (pred - exact).abs()
393 );
394 }
395
396 let dx = g.diff(simplified, 0);
398 let dx = g.simplify(dx);
399 println!(
400 "\nsymbolic derivative: d/dx [{}] = {}",
401 expr_str,
402 g.fmt_expr(dx)
403 );
404}