%23%20%2F%2F%2F%20script%0A%23%20requires-python%20%3D%20%22%3E%3D3.10%22%0A%23%20dependencies%20%3D%20%5B%0A%23%20%20%20%20%20%22marimo%3E%3D0.19.0%22%2C%0A%23%20%20%20%20%20%22pysr%3E%3D0.19%22%2C%0A%23%20%20%20%20%20%22numpy%3E%3D1.24%22%2C%0A%23%20%20%20%20%20%22matplotlib%3E%3D3.7%22%2C%0A%23%20%20%20%20%20%22pandas%3E%3D2.0%22%2C%0A%23%20%20%20%20%20%22sympy%3E%3D1.12%22%2C%0A%23%20%20%20%20%20%22egglog%3E%3D13.0%22%2C%0A%23%20%20%20%20%20%22scipy%3E%3D1.10%22%2C%0A%23%20%5D%0A%23%20%2F%2F%2F%0A%0Aimport%20marimo%0A%0A__generated_with%20%3D%20%220.23.0%22%0Aapp%20%3D%20marimo.App(width%3D%22medium%22)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%22%22%22%0A%20%20%20%20%23%20Boosted%20Symbolic%20Regression%20with%20PySR%20and%20egglog%0A%0A%20%20%20%20**Finding%20interpretable%20equations%20from%20data**%0A%0A%20%20%20%20Symbolic%20regression%20searches%20over%20the%20space%20of%20mathematical%20expressions%20to%0A%20%20%20%20find%20equations%20that%20best%20fit%20your%20data.%20Unlike%20neural%20networks%20or%20gradient%0A%20%20%20%20boosted%20trees%2C%20the%20output%20is%20a%20_human-readable%20equation_%20you%20can%20inspect%2C%0A%20%20%20%20verify%2C%20and%20trust.%0A%0A%20%20%20%20This%20notebook%20presents%20a%20technique%20of%20creating%20interpretable%20models%20with%20boosted%20symbolic%20regression.%20Symbolic%20boosting%20discovers%20mathematical%20formulas%20by%20building%20them%20up%20incrementally.%20Each%20round%20generates%20small%20random%20expressions%20and%20adds%20the%20best%20one%20to%20a%20growing%20additive%20ensemble.%20The%20ensemble%20inevitably%20accumulates%20redundant%20and%20compensatory%20terms%3A%200.83*x*y%20%2B%20(-0.90)*x*y%20%2B%201.18*y*(x%2By)%20-%201.01*(y-0.51)%C2%B2%20instead%20of%20the%20clean%20underlying%20x*y.%20Periodically%2C%20the%20entire%20ensemble%20is%20collapsed%20into%20a%20single%20expression%20and%20passed%20through%20an%20e-graph%20equality%20saturation%20engine%2C%20which%20applies%20algebraic%20rewrite%20rules%20(identity%20elimination%2C%20function%20inverses%2C%20power%20normalization)%20to%20discover%20simpler%20equivalent%20forms.%20The%20polynomial%20structure%20of%20the%20ensemble%20%E2%80%94%20where%20identical%20monomials%20like%20x*y%20appear%20scattered%20across%20many%20terms%20with%20different%20coefficients%20%E2%80%94%20is%20handled%20by%20a%20polynomial%20normal%20form%20that%20represents%20the%20expression%20as%20a%20multiset%20of%20multisets%2C%20merging%20like%20terms%20automatically%20and%20making%20distributivity%20structural%20rather%20than%20a%20rewrite%20rule.%0A%0A%20%20%20%20This%20separation%20(boosting%20for%20fast%20approximate%20search%2C%20polynomial%20canonicalization%20for%20algebraic%20cleanup%2C%20e-graphs%20for%20non-polynomial%20simplification)%20lets%20the%20system%20discover%20compact%20formulas%20from%20data%20at%20a%20fraction%20of%20the%20cost%20of%20evolutionary%20methods%2C%20trading%20some%20exactness%20on%20deeply%20nested%20transcendental%20equations%20for%20reliable%20polynomial%20recovery%20and%20interpretable%20output.%0A%0A%20%20%20%20We'll%20start%20with%20a%20review%20of%20symbolic%20regression%20in%20general%20then%20explain%20the%20boosting%20approach%20after.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.callout(%0A%20%20%20%20%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20%20%20%20%20%20%20%20%20**First%20run%20note%3A**%20PySR%20uses%20Julia%20under%20the%20hood.%20The%20very%20first%20time%0A%20%20%20%20%20%20%20%20%20%20%20%20you%20run%20it%2C%20Julia%20packages%20will%20compile%20%E2%80%94%20this%20can%20take%201%E2%80%932%20minutes.%0A%20%20%20%20%20%20%20%20%20%20%20%20Subsequent%20runs%20are%20much%20faster.%0A%20%20%20%20%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20kind%3D%22info%22%2C%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20What%20is%20Symbolic%20Regression%3F%0A%0A%20%20%20%20Traditional%20regression%20fits%20**parameters**%20of%20a%20fixed%20model%20form%3A%0A%0A%20%20%20%20%24%24y%20%3D%20%5Cbeta_0%20%2B%20%5Cbeta_1%20x%20%5Cquad%20%5Ctext%7B(linear)%7D%24%24%0A%0A%20%20%20%20Symbolic%20regression%20searches%20over%20both%20the%20**structure**%20and%20**parameters**%0A%20%20%20%20of%20the%20model.%20It%20explores%20combinations%20of%20operators%0A%20%20%20%20(%24%2B%2C%20-%2C%20%5Ctimes%2C%20%5Cdiv%2C%20%5Csin%2C%20%5Ccos%2C%20%5Cexp%2C%20%5Cldots%24)%2C%20constants%2C%20and%20variables%0A%20%20%20%20to%20find%20expressions%20like%3A%0A%0A%20%20%20%20%24%24y%20%3D%202.5%5C%2Cx%5E2%20%2B%200.3%5C%2Cx%20%5Cquad%20%5Ctext%7Bor%7D%20%5Cquad%20y%20%3D%20%5Csin(x_1)%20%2B%20%5Ccos(x_2)%24%24%0A%0A%20%20%20%20The%20key%20challenge%20is%20the%20**complexity%E2%80%93accuracy%20tradeoff**%3A%20a%20more%20complex%0A%20%20%20%20equation%20can%20always%20fit%20better%2C%20but%20a%20simpler%20one%20is%20easier%20to%20interpret%0A%20%20%20%20and%20less%20likely%20to%20overfit.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.callout(%0A%20%20%20%20%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Unlike%20black-box%20models%2C%20symbolic%20regression%20gives%20you%20an%20equation%20%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%22you%20can%20write%20on%20a%20whiteboard%2C%20differentiate%20analytically%2C%20and%20reason%20%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%22about%20physically.%22%0A%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20kind%3D%22success%22%2C%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%22%22%22%0A%20%20%20%20%23%23%201.%20Generate%20Synthetic%20Data%0A%0A%20%20%20%20We%20start%20with%20synthetic%20data%20so%20we%20**know%20the%20true%20equation**%20and%20can%0A%20%20%20%20evaluate%20how%20well%20PySR%20recovers%20it.%20Adjust%20the%20controls%20below%20to%20change%0A%20%20%20%20the%20dataset.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20n_points_slider%20%3D%20mo.ui.slider(%0A%20%20%20%20%20%20%20%20start%3D50%2C%20stop%3D500%2C%20step%3D50%2C%20value%3D200%2C%0A%20%20%20%20%20%20%20%20label%3D%22Number%20of%20data%20points%22%2C%20show_value%3DTrue%2C%0A%20%20%20%20)%0A%20%20%20%20noise_slider%20%3D%20mo.ui.slider(%0A%20%20%20%20%20%20%20%20start%3D0.0%2C%20stop%3D1.0%2C%20step%3D0.05%2C%20value%3D0.1%2C%0A%20%20%20%20%20%20%20%20label%3D%22Noise%20level%20(sigma)%22%2C%20show_value%3DTrue%2C%0A%20%20%20%20)%0A%20%20%20%20target_fn_dropdown%20%3D%20mo.ui.dropdown(%0A%20%20%20%20%20%20%20%20options%3D%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Simple%3A%202.5x%5E2%20%2B%200.3x%22%3A%20%22simple_quadratic%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Trigonometric%3A%20sin(x1)%20%2B%20cos(x2)%22%3A%20%22trig%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Physics-inspired%3A%203.0*x1%5E2%20%2B%200.5%2Fx2%22%3A%20%22physics%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Complex%3A%20x1%20*%20exp(-x2%5E2)%20%2B%202.3%22%3A%20%22complex%22%2C%0A%20%20%20%20%20%20%20%20%7D%2C%0A%20%20%20%20%20%20%20%20value%3D%22Simple%3A%202.5x%5E2%20%2B%200.3x%22%2C%0A%20%20%20%20%20%20%20%20label%3D%22Target%20function%22%2C%0A%20%20%20%20)%0A%20%20%20%20mo.hstack(%0A%20%20%20%20%20%20%20%20%5Bn_points_slider%2C%20noise_slider%2C%20target_fn_dropdown%5D%2C%0A%20%20%20%20%20%20%20%20gap%3D1.5%2C%0A%20%20%20%20%20%20%20%20justify%3D%22start%22%2C%0A%20%20%20%20)%0A%20%20%20%20return%20n_points_slider%2C%20noise_slider%2C%20target_fn_dropdown%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo%2C%20n_points_slider%2C%20noise_slider%2C%20np%2C%20plt%2C%20target_fn_dropdown)%3A%0A%20%20%20%20_n%20%3D%20n_points_slider.value%0A%20%20%20%20_sigma%20%3D%20noise_slider.value%0A%20%20%20%20_fn_key%20%3D%20target_fn_dropdown.value%0A%20%20%20%20_rng%20%3D%20np.random.default_rng(42)%0A%0A%20%20%20%20if%20_fn_key%20%3D%3D%20%22simple_quadratic%22%3A%0A%20%20%20%20%20%20%20%20X%20%3D%20_rng.uniform(-3%2C%203%2C%20(_n%2C%201))%0A%20%20%20%20%20%20%20%20y_true%20%3D%202.5%20*%20X%5B%3A%2C%200%5D%20**%202%20%2B%200.3%20*%20X%5B%3A%2C%200%5D%0A%20%20%20%20%20%20%20%20true_equation_latex%20%3D%20r%22%242.5%5C%2Cx%5E2%20%2B%200.3%5C%2Cx%24%22%0A%20%20%20%20%20%20%20%20n_features%20%3D%201%0A%20%20%20%20elif%20_fn_key%20%3D%3D%20%22trig%22%3A%0A%20%20%20%20%20%20%20%20X%20%3D%20_rng.uniform(-3%2C%203%2C%20(_n%2C%202))%0A%20%20%20%20%20%20%20%20y_true%20%3D%20np.sin(X%5B%3A%2C%200%5D)%20%2B%20np.cos(X%5B%3A%2C%201%5D)%0A%20%20%20%20%20%20%20%20true_equation_latex%20%3D%20r%22%24%5Csin(x_1)%20%2B%20%5Ccos(x_2)%24%22%0A%20%20%20%20%20%20%20%20n_features%20%3D%202%0A%20%20%20%20elif%20_fn_key%20%3D%3D%20%22physics%22%3A%0A%20%20%20%20%20%20%20%20X%20%3D%20_rng.uniform(0.5%2C%205%2C%20(_n%2C%202))%0A%20%20%20%20%20%20%20%20y_true%20%3D%203.0%20*%20X%5B%3A%2C%200%5D%20**%202%20%2B%200.5%20%2F%20X%5B%3A%2C%201%5D%0A%20%20%20%20%20%20%20%20true_equation_latex%20%3D%20r%22%243.0%5C%2Cx_1%5E2%20%2B%200.5%20%2F%20x_2%24%22%0A%20%20%20%20%20%20%20%20n_features%20%3D%202%0A%20%20%20%20else%3A%20%20%23%20complex%0A%20%20%20%20%20%20%20%20X%20%3D%20_rng.uniform(-2%2C%202%2C%20(_n%2C%202))%0A%20%20%20%20%20%20%20%20y_true%20%3D%20X%5B%3A%2C%200%5D%20*%20np.exp(-(X%5B%3A%2C%201%5D%20**%202))%20%2B%202.3%0A%20%20%20%20%20%20%20%20true_equation_latex%20%3D%20r%22%24x_1%20%5Cexp(-x_2%5E2)%20%2B%202.3%24%22%0A%20%20%20%20%20%20%20%20n_features%20%3D%202%0A%0A%20%20%20%20_noise%20%3D%20_rng.normal(0%2C%20_sigma%2C%20_n)%0A%20%20%20%20y%20%3D%20y_true%20%2B%20_noise%0A%0A%20%20%20%20%23%20---%20Visualization%20---%0A%20%20%20%20fig%2C%20ax%20%3D%20plt.subplots(figsize%3D(7%2C%204))%0A%20%20%20%20if%20n_features%20%3D%3D%201%3A%0A%20%20%20%20%20%20%20%20_order%20%3D%20np.argsort(X%5B%3A%2C%200%5D)%0A%20%20%20%20%20%20%20%20ax.scatter(X%5B%3A%2C%200%5D%2C%20y%2C%20alpha%3D0.4%2C%20s%3D15%2C%20label%3D%22Noisy%20data%22)%0A%20%20%20%20%20%20%20%20ax.plot(X%5B_order%2C%200%5D%2C%20y_true%5B_order%5D%2C%20color%3D%22red%22%2C%20lw%3D2%2C%20label%3D%22True%20function%22)%0A%20%20%20%20%20%20%20%20ax.set_xlabel(%22x%22)%0A%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20ax.scatter(y_true%2C%20y%2C%20alpha%3D0.4%2C%20s%3D15)%0A%20%20%20%20%20%20%20%20_lims%20%3D%20%5Bmin(y_true.min()%2C%20y.min())%2C%20max(y_true.max()%2C%20y.max())%5D%0A%20%20%20%20%20%20%20%20ax.plot(_lims%2C%20_lims%2C%20%22r--%22%2C%20lw%3D1%2C%20label%3D%22Perfect%20fit%22)%0A%20%20%20%20%20%20%20%20ax.set_xlabel(%22y%20(true)%22)%0A%20%20%20%20ax.set_ylabel(%22y%22)%0A%20%20%20%20ax.legend()%0A%20%20%20%20ax.set_title(%22Generated%20Data%22)%0A%20%20%20%20fig.tight_layout()%0A%0A%20%20%20%20mo.vstack(%5B%0A%20%20%20%20%20%20%20%20mo.md(f%22**True%20equation%3A**%20%7Btrue_equation_latex%7D%22)%2C%0A%20%20%20%20%20%20%20%20mo.hstack(%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20mo.stat(value%3Dstr(_n)%2C%20label%3D%22Data%20points%22)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20mo.stat(value%3Dstr(n_features)%2C%20label%3D%22Features%22)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20mo.stat(value%3Df%22%7B_sigma%3A.2f%7D%22%2C%20label%3D%22Noise%20(sigma)%22)%2C%0A%20%20%20%20%20%20%20%20%5D%2C%20justify%3D%22start%22)%2C%0A%20%20%20%20%20%20%20%20fig%2C%0A%20%20%20%20%5D)%0A%20%20%20%20return%20X%2C%20true_equation_latex%2C%20y%2C%20y_true%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%22%22%22%0A%20%20%20%20%23%23%202.%20Configure%20PySR%0A%0A%20%20%20%20Tune%20the%20search%20parameters%20below.%20Hover%20over%20parameter%20names%20in%20the%0A%20%20%20%20accordion%20for%20explanations.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20_explanations%20%3D%20mo.accordion(%7B%0A%20%20%20%20%20%20%20%20%22niterations%22%3A%20mo.md(%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Number%20of%20evolutionary%20cycles.%20More%20iterations%20let%20PySR%20explore%20%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%22further%20but%20take%20longer.%2020%E2%80%9340%20is%20a%20good%20starting%20point.%22%0A%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20%22maxsize%22%3A%20mo.md(%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Maximum%20complexity%20(number%20of%20nodes)%20for%20any%20candidate%20equation.%20%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Larger%20values%20allow%20more%20expressive%20equations%20but%20expand%20the%20search%20space.%22%0A%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20%22parsimony%22%3A%20mo.md(%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Penalty%20coefficient%20for%20complexity.%20Higher%20values%20push%20PySR%20toward%20%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%22simpler%20equations.%20Try%200.01%E2%80%930.05%20for%20a%20balanced%20search.%22%0A%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%7D)%0A%0A%20%20%20%20niterations_slider%20%3D%20mo.ui.slider(%0A%20%20%20%20%20%20%20%20start%3D5%2C%20stop%3D100%2C%20step%3D5%2C%20value%3D20%2C%0A%20%20%20%20%20%20%20%20label%3D%22niterations%22%2C%20show_value%3DTrue%2C%0A%20%20%20%20)%0A%20%20%20%20maxsize_slider%20%3D%20mo.ui.slider(%0A%20%20%20%20%20%20%20%20start%3D10%2C%20stop%3D40%2C%20step%3D5%2C%20value%3D25%2C%0A%20%20%20%20%20%20%20%20label%3D%22maxsize%22%2C%20show_value%3DTrue%2C%0A%20%20%20%20)%0A%20%20%20%20parsimony_slider%20%3D%20mo.ui.slider(%0A%20%20%20%20%20%20%20%20start%3D0.000%2C%20stop%3D0.100%2C%20step%3D0.005%2C%20value%3D0.010%2C%0A%20%20%20%20%20%20%20%20label%3D%22parsimony%22%2C%20show_value%3DTrue%2C%0A%20%20%20%20)%0A%0A%20%20%20%20mo.vstack(%5B%0A%20%20%20%20%20%20%20%20_explanations%2C%0A%20%20%20%20%20%20%20%20mo.hstack(%0A%20%20%20%20%20%20%20%20%20%20%20%20%5Bniterations_slider%2C%20maxsize_slider%2C%20parsimony_slider%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20gap%3D1.5%2C%20justify%3D%22start%22%2C%0A%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%5D)%0A%20%20%20%20return%20maxsize_slider%2C%20niterations_slider%2C%20parsimony_slider%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%22%22%22%0A%20%20%20%20%23%23%203.%20Run%20Symbolic%20Regression%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20run_button%20%3D%20mo.ui.run_button(label%3D%22Run%20PySR%22%2C%20kind%3D%22success%22)%0A%20%20%20%20mo.vstack(%5B%0A%20%20%20%20%20%20%20%20mo.callout(%0A%20%20%20%20%20%20%20%20%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22PySR%20uses%20evolutionary%20algorithms%20to%20search%20for%20equations.%20%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22This%20typically%20takes%20**30%E2%80%93120%20seconds**%20depending%20on%20settings.%20%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22Click%20the%20button%20below%20when%20you're%20ready.%22%0A%20%20%20%20%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20kind%3D%22warn%22%2C%0A%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20run_button%2C%0A%20%20%20%20%5D)%0A%20%20%20%20return%20(run_button%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(%0A%20%20%20%20PySRRegressor%2C%0A%20%20%20%20X%2C%0A%20%20%20%20maxsize_slider%2C%0A%20%20%20%20mo%2C%0A%20%20%20%20niterations_slider%2C%0A%20%20%20%20parsimony_slider%2C%0A%20%20%20%20run_button%2C%0A%20%20%20%20y%2C%0A)%3A%0A%20%20%20%20mo.stop(%0A%20%20%20%20%20%20%20%20not%20run_button.value%2C%0A%20%20%20%20%20%20%20%20mo.md(%22_Click%20**Run%20PySR**%20above%20to%20start%20the%20search._%22)%2C%0A%20%20%20%20)%0A%0A%20%20%20%20model%20%3D%20PySRRegressor(%0A%20%20%20%20%20%20%20%20niterations%3Dniterations_slider.value%2C%0A%20%20%20%20%20%20%20%20binary_operators%3D%5B%22%2B%22%2C%20%22-%22%2C%20%22*%22%2C%20%22%2F%22%5D%2C%0A%20%20%20%20%20%20%20%20unary_operators%3D%5B%22sin%22%2C%20%22cos%22%2C%20%22exp%22%2C%20%22sqrt%22%5D%2C%0A%20%20%20%20%20%20%20%20maxsize%3Dmaxsize_slider.value%2C%0A%20%20%20%20%20%20%20%20parsimony%3Dparsimony_slider.value%2C%0A%20%20%20%20%20%20%20%20populations%3D15%2C%0A%20%20%20%20%20%20%20%20random_state%3D42%2C%0A%20%20%20%20%20%20%20%20verbosity%3D0%2C%0A%20%20%20%20%20%20%20%20progress%3DFalse%2C%0A%20%20%20%20%20%20%20%20temp_equation_file%3DTrue%2C%0A%20%20%20%20)%0A%0A%20%20%20%20with%20mo.status.spinner(title%3D%22PySR%20is%20searching%20for%20equations...%22)%3A%0A%20%20%20%20%20%20%20%20model.fit(X%2C%20y)%0A%0A%20%20%20%20equations_df%20%3D%20model.equations_%0A%20%20%20%20return%20equations_df%2C%20model%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(equations_df%2C%20mo)%3A%0A%20%20%20%20_best_idx%20%3D%20equations_df%5B%22loss%22%5D.idxmin()%0A%20%20%20%20_best_eq%20%3D%20str(equations_df.loc%5B_best_idx%2C%20%22equation%22%5D)%0A%20%20%20%20_best_loss%20%3D%20equations_df.loc%5B_best_idx%2C%20%22loss%22%5D%0A%0A%20%20%20%20mo.vstack(%5B%0A%20%20%20%20%20%20%20%20mo.md(%22%23%23%204.%20Results%3A%20Hall%20of%20Fame%22)%2C%0A%20%20%20%20%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20%20%20%20%20%22PySR%20returns%20a%20**hall%20of%20fame**%20%E2%80%94%20the%20best%20equation%20found%20at%20each%20%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%22complexity%20level.%20This%20table%20is%20the%20starting%20point%20for%20choosing%20an%20%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%22interpretable%20model.%22%0A%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20mo.hstack(%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20mo.stat(value%3Dstr(len(equations_df))%2C%20label%3D%22Equations%20found%22)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20mo.stat(value%3Df%22%7B_best_loss%3A.6f%7D%22%2C%20label%3D%22Best%20loss%22)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20mo.stat(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20value%3Df%22%7Bequations_df%5B'complexity'%5D.min()%7D%E2%80%93%7Bequations_df%5B'complexity'%5D.max()%7D%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20label%3D%22Complexity%20range%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20%5D%2C%20justify%3D%22start%22)%2C%0A%20%20%20%20%5D)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(equations_df%2C%20mo)%3A%0A%20%20%20%20_display%20%3D%20equations_df%5B%5B%22complexity%22%2C%20%22loss%22%2C%20%22score%22%2C%20%22equation%22%5D%5D.copy()%0A%20%20%20%20_display%5B%22loss%22%5D%20%3D%20_display%5B%22loss%22%5D.map(lambda%20v%3A%20f%22%7Bv%3A.6f%7D%22)%0A%20%20%20%20_display%5B%22score%22%5D%20%3D%20_display%5B%22score%22%5D.map(lambda%20v%3A%20f%22%7Bv%3A.4f%7D%22)%0A%20%20%20%20equations_table%20%3D%20mo.ui.table(%0A%20%20%20%20%20%20%20%20data%3D_display%2C%0A%20%20%20%20%20%20%20%20label%3D%22Hall%20of%20Fame%20equations%22%2C%0A%20%20%20%20%20%20%20%20selection%3D%22single%22%2C%0A%20%20%20%20)%0A%20%20%20%20equations_table%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%205.%20The%20Pareto%20Front%0A%0A%20%20%20%20The%20**Pareto%20front**%20shows%20the%20tradeoff%20between%20complexity%20and%20accuracy.%0A%20%20%20%20Each%20point%20is%20the%20best%20equation%20at%20a%20given%20complexity.%0A%0A%20%20%20%20-%20Moving%20**right**%20increases%20complexity%20(more%20nodes%20in%20the%20expression%20tree).%0A%20%20%20%20-%20Moving%20**down**%20improves%20accuracy%20(lower%20loss).%0A%20%20%20%20-%20The%20**score**%20measures%20how%20much%20loss%20_decreases%20per%20unit%20of%20complexity_%20%E2%80%94%0A%20%20%20%20%20%20a%20sharp%20jump%20in%20score%20signals%20a%20meaningful%20structural%20improvement.%0A%0A%20%20%20%20The%20%22best%22%20model%20is%20often%20**not**%20the%20most%20accurate%20one%20%E2%80%94%20it's%20the%20one%0A%20%20%20%20that%20achieves%20good%20accuracy%20with%20minimal%20complexity.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(equations_df%2C%20np%2C%20plt)%3A%0A%20%20%20%20fig_pareto%2C%20(ax1%2C%20ax2)%20%3D%20plt.subplots(1%2C%202%2C%20figsize%3D(12%2C%204.5))%0A%0A%20%20%20%20_complexities%20%3D%20equations_df%5B%22complexity%22%5D.values%0A%20%20%20%20_losses%20%3D%20equations_df%5B%22loss%22%5D.values%0A%20%20%20%20_scores%20%3D%20equations_df%5B%22score%22%5D.values%0A%0A%20%20%20%20%23%20---%20Left%3A%20Complexity%20vs%20Loss%20---%0A%20%20%20%20ax1.plot(_complexities%2C%20_losses%2C%20%22o-%22%2C%20color%3D%22%234a90d9%22%2C%20markersize%3D6%2C%20lw%3D1.5)%0A%20%20%20%20_best_idx%20%3D%20np.argmin(_losses)%0A%20%20%20%20ax1.scatter(%0A%20%20%20%20%20%20%20%20%5B_complexities%5B_best_idx%5D%5D%2C%20%5B_losses%5B_best_idx%5D%5D%2C%0A%20%20%20%20%20%20%20%20color%3D%22red%22%2C%20s%3D120%2C%20zorder%3D5%2C%20edgecolors%3D%22darkred%22%2C%20lw%3D2%2C%0A%20%20%20%20%20%20%20%20label%3Df%22Best%20(complexity%3D%7B_complexities%5B_best_idx%5D%7D)%22%2C%0A%20%20%20%20)%0A%0A%20%20%20%20%23%20Highlight%20best-score%20equation%0A%20%20%20%20_best_score_idx%20%3D%20np.argmax(_scores)%0A%20%20%20%20ax1.scatter(%0A%20%20%20%20%20%20%20%20%5B_complexities%5B_best_score_idx%5D%5D%2C%20%5B_losses%5B_best_score_idx%5D%5D%2C%0A%20%20%20%20%20%20%20%20color%3D%22orange%22%2C%20s%3D120%2C%20zorder%3D5%2C%20marker%3D%22D%22%2C%20edgecolors%3D%22darkorange%22%2C%20lw%3D2%2C%0A%20%20%20%20%20%20%20%20label%3Df%22Best%20score%20(complexity%3D%7B_complexities%5B_best_score_idx%5D%7D)%22%2C%0A%20%20%20%20)%0A%0A%20%20%20%20ax1.set_xlabel(%22Complexity%22)%0A%20%20%20%20ax1.set_ylabel(%22Loss%22)%0A%20%20%20%20ax1.set_yscale(%22log%22)%0A%20%20%20%20ax1.set_title(%22Pareto%20Front%3A%20Complexity%20vs%20Loss%22)%0A%20%20%20%20ax1.legend(fontsize%3D8)%0A%20%20%20%20ax1.grid(True%2C%20alpha%3D0.3)%0A%0A%20%20%20%20%23%20---%20Right%3A%20Complexity%20vs%20Score%20---%0A%20%20%20%20_colors%20%3D%20%5B%22%23e74c3c%22%20if%20s%20%3D%3D%20_scores.max()%20else%20%22%234a90d9%22%20for%20s%20in%20_scores%5D%0A%20%20%20%20ax2.bar(_complexities%2C%20_scores%2C%20color%3D_colors%2C%20edgecolor%3D%22white%22%2C%20width%3D0.6)%0A%20%20%20%20ax2.set_xlabel(%22Complexity%22)%0A%20%20%20%20ax2.set_ylabel(%22Score%22)%0A%20%20%20%20ax2.set_title(%22Score%20by%20Complexity%22)%0A%20%20%20%20ax2.grid(True%2C%20alpha%3D0.3%2C%20axis%3D%22y%22)%0A%0A%20%20%20%20fig_pareto.tight_layout()%0A%20%20%20%20fig_pareto%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%22%22%22%0A%20%20%20%20%23%23%206.%20Explore%20Individual%20Equations%0A%0A%20%20%20%20Use%20the%20slider%20below%20to%20select%20an%20equation%20by%20its%20complexity%20level.%0A%20%20%20%20The%20tabs%20show%20the%20equation%20details%2C%20how%20well%20it%20fits%20the%20data%2C%20and%0A%20%20%20%20its%20residual%20errors.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(equations_df%2C%20mo)%3A%0A%20%20%20%20_complexity_values%20%3D%20sorted(equations_df%5B%22complexity%22%5D.unique().tolist())%0A%20%20%20%20equation_selector%20%3D%20mo.ui.slider(%0A%20%20%20%20%20%20%20%20steps%3D_complexity_values%2C%0A%20%20%20%20%20%20%20%20value%3D_complexity_values%5Blen(_complexity_values)%20%2F%2F%202%5D%2C%0A%20%20%20%20%20%20%20%20label%3D%22Equation%20complexity%22%2C%0A%20%20%20%20%20%20%20%20show_value%3DTrue%2C%0A%20%20%20%20%20%20%20%20debounce%3DTrue%2C%0A%20%20%20%20)%0A%20%20%20%20equation_selector%0A%20%20%20%20return%20(equation_selector%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(%0A%20%20%20%20X%2C%0A%20%20%20%20equation_selector%2C%0A%20%20%20%20equations_df%2C%0A%20%20%20%20mo%2C%0A%20%20%20%20model%2C%0A%20%20%20%20np%2C%0A%20%20%20%20plt%2C%0A%20%20%20%20sympy%2C%0A%20%20%20%20true_equation_latex%2C%0A%20%20%20%20y%2C%0A%20%20%20%20y_true%2C%0A)%3A%0A%20%20%20%20_sel_complexity%20%3D%20equation_selector.value%0A%20%20%20%20_row%20%3D%20equations_df%5Bequations_df%5B%22complexity%22%5D%20%3D%3D%20_sel_complexity%5D.iloc%5B0%5D%0A%20%20%20%20_eq_str%20%3D%20str(_row%5B%22equation%22%5D)%0A%20%20%20%20_loss%20%3D%20_row%5B%22loss%22%5D%0A%20%20%20%20_score%20%3D%20_row%5B%22score%22%5D%0A%20%20%20%20_complexity%20%3D%20_row%5B%22complexity%22%5D%0A%0A%20%20%20%20%23%20Get%20sympy%20expression%20for%20LaTeX%0A%20%20%20%20try%3A%0A%20%20%20%20%20%20%20%20_sympy_expr%20%3D%20_row%5B%22sympy_format%22%5D%0A%20%20%20%20%20%20%20%20_latex_eq%20%3D%20sympy.latex(_sympy_expr)%0A%20%20%20%20except%20Exception%3A%0A%20%20%20%20%20%20%20%20_latex_eq%20%3D%20_eq_str%0A%0A%20%20%20%20%23%20Predictions%20for%20the%20selected%20equation%0A%20%20%20%20_idx%20%3D%20equations_df%5Bequations_df%5B%22complexity%22%5D%20%3D%3D%20_sel_complexity%5D.index%5B0%5D%0A%20%20%20%20_y_pred%20%3D%20model.predict(X%2C%20index%3D_idx)%0A%0A%20%20%20%20_residuals%20%3D%20y%20-%20_y_pred%0A%20%20%20%20_mae%20%3D%20np.mean(np.abs(_residuals))%0A%20%20%20%20_ss_res%20%3D%20np.sum(_residuals%20**%202)%0A%20%20%20%20_ss_tot%20%3D%20np.sum((y%20-%20np.mean(y))%20**%202)%0A%20%20%20%20_r2%20%3D%201%20-%20_ss_res%20%2F%20_ss_tot%20if%20_ss_tot%20%3E%200%20else%200.0%0A%0A%20%20%20%20%23%20---%20Tab%201%3A%20Equation%20Details%20---%0A%20%20%20%20_tab_details%20%3D%20mo.vstack(%5B%0A%20%20%20%20%20%20%20%20mo.md(f%22%23%23%23%20Selected%20Equation%5Cn%5Cn%24%24%7B_latex_eq%7D%24%24%22)%2C%0A%20%20%20%20%20%20%20%20mo.hstack(%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20mo.stat(value%3Dstr(int(_complexity))%2C%20label%3D%22Complexity%22)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20mo.stat(value%3Df%22%7B_loss%3A.6f%7D%22%2C%20label%3D%22Loss%22)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20mo.stat(value%3Df%22%7B_score%3A.4f%7D%22%2C%20label%3D%22Score%22)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20mo.stat(value%3Df%22%7B_r2%3A.4f%7D%22%2C%20label%3D%22R-squared%22)%2C%0A%20%20%20%20%20%20%20%20%5D%2C%20justify%3D%22start%22)%2C%0A%20%20%20%20%20%20%20%20mo.md(f%22**True%20equation%3A**%20%7Btrue_equation_latex%7D%22)%2C%0A%20%20%20%20%5D)%0A%0A%20%20%20%20%23%20---%20Tab%202%3A%20Fit%20Visualization%20---%0A%20%20%20%20_fig_fit%2C%20_ax_fit%20%3D%20plt.subplots(figsize%3D(7%2C%204))%0A%20%20%20%20if%20X.shape%5B1%5D%20%3D%3D%201%3A%0A%20%20%20%20%20%20%20%20_order%20%3D%20np.argsort(X%5B%3A%2C%200%5D)%0A%20%20%20%20%20%20%20%20_ax_fit.scatter(X%5B%3A%2C%200%5D%2C%20y%2C%20alpha%3D0.3%2C%20s%3D12%2C%20label%3D%22Data%22%2C%20color%3D%22gray%22)%0A%20%20%20%20%20%20%20%20_ax_fit.plot(%0A%20%20%20%20%20%20%20%20%20%20%20%20X%5B_order%2C%200%5D%2C%20_y_pred%5B_order%5D%2C%20color%3D%22%23e74c3c%22%2C%20lw%3D2%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20label%3Df%22Predicted%20(complexity%3D%7Bint(_complexity)%7D)%22%2C%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20_ax_fit.plot(%0A%20%20%20%20%20%20%20%20%20%20%20%20X%5B_order%2C%200%5D%2C%20y_true%5B_order%5D%2C%20color%3D%22%232ecc71%22%2C%20lw%3D2%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20linestyle%3D%22--%22%2C%20label%3D%22True%20function%22%2C%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20_ax_fit.set_xlabel(%22x%22)%0A%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20_ax_fit.scatter(y%2C%20_y_pred%2C%20alpha%3D0.3%2C%20s%3D12%2C%20color%3D%22%234a90d9%22)%0A%20%20%20%20%20%20%20%20_lims%20%3D%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20min(y.min()%2C%20_y_pred.min())%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20max(y.max()%2C%20_y_pred.max())%2C%0A%20%20%20%20%20%20%20%20%5D%0A%20%20%20%20%20%20%20%20_ax_fit.plot(_lims%2C%20_lims%2C%20%22r--%22%2C%20lw%3D1%2C%20label%3D%22Perfect%20prediction%22)%0A%20%20%20%20%20%20%20%20_ax_fit.set_xlabel(%22y%20(actual)%22)%0A%20%20%20%20_ax_fit.set_ylabel(%22y%20(predicted)%22)%0A%20%20%20%20_ax_fit.legend(fontsize%3D8)%0A%20%20%20%20_ax_fit.set_title(f%22Fit%3A%20complexity%20%3D%20%7Bint(_complexity)%7D%22)%0A%20%20%20%20_ax_fit.grid(True%2C%20alpha%3D0.3)%0A%20%20%20%20_fig_fit.tight_layout()%0A%0A%20%20%20%20%23%20---%20Tab%203%3A%20Residuals%20---%0A%20%20%20%20_fig_res%2C%20(_ax_r1%2C%20_ax_r2)%20%3D%20plt.subplots(1%2C%202%2C%20figsize%3D(10%2C%203.5))%0A%20%20%20%20_ax_r1.scatter(%0A%20%20%20%20%20%20%20%20_y_pred%2C%20_residuals%2C%20alpha%3D0.3%2C%20s%3D12%2C%20color%3D%22%239b59b6%22%2C%0A%20%20%20%20)%0A%20%20%20%20_ax_r1.axhline(0%2C%20color%3D%22red%22%2C%20linestyle%3D%22--%22%2C%20lw%3D1)%0A%20%20%20%20_ax_r1.set_xlabel(%22Predicted%22)%0A%20%20%20%20_ax_r1.set_ylabel(%22Residual%22)%0A%20%20%20%20_ax_r1.set_title(%22Residuals%20vs%20Predicted%22)%0A%20%20%20%20_ax_r1.grid(True%2C%20alpha%3D0.3)%0A%0A%20%20%20%20_ax_r2.hist(_residuals%2C%20bins%3D25%2C%20color%3D%22%239b59b6%22%2C%20edgecolor%3D%22white%22%2C%20alpha%3D0.8)%0A%20%20%20%20_ax_r2.set_xlabel(%22Residual%22)%0A%20%20%20%20_ax_r2.set_ylabel(%22Count%22)%0A%20%20%20%20_ax_r2.set_title(%22Residual%20Distribution%22)%0A%20%20%20%20_ax_r2.grid(True%2C%20alpha%3D0.3%2C%20axis%3D%22y%22)%0A%20%20%20%20_fig_res.tight_layout()%0A%0A%20%20%20%20_tab_residuals%20%3D%20mo.vstack(%5B%0A%20%20%20%20%20%20%20%20mo.hstack(%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20mo.stat(value%3Df%22%7B_mae%3A.4f%7D%22%2C%20label%3D%22MAE%22)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20mo.stat(value%3Df%22%7B_r2%3A.4f%7D%22%2C%20label%3D%22R-squared%22)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20mo.stat(value%3Df%22%7Bnp.std(_residuals)%3A.4f%7D%22%2C%20label%3D%22Residual%20Std%22)%2C%0A%20%20%20%20%20%20%20%20%5D%2C%20justify%3D%22start%22)%2C%0A%20%20%20%20%20%20%20%20_fig_res%2C%0A%20%20%20%20%5D)%0A%0A%20%20%20%20mo.ui.tabs(%7B%0A%20%20%20%20%20%20%20%20%22Equation%20Details%22%3A%20_tab_details%2C%0A%20%20%20%20%20%20%20%20%22Fit%20Visualization%22%3A%20_fig_fit%2C%0A%20%20%20%20%20%20%20%20%22Residuals%22%3A%20_tab_residuals%2C%0A%20%20%20%20%7D)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%22%22%22%0A%20%20%20%20%23%23%207.%20Compare%20Two%20Models%0A%0A%20%20%20%20Select%20two%20complexity%20levels%20to%20compare%20their%20equations%20side%20by%20side.%0A%20%20%20%20This%20helps%20you%20decide%20which%20point%20on%20the%20Pareto%20front%20is%20the%20best%0A%20%20%20%20tradeoff%20for%20your%20use%20case.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(equations_df%2C%20mo)%3A%0A%20%20%20%20_cvals%20%3D%20sorted(equations_df%5B%22complexity%22%5D.unique().tolist())%0A%20%20%20%20compare_a%20%3D%20mo.ui.slider(%0A%20%20%20%20%20%20%20%20steps%3D_cvals%2C%20value%3D_cvals%5B0%5D%2C%0A%20%20%20%20%20%20%20%20label%3D%22Model%20A%20complexity%22%2C%20show_value%3DTrue%2C%0A%20%20%20%20)%0A%20%20%20%20compare_b%20%3D%20mo.ui.slider(%0A%20%20%20%20%20%20%20%20steps%3D_cvals%2C%20value%3D_cvals%5B-1%5D%2C%0A%20%20%20%20%20%20%20%20label%3D%22Model%20B%20complexity%22%2C%20show_value%3DTrue%2C%0A%20%20%20%20)%0A%20%20%20%20mo.hstack(%5Bcompare_a%2C%20compare_b%5D%2C%20gap%3D2%2C%20justify%3D%22start%22)%0A%20%20%20%20return%20compare_a%2C%20compare_b%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(%0A%20%20%20%20X%2C%0A%20%20%20%20compare_a%2C%0A%20%20%20%20compare_b%2C%0A%20%20%20%20equations_df%2C%0A%20%20%20%20mo%2C%0A%20%20%20%20model%2C%0A%20%20%20%20np%2C%0A%20%20%20%20plt%2C%0A%20%20%20%20sympy%2C%0A%20%20%20%20y%2C%0A%20%20%20%20y_true%2C%0A)%3A%0A%20%20%20%20def%20_build_column(complexity_val)%3A%0A%20%20%20%20%20%20%20%20_row%20%3D%20equations_df%5Bequations_df%5B%22complexity%22%5D%20%3D%3D%20complexity_val%5D.iloc%5B0%5D%0A%20%20%20%20%20%20%20%20_idx%20%3D%20equations_df%5Bequations_df%5B%22complexity%22%5D%20%3D%3D%20complexity_val%5D.index%5B0%5D%0A%20%20%20%20%20%20%20%20_y_pred%20%3D%20model.predict(X%2C%20index%3D_idx)%0A%0A%20%20%20%20%20%20%20%20try%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20_latex%20%3D%20sympy.latex(_row%5B%22sympy_format%22%5D)%0A%20%20%20%20%20%20%20%20except%20Exception%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20_latex%20%3D%20str(_row%5B%22equation%22%5D)%0A%0A%20%20%20%20%20%20%20%20_residuals%20%3D%20y%20-%20_y_pred%0A%20%20%20%20%20%20%20%20_mae%20%3D%20np.mean(np.abs(_residuals))%0A%20%20%20%20%20%20%20%20_ss_res%20%3D%20np.sum(_residuals%20**%202)%0A%20%20%20%20%20%20%20%20_ss_tot%20%3D%20np.sum((y%20-%20np.mean(y))%20**%202)%0A%20%20%20%20%20%20%20%20_r2%20%3D%201%20-%20_ss_res%20%2F%20_ss_tot%20if%20_ss_tot%20%3E%200%20else%200.0%0A%0A%20%20%20%20%20%20%20%20_fig%2C%20_ax%20%3D%20plt.subplots(figsize%3D(5%2C%203.5))%0A%20%20%20%20%20%20%20%20if%20X.shape%5B1%5D%20%3D%3D%201%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20_order%20%3D%20np.argsort(X%5B%3A%2C%200%5D)%0A%20%20%20%20%20%20%20%20%20%20%20%20_ax.scatter(X%5B%3A%2C%200%5D%2C%20y%2C%20alpha%3D0.3%2C%20s%3D10%2C%20color%3D%22gray%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20_ax.plot(X%5B_order%2C%200%5D%2C%20_y_pred%5B_order%5D%2C%20color%3D%22%23e74c3c%22%2C%20lw%3D2)%0A%20%20%20%20%20%20%20%20%20%20%20%20_ax.plot(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20X%5B_order%2C%200%5D%2C%20y_true%5B_order%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20color%3D%22%232ecc71%22%2C%20lw%3D2%2C%20linestyle%3D%22--%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20%20%20%20%20_ax.set_xlabel(%22x%22)%0A%20%20%20%20%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20_ax.scatter(y%2C%20_y_pred%2C%20alpha%3D0.3%2C%20s%3D10%2C%20color%3D%22%234a90d9%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20_lims%20%3D%20%5Bmin(y.min()%2C%20_y_pred.min())%2C%20max(y.max()%2C%20_y_pred.max())%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20_ax.plot(_lims%2C%20_lims%2C%20%22r--%22%2C%20lw%3D1)%0A%20%20%20%20%20%20%20%20%20%20%20%20_ax.set_xlabel(%22Actual%22)%0A%20%20%20%20%20%20%20%20_ax.set_ylabel(%22Predicted%22)%0A%20%20%20%20%20%20%20%20_ax.set_title(f%22Complexity%20%3D%20%7Bint(complexity_val)%7D%22)%0A%20%20%20%20%20%20%20%20_ax.grid(True%2C%20alpha%3D0.3)%0A%20%20%20%20%20%20%20%20_fig.tight_layout()%0A%0A%20%20%20%20%20%20%20%20return%20mo.vstack(%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20mo.md(f%22%24%24%7B_latex%7D%24%24%22)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20mo.hstack(%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20mo.stat(value%3Df%22%7B_row%5B'loss'%5D%3A.6f%7D%22%2C%20label%3D%22Loss%22)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20mo.stat(value%3Df%22%7B_r2%3A.4f%7D%22%2C%20label%3D%22R-squared%22)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20mo.stat(value%3Df%22%7B_mae%3A.4f%7D%22%2C%20label%3D%22MAE%22)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%5D%2C%20justify%3D%22start%22)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20_fig%2C%0A%20%20%20%20%20%20%20%20%5D)%0A%0A%20%20%20%20_col_a%20%3D%20_build_column(compare_a.value)%0A%20%20%20%20_col_b%20%3D%20_build_column(compare_b.value)%0A%0A%20%20%20%20_loss_a%20%3D%20equations_df%5Bequations_df%5B%22complexity%22%5D%20%3D%3D%20compare_a.value%5D.iloc%5B0%5D%5B%22loss%22%5D%0A%20%20%20%20_loss_b%20%3D%20equations_df%5Bequations_df%5B%22complexity%22%5D%20%3D%3D%20compare_b.value%5D.iloc%5B0%5D%5B%22loss%22%5D%0A%0A%20%20%20%20if%20_loss_a%20%3C%20_loss_b%3A%0A%20%20%20%20%20%20%20%20_verdict%20%3D%20f%22Model%20A%20(complexity%20%7Bcompare_a.value%7D)%20has%20lower%20loss.%22%0A%20%20%20%20elif%20_loss_b%20%3C%20_loss_a%3A%0A%20%20%20%20%20%20%20%20_verdict%20%3D%20f%22Model%20B%20(complexity%20%7Bcompare_b.value%7D)%20has%20lower%20loss.%22%0A%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20_verdict%20%3D%20%22Both%20models%20have%20equal%20loss.%22%0A%0A%20%20%20%20if%20compare_a.value%20%3C%20compare_b.value%3A%0A%20%20%20%20%20%20%20%20_verdict%20%2B%3D%20f%22%20Model%20A%20is%20simpler%20(%7Bcompare_a.value%7D%20vs%20%7Bcompare_b.value%7D).%22%0A%20%20%20%20elif%20compare_b.value%20%3C%20compare_a.value%3A%0A%20%20%20%20%20%20%20%20_verdict%20%2B%3D%20f%22%20Model%20B%20is%20simpler%20(%7Bcompare_b.value%7D%20vs%20%7Bcompare_a.value%7D).%22%0A%0A%20%20%20%20mo.vstack(%5B%0A%20%20%20%20%20%20%20%20mo.hstack(%5B_col_a%2C%20_col_b%5D%2C%20gap%3D2%2C%20widths%3D%22equal%22)%2C%0A%20%20%20%20%20%20%20%20mo.callout(mo.md(f%22**Verdict%3A**%20%7B_verdict%7D%22)%2C%20kind%3D%22neutral%22)%2C%0A%20%20%20%20%5D)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%22%22%22%0A%20%20%20%20%23%23%208.%20Symbolic%20Boosting%20%26%20Expression%20Simplification%0A%0A%20%20%20%20A%20symbolic%20regression%20system%20that%20uses%20**boosting**%20to%20assemble%20formulas%2C%0A%20%20%20%20then%20compresses%20the%20ensemble%20back%20into%20a%20compact%20equation%20using%0A%20%20%20%20polynomial%20normalization%20and%20pruning.%0A%0A%20%20%20%20%60%60%60%0A%20%20%20%20Residual%20Fitting%20%20%E2%86%92%20%20Ensemble%20Growth%20%20%E2%86%92%20%20Periodic%20Collapse%20%20%E2%86%92%20%20Final%20Distillation%0A%20%20%20%20%20%20%20%20%20(many%20ugly%20terms)%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20(poly%20%2B%20prune)%20%20%20%20%20%20%20%20(e-graph%20cleanup)%0A%20%20%20%20%60%60%60%0A%0A%20%20%20%20The%20surprising%20result%3A%20**boosting%20discovers%20the%20algebraic%20pieces%2C%0A%20%20%20%20simplification%20recovers%20the%20law.**%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.accordion(%7B%0A%20%20%20%20%20%20%20%20%22How%20symbolic%20boosting%20works%22%3A%20mo.md(%0A%20%20%20%20%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20%20%20%20%20%20%20%20%20Symbolic%20boosting%20builds%20an%20additive%20ensemble%20of%20tiny%20expression%0A%20%20%20%20%20%20%20%20%20%20%20%20trees%20%E2%80%94%20analogous%20to%20gradient%20boosting%20with%20decision%20trees%2C%20but%0A%20%20%20%20%20%20%20%20%20%20%20%20with%20symbolic%20weak%20learners%3A%0A%0A%20%20%20%20%20%20%20%20%20%20%20%201.%20**Initialise**%20residuals%20as%20%60y%20-%20mean(y)%60.%0A%20%20%20%20%20%20%20%20%20%20%20%202.%20Each%20round%2C%20generate%20many%20random%20expression%20trees%20(max%207%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20nodes)%2C%20fit%20their%20parameters%20against%20the%20current%20residuals%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20and%20pick%20the%20one%20with%20the%20highest%20gain.%0A%20%20%20%20%20%20%20%20%20%20%20%203.%20Shrink%20the%20winning%20term%20by%20a%20learning%20rate%20and%20add%20it%20to%20the%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20ensemble.%20%20Update%20residuals.%0A%20%20%20%20%20%20%20%20%20%20%20%204.%20Every%20few%20rounds%2C%20**collapse**%20the%20ensemble%3A%20polynomial%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20expansion%20merges%20like%20terms%20and%20magnitude%20pruning%20drops%20junk.%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20The%20ensemble%20grows%20messy%20%E2%80%94%20then%20collapses.%20%20This%20sawtooth%0A%20%20%20%20%20%20%20%20%20%20%20%20pattern%20repeats%20until%20the%20expression%20converges.%0A%20%20%20%20%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20%22Why%20simplification%20matters%20%E2%80%94%20a%20worked%20example%22%3A%20mo.md(%0A%20%20%20%20%20%20%20%20%20%20%20%20r%22%22%22%0A%20%20%20%20%20%20%20%20%20%20%20%20Suppose%20boosting%20yields%3A%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%24%24-0.18%5C%2Cy(x%2By)%20%2B%201.18%5C%2Cy(x%2By)%20-%201.01(y-0.51)%5E2%20-%200.42%5C%2Cy%20-%200.76%24%24%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20**Polynomial%20normal%20form**%20expands%20and%20collects%3A%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%7C%20Step%20%7C%20What%20happens%20%7C%0A%20%20%20%20%20%20%20%20%20%20%20%20%7C------%7C-------------%7C%0A%20%20%20%20%20%20%20%20%20%20%20%20%7C%20Expand%20products%20%7C%20%24y(x%2By)%20%5Cto%20xy%20%2B%20y%5E2%24%20%7C%0A%20%20%20%20%20%20%20%20%20%20%20%20%7C%20Merge%20like%20terms%20%7C%20%24-0.18%5C%2Cxy%20%2B%201.18%5C%2Cxy%20%5Cto%201.0%5C%2Cxy%24%20%7C%0A%20%20%20%20%20%20%20%20%20%20%20%20%7C%20Cancel%20near-zero%20%7C%20%24y%5E2%24%20coeff%20%E2%89%88%20%E2%88%920.01%20%E2%86%92%20dropped%20%7C%0A%20%20%20%20%20%20%20%20%20%20%20%20%7C%20Collect%20constants%20%7C%20%24-0.26%20%2B%20(-0.76)%20%5Cto%20-1.02%24%20%7C%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20Result%3A%20%24xy%20%2B%200.77%5C%2Cy%20-%201.02%24%20%E2%80%94%20**3%20terms%20instead%20of%2017%20nodes.**%0A%20%20%20%20%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20%22The%20three%20compression%20stages%22%3A%20mo.md(%0A%20%20%20%20%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20%20%20%20%20%20%20%20%201.%20**Polynomial%20Normal%20Form**%20%E2%80%94%20Treat%20transcendentals%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20(%60sin%60%2C%20%60cos%60%2C%20%60exp%60%2C%20%E2%80%A6)%20as%20opaque%20atoms%2C%20then%20expand%20the%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20remaining%20polynomial.%20%20Identical%20monomials%20merge%3B%20coefficients%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20smaller%20than%2010%E2%81%BB%E2%81%B9%20are%20dropped.%0A%0A%20%20%20%20%20%20%20%20%20%20%20%202.%20**Magnitude%20Pruning**%20%E2%80%94%20Evaluate%20every%20monomial%20on%20the%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20training%20data%2C%20measure%20its%20peak%20absolute%20contribution%2C%20and%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20discard%20any%20term%20contributing%20less%20than%203%20%25%20of%20the%20dominant%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20term.%0A%0A%20%20%20%20%20%20%20%20%20%20%20%203.%20**Equality%20Saturation**%20%E2%80%94%20Insert%20the%20expression%20into%20an%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20*e-graph*%20and%20apply%20algebraic%20rewrite%20rules%20(%60log(exp(x))%E2%86%92x%60%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%60x%2B0%E2%86%92x%60%2C%20%60sqrt(x)%C2%B2%E2%86%92x%60%2C%20%E2%80%A6).%20%20Extract%20the%20smallest%20equivalent.%0A%20%20%20%20%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%7D)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20boost_rounds_slider%20%3D%20mo.ui.slider(%0A%20%20%20%20%20%20%20%20start%3D10%2C%20stop%3D50%2C%20step%3D5%2C%20value%3D30%2C%0A%20%20%20%20%20%20%20%20label%3D%22Boosting%20rounds%22%2C%20show_value%3DTrue%2C%0A%20%20%20%20)%0A%20%20%20%20boost_lr_slider%20%3D%20mo.ui.slider(%0A%20%20%20%20%20%20%20%20start%3D0.05%2C%20stop%3D0.50%2C%20step%3D0.05%2C%20value%3D0.10%2C%0A%20%20%20%20%20%20%20%20label%3D%22Learning%20rate%22%2C%20show_value%3DTrue%2C%0A%20%20%20%20)%0A%20%20%20%20boost_candidates_slider%20%3D%20mo.ui.slider(%0A%20%20%20%20%20%20%20%20start%3D10%2C%20stop%3D100%2C%20step%3D10%2C%20value%3D50%2C%0A%20%20%20%20%20%20%20%20label%3D%22Candidates%20%2F%20round%22%2C%20show_value%3DTrue%2C%0A%20%20%20%20)%0A%20%20%20%20boost_run_btn%20%3D%20mo.ui.run_button(%0A%20%20%20%20%20%20%20%20label%3D%22Run%20Symbolic%20Boosting%22%2C%20kind%3D%22success%22%2C%0A%20%20%20%20)%0A%20%20%20%20mo.vstack(%5B%0A%20%20%20%20%20%20%20%20mo.md(%22%23%23%23%20Configure%20%26%20Run%22)%2C%0A%20%20%20%20%20%20%20%20mo.hstack(%0A%20%20%20%20%20%20%20%20%20%20%20%20%5Bboost_rounds_slider%2C%20boost_lr_slider%2C%20boost_candidates_slider%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20gap%3D1.5%2C%20justify%3D%22start%22%2C%0A%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20mo.callout(%0A%20%20%20%20%20%20%20%20%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22Boosting%20builds%20an%20ensemble%20of%20random%20expression%20trees%2C%20%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22fitting%20each%20to%20the%20current%20residuals.%20%20Every%205%20rounds%20the%20%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22ensemble%20is%20collapsed%20via%20polynomial%20simplification.%20%20%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22Typical%20runtime%3A%20**5%E2%80%9330%20seconds**.%22%0A%20%20%20%20%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20kind%3D%22warn%22%2C%0A%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20boost_run_btn%2C%0A%20%20%20%20%5D)%0A%20%20%20%20return%20(%0A%20%20%20%20%20%20%20%20boost_candidates_slider%2C%0A%20%20%20%20%20%20%20%20boost_lr_slider%2C%0A%20%20%20%20%20%20%20%20boost_rounds_slider%2C%0A%20%20%20%20%20%20%20%20boost_run_btn%2C%0A%20%20%20%20)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(%0A%20%20%20%20X%2C%0A%20%20%20%20boost_candidates_slider%2C%0A%20%20%20%20boost_lr_slider%2C%0A%20%20%20%20boost_rounds_slider%2C%0A%20%20%20%20boost_run_btn%2C%0A%20%20%20%20mo%2C%0A%20%20%20%20np%2C%0A%20%20%20%20scipy_minimize%2C%0A%20%20%20%20sympy%2C%0A%20%20%20%20y%2C%0A)%3A%0A%0A%20%20%20%20mo.stop(%0A%20%20%20%20%20%20%20%20not%20boost_run_btn.value%2C%0A%20%20%20%20%20%20%20%20mo.md(%22_Click%20**Run%20Symbolic%20Boosting**%20above%20to%20start._%22)%2C%0A%20%20%20%20)%0A%0A%20%20%20%20%23%20%E2%94%80%E2%94%80%20random%20expression%20tree%20generation%20%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%0A%0A%20%20%20%20def%20make_random_tree(n_feat%2C%20max_nodes%2C%20rng%2C%20pc)%3A%0A%20%20%20%20%20%20%20%20vs%20%3D%20%5Bsympy.Symbol(f%22x%7Bi%7D%22)%20for%20i%20in%20range(n_feat)%5D%0A%20%20%20%20%20%20%20%20def%20build(budget)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20budget%20%3C%3D%201%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20rng.random()%20%3C%200.5%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20rng.choice(vs)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20p%20%3D%20sympy.Symbol(f%22_p%7Bpc%5B0%5D%7D%22)%3B%20pc%5B0%5D%20%2B%3D%201%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20p%0A%20%20%20%20%20%20%20%20%20%20%20%20op%20%3D%20rng.choice(%5B%22add%22%2C%20%22mul%22%2C%20%22sub%22%2C%20%22neg%22%2C%20%22sq%22%5D)%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20op%20in%20(%22neg%22%2C%20%22sq%22)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20c%20%3D%20build(budget%20-%201)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20-c%20if%20op%20%3D%3D%20%22neg%22%20else%20c%20**%202%0A%20%20%20%20%20%20%20%20%20%20%20%20s%20%3D%20rng.integers(1%2C%20budget)%0A%20%20%20%20%20%20%20%20%20%20%20%20left%2C%20right%20%3D%20build(s)%2C%20build(budget%20-%20s)%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20op%20%3D%3D%20%22add%22%3A%20return%20left%20%2B%20right%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20op%20%3D%3D%20%22sub%22%3A%20return%20left%20-%20right%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20left%20*%20right%0A%20%20%20%20%20%20%20%20return%20build(max_nodes)%0A%0A%20%20%20%20%23%20%E2%94%80%E2%94%80%20parameter%20fitting%20%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%0A%0A%20%20%20%20def%20fit_candidate(expr%2C%20X_data%2C%20residuals%2C%20rng)%3A%0A%20%20%20%20%20%20%20%20params%20%3D%20sorted(%0A%20%20%20%20%20%20%20%20%20%20%20%20%5Bs%20for%20s%20in%20expr.free_symbols%20if%20str(s).startswith(%22_p%22)%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20key%3Dstr%2C%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20feats%20%3D%20sorted(%0A%20%20%20%20%20%20%20%20%20%20%20%20%5Bs%20for%20s%20in%20expr.free_symbols%20if%20not%20str(s).startswith(%22_p%22)%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20key%3Dstr%2C%0A%20%20%20%20%20%20%20%20)%0A%0A%20%20%20%20%20%20%20%20def%20eval_with(expr_inner%2C%20syms_inner)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20fn%20%3D%20sympy.lambdify(syms_inner%20or%20%5B%22dummy%22%5D%2C%20expr_inner%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20modules%3D%5B%22numpy%22%5D)%0A%20%20%20%20%20%20%20%20%20%20%20%20args%20%3D%20%5BX_data%5B%3A%2C%20int(str(s)%5B1%3A%5D)%5D%20for%20s%20in%20syms_inner%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20raw%20%3D%20fn(*args)%20if%20args%20else%20np.full(len(X_data)%2C%20float(expr_inner))%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20np.nan_to_num(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20np.atleast_1d(np.asarray(raw%2C%20float))%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20nan%3D0.0%2C%20posinf%3D0.0%2C%20neginf%3D0.0%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20)%0A%0A%20%20%20%20%20%20%20%20if%20not%20params%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20try%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20expr%2C%20eval_with(expr%2C%20feats)%0A%20%20%20%20%20%20%20%20%20%20%20%20except%20Exception%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20expr%2C%20np.zeros(len(X_data))%0A%0A%20%20%20%20%20%20%20%20all_syms%20%3D%20feats%20%2B%20params%0A%20%20%20%20%20%20%20%20fn%20%3D%20sympy.lambdify(all_syms%2C%20expr%2C%20modules%3D%5B%22numpy%22%5D)%0A%0A%20%20%20%20%20%20%20%20def%20mse_loss(pv)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20try%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20args%20%3D%20(%5BX_data%5B%3A%2C%20int(str(s)%5B1%3A%5D)%5D%20for%20s%20in%20feats%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2B%20list(pv))%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20p%20%3D%20np.nan_to_num(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20np.atleast_1d(np.asarray(fn(*args)%2C%20float))%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20nan%3D0.0%2C%20posinf%3D1e6%2C%20neginf%3D-1e6%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20float(np.mean((residuals%20-%20p)%20**%202))%0A%20%20%20%20%20%20%20%20%20%20%20%20except%20Exception%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%201e12%0A%0A%20%20%20%20%20%20%20%20best_loss%2C%20best_vals%20%3D%20float(%22inf%22)%2C%20np.zeros(len(params))%0A%20%20%20%20%20%20%20%20for%20trial%20in%20range(2)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20try%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20res%20%3D%20scipy_minimize(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20mse_loss%2C%20rng.standard_normal(len(params))%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20method%3D%22L-BFGS-B%22%2C%20options%3D%7B%22maxiter%22%3A%2030%7D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20res.fun%20%3C%20best_loss%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20best_loss%2C%20best_vals%20%3D%20res.fun%2C%20res.x%0A%20%20%20%20%20%20%20%20%20%20%20%20except%20Exception%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pass%0A%0A%20%20%20%20%20%20%20%20subs%20%3D%20%7Bp%3A%20round(float(v)%2C%206)%20for%20p%2C%20v%20in%20zip(params%2C%20best_vals)%7D%0A%20%20%20%20%20%20%20%20fitted%20%3D%20expr.subs(subs)%0A%20%20%20%20%20%20%20%20fs%20%3D%20sorted(fitted.free_symbols%2C%20key%3Dstr)%0A%20%20%20%20%20%20%20%20try%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20fitted%2C%20eval_with(fitted%2C%20fs)%0A%20%20%20%20%20%20%20%20except%20Exception%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20fitted%2C%20np.zeros(len(X_data))%0A%0A%20%20%20%20%23%20%E2%94%80%E2%94%80%20polynomial%20normal%20form%20%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%0A%0A%20%20%20%20TRANSCENDENTALS%20%3D%20(sympy.sin%2C%20sympy.cos%2C%20sympy.exp%2C%20sympy.log)%0A%0A%20%20%20%20def%20poly_nf(expr%2C%20threshold%3D1e-9)%3A%0A%20%20%20%20%20%20%20%20atom_map%2C%20ctr%20%3D%20%7B%7D%2C%20%5B0%5D%0A%20%20%20%20%20%20%20%20def%20replace_trans(e)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20e.func%20in%20TRANSCENDENTALS%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20inner%20%3D%20poly_nf(e.args%5B0%5D%2C%20threshold)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20ne%20%3D%20e.func(inner)%3B%20k%20%3D%20str(ne)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20k%20not%20in%20atom_map%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20s%20%3D%20sympy.Symbol(f%22atom%7Bctr%5B0%5D%7D%22)%3B%20ctr%5B0%5D%20%2B%3D%201%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20atom_map%5Bk%5D%20%3D%20(s%2C%20ne)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20atom_map%5Bk%5D%5B0%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20(e.func(*%5Breplace_trans(a)%20for%20a%20in%20e.args%5D)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20e.args%20else%20e)%0A%20%20%20%20%20%20%20%20replaced%20%3D%20replace_trans(expr)%0A%20%20%20%20%20%20%20%20expanded%20%3D%20sympy.expand(replaced)%0A%20%20%20%20%20%20%20%20terms%20%3D%20sympy.Add.make_args(expanded)%0A%20%20%20%20%20%20%20%20if%20len(terms)%20%3E%20200%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20expr%0A%20%20%20%20%20%20%20%20kept%20%3D%20%5Bt%20for%20t%20in%20terms%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20abs(float(t.as_coeff_Mul()%5B0%5D))%20%3E%20threshold%5D%0A%20%20%20%20%20%20%20%20result%20%3D%20sympy.Add(*kept)%20if%20kept%20else%20sympy.Float(0)%0A%20%20%20%20%20%20%20%20for%20key%2C%20(s%2C%20orig)%20in%20atom_map.items()%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20result%20%3D%20result.subs(s%2C%20orig)%0A%20%20%20%20%20%20%20%20return%20result%0A%0A%20%20%20%20%23%20%E2%94%80%E2%94%80%20magnitude%20pruning%20%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%0A%0A%20%20%20%20def%20mag_prune(expr%2C%20X_data%2C%20threshold%3D0.03)%3A%0A%20%20%20%20%20%20%20%20expanded%20%3D%20sympy.expand(expr)%0A%20%20%20%20%20%20%20%20terms%20%3D%20list(sympy.Add.make_args(expanded))%0A%20%20%20%20%20%20%20%20if%20len(terms)%20%3C%3D%201%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20expr%0A%20%20%20%20%20%20%20%20fsyms%20%3D%20sorted(expr.free_symbols%2C%20key%3Dstr)%0A%20%20%20%20%20%20%20%20peaks%20%3D%20%5B%5D%0A%20%20%20%20%20%20%20%20for%20t%20in%20terms%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20try%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20fn%20%3D%20sympy.lambdify(fsyms%2C%20t%2C%20modules%3D%5B%22numpy%22%5D)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20vals%20%3D%20fn(*%5BX_data%5B%3A%2C%20int(str(s)%5B1%3A%5D)%5D%20for%20s%20in%20fsyms%5D)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20peaks.append(float(np.nanmax(np.abs(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20np.nan_to_num(vals%2C%20nan%3D0.0)))))%0A%20%20%20%20%20%20%20%20%20%20%20%20except%20Exception%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20peaks.append(float(%22inf%22))%0A%20%20%20%20%20%20%20%20mx%20%3D%20max(peaks)%20if%20peaks%20else%201.0%0A%20%20%20%20%20%20%20%20if%20mx%20%3D%3D%200%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20expr%0A%20%20%20%20%20%20%20%20kept%20%3D%20%5Bt%20for%20t%2C%20p%20in%20zip(terms%2C%20peaks)%20if%20p%20%2F%20mx%20%3E%3D%20threshold%5D%0A%20%20%20%20%20%20%20%20return%20sympy.Add(*kept)%20if%20kept%20else%20expr%0A%0A%20%20%20%20%23%20%E2%94%80%E2%94%80%20boosting%20loop%20%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%0A%0A%20%20%20%20n_rounds%20%3D%20boost_rounds_slider.value%0A%20%20%20%20lr%20%3D%20boost_lr_slider.value%0A%20%20%20%20n_cands%20%3D%20boost_candidates_slider.value%0A%20%20%20%20collapse_every%20%3D%205%0A%0A%20%20%20%20rng%20%3D%20np.random.default_rng(42)%0A%20%20%20%20bias%20%3D%20float(np.mean(y))%0A%20%20%20%20residuals%20%3D%20y%20-%20bias%0A%20%20%20%20predictions%20%3D%20np.full(len(y)%2C%20bias)%0A%20%20%20%20ensemble%20%3D%20sympy.Float(round(bias%2C%206))%0A%20%20%20%20raw_ensemble%20%3D%20sympy.Float(round(bias%2C%206))%0A%20%20%20%20ss_tot%20%3D%20float(np.sum((y%20-%20np.mean(y))%20**%202))%0A%0A%20%20%20%20boost_history%20%3D%20%5B%5D%0A%0A%20%20%20%20with%20mo.status.spinner(title%3D%22Symbolic%20boosting%20in%20progress...%22)%3A%0A%20%20%20%20%20%20%20%20for%20rnd%20in%20range(n_rounds)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20pc%20%3D%20%5B0%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20best_gain%2C%20best_expr%2C%20best_preds%2C%20best_alpha%20%3D%20-1.0%2C%20None%2C%20None%2C%200.0%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20for%20cand%20in%20range(n_cands)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20tree%20%3D%20make_random_tree(X.shape%5B1%5D%2C%207%2C%20rng%2C%20pc)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20fitted%2C%20preds_c%20%3D%20fit_candidate(tree%2C%20X%2C%20residuals%2C%20rng)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20dp%20%3D%20float(np.dot(residuals%2C%20preds_c))%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pp%20%3D%20float(np.dot(preds_c%2C%20preds_c))%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20pp%20%3C%201e-12%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20continue%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20alpha%20%3D%20dp%20%2F%20pp%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20gain%20%3D%20alpha%20**%202%20*%20pp%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20gain%20%3E%20best_gain%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20best_gain%20%3D%20gain%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20best_expr%2C%20best_preds%2C%20best_alpha%20%3D%20fitted%2C%20preds_c%2C%20alpha%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20best_expr%20is%20None%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20continue%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20eff%20%3D%20lr%20*%20best_alpha%0A%20%20%20%20%20%20%20%20%20%20%20%20ensemble%20%3D%20ensemble%20%2B%20eff%20*%20best_expr%0A%20%20%20%20%20%20%20%20%20%20%20%20raw_ensemble%20%3D%20raw_ensemble%20%2B%20eff%20*%20best_expr%0A%20%20%20%20%20%20%20%20%20%20%20%20predictions%20%3D%20predictions%20%2B%20eff%20*%20best_preds%0A%20%20%20%20%20%20%20%20%20%20%20%20residuals%20%3D%20y%20-%20predictions%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20ops_before%20%3D%20int(sympy.count_ops(ensemble))%0A%20%20%20%20%20%20%20%20%20%20%20%20did_collapse%20%3D%20False%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20(rnd%20%2B%201)%20%25%20collapse_every%20%3D%3D%200%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20ensemble%20%3D%20poly_nf(ensemble)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20ensemble%20%3D%20mag_prune(ensemble%2C%20X)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20did_collapse%20%3D%20True%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20ops_after%20%3D%20int(sympy.count_ops(ensemble))%0A%20%20%20%20%20%20%20%20%20%20%20%20r2%20%3D%201.0%20-%20float(np.sum(residuals%20**%202))%20%2F%20ss_tot%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20boost_history.append(%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22round%22%3A%20rnd%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22ops_before%22%3A%20ops_before%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22ops_after%22%3A%20ops_after%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22r2%22%3A%20r2%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22collapsed%22%3A%20did_collapse%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D)%0A%0A%20%20%20%20boost_ensemble%20%3D%20ensemble%0A%20%20%20%20boost_raw_ensemble%20%3D%20raw_ensemble%0A%0A%20%20%20%20final_r2%20%3D%20boost_history%5B-1%5D%5B%22r2%22%5D%20if%20boost_history%20else%200.0%0A%20%20%20%20raw_ops%20%3D%20int(sympy.count_ops(boost_raw_ensemble))%0A%20%20%20%20final_ops%20%3D%20int(sympy.count_ops(boost_ensemble))%0A%0A%20%20%20%20mo.vstack(%5B%0A%20%20%20%20%20%20%20%20mo.md(f%22%23%23%23%20Boosting%20complete%20%E2%80%94%20%7Bn_rounds%7D%20rounds%22)%2C%0A%20%20%20%20%20%20%20%20mo.hstack(%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20mo.stat(value%3Df%22%7Bfinal_r2%3A.4f%7D%22%2C%20label%3D%22Final%20R%C2%B2%22)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20mo.stat(value%3Dstr(raw_ops)%2C%20label%3D%22Raw%20ensemble%20ops%22)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20mo.stat(value%3Dstr(final_ops)%2C%20label%3D%22After%20collapse%22)%2C%0A%20%20%20%20%20%20%20%20%5D%2C%20justify%3D%22start%22)%2C%0A%20%20%20%20%5D)%0A%20%20%20%20return%20boost_ensemble%2C%20boost_history%2C%20boost_raw_ensemble%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(boost_history%2C%20mo%2C%20plt)%3A%0A%20%20%20%20%23%20Build%20the%20sawtooth%20series%3A%20for%20collapse%20rounds%2C%20record%20both%20the%0A%20%20%20%20%23%20pre-collapse%20and%20post-collapse%20op%20counts%20at%20the%20same%20x-coordinate.%0A%20%20%20%20_ops_x%2C%20_ops_y%20%3D%20%5B%5D%2C%20%5B%5D%0A%20%20%20%20for%20h%20in%20boost_history%3A%0A%20%20%20%20%20%20%20%20_ops_x.append(h%5B%22round%22%5D)%0A%20%20%20%20%20%20%20%20_ops_y.append(h%5B%22ops_before%22%5D)%0A%20%20%20%20%20%20%20%20if%20h%5B%22collapsed%22%5D%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20_ops_x.append(h%5B%22round%22%5D)%0A%20%20%20%20%20%20%20%20%20%20%20%20_ops_y.append(h%5B%22ops_after%22%5D)%0A%0A%20%20%20%20_r2_x%20%3D%20%5Bh%5B%22round%22%5D%20for%20h%20in%20boost_history%5D%0A%20%20%20%20_r2_y%20%3D%20%5Bh%5B%22r2%22%5D%20for%20h%20in%20boost_history%5D%0A%20%20%20%20_collapse_rounds%20%3D%20%5Bh%5B%22round%22%5D%20for%20h%20in%20boost_history%20if%20h%5B%22collapsed%22%5D%5D%0A%0A%20%20%20%20_fig%2C%20(_ax1%2C%20_ax2)%20%3D%20plt.subplots(2%2C%201%2C%20figsize%3D(9%2C%206)%2C%20sharex%3DTrue)%0A%0A%20%20%20%20%23%20Top%3A%20ops%20over%20time%20(sawtooth)%0A%20%20%20%20_ax1.plot(_ops_x%2C%20_ops_y%2C%20color%3D%22%231976d2%22%2C%20lw%3D2)%0A%20%20%20%20_ax1.fill_between(_ops_x%2C%20_ops_y%2C%20alpha%3D0.15%2C%20color%3D%22%231976d2%22)%0A%20%20%20%20for%20_cr%20in%20_collapse_rounds%3A%0A%20%20%20%20%20%20%20%20_ax1.axvline(_cr%2C%20color%3D%22%23d32f2f%22%2C%20ls%3D%22--%22%2C%20lw%3D0.8%2C%20alpha%3D0.6)%0A%20%20%20%20_ax1.set_ylabel(%22Expression%20complexity%20(ops)%22)%0A%20%20%20%20_ax1.set_title(%0A%20%20%20%20%20%20%20%20%22Ensemble%20grows%20messy%2C%20then%20collapses%20at%20each%20simplification%22%0A%20%20%20%20)%0A%20%20%20%20if%20_collapse_rounds%3A%0A%20%20%20%20%20%20%20%20_ax1.axvline(%0A%20%20%20%20%20%20%20%20%20%20%20%20_collapse_rounds%5B0%5D%2C%20color%3D%22%23d32f2f%22%2C%20ls%3D%22--%22%2C%20lw%3D0.8%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20alpha%3D0.6%2C%20label%3D%22collapse%22%2C%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20_ax1.legend(fontsize%3D8)%0A%0A%20%20%20%20%23%20Bottom%3A%20R%C2%B2%20over%20time%0A%20%20%20%20_ax2.plot(_r2_x%2C%20_r2_y%2C%20color%3D%22%23388e3c%22%2C%20lw%3D2)%0A%20%20%20%20_ax2.fill_between(_r2_x%2C%20_r2_y%2C%20alpha%3D0.15%2C%20color%3D%22%23388e3c%22)%0A%20%20%20%20for%20_cr%20in%20_collapse_rounds%3A%0A%20%20%20%20%20%20%20%20_ax2.axvline(_cr%2C%20color%3D%22%23d32f2f%22%2C%20ls%3D%22--%22%2C%20lw%3D0.8%2C%20alpha%3D0.6)%0A%20%20%20%20_ax2.set_xlabel(%22Boosting%20round%22)%0A%20%20%20%20_ax2.set_ylabel(%22R%C2%B2%22)%0A%20%20%20%20_ax2.set_ylim(max(0%2C%20min(_r2_y)%20-%200.05)%20if%20_r2_y%20else%200%2C%201.01)%0A%20%20%20%20_ax2.set_title(%22Prediction%20accuracy%20climbs%20steadily%20despite%20collapses%22)%0A%0A%20%20%20%20_fig.tight_layout()%0A%0A%20%20%20%20mo.vstack(%5B%0A%20%20%20%20%20%20%20%20mo.md(%22%23%23%23%20Ensemble%20Growth%20%26%20Collapse%22)%2C%0A%20%20%20%20%20%20%20%20_fig%2C%0A%20%20%20%20%20%20%20%20mo.callout(%0A%20%20%20%20%20%20%20%20%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22**Top%3A**%20expression%20complexity%20spikes%20as%20new%20terms%20are%20%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22added%2C%20then%20drops%20sharply%20at%20each%20collapse%20event%20(red%20%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22dashes).%20%20**Bottom%3A**%20R%C2%B2%20climbs%20steadily%20%E2%80%94%20simplification%20%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22does%20not%20hurt%20accuracy.%22%0A%20%20%20%20%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20kind%3D%22info%22%2C%0A%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%5D)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(boost_ensemble%2C%20boost_raw_ensemble%2C%20mo%2C%20sympy)%3A%0A%20%20%20%20_raw_ops%20%3D%20int(sympy.count_ops(boost_raw_ensemble))%0A%20%20%20%20_clean_ops%20%3D%20int(sympy.count_ops(boost_ensemble))%0A%0A%20%20%20%20mo.vstack(%5B%0A%20%20%20%20%20%20%20%20mo.md(%22%23%23%23%20Before%20%26%20After%20Simplification%22)%2C%0A%20%20%20%20%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20%20%20%20%20f%22**Raw%20ensemble**%20(%7B_raw_ops%7D%20ops)%20%E2%80%94%20what%20boosting%20built%20%22%0A%20%20%20%20%20%20%20%20%20%20%20%20f%22before%20any%20simplification%3A%22%0A%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20mo.md(f%22%24%24%7Bsympy.latex(boost_raw_ensemble)%7D%24%24%22)%2C%0A%20%20%20%20%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20%20%20%20%20f%22**Collapsed%20ensemble**%20(%7B_clean_ops%7D%20ops)%20%E2%80%94%20after%20polynomial%20%22%0A%20%20%20%20%20%20%20%20%20%20%20%20f%22normal%20form%20%2B%20magnitude%20pruning%3A%22%0A%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20mo.md(f%22%24%24%5C%5Cboxed%7B%7B%7Bsympy.latex(boost_ensemble)%7D%7D%7D%24%24%22)%2C%0A%20%20%20%20%20%20%20%20mo.callout(%0A%20%20%20%20%20%20%20%20%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20f%22**%7B_raw_ops%7D%20ops%20%E2%86%92%20%7B_clean_ops%7D%20ops.**%20%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20f%22Polynomial%20expansion%20merged%20like%20terms%3B%20magnitude%20pruning%20%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20f%22dropped%20the%20junk.%20%20The%20messy%20additive%20ensemble%20compressed%20%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20f%22into%20a%20compact%20formula.%22%0A%20%20%20%20%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20kind%3D%22success%22%2C%0A%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%5D)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%22%22%22%0A%20%20%20%20%23%23%23%20E-graph%20Cleanup%20(Equality%20Saturation)%0A%0A%20%20%20%20As%20a%20final%20pass%2C%20we%20insert%20the%20simplified%20expression%20into%20an%20**e-graph**%0A%20%20%20%20and%20apply%20algebraic%20rewrite%20rules.%20%20This%20catches%20identities%20that%0A%20%20%20%20polynomial%20expansion%20misses%20%E2%80%94%20e.g.%20%60log(exp(x))%20%E2%86%92%20x%60%2C%20%60x%20%2B%200%20%E2%86%92%20x%60%2C%0A%20%20%20%20%60sqrt(x)%C2%B2%20%E2%86%92%20x%60.%0A%0A%20%20%20%20%7C%20Category%20%7C%20Rules%20%7C%0A%20%20%20%20%7C----------%7C-------%7C%0A%20%20%20%20%7C%20**Identity**%20%7C%20%60x%20%2B%200%20%E2%86%92%20x%60%2C%20%20%60x%20*%201%20%E2%86%92%20x%60%2C%20%20%60x%20*%200%20%E2%86%92%200%60%20%7C%0A%20%20%20%20%7C%20**Inverse**%20%7C%20%60log(exp(x))%20%E2%86%92%20x%60%2C%20%20%60exp(log(x))%20%E2%86%92%20x%60%2C%20%20%60%E2%88%92(%E2%88%92x)%20%E2%86%92%20x%60%20%7C%0A%20%20%20%20%7C%20**Power**%20%7C%20%60sqrt(x)%C2%B2%20%E2%86%92%20x%60%2C%20%20%60x%20*%20x%20%E2%86%92%20x%C2%B2%60%20%7C%0A%0A%20%20%20%20E-graphs%20help%2C%20but%20only%20when%20**carefully%20constrained**%20%E2%80%94%20unbounded%0A%20%20%20%20distributivity%20rules%20cause%20the%20graph%20to%20explode.%20%20We%20cap%20saturation%0A%20%20%20%20at%2030%20iterations.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(boost_ensemble%2C%20boost_raw_ensemble%2C%20egglog%2C%20mo%2C%20sympy)%3A%0A%20%20%20%20class%20MathE(egglog.Expr)%3A%0A%20%20%20%20%20%20%20%20def%20__init__(self%2C%20value%3A%20egglog.f64Like)%20-%3E%20None%3A%20...%0A%20%20%20%20%20%20%20%20%40classmethod%0A%20%20%20%20%20%20%20%20def%20v(cls%2C%20name%3A%20egglog.StringLike)%20-%3E%20%22MathE%22%3A%20...%0A%20%20%20%20%20%20%20%20def%20__add__(self%2C%20other%3A%20%22MathE%22)%20-%3E%20%22MathE%22%3A%20...%0A%20%20%20%20%20%20%20%20def%20__mul__(self%2C%20other%3A%20%22MathE%22)%20-%3E%20%22MathE%22%3A%20...%0A%20%20%20%20%20%20%20%20def%20__sub__(self%2C%20other%3A%20%22MathE%22)%20-%3E%20%22MathE%22%3A%20...%0A%20%20%20%20%20%20%20%20def%20__truediv__(self%2C%20other%3A%20%22MathE%22)%20-%3E%20%22MathE%22%3A%20...%0A%20%20%20%20%20%20%20%20def%20__neg__(self)%20-%3E%20%22MathE%22%3A%20...%0A%20%20%20%20%20%20%20%20def%20__pow__(self%2C%20other%3A%20%22MathE%22)%20-%3E%20%22MathE%22%3A%20...%0A%20%20%20%20%20%20%20%20%40classmethod%0A%20%20%20%20%20%20%20%20def%20Sin(cls%2C%20x%3A%20%22MathE%22)%20-%3E%20%22MathE%22%3A%20...%0A%20%20%20%20%20%20%20%20%40classmethod%0A%20%20%20%20%20%20%20%20def%20Cos(cls%2C%20x%3A%20%22MathE%22)%20-%3E%20%22MathE%22%3A%20...%0A%20%20%20%20%20%20%20%20%40classmethod%0A%20%20%20%20%20%20%20%20def%20Exp(cls%2C%20x%3A%20%22MathE%22)%20-%3E%20%22MathE%22%3A%20...%0A%20%20%20%20%20%20%20%20%40classmethod%0A%20%20%20%20%20%20%20%20def%20Log(cls%2C%20x%3A%20%22MathE%22)%20-%3E%20%22MathE%22%3A%20...%0A%20%20%20%20%20%20%20%20%40classmethod%0A%20%20%20%20%20%20%20%20def%20Sqrt(cls%2C%20x%3A%20%22MathE%22)%20-%3E%20%22MathE%22%3A%20...%0A%0A%20%20%20%20ea%2C%20eb%20%3D%20egglog.vars_(%22a%20b%22%2C%20MathE)%0A%20%20%20%20ezero%2C%20eone%2C%20etwo%20%3D%20MathE(0.0)%2C%20MathE(1.0)%2C%20MathE(2.0)%0A%0A%20%20%20%20erules%20%3D%20%5B%0A%20%20%20%20%20%20%20%20egglog.rewrite(ea%20%2B%20ezero).to(ea)%2C%0A%20%20%20%20%20%20%20%20egglog.rewrite(ezero%20%2B%20ea).to(ea)%2C%0A%20%20%20%20%20%20%20%20egglog.rewrite(ea%20*%20eone).to(ea)%2C%0A%20%20%20%20%20%20%20%20egglog.rewrite(eone%20*%20ea).to(ea)%2C%0A%20%20%20%20%20%20%20%20egglog.rewrite(ea%20*%20ezero).to(ezero)%2C%0A%20%20%20%20%20%20%20%20egglog.rewrite(ezero%20*%20ea).to(ezero)%2C%0A%20%20%20%20%20%20%20%20egglog.rewrite(ea%20-%20ezero).to(ea)%2C%0A%20%20%20%20%20%20%20%20egglog.rewrite(ea%20%2F%20eone).to(ea)%2C%0A%20%20%20%20%20%20%20%20egglog.rewrite(-(-ea)).to(ea)%2C%0A%20%20%20%20%20%20%20%20egglog.rewrite(MathE.Log(MathE.Exp(ea))).to(ea)%2C%0A%20%20%20%20%20%20%20%20egglog.rewrite(MathE.Exp(MathE.Log(ea))).to(ea)%2C%0A%20%20%20%20%20%20%20%20egglog.rewrite(MathE.Sqrt(ea)%20**%20etwo).to(ea)%2C%0A%20%20%20%20%20%20%20%20egglog.rewrite(ea%20*%20ea).to(ea%20**%20etwo)%2C%0A%20%20%20%20%5D%0A%0A%20%20%20%20def%20to_egg(expr)%3A%0A%20%20%20%20%20%20%20%20if%20isinstance(expr%2C%20(sympy.Integer%2C%20sympy.Float%2C%20sympy.Rational))%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20MathE(float(expr))%0A%20%20%20%20%20%20%20%20if%20isinstance(expr%2C%20sympy.Symbol)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20MathE.v(str(expr))%0A%20%20%20%20%20%20%20%20if%20isinstance(expr%2C%20sympy.Add)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20r%20%3D%20to_egg(expr.args%5B0%5D)%0A%20%20%20%20%20%20%20%20%20%20%20%20for%20a%20in%20expr.args%5B1%3A%5D%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20r%20%3D%20r%20%2B%20to_egg(a)%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20r%0A%20%20%20%20%20%20%20%20if%20isinstance(expr%2C%20sympy.Mul)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20r%20%3D%20to_egg(expr.args%5B0%5D)%0A%20%20%20%20%20%20%20%20%20%20%20%20for%20a%20in%20expr.args%5B1%3A%5D%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20r%20%3D%20r%20*%20to_egg(a)%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20r%0A%20%20%20%20%20%20%20%20if%20isinstance(expr%2C%20sympy.Pow)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20base%2C%20exp_%20%3D%20expr.args%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20exp_%20%3D%3D%20sympy.Rational(1%2C%202)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20MathE.Sqrt(to_egg(base))%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20exp_%20in%20(-1%2C%20sympy.Integer(-1))%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20eone%20%2F%20to_egg(base)%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20to_egg(base)%20**%20to_egg(exp_)%0A%20%20%20%20%20%20%20%20if%20isinstance(expr%2C%20sympy.sin)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20MathE.Sin(to_egg(expr.args%5B0%5D))%0A%20%20%20%20%20%20%20%20if%20isinstance(expr%2C%20sympy.cos)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20MathE.Cos(to_egg(expr.args%5B0%5D))%0A%20%20%20%20%20%20%20%20if%20isinstance(expr%2C%20sympy.exp)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20MathE.Exp(to_egg(expr.args%5B0%5D))%0A%20%20%20%20%20%20%20%20if%20isinstance(expr%2C%20sympy.log)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20MathE.Log(to_egg(expr.args%5B0%5D))%0A%20%20%20%20%20%20%20%20return%20MathE.v(str(expr))%0A%0A%20%20%20%20def%20from_egg(eexpr)%3A%0A%20%20%20%20%20%20%20%20fn%20%3D%20str(egglog.get_callable_fn(eexpr))%0A%20%20%20%20%20%20%20%20args%20%3D%20egglog.get_callable_args(eexpr)%0A%20%20%20%20%20%20%20%20if%20args%20is%20None%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20val%20%3D%20egglog.get_literal_value(eexpr)%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20isinstance(val%2C%20(int%2C%20float))%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20sympy.Float(val)%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20isinstance(val%2C%20str)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20sympy.Symbol(val)%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20sympy.Float(float(val))%0A%20%20%20%20%20%20%20%20children%20%3D%20%5Bfrom_egg(a)%20for%20a%20in%20args%5D%0A%20%20%20%20%20%20%20%20dispatch%20%3D%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%22%C2%B7%20%2B%20%C2%B7%22%3A%20lambda%20c%3A%20sympy.Add(*c)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22%C2%B7%20*%20%C2%B7%22%3A%20lambda%20c%3A%20sympy.Mul(*c)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22%C2%B7%20-%20%C2%B7%22%3A%20lambda%20c%3A%20c%5B0%5D%20-%20c%5B1%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22%C2%B7%20%2F%20%C2%B7%22%3A%20lambda%20c%3A%20c%5B0%5D%20%2F%20c%5B1%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22%C2%B7%20**%20%C2%B7%22%3A%20lambda%20c%3A%20c%5B0%5D%20**%20c%5B1%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22-%C2%B7%22%3A%20lambda%20c%3A%20-c%5B0%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22MathE%22%3A%20lambda%20c%3A%20c%5B0%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22MathE.v%22%3A%20lambda%20c%3A%20c%5B0%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22MathE.Sin%22%3A%20lambda%20c%3A%20sympy.sin(c%5B0%5D)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22MathE.Cos%22%3A%20lambda%20c%3A%20sympy.cos(c%5B0%5D)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22MathE.Exp%22%3A%20lambda%20c%3A%20sympy.exp(c%5B0%5D)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22MathE.Log%22%3A%20lambda%20c%3A%20sympy.log(c%5B0%5D)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22MathE.Sqrt%22%3A%20lambda%20c%3A%20sympy.sqrt(c%5B0%5D)%2C%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20if%20fn%20in%20dispatch%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20dispatch%5Bfn%5D(children)%0A%20%20%20%20%20%20%20%20return%20sympy.Symbol(f%22%3F%7Bfn%7D%3F%22)%0A%0A%20%20%20%20eg%20%3D%20egglog.EGraph()%0A%20%20%20%20eg.register(*erules)%0A%20%20%20%20ref%20%3D%20eg.let(%22expr%22%2C%20to_egg(boost_ensemble))%0A%20%20%20%20eg.run(30)%0A%20%20%20%20extracted%20%3D%20eg.extract(ref)%0A%20%20%20%20egraph_simplified%20%3D%20from_egg(extracted)%0A%0A%20%20%20%20before_ops%20%3D%20sympy.count_ops(boost_raw_ensemble)%0A%20%20%20%20after_ops%20%3D%20sympy.count_ops(egraph_simplified)%0A%0A%20%20%20%20mo.vstack(%5B%0A%20%20%20%20%20%20%20%20mo.hstack(%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20mo.stat(value%3Dstr(before_ops)%2C%20label%3D%22Ops%20(before)%22)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20mo.stat(value%3Dstr(after_ops)%2C%20label%3D%22Ops%20(after)%22)%2C%0A%20%20%20%20%20%20%20%20%5D%2C%20justify%3D%22start%22)%2C%0A%20%20%20%20%20%20%20%20mo.md(f%22**Before%20e-graph%3A**%5Cn%5Cn%24%24%7Bsympy.latex(boost_raw_ensemble)%7D%24%24%22)%2C%0A%20%20%20%20%20%20%20%20mo.md(f%22**After%20e-graph%3A**%5Cn%5Cn%24%24%7Bsympy.latex(egraph_simplified)%7D%24%24%22)%2C%0A%20%20%20%20%5D)%0A%20%20%20%20return%20(egraph_simplified%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(%0A%20%20%20%20X%2C%0A%20%20%20%20boost_raw_ensemble%2C%0A%20%20%20%20egraph_simplified%2C%0A%20%20%20%20mo%2C%0A%20%20%20%20np%2C%0A%20%20%20%20plt%2C%0A%20%20%20%20sympy%2C%0A%20%20%20%20true_equation_latex%2C%0A%20%20%20%20y%2C%0A)%3A%0A%20%20%20%20def%20_eval_expr(expr)%3A%0A%20%20%20%20%20%20%20%20syms%20%3D%20sorted(expr.free_symbols%2C%20key%3Dstr)%0A%20%20%20%20%20%20%20%20if%20not%20syms%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20np.full(len(y)%2C%20float(expr))%0A%20%20%20%20%20%20%20%20fn%20%3D%20sympy.lambdify(syms%2C%20expr%2C%20modules%3D%5B%22numpy%22%5D)%0A%20%20%20%20%20%20%20%20args%20%3D%20%5BX%5B%3A%2C%20int(str(s)%5B1%3A%5D)%5D%20for%20s%20in%20syms%5D%0A%20%20%20%20%20%20%20%20vals%20%3D%20fn(*args)%0A%20%20%20%20%20%20%20%20return%20np.nan_to_num(%0A%20%20%20%20%20%20%20%20%20%20%20%20np.atleast_1d(np.asarray(vals%2C%20float))%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20nan%3D0.0%2C%20posinf%3D0.0%2C%20neginf%3D0.0%2C%0A%20%20%20%20%20%20%20%20)%0A%0A%20%20%20%20def%20_r2(y_pred)%3A%0A%20%20%20%20%20%20%20%20ss_res%20%3D%20float(np.sum((y%20-%20y_pred)%20**%202))%0A%20%20%20%20%20%20%20%20ss_tot%20%3D%20float(np.sum((y%20-%20np.mean(y))%20**%202))%0A%20%20%20%20%20%20%20%20return%201.0%20-%20ss_res%20%2F%20ss_tot%20if%20ss_tot%20%3E%200%20else%200.0%0A%0A%20%20%20%20_stages%20%3D%20%5B%0A%20%20%20%20%20%20%20%20(%22Raw%20ensemble%22%2C%20boost_raw_ensemble)%2C%0A%20%20%20%20%20%20%20%20(%22Final%20(e-graph)%22%2C%20egraph_simplified)%2C%0A%20%20%20%20%5D%0A%0A%20%20%20%20_ops%20%3D%20%5Bint(sympy.count_ops(e))%20for%20_%2C%20e%20in%20_stages%5D%0A%20%20%20%20_r2s%20%3D%20%5B_r2(_eval_expr(e))%20for%20_%2C%20e%20in%20_stages%5D%0A%0A%20%20%20%20_fig%2C%20(_ax1%2C%20_ax2)%20%3D%20plt.subplots(1%2C%202%2C%20figsize%3D(10%2C%204))%0A%20%20%20%20_colors%20%3D%20%5B%22%23d32f2f%22%2C%20%22%23388e3c%22%5D%0A%0A%20%20%20%20_ax1.bar(%0A%20%20%20%20%20%20%20%20%5Bs%5B0%5D%20for%20s%20in%20_stages%5D%2C%20_ops%2C%0A%20%20%20%20%20%20%20%20color%3D_colors%2C%20edgecolor%3D%22white%22%2C%0A%20%20%20%20)%0A%20%20%20%20_ax1.set_ylabel(%22Operation%20count%22)%0A%20%20%20%20_ax1.set_title(%22Complexity%20collapses%20%E2%80%A6%22)%0A%0A%20%20%20%20_ax2.bar(%0A%20%20%20%20%20%20%20%20%5Bs%5B0%5D%20for%20s%20in%20_stages%5D%2C%20_r2s%2C%0A%20%20%20%20%20%20%20%20color%3D_colors%2C%20edgecolor%3D%22white%22%2C%0A%20%20%20%20)%0A%20%20%20%20_ax2.set_ylabel(%22R%C2%B2%22)%0A%20%20%20%20_ax2.set_ylim(min(min(_r2s)%20-%200.05%2C%200.9)%2C%201.01)%0A%20%20%20%20_ax2.set_title(%22%E2%80%A6%20while%20accuracy%20stays%20flat%22)%0A%20%20%20%20_fig.tight_layout()%0A%0A%20%20%20%20_r2_drop%20%3D%20abs(_r2s%5B0%5D%20-%20_r2s%5B-1%5D)%0A%0A%20%20%20%20mo.vstack(%5B%0A%20%20%20%20%20%20%20%20mo.md(%22%23%23%23%20Final%20Result%22)%2C%0A%20%20%20%20%20%20%20%20_fig%2C%0A%20%20%20%20%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20%20%20%20%20f%22**True%20equation%3A**%20%7Btrue_equation_latex%7D%5Cn%5Cn%22%0A%20%20%20%20%20%20%20%20%20%20%20%20f%22**Discovered%3A**%20%24%24%7Bsympy.latex(egraph_simplified)%7D%24%24%22%0A%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20mo.callout(%0A%20%20%20%20%20%20%20%20%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20f%22**Verdict%3A**%20**%7B_ops%5B0%5D%7D%20ops%20%E2%86%92%20%7B_ops%5B-1%5D%7D%20ops**%20%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20f%22with%20%CE%94R%C2%B2%20%3D%20%7B_r2_drop%3A.6f%7D.%22%0A%20%20%20%20%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20kind%3D%22success%22%2C%0A%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20mo.callout(%0A%20%20%20%20%20%20%20%20%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22**Symbolic%20boosting%20is%20a%20neat%20search%20strategy.**%20%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22Algebraic%20normalization%20acts%20like%20compression.%20%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22E-graphs%20help%2C%20but%20only%20when%20carefully%20constrained.%22%0A%20%20%20%20%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20kind%3D%22info%22%2C%0A%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%5D)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%22%22%22%0A%20%20%20%20%23%23%209.%20Takeaways%20%26%20Next%20Steps%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.accordion(%7B%0A%20%20%20%20%20%20%20%20%22When%20to%20use%20symbolic%20regression%22%3A%20mo.md(%0A%20%20%20%20%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20%20%20%20%20%20%20%20%20-%20You%20need%20an%20**interpretable**%20model%20(scientific%20discovery%2C%20regulation%2C%20trust)%0A%20%20%20%20%20%20%20%20%20%20%20%20-%20Your%20dataset%20is%20small-to-medium%20sized%20(hundreds%20to%20low%20thousands%20of%20rows)%0A%20%20%20%20%20%20%20%20%20%20%20%20-%20You%20suspect%20the%20underlying%20relationship%20has%20a%20closed-form%20expression%0A%20%20%20%20%20%20%20%20%20%20%20%20-%20You%20want%20a%20model%20you%20can%20differentiate%2C%20integrate%2C%20or%20simplify%20analytically%0A%20%20%20%20%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20%22Limitations%22%3A%20mo.md(%0A%20%20%20%20%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20%20%20%20%20%20%20%20%20-%20**Slower**%20than%20gradient-based%20methods%20%E2%80%94%20not%20for%20real-time%20model%20training%0A%20%20%20%20%20%20%20%20%20%20%20%20-%20Scales%20poorly%20beyond%20~10%20input%20features%0A%20%20%20%20%20%20%20%20%20%20%20%20-%20Results%20are%20**stochastic**%20%E2%80%94%20different%20runs%20may%20find%20different%20equations%0A%20%20%20%20%20%20%20%20%20%20%20%20-%20Cannot%20capture%20truly%20non-analytic%20relationships%0A%20%20%20%20%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20%22Tips%20for%20tuning%20PySR%22%3A%20mo.md(%0A%20%20%20%20%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20%20%20%20%20%20%20%20%20-%20Start%20with%20**low%20iterations**%20(10%E2%80%9320)%20to%20get%20a%20quick%20feel%2C%20then%20increase%0A%20%20%20%20%20%20%20%20%20%20%20%20-%20Set%20%60parsimony%60%20higher%20(0.03%E2%80%930.05)%20if%20results%20are%20too%20complex%0A%20%20%20%20%20%20%20%20%20%20%20%20-%20Add%20domain-specific%20operators%20if%20you%20know%20the%20physics%20(e.g.%2C%20%60square%60%2C%20%60cube%60)%0A%20%20%20%20%20%20%20%20%20%20%20%20-%20Use%20%60maxsize%60%20to%20cap%20complexity%20and%20speed%20up%20the%20search%0A%20%20%20%20%20%20%20%20%20%20%20%20-%20Set%20%60random_state%60%20for%20reproducibility%20during%20development%0A%20%20%20%20%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%7D)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%22%22%22%0A%20%20%20%20%23%23%23%20Export%20the%20Best%20Equation%0A%0A%20%20%20%20Once%20you've%20chosen%20a%20model%20from%20the%20Pareto%20front%2C%20here's%20how%20to%20use%20it%3A%0A%0A%20%20%20%20%60%60%60python%0A%20%20%20%20%23%20Best%20equation%20as%20a%20SymPy%20expression%0A%20%20%20%20expr%20%3D%20model.sympy()%0A%0A%20%20%20%20%23%20LaTeX%20string%0A%20%20%20%20latex_str%20%3D%20model.latex()%0A%0A%20%20%20%20%23%20Predict%20on%20new%20data%0A%20%20%20%20y_new%20%3D%20model.predict(X_new)%0A%0A%20%20%20%20%23%20Get%20a%20specific%20equation%20by%20index%0A%20%20%20%20expr_at_3%20%3D%20model.sympy(index%3D3)%0A%20%20%20%20%60%60%60%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_()%3A%0A%20%20%20%20import%20marimo%20as%20mo%0A%0A%20%20%20%20return%20(mo%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_()%3A%0A%20%20%20%20import%20numpy%20as%20np%0A%20%20%20%20import%20matplotlib.pyplot%20as%20plt%0A%20%20%20%20import%20pandas%20as%20pd%0A%20%20%20%20from%20pysr%20import%20PySRRegressor%0A%20%20%20%20import%20sympy%0A%20%20%20%20from%20scipy.optimize%20import%20minimize%20as%20scipy_minimize%0A%20%20%20%20import%20egglog%0A%0A%20%20%20%20return%20PySRRegressor%2C%20egglog%2C%20np%2C%20plt%2C%20scipy_minimize%2C%20sympy%0A%0A%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20app.run()%0A
36675baa728038d898cda205d0071266