From ea2127fcfdb85c54bd8e6dec1d8dc42229df5e30 Mon Sep 17 00:00:00 2001 From: Cole Mathis Date: Thu, 18 Sep 2025 22:17:12 +0000 Subject: [PATCH 01/36] Update readme to link to dashboard --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 0cd6f21..c6a43e2 100644 --- a/README.md +++ b/README.md @@ -26,3 +26,8 @@ Documentation: * Help: `cargo run -- --help` The documentation for the configuration file is in the `Config` object. + + +# Interactive Dashboard + +Can be found here: https://github.com/mathis-group/Alchemy-Dashboard \ No newline at end of file From c3a6626cc5b635713315376be0bd157ea76eddd9 Mon Sep 17 00:00:00 2001 From: Cole Mathis Date: Tue, 23 Sep 2025 12:41:04 -0700 Subject: [PATCH 02/36] Cherry picked relevant files from --- Cargo.toml | 20 +++- pyproject.toml | 12 ++ src/lib.rs | 301 +++++++++++++++++++++++++++++++++++++++++++++--- test_alchemy.py | 80 +++++++++++++ 4 files changed, 397 insertions(+), 16 deletions(-) create mode 100644 pyproject.toml create mode 100644 test_alchemy.py diff --git a/Cargo.toml b/Cargo.toml index 5e446cb..27b52b8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,17 +3,33 @@ name = "alchemy" version = "0.1.0" edition = "2021" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[lib] +name = "alchemy" +crate-type = ["cdylib"] + +[package.metadata.maturin] +name = "alchemy" +python-source = "python" [dependencies] +pyo3 = { version = "0.22", features = ["extension-module", "abi3-py37"] } async-std = "1.12.0" clap = { version = "4.5.4", features = ["derive"] } futures = "0.3.30" -lambda_calculus = {git = "https://github.com/agentelement/lambda_calculus", branch = "size-feat"} +lambda_calculus = { git = "https://github.com/agentelement/lambda_calculus", branch = "size-feat" } +plotters = "0.3.6" rand = "0.8" rand_chacha = "0.3.1" serde = { version = "1.0.202", features = ["derive"] } serde_json = "1.0.117" +tokio = "1.39.2" + +[build-dependencies] +pyo3-build-config = "0.22" + +[features] +extension-module = ["pyo3/extension-module"] +default = ["extension-module"] [profile.profiling] inherits = "release" diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..08d8da4 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,12 @@ +[build-system] +requires = ["maturin>=1,<2"] +build-backend = "maturin" + +[project] +name = "alchemy" +requires-python = ">=3.7" +classifiers = [ + "Programming Language :: Rust", + "Programming Language :: Python :: Implementation :: CPython", + "Programming Language :: Python :: Implementation :: PyPy", +] \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 57d720a..0c8a047 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,20 +1,293 @@ -/// Simulation analysis -pub mod analysis; +use pyo3::prelude::*; +use pyo3::wrap_pyfunction; +use serde::{Deserialize, Serialize}; -/// Global configuration -pub mod config; -/// Random expression generators -pub mod generators; +// --- Module Imports --- +mod config; +mod soup; +mod experiments; +mod generators; +mod utils; +mod analysis; -/// Main AlChemy simulation module -pub mod supercollider; +use lambda_calculus::{parse, term::Notation::Classic, Term}; +use std::io::{self, BufRead}; -/// Experimental stuff -pub mod experiments; +pub fn read_inputs() -> impl Iterator { + io::stdin() + .lock() + .lines() + .filter_map(|line| line.ok()) + .filter_map(|line| parse(&line, Classic).ok()) +} -/// Utilities -pub mod utils; -/// Lambda-calculus stuff -pub mod lambda; +// --- Exposing Core Configurations --- +use crate::config::{Reactor as RustReactor, ConfigSeed}; +use crate::soup::{Soup, ReactionError}; +use crate::generators::{BTreeGen as RustBTreeGen, FontanaGen as RustFontanaGen, Standardization as RustStandardization}; +use crate::utils::{decode_hex, encode_hex}; +use crate::experiments::{look_for_add, entropy_series, entropy_test, sync_entropy_test}; + + +// --- Error Handling Wrappers --- +#[pyclass] +#[derive(Debug, Clone)] +pub struct PyReactionError { + kind: ReactionErrorKind, +} + +#[derive(Debug, Clone)] +pub enum ReactionErrorKind { + ExceedsReductionLimit, + NotEnoughExpressions, + IsIdentity, + IsParent, + HasFreeVariables, + ExceedsDepthLimit, +} + +impl From for PyReactionError { + fn from(error: ReactionError) -> Self { + let kind = match error { + ReactionError::ExceedsReductionLimit => ReactionErrorKind::ExceedsReductionLimit, + ReactionError::NotEnoughExpressions => ReactionErrorKind::NotEnoughExpressions, + ReactionError::IsIdentity => ReactionErrorKind::IsIdentity, + ReactionError::IsParent => ReactionErrorKind::IsParent, + ReactionError::HasFreeVariables => ReactionErrorKind::HasFreeVariables, + ReactionError::ExceedsDepthLimit => ReactionErrorKind::ExceedsDepthLimit, + }; + PyReactionError { kind } + } +} + +// --- Reactor Wrapper --- +#[pyclass] +pub struct PyReactor { + inner: RustReactor, +} + +#[pymethods] +impl PyReactor { + #[new] + fn new() -> Self { + PyReactor { + inner: RustReactor::new(), + } + } +} + +// --- Standardization Wrapper --- +#[pyclass] +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct PyStandardization { + standardization: RustStandardization, +} + +#[pymethods] +impl PyStandardization { + #[new] + fn new(kind: &str) -> PyResult { + let standardization = match kind { + "prefix" => RustStandardization::Prefix, + "postfix" => RustStandardization::Postfix, + "none" => RustStandardization::None, + _ => return Err(pyo3::exceptions::PyValueError::new_err("Invalid standardization type")) + }; + Ok(PyStandardization { standardization }) + } +} + +impl From for RustStandardization { + fn from(py_std: PyStandardization) -> Self { + py_std.standardization + } +} + +// --- Soup Wrapper --- +#[pyclass] +pub struct PySoup { + inner: Soup, +} + +#[pymethods] +impl PySoup { + #[new] + fn new() -> Self { + PySoup { inner: Soup::new() } + } + + #[staticmethod] + fn from_config(cfg: &PyReactor) -> Self { + PySoup { inner: Soup::from_config(&cfg.inner) } + } + + fn set_limit(&mut self, limit: usize) { + self.inner.set_limit(limit); + } + + fn perturb(&mut self, expressions: Vec) -> PyResult<()> { + let terms = expressions + .into_iter() + .filter_map(|s| parse(&s, Classic).ok()); + self.inner.perturb(terms); // Pass iterator directly + Ok(()) + } + + fn simulate_for(&mut self, n: usize, log: bool) -> usize { + self.inner.simulate_for(n, log) + } + + fn expressions(&self) -> Vec { + self.inner + .expressions() + .map(|term| term.to_string()) + .collect() + } + + fn len(&self) -> usize { + self.inner.len() + } + + fn collisions(&self) -> usize { + self.inner.collisions() + } + + fn unique_expressions(&self) -> Vec { + self.inner + .unique_expressions() + .into_iter() + .map(|term| term.to_string()) + .collect() + } + + fn expression_counts(&self) -> Vec<(String, u32)> { + self.inner + .expression_counts() + .into_iter() + .map(|(term, count)| (term.to_string(), count)) + .collect() + } + + fn population_entropy(&self) -> f32 { + self.inner.population_entropy() + } +} + +// --- BTreeGen Wrapper --- +#[pyclass] +pub struct PyBTreeGen { + inner: RustBTreeGen, +} + +#[pymethods] +impl PyBTreeGen { + #[new] + fn new() -> Self { + PyBTreeGen { inner: RustBTreeGen::new() } + } + + #[staticmethod] + fn from_config(size: u32, freevar_generation_probability: f64, max_free_vars: u32, std: PyStandardization) -> Self { + let config = config::BTreeGen { + size, + freevar_generation_probability, + n_max_free_vars: max_free_vars, + standardization: std.into(), + seed: ConfigSeed(Some([0; 32])), + }; + PyBTreeGen { inner: RustBTreeGen::from_config(&config) } + } + + fn generate(&mut self) -> String { + self.inner.generate().to_string() + } + + fn generate_n(&mut self, n: usize) -> Vec { + self.inner.generate_n(n).into_iter().map(|t| t.to_string()).collect() + } +} + +// --- FontanaGen Wrapper --- +#[pyclass] +pub struct PyFontanaGen { + inner: RustFontanaGen, +} + +#[pymethods] +impl PyFontanaGen { + #[staticmethod] + fn from_config(abs_range: (f64, f64), app_range: (f64, f64), max_depth: u32, max_free_vars: u32) -> Self { + let config = config::FontanaGen { + abstraction_prob_range: abs_range, + application_prob_range: app_range, + max_depth, + n_max_free_vars: max_free_vars, + seed: ConfigSeed(Some([0; 32])), + }; + PyFontanaGen { inner: RustFontanaGen::from_config(&config) } + } + + fn generate(&self) -> Option { + self.inner.generate().map(|term| term.to_string()) + } +} + +// --- Utilities --- +#[pyfunction] +fn decode_hex_py(hex_string: &str) -> PyResult> { + decode_hex(hex_string).map_err(|e| pyo3::exceptions::PyValueError::new_err(e.to_string())) +} + +#[pyfunction] +fn encode_hex_py(bytes: Vec) -> String { + encode_hex(&bytes) +} + +// --- Experiment Functions --- +#[pyfunction] +fn run_look_for_add() -> PyResult<()> { + async_std::task::block_on(look_for_add()); + Ok(()) +} + +#[pyfunction] +fn run_entropy_series() -> PyResult<()> { + async_std::task::block_on(entropy_series()); + Ok(()) +} + +#[pyfunction] +fn run_entropy_test() -> PyResult<()> { + async_std::task::block_on(entropy_test()); + Ok(()) +} + +#[pyfunction] +fn run_sync_entropy_test() -> PyResult<()> { + sync_entropy_test(); + Ok(()) +} + +// --- Python Module Initialization --- +#[pymodule] +fn alchemy(py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> { + // Register classes + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + + // Register functions + m.add_function(wrap_pyfunction!(decode_hex_py, m)?)?; + m.add_function(wrap_pyfunction!(encode_hex_py, m)?)?; + m.add_function(wrap_pyfunction!(run_look_for_add, m)?)?; + m.add_function(wrap_pyfunction!(run_entropy_series, m)?)?; + m.add_function(wrap_pyfunction!(run_entropy_test, m)?)?; + m.add_function(wrap_pyfunction!(run_sync_entropy_test, m)?)?; + + Ok(()) +} diff --git a/test_alchemy.py b/test_alchemy.py new file mode 100644 index 0000000..4addacd --- /dev/null +++ b/test_alchemy.py @@ -0,0 +1,80 @@ +import sys +sys.path.append('/Users/ridhamap/Python_wrapper_alchemy/alchemy-reimplemented/alchemy_new/lib/python3.12/site-packages') +import alchemy + +def test_reactor(): + print("Testing PyReactor:") + reactor = alchemy.PyReactor() + print("PyReactor instance created successfully.") + +def test_standardization(): + print("Testing PyStandardization:") + try: + std = alchemy.PyStandardization("prefix") + print("Standardization created with 'prefix':", std) + except ValueError as e: + print("Error in creating Standardization:", e) + +def test_soup(): + print("Testing PySoup:") + soup = alchemy.PySoup() + print("Empty PySoup instance created.") + print("Initial length of soup:", soup.len()) + + valid_expressions = ["λx.x", "λx.λy.xy", "(λx.x) y", "λx.λy.(yx)", "λf.λx.(f (f x))"] + soup.perturb(valid_expressions) + + print("Soup after perturbation:", soup.expressions()) + print("Unique expressions:", soup.unique_expressions()) + print("Population entropy:", soup.population_entropy()) + + +def test_btree_gen(): + print("Testing PyBTreeGen:") + # Change "postfix" to a supported type, such as "prefix" + gen = alchemy.PyBTreeGen.from_config(5, 0.5, 3, alchemy.PyStandardization("prefix")) + print("BTreeGen instance created.") + print("Generated expression:", gen.generate()) + print("Generated 3 expressions:", gen.generate_n(3)) + + +def test_fontana_gen(): + print("Testing PyFontanaGen:") + fontana_gen = alchemy.PyFontanaGen.from_config((0.1, 0.5), (0.2, 0.6), 5, 2) + print("FontanaGen instance created.") + result = fontana_gen.generate() + print("Generated expression from FontanaGen:", result) + +def test_utilities(): + print("Testing Utilities:") + encoded = alchemy.encode_hex_py([104, 101, 108, 108, 111]) + print("Encoded hex:", encoded) + try: + decoded = alchemy.decode_hex_py(encoded) + print("Decoded hex:", decoded) + except ValueError as e: + print("Error in decoding hex:", e) + +def test_experiments(): + print("Testing Experiment Functions:") + try: + + #alchemy.run_look_for_add() + #print("run_look_for_add executed successfully.") + alchemy.run_entropy_series() + print("run_entropy_series executed successfully.") + alchemy.run_entropy_test() + print("run_entropy_test executed successfully.") + alchemy.run_sync_entropy_test() + print("run_sync_entropy_test executed successfully.") + except Exception as e: + print("Error in running experiment functions:", e) + +if __name__ == "__main__": + test_reactor() + test_standardization() + test_soup() + test_btree_gen() + test_fontana_gen() + test_utilities() + test_experiments() From d28b42666f9bb3ddc4d792e8faf63d51ad0957a1 Mon Sep 17 00:00:00 2001 From: devyanshnegi Date: Thu, 25 Sep 2025 14:44:45 -0700 Subject: [PATCH 03/36] Fix clippy warnings- Removed src/expirements from clippy Added default controucters for LambdaSoup, BTreeGen Implemented Rust Suggestions Renamed lambda module name to core Temporarily Suppressed dead code warning for FontanaGen --- src/experiments/discovery.rs | 3 +++ src/experiments/distribution.rs | 3 +++ src/experiments/entropy.rs | 3 +++ src/experiments/kinetics.rs | 3 +++ src/experiments/magic_test_function.rs | 3 +++ src/experiments/mod.rs | 3 +++ src/experiments/search_by_behavior.rs | 3 +++ src/generators.rs | 7 +++++++ src/lambda/{lambda.rs => core.rs} | 6 ++++++ src/lambda/mod.rs | 2 +- src/lambda/recursive.rs | 8 ++++---- src/supercollider.rs | 4 ++++ src/utils.rs | 4 ++-- 13 files changed, 45 insertions(+), 7 deletions(-) rename src/lambda/{lambda.rs => core.rs} (99%) diff --git a/src/experiments/discovery.rs b/src/experiments/discovery.rs index fd7266b..63c2b48 100644 --- a/src/experiments/discovery.rs +++ b/src/experiments/discovery.rs @@ -1,3 +1,6 @@ +#![allow(clippy::all)] +#![allow(warnings)] + use async_std::task::{block_on, spawn}; use futures::stream::{FuturesUnordered, StreamExt}; use lambda_calculus::{ diff --git a/src/experiments/distribution.rs b/src/experiments/distribution.rs index 268a2bb..feb37eb 100644 --- a/src/experiments/distribution.rs +++ b/src/experiments/distribution.rs @@ -1,3 +1,6 @@ +#![allow(clippy::all)] +#![allow(warnings)] + use std::collections::HashMap; use lambda_calculus::Term; diff --git a/src/experiments/entropy.rs b/src/experiments/entropy.rs index 35901bb..326c177 100644 --- a/src/experiments/entropy.rs +++ b/src/experiments/entropy.rs @@ -1,3 +1,6 @@ +#![allow(clippy::all)] +#![allow(warnings)] + use async_std::task::{block_on, spawn}; use futures::{stream::FuturesUnordered, StreamExt}; use lambda_calculus::Term; diff --git a/src/experiments/kinetics.rs b/src/experiments/kinetics.rs index d4a4efe..6f0087c 100644 --- a/src/experiments/kinetics.rs +++ b/src/experiments/kinetics.rs @@ -1,3 +1,6 @@ +#![allow(clippy::all)] +#![allow(warnings)] + use async_std::task::{block_on, spawn}; use futures::stream::{FuturesUnordered, StreamExt}; use lambda_calculus::{data::num::church::succ, Term}; diff --git a/src/experiments/magic_test_function.rs b/src/experiments/magic_test_function.rs index 251cac3..5a647ff 100644 --- a/src/experiments/magic_test_function.rs +++ b/src/experiments/magic_test_function.rs @@ -1,3 +1,6 @@ +#![allow(clippy::all)] +#![allow(warnings)] + use async_std::task::{block_on, spawn}; use futures::{stream::FuturesUnordered, StreamExt}; use lambda_calculus::reduction::Order::HAP; diff --git a/src/experiments/mod.rs b/src/experiments/mod.rs index 32beb14..c00ea94 100644 --- a/src/experiments/mod.rs +++ b/src/experiments/mod.rs @@ -1,3 +1,6 @@ +#![allow(clippy::all)] +#![allow(warnings)] + pub mod entropy; pub mod search_by_behavior; diff --git a/src/experiments/search_by_behavior.rs b/src/experiments/search_by_behavior.rs index 9b26b0c..07794f3 100644 --- a/src/experiments/search_by_behavior.rs +++ b/src/experiments/search_by_behavior.rs @@ -1,3 +1,6 @@ +#![allow(clippy::all)] +#![allow(warnings)] + use async_std::task::{block_on, spawn}; use futures::{stream::FuturesUnordered, StreamExt}; use lambda_calculus::{app, Term}; diff --git a/src/generators.rs b/src/generators.rs index cb89954..bed0c75 100644 --- a/src/generators.rs +++ b/src/generators.rs @@ -82,6 +82,12 @@ pub struct BTreeGen { rng: ChaCha8Rng, } +impl Default for BTreeGen { + fn default() -> BTreeGen { + Self::new() + } +} + impl BTreeGen { pub fn new() -> BTreeGen { BTreeGen::from_config(&config::BTreeGen::new()) @@ -146,6 +152,7 @@ impl BTreeGen { } } +#[allow(dead_code)] pub struct FontanaGen { abs_range: (f64, f64), app_range: (f64, f64), diff --git a/src/lambda/lambda.rs b/src/lambda/core.rs similarity index 99% rename from src/lambda/lambda.rs rename to src/lambda/core.rs index 6f84c72..186962e 100644 --- a/src/lambda/lambda.rs +++ b/src/lambda/core.rs @@ -247,6 +247,12 @@ impl fmt::Display for LambdaParticle { } } +impl Default for LambdaSoup { + fn default() -> Self { + Self::new() + } +} + impl LambdaSoup { /// Generate an empty soup with the following configuration options: pub fn new() -> Self { diff --git a/src/lambda/mod.rs b/src/lambda/mod.rs index 3cebbaa..f93920c 100644 --- a/src/lambda/mod.rs +++ b/src/lambda/mod.rs @@ -1,4 +1,4 @@ -pub mod lambda; +pub mod core; pub mod recursive; diff --git a/src/lambda/recursive.rs b/src/lambda/recursive.rs index 91bc317..5a7404e 100644 --- a/src/lambda/recursive.rs +++ b/src/lambda/recursive.rs @@ -94,8 +94,8 @@ fn uses_both_arguments_helper(expr: &Term, depth: usize) -> (bool, bool) { Term::Abs(ref boxed) => uses_both_arguments_helper(boxed, depth + 1), Term::App(ref boxed) => { let (ref left, ref right) = **boxed; - let (l0, l1) = uses_both_arguments_helper(&left, depth); - let (r0, r1) = uses_both_arguments_helper(&right, depth); + let (l0, l1) = uses_both_arguments_helper(left, depth); + let (r0, r1) = uses_both_arguments_helper(right, depth); (l0 || r0, l1 || r1) } Term::Var(n) => (*n == depth, *n == depth - 1), @@ -257,11 +257,11 @@ impl Collider for Alche left: LambdaParticle, right: LambdaParticle, ) -> Result { - return if left.recursive { + if left.recursive { self.recursive_collide(left, right) } else { self.nonrecursive_collide(left, right) - }; + } } } diff --git a/src/supercollider.rs b/src/supercollider.rs index 31fb7f4..772177d 100644 --- a/src/supercollider.rs +++ b/src/supercollider.rs @@ -223,6 +223,10 @@ where self.expressions.len() } + pub fn is_empty(&self) -> bool { + self.expressions.is_empty() + } + /// Get the number of successful collisions pub fn collisions(&self) -> usize { self.n_collisions diff --git a/src/utils.rs b/src/utils.rs index c8b3544..70c17ef 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -97,7 +97,7 @@ where U: Ord, { fn partial_cmp(&self, other: &Self) -> Option { - Some(self.priority.cmp(&other.priority)) + Some(self.cmp(other)) } } @@ -144,6 +144,6 @@ where for i in series { write!(file, "{:?}; ", i)?; } - write!(file, "\n")?; + writeln!(file)?; Ok(()) } From 9e0044543dcdf51abe63ff47043c7d5ab42b638f Mon Sep 17 00:00:00 2001 From: Cole Mathis Date: Thu, 25 Sep 2025 15:34:34 -0700 Subject: [PATCH 04/36] Working on integrating changes from functional super collider into python bindings --- Cargo.lock | 808 +++++++++++++++++++++++++++++++++++++++++++++++++- src/config.rs | 2 +- src/lib.rs | 82 ++--- 3 files changed, 844 insertions(+), 48 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b0b69a0..4df5bda 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,21 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 + +[[package]] +name = "addr2line" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" [[package]] name = "alchemy" @@ -10,10 +25,23 @@ dependencies = [ "clap", "futures", "lambda_calculus", + "plotters", + "pyo3", + "pyo3-build-config", "rand", "rand_chacha", "serde", "serde_json", + "tokio", +] + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", ] [[package]] @@ -219,6 +247,21 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +[[package]] +name = "backtrace" +version = "0.3.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-targets 0.52.6", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -250,18 +293,47 @@ version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" +[[package]] +name = "bytemuck" +version = "1.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3995eaeebcdf32f91f980d360f78732ddc061097ab4e39991ae7a6ace9194677" + [[package]] name = "byteorder" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" +[[package]] +name = "cc" +version = "1.2.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80f41ae168f955c12fb8960b057d70d0ca153fb83182b57d86380443527be7e9" +dependencies = [ + "find-msvc-tools", + "shlex", +] + [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chrono" +version = "0.4.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" +dependencies = [ + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-link", +] + [[package]] name = "clap" version = "4.5.15" @@ -302,6 +374,12 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + [[package]] name = "colorchoice" version = "1.0.2" @@ -317,12 +395,115 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "core-graphics" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-graphics-types", + "foreign-types", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "libc", +] + +[[package]] +name = "core-text" +version = "20.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9d2790b5c08465d49f8dc05c8bcae9fea467855947db39b0f8145c091aaced5" +dependencies = [ + "core-foundation", + "core-graphics", + "foreign-types", + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + [[package]] name = "crossbeam-utils" version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" +[[package]] +name = "dirs" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.59.0", +] + +[[package]] +name = "dlib" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" +dependencies = [ + "libloading", +] + +[[package]] +name = "dwrote" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20c93d234bac0cdd0e2ac08bc8a5133f8df2169e95b262dfcea5e5cb7855672f" +dependencies = [ + "lazy_static", + "libc", + "winapi", + "wio", +] + [[package]] name = "errno" version = "0.3.9" @@ -375,6 +556,100 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" +[[package]] +name = "fdeflate" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "find-msvc-tools" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ced73b1dacfc750a6db6c0a0c3a3853c8b41997e2e2c563dc90804ae6867959" + +[[package]] +name = "flate2" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "float-ord" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce81f49ae8a0482e4c55ea62ebbd7e5a686af544c00b9d090bba3ff9be97b3d" + +[[package]] +name = "font-kit" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c7e611d49285d4c4b2e1727b72cf05353558885cc5252f93707b845dfcaf3d3" +dependencies = [ + "bitflags 2.6.0", + "byteorder", + "core-foundation", + "core-graphics", + "core-text", + "dirs", + "dwrote", + "float-ord", + "freetype-sys", + "lazy_static", + "libc", + "log", + "pathfinder_geometry", + "pathfinder_simd", + "walkdir", + "winapi", + "yeslogic-fontconfig-sys", +] + +[[package]] +name = "foreign-types" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" +dependencies = [ + "foreign-types-macros", + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "foreign-types-shared" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" + +[[package]] +name = "freetype-sys" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e7edc5b9669349acfda99533e9e0bcf26a51862ab43b08ee7745c55d28eb134" +dependencies = [ + "cc", + "libc", + "pkg-config", +] + [[package]] name = "futures" version = "0.3.30" @@ -503,6 +778,22 @@ dependencies = [ "wasi", ] +[[package]] +name = "gif" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80792593675e051cf94a4b111980da2ba60d4a83e43e0048c5693baab3977045" +dependencies = [ + "color_quant", + "weezl", +] + +[[package]] +name = "gimli" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" + [[package]] name = "gloo-timers" version = "0.2.6" @@ -533,6 +824,50 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" +[[package]] +name = "iana-time-zone" +version = "0.1.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "image" +version = "0.24.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5690139d2f55868e080017335e4b94cb7414274c74f1669c84fb5feba2c9f69d" +dependencies = [ + "bytemuck", + "byteorder", + "color_quant", + "jpeg-decoder", + "num-traits", + "png", +] + +[[package]] +name = "indoc" +version = "2.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c7245a08504955605670dbf141fceab975f15ca21570696aebe9d2e71576bd" + [[package]] name = "instant" version = "0.1.13" @@ -553,6 +888,17 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "io-uring" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "046fa2d4d00aea763528b4950358d0ead425372445dc8ff86312b3c69ff7727b" +dependencies = [ + "bitflags 2.6.0", + "cfg-if", + "libc", +] + [[package]] name = "is_terminal_polyfill" version = "1.70.1" @@ -565,6 +911,12 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +[[package]] +name = "jpeg-decoder" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00810f1d8b74be64b13dbf3db89ac67740615d6c891f0e7b6179326533011a07" + [[package]] name = "js-sys" version = "0.3.70" @@ -588,11 +940,37 @@ name = "lambda_calculus" version = "3.3.0" source = "git+https://github.com/agentelement/lambda_calculus?branch=size-feat#8346381fc0df29f3ed5022913d58d3a5244c22e7" +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + [[package]] name = "libc" -version = "0.2.155" +version = "0.2.176" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58f929b4d672ea937a23a1ab494143d968337a5f47e56d0815df1e0890ddf174" + +[[package]] +name = "libloading" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" +dependencies = [ + "cfg-if", + "windows-link", +] + +[[package]] +name = "libredox" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb" +dependencies = [ + "bitflags 2.6.0", + "libc", +] [[package]] name = "linux-raw-sys" @@ -621,18 +999,91 @@ version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", + "simd-adler32", +] + +[[package]] +name = "mio" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.59.0", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "object" +version = "0.36.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" +dependencies = [ + "memchr", +] + [[package]] name = "once_cell" version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + [[package]] name = "parking" version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" +[[package]] +name = "pathfinder_geometry" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b7e7b4ea703700ce73ebf128e1450eb69c3a8329199ffbfb9b2a0418e5ad3" +dependencies = [ + "log", + "pathfinder_simd", +] + +[[package]] +name = "pathfinder_simd" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf9027960355bf3afff9841918474a81a5f972ac6d226d518060bba758b5ad57" +dependencies = [ + "rustc_version", +] + [[package]] name = "pin-project-lite" version = "0.2.14" @@ -656,6 +1107,71 @@ dependencies = [ "futures-io", ] +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "plotters" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747" +dependencies = [ + "chrono", + "font-kit", + "image", + "lazy_static", + "num-traits", + "pathfinder_geometry", + "plotters-backend", + "plotters-bitmap", + "plotters-svg", + "ttf-parser", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" + +[[package]] +name = "plotters-bitmap" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72ce181e3f6bf82d6c1dc569103ca7b1bd964c60ba03d7e6cdfbb3e3eb7f7405" +dependencies = [ + "gif", + "image", + "plotters-backend", +] + +[[package]] +name = "plotters-svg" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670" +dependencies = [ + "plotters-backend", +] + +[[package]] +name = "png" +version = "0.17.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + [[package]] name = "polling" version = "2.8.0" @@ -687,6 +1203,12 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "portable-atomic" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" + [[package]] name = "ppv-lite86" version = "0.2.20" @@ -698,13 +1220,76 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" dependencies = [ "unicode-ident", ] +[[package]] +name = "pyo3" +version = "0.22.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f402062616ab18202ae8319da13fa4279883a2b8a9d9f83f20dbade813ce1884" +dependencies = [ + "cfg-if", + "indoc", + "libc", + "memoffset", + "once_cell", + "portable-atomic", + "pyo3-build-config", + "pyo3-ffi", + "pyo3-macros", + "unindent", +] + +[[package]] +name = "pyo3-build-config" +version = "0.22.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b14b5775b5ff446dd1056212d778012cbe8a0fbffd368029fd9e25b514479c38" +dependencies = [ + "once_cell", + "target-lexicon", +] + +[[package]] +name = "pyo3-ffi" +version = "0.22.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ab5bcf04a2cdcbb50c7d6105de943f543f9ed92af55818fd17b660390fc8636" +dependencies = [ + "libc", + "pyo3-build-config", +] + +[[package]] +name = "pyo3-macros" +version = "0.22.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fd24d897903a9e6d80b968368a34e1525aeb719d568dba8b3d4bfa5dc67d453" +dependencies = [ + "proc-macro2", + "pyo3-macros-backend", + "quote", + "syn", +] + +[[package]] +name = "pyo3-macros-backend" +version = "0.22.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36c011a03ba1e50152b4b394b479826cad97e7a21eb52df179cd91ac411cbfbe" +dependencies = [ + "heck", + "proc-macro2", + "pyo3-build-config", + "quote", + "syn", +] + [[package]] name = "quote" version = "1.0.36" @@ -744,6 +1329,32 @@ dependencies = [ "getrandom", ] +[[package]] +name = "redox_users" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" +dependencies = [ + "getrandom", + "libredox", + "thiserror", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + [[package]] name = "rustix" version = "0.37.27" @@ -777,6 +1388,21 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "semver" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" + [[package]] name = "serde" version = "1.0.207" @@ -809,6 +1435,18 @@ dependencies = [ "serde", ] +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + [[package]] name = "slab" version = "0.4.9" @@ -836,15 +1474,55 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "syn" -version = "2.0.74" +version = "2.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fceb41e3d546d0bd83421d3409b1460cc7444cd389341a4c880fe7a042cb3d7" +checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] +[[package]] +name = "target-lexicon" +version = "0.12.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" + +[[package]] +name = "thiserror" +version = "2.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio" +version = "1.47.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" +dependencies = [ + "backtrace", + "io-uring", + "libc", + "mio", + "pin-project-lite", + "slab", +] + [[package]] name = "tracing" version = "0.1.40" @@ -861,12 +1539,24 @@ version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +[[package]] +name = "ttf-parser" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17f77d76d837a7830fe1d4f12b7b4ba4192c1888001c7164257e4bc6d21d96b4" + [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +[[package]] +name = "unindent" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7264e107f553ccae879d21fbea1d6724ac785e8c3bfc762137959b5802826ef3" + [[package]] name = "utf8parse" version = "0.2.2" @@ -885,6 +1575,16 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "317211a0dc0ceedd78fb2ca9a44aed3d7b9b26f81870d485c07122b4350673b7" +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -968,6 +1668,12 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "weezl" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a751b3277700db47d3e574514de2eced5e54dc8a5436a3bf7a0b248b2cee16f3" + [[package]] name = "winapi" version = "0.3.9" @@ -984,12 +1690,80 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.59.0", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-core" +version = "0.62.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57fe7168f7de578d2d8a05b07fd61870d2e73b4020e9f49aa00da8471723497c" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65" + +[[package]] +name = "windows-result" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7084dcc306f89883455a206237404d3eaf961e5bd7e0f312f7c91f57eb44167f" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7218c655a553b0bed4426cf54b20d7ba363ef543b52d515b3e48d7fd55318dda" +dependencies = [ + "windows-link", +] + [[package]] name = "windows-sys" version = "0.48.0" @@ -1138,6 +1912,26 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "wio" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d129932f4644ac2396cb456385cbf9e63b5b30c6e8dc4820bdca4eb082037a5" +dependencies = [ + "winapi", +] + +[[package]] +name = "yeslogic-fontconfig-sys" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "503a066b4c037c440169d995b869046827dbc71263f6e8f3be6d77d4f3229dbd" +dependencies = [ + "dlib", + "once_cell", + "pkg-config", +] + [[package]] name = "zerocopy" version = "0.7.35" diff --git a/src/config.rs b/src/config.rs index e0ab01a..1a4ccde 100644 --- a/src/config.rs +++ b/src/config.rs @@ -225,7 +225,7 @@ impl Config { /// to ser/de to/from a hex string. #[warn(missing_docs)] #[derive(Debug, Clone, Copy)] -pub struct ConfigSeed(Option<[u8; 32]>); +pub struct ConfigSeed(pub Option<[u8; 32]>); impl ConfigSeed { /// Get the seed item diff --git a/src/lib.rs b/src/lib.rs index 0c8a047..6691434 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,11 +5,13 @@ use serde::{Deserialize, Serialize}; // --- Module Imports --- mod config; -mod soup; +mod supercollider; mod experiments; mod generators; mod utils; mod analysis; +mod lambda; + use lambda_calculus::{parse, term::Notation::Classic, Term}; use std::io::{self, BufRead}; @@ -25,17 +27,17 @@ pub fn read_inputs() -> impl Iterator { // --- Exposing Core Configurations --- use crate::config::{Reactor as RustReactor, ConfigSeed}; -use crate::soup::{Soup, ReactionError}; +use crate::supercollider::Soup; use crate::generators::{BTreeGen as RustBTreeGen, FontanaGen as RustFontanaGen, Standardization as RustStandardization}; use crate::utils::{decode_hex, encode_hex}; -use crate::experiments::{look_for_add, entropy_series, entropy_test, sync_entropy_test}; - +//use crate::experiments::{look_for_add, entropy_series, entropy_test, sync_entropy_test}; +use crate::lambda::lambda::LambdaCollisionError; // --- Error Handling Wrappers --- #[pyclass] #[derive(Debug, Clone)] pub struct PyReactionError { - kind: ReactionErrorKind, + kind: LambdaCollisionError, } #[derive(Debug, Clone)] @@ -48,15 +50,15 @@ pub enum ReactionErrorKind { ExceedsDepthLimit, } -impl From for PyReactionError { - fn from(error: ReactionError) -> Self { +impl From for PyReactionError { + fn from(error: LambdaCollisionError) -> Self { let kind = match error { - ReactionError::ExceedsReductionLimit => ReactionErrorKind::ExceedsReductionLimit, - ReactionError::NotEnoughExpressions => ReactionErrorKind::NotEnoughExpressions, - ReactionError::IsIdentity => ReactionErrorKind::IsIdentity, - ReactionError::IsParent => ReactionErrorKind::IsParent, - ReactionError::HasFreeVariables => ReactionErrorKind::HasFreeVariables, - ReactionError::ExceedsDepthLimit => ReactionErrorKind::ExceedsDepthLimit, + LambdaCollisionError::ExceedsReductionLimit => ReactionErrorKind::ExceedsReductionLimit, + LambdaCollisionError::NotEnoughExpressions => ReactionErrorKind::NotEnoughExpressions, + LambdaCollisionError::IsIdentity => ReactionErrorKind::IsIdentity, + LambdaCollisionError::IsParent => ReactionErrorKind::IsParent, + LambdaCollisionError::HasFreeVariables => ReactionErrorKind::HasFreeVariables, + LambdaCollisionError::ExceedsDepthLimit => ReactionErrorKind::ExceedsDepthLimit, }; PyReactionError { kind } } @@ -246,29 +248,29 @@ fn encode_hex_py(bytes: Vec) -> String { } // --- Experiment Functions --- -#[pyfunction] -fn run_look_for_add() -> PyResult<()> { - async_std::task::block_on(look_for_add()); - Ok(()) -} - -#[pyfunction] -fn run_entropy_series() -> PyResult<()> { - async_std::task::block_on(entropy_series()); - Ok(()) -} - -#[pyfunction] -fn run_entropy_test() -> PyResult<()> { - async_std::task::block_on(entropy_test()); - Ok(()) -} - -#[pyfunction] -fn run_sync_entropy_test() -> PyResult<()> { - sync_entropy_test(); - Ok(()) -} +// #[pyfunction] +// fn run_look_for_add() -> PyResult<()> { +// async_std::task::block_on(look_for_add()); +// Ok(()) +// } + +// #[pyfunction] +// fn run_entropy_series() -> PyResult<()> { +// async_std::task::block_on(entropy_series()); +// Ok(()) +// } + +// #[pyfunction] +// fn run_entropy_test() -> PyResult<()> { +// async_std::task::block_on(entropy_test()); +// Ok(()) +// } + +// #[pyfunction] +// fn run_sync_entropy_test() -> PyResult<()> { +// sync_entropy_test(); +// Ok(()) +// } // --- Python Module Initialization --- #[pymodule] @@ -284,10 +286,10 @@ fn alchemy(py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> { // Register functions m.add_function(wrap_pyfunction!(decode_hex_py, m)?)?; m.add_function(wrap_pyfunction!(encode_hex_py, m)?)?; - m.add_function(wrap_pyfunction!(run_look_for_add, m)?)?; - m.add_function(wrap_pyfunction!(run_entropy_series, m)?)?; - m.add_function(wrap_pyfunction!(run_entropy_test, m)?)?; - m.add_function(wrap_pyfunction!(run_sync_entropy_test, m)?)?; + // m.add_function(wrap_pyfunction!(run_look_for_add, m)?)?; + // m.add_function(wrap_pyfunction!(run_entropy_series, m)?)?; + // m.add_function(wrap_pyfunction!(run_entropy_test, m)?)?; + // m.add_function(wrap_pyfunction!(run_sync_entropy_test, m)?)?; Ok(()) } From 9047adf1333f96cb6e56780a26c1c49f804af22d Mon Sep 17 00:00:00 2001 From: devyanshnegi Date: Thu, 2 Oct 2025 09:17:29 -0700 Subject: [PATCH 05/36] Fonatana Gen Implementation --- src/config.rs | 37 ++++++++------ src/generators.rs | 126 ++++++++++++++++++++++++++++++++++++++++++---- src/main.rs | 8 +-- 3 files changed, 142 insertions(+), 29 deletions(-) diff --git a/src/config.rs b/src/config.rs index e0ab01a..af324f3 100644 --- a/src/config.rs +++ b/src/config.rs @@ -107,7 +107,6 @@ pub struct BTreeGen { pub standardization: Standardization, } -/// Configuration for Fontana's generator #[warn(missing_docs)] #[derive(Serialize, Deserialize, Debug)] pub struct FontanaGen { @@ -115,6 +114,12 @@ pub struct FontanaGen { /// randomly. Default: `None` pub seed: ConfigSeed, + /// Minimum depth before leaf nodes can be generated + pub min_depth: u32, + + /// Maximum depth of the generated trees + pub max_depth: u32, + /// Probability range of an abstraction being generated. Linearly changes from start to end, /// varying with depth pub abstraction_prob_range: (f64, f64), @@ -123,13 +128,28 @@ pub struct FontanaGen { /// varying with depth pub application_prob_range: (f64, f64), - /// Maximum depth of the generated trees - pub max_depth: u32, + /// Probability that a leaf vertex is a free variable + pub free_variable_probability: f64, /// Size of the free variable palette pub n_max_free_vars: u32, } +impl GenConfig for FontanaGen { + fn new() -> Self { + FontanaGen { + seed: ConfigSeed(None), + min_depth: 0, + max_depth: 10, + n_max_free_vars: 6, + application_prob_range: (0.3, 0.5), + abstraction_prob_range: (0.5, 0.3), + free_variable_probability: 0.0, + } + } +} + + impl Reactor { /// Produce a new `ReactorConfig` struct with default values. pub fn new() -> Self { @@ -169,17 +189,6 @@ impl GenConfig for BTreeGen { } } -impl GenConfig for FontanaGen { - fn new() -> Self { - FontanaGen { - max_depth: 10, - n_max_free_vars: 6, - application_prob_range: (0.3, 0.5), - abstraction_prob_range: (0.5, 0.3), - seed: ConfigSeed(None), - } - } -} impl Config { /// Create a config object from a string diff --git a/src/generators.rs b/src/generators.rs index cb89954..13437d1 100644 --- a/src/generators.rs +++ b/src/generators.rs @@ -147,31 +147,135 @@ impl BTreeGen { } pub struct FontanaGen { - abs_range: (f64, f64), - app_range: (f64, f64), - depth_cutoff: u32, - free_vars_count: u32, + min_depth: u32, + max_depth: u32, + + abs_prob: (f32, f32), + app_prob: (f32, f32), + + abs_incr: f32, + app_incr: f32, + + free_prob: f32, + max_vars: u32, seed: [u8; 32], rng: ChaCha8Rng, } impl FontanaGen { + pub fn new( + min_depth: u32, + max_depth: u32, + abs_prob: (f32, f32), + app_prob: (f32, f32), + free_prob: f32, + max_vars: u32, + seed: [u8; 32], + ) -> FontanaGen { + let abs_incr = (abs_prob.1 - abs_prob.0) / ((max_depth - 1) as f32); + let app_incr = (app_prob.1 - app_prob.0) / ((max_depth - 1) as f32); + + FontanaGen { + min_depth, + max_depth, + abs_prob, + app_prob, + abs_incr, + app_incr, + free_prob, + max_vars, + seed, + rng: ChaCha8Rng::from_seed(seed), + } + } + pub fn from_config(cfg: &config::FontanaGen) -> FontanaGen { let seed = cfg.seed.get(); let rng = ChaCha8Rng::from_seed(seed); + + let abs_incr: f32 = ((cfg.abstraction_prob_range.1 - cfg.abstraction_prob_range.0) + / ((cfg.max_depth - 1) as f64)) as f32; + let app_incr: f32 = ((cfg.application_prob_range.1 - cfg.application_prob_range.0) + / ((cfg.max_depth - 1) as f64)) as f32; + FontanaGen { - abs_range: cfg.abstraction_prob_range, - app_range: cfg.application_prob_range, - depth_cutoff: cfg.max_depth, - free_vars_count: cfg.n_max_free_vars, + min_depth: 0, + max_depth: cfg.max_depth, + + abs_prob: (cfg.abstraction_prob_range.0 as f32, cfg.abstraction_prob_range.1 as f32), + app_prob: (cfg.application_prob_range.0 as f32, cfg.application_prob_range.1 as f32), + + abs_incr, + app_incr, + free_prob: 0.0, + max_vars: cfg.n_max_free_vars, + seed, - rng, + rng: ChaCha8Rng::from_seed(seed), } } - pub fn generate(&self) -> Option { - None + pub fn generate(&mut self) -> Term{ + self.rand_lambda(0, self.abs_prob.0, self.app_prob.0) + } + + pub fn generate_n(&mut self, n: usize) -> Vec { + let mut v = Vec::with_capacity(n); + for _ in 0..n { + v.push(self.generate()) + } + v + } + + pub fn seed(&self) -> [u8; 32] { + self.seed + } + + pub fn rand_lambda(&mut self, depth: u32, p_abs: f32, p_app: f32) -> Term { + if depth >= self.max_depth { // over max depth generating leaf + let var = if self.rng.gen_bool(self.free_prob as f64) || depth == 0 { + depth + self.rng.gen_range(1..=self.max_vars) + } else { + self.rng.gen_range(1..=depth) + }; + return Term::Var(var as usize); + } else if depth < self.min_depth { // under min length will not generate leaf + let coin = self.rng.gen_bool(0.5); + if coin { + let n_abs = p_abs + self.abs_incr; + let n_app = p_app + self.app_incr; + return Term::Abs(Box::new(self.rand_lambda(depth + 1, n_abs, n_app))); + } else { + let n_abs = p_abs + self.abs_incr; + let n_app = p_app + self.app_incr; + return Term::App(Box::new(( + self.rand_lambda(depth+1, n_abs, n_app), + self.rand_lambda(depth+1, n_abs, n_app), + ))); + }; + } else { + let coin: f32 = self.rng.gen(); + if coin <= p_abs { + let n_abs = p_abs + self.abs_incr; + let n_app = p_app + self.app_incr; + return Term::Abs(Box::new(self.rand_lambda(depth + 1, n_abs, n_app))); + } else if coin <= p_abs + p_app { + let n_abs = p_abs + self.abs_incr; + let n_app = p_app + self.app_incr; + return Term::App(Box::new(( + self.rand_lambda(depth+1, n_abs, n_app), + self.rand_lambda(depth+1, n_abs, n_app), + ))); + } else { + let var = if self.rng.gen_bool(self.free_prob as f64) || depth == 0 { + depth + self.rng.gen_range(1..=self.max_vars) + } else { + self.rng.gen_range(1..=depth) + }; + return Term::Var(var as usize); + } + } } } diff --git a/src/main.rs b/src/main.rs index 66f9ad6..df293fd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -124,10 +124,10 @@ pub fn generate_expressions_and_seed_soup(cfg: &config::Config) -> lambda::recur gen.generate_n(cfg.sample_size) } config::Generator::Fontana(gen_cfg) => { - let gen = generators::FontanaGen::from_config(gen_cfg); - std::iter::from_fn(move || gen.generate()) - .take(cfg.sample_size) - .collect::>() + let mut gen = generators::FontanaGen::from_config(gen_cfg); + gen.generate_n(cfg.sample_size); + + } }; let mut soup = lambda::recursive::LambdaSoup::from_config(&cfg.reactor_config); From 99c0a50ee59fac7a9ed60d26f7feb2e2b3cc2b01 Mon Sep 17 00:00:00 2001 From: Devyansh <67937759+devyanshnegi@users.noreply.github.com> Date: Thu, 2 Oct 2025 09:20:00 -0700 Subject: [PATCH 06/36] Update main.rs Fixed syntax error --- src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index df293fd..3581afe 100644 --- a/src/main.rs +++ b/src/main.rs @@ -125,7 +125,7 @@ pub fn generate_expressions_and_seed_soup(cfg: &config::Config) -> lambda::recur } config::Generator::Fontana(gen_cfg) => { let mut gen = generators::FontanaGen::from_config(gen_cfg); - gen.generate_n(cfg.sample_size); + gen.generate_n(cfg.sample_size) } From 0d530dc99b233014e38f9386c31b87a2f9b068d3 Mon Sep 17 00:00:00 2001 From: "ridham.patel" Date: Thu, 16 Oct 2025 13:47:52 -0700 Subject: [PATCH 07/36] lib: make modules public; add rlib crate-type; fix PySoup.perturb API; wire CLI --- Cargo.toml | 4 +- src/lib.rs | 464 +++++++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 399 insertions(+), 69 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 27b52b8..94c62ca 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,9 +3,11 @@ name = "alchemy" version = "0.1.0" edition = "2021" + [lib] name = "alchemy" -crate-type = ["cdylib"] +path = "src/lib.rs" +crate-type = ["cdylib", "rlib"] [package.metadata.maturin] name = "alchemy" diff --git a/src/lib.rs b/src/lib.rs index 6691434..9045cfd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,16 +1,317 @@ +// use pyo3::prelude::*; +// use pyo3::wrap_pyfunction; +// use serde::{Deserialize, Serialize}; + + +// // --- Module Imports --- +// mod config; +// mod supercollider; +// mod experiments; +// mod generators; +// mod utils; +// mod analysis; +// mod lambda; + + +// use lambda_calculus::{parse, term::Notation::Classic, Term}; +// use std::io::{self, BufRead}; + +// pub fn read_inputs() -> impl Iterator { +// io::stdin() +// .lock() +// .lines() +// .filter_map(|line| line.ok()) +// .filter_map(|line| parse(&line, Classic).ok()) +// } + + +// // --- Exposing Core Configurations --- +// use crate::config::{Reactor as RustReactor, ConfigSeed}; +// //use crate::supercollider::Soup; +// use crate::generators::{BTreeGen as RustBTreeGen, FontanaGen as RustFontanaGen, Standardization as RustStandardization}; +// use crate::utils::{decode_hex, encode_hex}; +// //use crate::experiments::{look_for_add, entropy_series, entropy_test, sync_entropy_test}; +// //use crate::lambda::lambda::LambdaCollisionError; + +// use crate::supercollider::Soup as GenericSoup; +// use crate::lambda::recursive::{ +// LambdaParticle, AlchemyCollider, LambdaCollisionOk, LambdaCollisionError, +// }; +// type RustSoup = GenericSoup; + +// // --- Error Handling Wrappers --- +// #[pyclass] +// #[derive(Debug, Clone)] +// pub struct PyReactionError { +// kind: LambdaCollisionError, +// } + +// #[derive(Debug, Clone)] +// pub enum ReactionErrorKind { +// ExceedsReductionLimit, +// NotEnoughExpressions, +// IsIdentity, +// IsParent, +// HasFreeVariables, +// ExceedsDepthLimit, +// } + +// impl From for PyReactionError { +// fn from(error: LambdaCollisionError) -> Self { +// let kind = match error { +// LambdaCollisionError::ExceedsReductionLimit => ReactionErrorKind::ExceedsReductionLimit, +// LambdaCollisionError::NotEnoughExpressions => ReactionErrorKind::NotEnoughExpressions, +// LambdaCollisionError::IsIdentity => ReactionErrorKind::IsIdentity, +// LambdaCollisionError::IsParent => ReactionErrorKind::IsParent, +// LambdaCollisionError::HasFreeVariables => ReactionErrorKind::HasFreeVariables, +// LambdaCollisionError::ExceedsDepthLimit => ReactionErrorKind::ExceedsDepthLimit, +// }; +// PyReactionError { kind } +// } +// } + +// // --- Reactor Wrapper --- +// #[pyclass] +// pub struct PyReactor { +// inner: RustReactor, +// } + +// #[pymethods] +// impl PyReactor { +// #[new] +// fn new() -> Self { +// PyReactor { +// inner: RustReactor::new(), +// } +// } +// } + +// // --- Standardization Wrapper --- +// #[pyclass] +// #[derive(Debug, Clone, Serialize, Deserialize)] +// pub struct PyStandardization { +// standardization: RustStandardization, +// } + +// #[pymethods] +// impl PyStandardization { +// #[new] +// fn new(kind: &str) -> PyResult { +// let standardization = match kind { +// "prefix" => RustStandardization::Prefix, +// "postfix" => RustStandardization::Postfix, +// "none" => RustStandardization::None, +// _ => return Err(pyo3::exceptions::PyValueError::new_err("Invalid standardization type")) +// }; +// Ok(PyStandardization { standardization }) +// } +// } + +// impl From for RustStandardization { +// fn from(py_std: PyStandardization) -> Self { +// py_std.standardization +// } +// } + +// // --- Soup Wrapper --- +// #[pyclass] +// pub struct PySoup { +// inner: Soup, +// } + +// #[pymethods] +// impl PySoup { +// #[new] +// fn new() -> Self { +// PySoup { inner: Soup::new() } +// } + +// #[staticmethod] +// fn from_config(cfg: &PyReactor) -> Self { +// PySoup { inner: Soup::from_config(&cfg.inner) } +// } + +// fn set_limit(&mut self, limit: usize) { +// self.inner.set_limit(limit); +// } + +// fn perturb(&mut self, expressions: Vec) -> PyResult<()> { +// let terms = expressions +// .into_iter() +// .filter_map(|s| parse(&s, Classic).ok()); +// self.inner.perturb(terms); // Pass iterator directly +// Ok(()) +// } + +// fn simulate_for(&mut self, n: usize, log: bool) -> usize { +// self.inner.simulate_for(n, log) +// } + +// fn expressions(&self) -> Vec { +// self.inner +// .expressions() +// .map(|term| term.to_string()) +// .collect() +// } + +// fn len(&self) -> usize { +// self.inner.len() +// } + +// fn collisions(&self) -> usize { +// self.inner.collisions() +// } + +// fn unique_expressions(&self) -> Vec { +// self.inner +// .unique_expressions() +// .into_iter() +// .map(|term| term.to_string()) +// .collect() +// } + +// fn expression_counts(&self) -> Vec<(String, u32)> { +// self.inner +// .expression_counts() +// .into_iter() +// .map(|(term, count)| (term.to_string(), count)) +// .collect() +// } + +// fn population_entropy(&self) -> f32 { +// self.inner.population_entropy() +// } +// } + +// // --- BTreeGen Wrapper --- +// #[pyclass] +// pub struct PyBTreeGen { +// inner: RustBTreeGen, +// } + +// #[pymethods] +// impl PyBTreeGen { +// #[new] +// fn new() -> Self { +// PyBTreeGen { inner: RustBTreeGen::new() } +// } + +// #[staticmethod] +// fn from_config(size: u32, freevar_generation_probability: f64, max_free_vars: u32, std: PyStandardization) -> Self { +// let config = config::BTreeGen { +// size, +// freevar_generation_probability, +// n_max_free_vars: max_free_vars, +// standardization: std.into(), +// seed: ConfigSeed(Some([0; 32])), +// }; +// PyBTreeGen { inner: RustBTreeGen::from_config(&config) } +// } + +// fn generate(&mut self) -> String { +// self.inner.generate().to_string() +// } + +// fn generate_n(&mut self, n: usize) -> Vec { +// self.inner.generate_n(n).into_iter().map(|t| t.to_string()).collect() +// } +// } + +// // --- FontanaGen Wrapper --- +// #[pyclass] +// pub struct PyFontanaGen { +// inner: RustFontanaGen, +// } + +// #[pymethods] +// impl PyFontanaGen { +// #[staticmethod] +// fn from_config(abs_range: (f64, f64), app_range: (f64, f64), max_depth: u32, max_free_vars: u32) -> Self { +// let config = config::FontanaGen { +// abstraction_prob_range: abs_range, +// application_prob_range: app_range, +// max_depth, +// n_max_free_vars: max_free_vars, +// seed: ConfigSeed(Some([0; 32])), +// }; +// PyFontanaGen { inner: RustFontanaGen::from_config(&config) } +// } + +// fn generate(&self) -> Option { +// self.inner.generate().map(|term| term.to_string()) +// } +// } + +// // --- Utilities --- +// #[pyfunction] +// fn decode_hex_py(hex_string: &str) -> PyResult> { +// decode_hex(hex_string).map_err(|e| pyo3::exceptions::PyValueError::new_err(e.to_string())) +// } + +// #[pyfunction] +// fn encode_hex_py(bytes: Vec) -> String { +// encode_hex(&bytes) +// } + +// // --- Experiment Functions --- +// // #[pyfunction] +// // fn run_look_for_add() -> PyResult<()> { +// // async_std::task::block_on(look_for_add()); +// // Ok(()) +// // } + +// // #[pyfunction] +// // fn run_entropy_series() -> PyResult<()> { +// // async_std::task::block_on(entropy_series()); +// // Ok(()) +// // } + +// // #[pyfunction] +// // fn run_entropy_test() -> PyResult<()> { +// // async_std::task::block_on(entropy_test()); +// // Ok(()) +// // } + +// // #[pyfunction] +// // fn run_sync_entropy_test() -> PyResult<()> { +// // sync_entropy_test(); +// // Ok(()) +// // } + +// // --- Python Module Initialization --- +// #[pymodule] +// fn alchemy(py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> { +// // Register classes +// m.add_class::()?; +// m.add_class::()?; +// m.add_class::()?; +// m.add_class::()?; +// m.add_class::()?; +// m.add_class::()?; + +// // Register functions +// m.add_function(wrap_pyfunction!(decode_hex_py, m)?)?; +// m.add_function(wrap_pyfunction!(encode_hex_py, m)?)?; +// // m.add_function(wrap_pyfunction!(run_look_for_add, m)?)?; +// // m.add_function(wrap_pyfunction!(run_entropy_series, m)?)?; +// // m.add_function(wrap_pyfunction!(run_entropy_test, m)?)?; +// // m.add_function(wrap_pyfunction!(run_sync_entropy_test, m)?)?; + +// Ok(()) +// } + use pyo3::prelude::*; use pyo3::wrap_pyfunction; use serde::{Deserialize, Serialize}; - // --- Module Imports --- -mod config; -mod supercollider; -mod experiments; -mod generators; -mod utils; -mod analysis; -mod lambda; +pub mod config; +pub mod experiments; +pub mod generators; +pub mod utils; +pub mod analysis; +pub mod lambda; +pub mod supercollider; use lambda_calculus::{parse, term::Notation::Classic, Term}; @@ -24,20 +325,31 @@ pub fn read_inputs() -> impl Iterator { .filter_map(|line| parse(&line, Classic).ok()) } - // --- Exposing Core Configurations --- use crate::config::{Reactor as RustReactor, ConfigSeed}; -use crate::supercollider::Soup; -use crate::generators::{BTreeGen as RustBTreeGen, FontanaGen as RustFontanaGen, Standardization as RustStandardization}; +use crate::generators::{ + BTreeGen as RustBTreeGen, FontanaGen as RustFontanaGen, Standardization as RustStandardization, +}; use crate::utils::{decode_hex, encode_hex}; -//use crate::experiments::{look_for_add, entropy_series, entropy_test, sync_entropy_test}; -use crate::lambda::lambda::LambdaCollisionError; +// use crate::experiments::{look_for_add, entropy_series, entropy_test, sync_entropy_test}; + +// Bring in the concrete lambda types so we can fully specify Soup. +use crate::lambda::recursive::{ + AlchemyCollider, LambdaCollisionError, LambdaCollisionOk, LambdaParticle, +}; + +// Create a concrete alias for the generic Soup. Without this, the compiler +// complains about missing generic parameters and ambiguous new()/from_config() impls. +use crate::supercollider::Soup as GenericSoup; +type RustSoup = + GenericSoup; // --- Error Handling Wrappers --- #[pyclass] #[derive(Debug, Clone)] pub struct PyReactionError { - kind: LambdaCollisionError, + // Store the kind you map to below rather than LambdaCollisionError itself. + kind: ReactionErrorKind, } #[derive(Debug, Clone)] @@ -48,17 +360,26 @@ pub enum ReactionErrorKind { IsParent, HasFreeVariables, ExceedsDepthLimit, + RecursiveArgument, + BadArgument, } +// Map the recursive LambdaCollisionError into your public PyReactionError. impl From for PyReactionError { fn from(error: LambdaCollisionError) -> Self { let kind = match error { - LambdaCollisionError::ExceedsReductionLimit => ReactionErrorKind::ExceedsReductionLimit, - LambdaCollisionError::NotEnoughExpressions => ReactionErrorKind::NotEnoughExpressions, + LambdaCollisionError::ExceedsReductionLimit => { + ReactionErrorKind::ExceedsReductionLimit + } + LambdaCollisionError::NotEnoughExpressions => { + ReactionErrorKind::NotEnoughExpressions + } LambdaCollisionError::IsIdentity => ReactionErrorKind::IsIdentity, LambdaCollisionError::IsParent => ReactionErrorKind::IsParent, LambdaCollisionError::HasFreeVariables => ReactionErrorKind::HasFreeVariables, LambdaCollisionError::ExceedsDepthLimit => ReactionErrorKind::ExceedsDepthLimit, + LambdaCollisionError::RecursiveArgument => ReactionErrorKind::RecursiveArgument, + LambdaCollisionError::BadArgument => ReactionErrorKind::BadArgument, }; PyReactionError { kind } } @@ -95,7 +416,11 @@ impl PyStandardization { "prefix" => RustStandardization::Prefix, "postfix" => RustStandardization::Postfix, "none" => RustStandardization::None, - _ => return Err(pyo3::exceptions::PyValueError::new_err("Invalid standardization type")) + _ => { + return Err(pyo3::exceptions::PyValueError::new_err( + "Invalid standardization type", + )) + } }; Ok(PyStandardization { standardization }) } @@ -110,44 +435,40 @@ impl From for RustStandardization { // --- Soup Wrapper --- #[pyclass] pub struct PySoup { - inner: Soup, + inner: RustSoup, } #[pymethods] impl PySoup { #[new] fn new() -> Self { - PySoup { inner: Soup::new() } + PySoup { + inner: RustSoup::new(), + } } #[staticmethod] fn from_config(cfg: &PyReactor) -> Self { - PySoup { inner: Soup::from_config(&cfg.inner) } - } - - fn set_limit(&mut self, limit: usize) { - self.inner.set_limit(limit); + PySoup { + inner: RustSoup::from_config(&cfg.inner), + } } fn perturb(&mut self, expressions: Vec) -> PyResult<()> { - let terms = expressions - .into_iter() - .filter_map(|s| parse(&s, Classic).ok()); - self.inner.perturb(terms); // Pass iterator directly - Ok(()) + // Parse incoming strings to Terms… + let terms = expressions + .into_iter() + .filter_map(|s| parse(&s, Classic).ok()); + + // …and let LambdaSoup wrap them into LambdaParticle (recursive = false) + self.inner.add_lambda_expressions(terms); + Ok(()) } fn simulate_for(&mut self, n: usize, log: bool) -> usize { self.inner.simulate_for(n, log) } - fn expressions(&self) -> Vec { - self.inner - .expressions() - .map(|term| term.to_string()) - .collect() - } - fn len(&self) -> usize { self.inner.len() } @@ -156,6 +477,15 @@ impl PySoup { self.inner.collisions() } + /// Return the list of expressions in the soup in Church notation. + fn expressions(&self) -> Vec { + self.inner + .lambda_expressions() + .map(|term| term.to_string()) + .collect() + } + + /// Return the set of unique expressions (each term converted to a string). fn unique_expressions(&self) -> Vec { self.inner .unique_expressions() @@ -164,6 +494,7 @@ impl PySoup { .collect() } + /// Return a (term, count) list giving population counts for each unique term. fn expression_counts(&self) -> Vec<(String, u32)> { self.inner .expression_counts() @@ -172,6 +503,7 @@ impl PySoup { .collect() } + /// Return the Shannon-like entropy of the current population (base‑10 logarithm). fn population_entropy(&self) -> f32 { self.inner.population_entropy() } @@ -187,11 +519,18 @@ pub struct PyBTreeGen { impl PyBTreeGen { #[new] fn new() -> Self { - PyBTreeGen { inner: RustBTreeGen::new() } + PyBTreeGen { + inner: RustBTreeGen::new(), + } } #[staticmethod] - fn from_config(size: u32, freevar_generation_probability: f64, max_free_vars: u32, std: PyStandardization) -> Self { + fn from_config( + size: u32, + freevar_generation_probability: f64, + max_free_vars: u32, + std: PyStandardization, + ) -> Self { let config = config::BTreeGen { size, freevar_generation_probability, @@ -199,7 +538,9 @@ impl PyBTreeGen { standardization: std.into(), seed: ConfigSeed(Some([0; 32])), }; - PyBTreeGen { inner: RustBTreeGen::from_config(&config) } + PyBTreeGen { + inner: RustBTreeGen::from_config(&config), + } } fn generate(&mut self) -> String { @@ -207,7 +548,11 @@ impl PyBTreeGen { } fn generate_n(&mut self, n: usize) -> Vec { - self.inner.generate_n(n).into_iter().map(|t| t.to_string()).collect() + self.inner + .generate_n(n) + .into_iter() + .map(|t| t.to_string()) + .collect() } } @@ -220,7 +565,12 @@ pub struct PyFontanaGen { #[pymethods] impl PyFontanaGen { #[staticmethod] - fn from_config(abs_range: (f64, f64), app_range: (f64, f64), max_depth: u32, max_free_vars: u32) -> Self { + fn from_config( + abs_range: (f64, f64), + app_range: (f64, f64), + max_depth: u32, + max_free_vars: u32, + ) -> Self { let config = config::FontanaGen { abstraction_prob_range: abs_range, application_prob_range: app_range, @@ -228,7 +578,9 @@ impl PyFontanaGen { n_max_free_vars: max_free_vars, seed: ConfigSeed(Some([0; 32])), }; - PyFontanaGen { inner: RustFontanaGen::from_config(&config) } + PyFontanaGen { + inner: RustFontanaGen::from_config(&config), + } } fn generate(&self) -> Option { @@ -247,34 +599,9 @@ fn encode_hex_py(bytes: Vec) -> String { encode_hex(&bytes) } -// --- Experiment Functions --- -// #[pyfunction] -// fn run_look_for_add() -> PyResult<()> { -// async_std::task::block_on(look_for_add()); -// Ok(()) -// } - -// #[pyfunction] -// fn run_entropy_series() -> PyResult<()> { -// async_std::task::block_on(entropy_series()); -// Ok(()) -// } - -// #[pyfunction] -// fn run_entropy_test() -> PyResult<()> { -// async_std::task::block_on(entropy_test()); -// Ok(()) -// } - -// #[pyfunction] -// fn run_sync_entropy_test() -> PyResult<()> { -// sync_entropy_test(); -// Ok(()) -// } - // --- Python Module Initialization --- #[pymodule] -fn alchemy(py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> { +fn alchemy(_py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> { // Register classes m.add_class::()?; m.add_class::()?; @@ -293,3 +620,4 @@ fn alchemy(py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> { Ok(()) } + From 7bb481b330ca1e045a79ee78f0573f5b9fe92994 Mon Sep 17 00:00:00 2001 From: jjoseph12 <72823288+jjoseph12@users.noreply.github.com> Date: Thu, 16 Oct 2025 14:19:45 -0700 Subject: [PATCH 08/36] fixed some generator.rs and other issues, check updated_readme --- .DS_Store | Bin 0 -> 6148 bytes README.md | 33 ------ src/Updated_ReadMe.md | 50 ++++++++ src/config.rs | 3 +- src/experiments/kinetics.rs | 9 +- src/experiments/magic_test_function.rs | 90 +-------------- src/experiments/search_by_behavior.rs | 4 +- src/generators.rs | 153 +++++++++++++------------ src/main.rs | 19 ++- 9 files changed, 159 insertions(+), 202 deletions(-) create mode 100644 .DS_Store delete mode 100644 README.md create mode 100644 src/Updated_ReadMe.md diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..7ee4e6bc875973af9673761425ac798ed4ec2a58 GIT binary patch literal 6148 zcmeHK%TB{E5FED^eMzWCj`@O0{6Q#1s24!u#0~nOpaz2akT~YfKky5DA19c#EfhN~ zM}(@~${yF=@p>nU9Ro0f&GZ=P0cf!ZHae`fn2bxQSk3oziN?;6qmK+ZCRkLwwL=w9 z1^$}?^6qwUiaRVZ=bQWU{*JPIGS2cb$7ORkVd;NaV=LyY$$UZ2cNc*-^LWN1UPD%G9h-8xv=D*&;9|u_#29`(K&x&-C(+QydDmil0RE3$;nxpu-vkUiC^K+Qds%p*f!)SK4P)Pu|yig U)MMe0Jv99gurg?+3jC=8-~4Z$c>n+a literal 0 HcmV?d00001 diff --git a/README.md b/README.md deleted file mode 100644 index c6a43e2..0000000 --- a/README.md +++ /dev/null @@ -1,33 +0,0 @@ -A reimplementation of Walter Fontana's Alchemy. Pipe lambda expressions into -`stdin` to start a default simulation. - -Usage: - -`alchemy` - -Build: - -`cargo build` - -Testing: - -`cargo run -- {args}` - -With the binary tree generators from the -[lambda-btree](https://github.com/AgentElement/lambda-btree) crate, you can -run a simple alchemy simulation with the following command. - -`python /path/to/src/fontana_generator.py | cargo run -- {args}` - - -Documentation: - -* Full documentation: `cargo doc --open` -* Help: `cargo run -- --help` - -The documentation for the configuration file is in the `Config` object. - - -# Interactive Dashboard - -Can be found here: https://github.com/mathis-group/Alchemy-Dashboard \ No newline at end of file diff --git a/src/Updated_ReadMe.md b/src/Updated_ReadMe.md new file mode 100644 index 0000000..963fa5e --- /dev/null +++ b/src/Updated_ReadMe.md @@ -0,0 +1,50 @@ +# Modern AlChemy – Current Issues (fontanagen branch) + +##### How to Run +- **Build:** `cargo build` +- **Run default simulation:** `cargo run` +- **Fontana generator demo:** `cargo run -- --config-file /path/to/fontana_config.json` +- **Dump default config:** `cargo run -- --dump-config` +- **Dump default config with CL:** cargo run -- --config-file /tmp/fontana_cfg.json --generate 3 +- **Run experiments:** `cargo run -- --experiment ` (not sure how to run this part) + +##### Currently Resolved + +##### Fontana generator looks at configuration +- `src/generators.rs`: `FontanaGen::from_config` now uses `min_depth`, `max_depth`, and `free_variable_probability`, clamps probability ranges, checks depth bounds, and removed the unused local RNG binding. +- Added a defensive helpers to avoid divide-by-zero (`max_depth` guard), empty ranges when depth is zero, and runaway probabilities (clamped per depth). Generation can now use the configured `n_max_free_vars` safely. +- CLI `--generate` now obeys the active generator in the loaded config, so Fontana samples can be printed without editing source. + +##### Previous issue: + +- `src/generators.rs:202-217`: `min_depth` and `free_prob` are hard-coded to `0`, ignoring `config::FontanaGen.min_depth` and `.free_variable_probability`. (but it seems like in config.rs it matches the same values? do we hard code or use that?) +- `src/generators.rs:197-200`: the local `rng` binding is never used; we instantiate the struct with `ChaCha8Rng::from_seed(seed)` directly, so this variable only triggers warnings. +- `src/generators.rs:197-200`: division by `cfg.max_depth - 1` won't work if `max_depth <= 1`. +- `src/generators.rs:236-279`: `p_abs`/`p_app` can exceed `1.0`, meaning leaf nodes might never be produced before hitting maximum depth; when `depth == 0`, `rng.gen_range(1..=depth)` is an empty range. +- `src/main.rs:156`: `--generate` always uses `BTreeGen`. Need to do for FontanaGen + + +##### Warning cleanup +- Removed unused helper functions and tightened imports in `src/experiments/magic_test_function.rs`; wrapped tests in `#[cfg(test)]` to avoid dev-build noise. +- Added documentation for `config::FontanaGen`, so the `#[warn(missing_docs)]` lint is satisfied. +- `cargo build` and `cargo test` now run warning-free. + +##### Remaining Known Issues + +##### Postfix standardization unimplemented +- `src/generators.rs:134-136`: `postfix_standardize` calls `unimplemented!`. +- Any config selecting `Standardization::Postfix` crashes. +- Need to add the transformation or disable the option in configs/CLI until ready. + +##### Collision metrics mislabelled +- `src/lambda/recursive.rs:227-232`: tuples are pushed as `(expr, size, reductions)` but collected as reductions → `t.1`, sizes → `t.2`, swapping the data. +- **Does this imapct antyhing? does it affect the analytics and experiment outputs report incorrect reduction counts vs sizes. + +##### Jacard metric incorrect +- `src/analysis.rs:57-68`: calculates `intersection / (|A| + |B|)` +- should be divded by A U B + +##### Documentation +- `scripts/discovery.sh:15` (and similar files): `cd ~/cwd/functional-supercollider`, which doesn’t exist here. + + diff --git a/src/config.rs b/src/config.rs index af324f3..318580c 100644 --- a/src/config.rs +++ b/src/config.rs @@ -109,6 +109,7 @@ pub struct BTreeGen { #[warn(missing_docs)] #[derive(Serialize, Deserialize, Debug)] +/// Generator-specific configuration derived from Walter Fontana's original scheme. pub struct FontanaGen { /// The seed for the lambda expression generator. If set to `None`, then a seed is chosen /// randomly. Default: `None` @@ -149,7 +150,6 @@ impl GenConfig for FontanaGen { } } - impl Reactor { /// Produce a new `ReactorConfig` struct with default values. pub fn new() -> Self { @@ -189,7 +189,6 @@ impl GenConfig for BTreeGen { } } - impl Config { /// Create a config object from a string pub fn from_config_str(s: &str) -> Config { diff --git a/src/experiments/kinetics.rs b/src/experiments/kinetics.rs index d4a4efe..89d9575 100644 --- a/src/experiments/kinetics.rs +++ b/src/experiments/kinetics.rs @@ -131,8 +131,13 @@ pub fn kinetic_succ_experiment() { let mut futures = FuturesUnordered::new(); let sample_size = 5000; - let good_fracs = [0.0, 0.0002, 0.0004, 0.0008, 0.0016, 0.0032, 0.0064, 0.0128, 0.0256, 0.0512, 0.1024]; - let test_fracs = [0.0, 0.05, 0.10, 0.15, 0.20, 0.25, 0.30, 0.35, 0.40, 0.45, 0.50, 0.55, 0.60, 0.65, 0.70, 0.75, 0.80]; + let good_fracs = [ + 0.0, 0.0002, 0.0004, 0.0008, 0.0016, 0.0032, 0.0064, 0.0128, 0.0256, 0.0512, 0.1024, + ]; + let test_fracs = [ + 0.0, 0.05, 0.10, 0.15, 0.20, 0.25, 0.30, 0.35, 0.40, 0.45, 0.50, 0.55, 0.60, 0.65, 0.70, + 0.75, 0.80, + ]; for (i, good_frac) in good_fracs.iter().enumerate() { for (j, test_frac) in test_fracs.iter().enumerate() { diff --git a/src/experiments/magic_test_function.rs b/src/experiments/magic_test_function.rs index 251cac3..f624de6 100644 --- a/src/experiments/magic_test_function.rs +++ b/src/experiments/magic_test_function.rs @@ -4,10 +4,7 @@ use lambda_calculus::reduction::Order::HAP; use lambda_calculus::{ abs, app, combinators::{I, K, S}, - data::{ - boolean::{self, and}, - num::church::{add, eq, succ}, - }, + data::num::church::{add, eq, succ}, parse, term::Notation::Classic, IntoChurchNum, @@ -17,7 +14,6 @@ use rand::random; use crate::{ config::{self, ConfigSeed}, - generators::BTreeGen, lambda::recursive::{has_two_args, is_truthy, uses_both_arguments, LambdaSoup}, utils::{dump_series_to_file, read_inputs}, }; @@ -46,31 +42,10 @@ pub fn addtwo() -> Term { comp } -// Triplet permutation combinators -fn p123() -> Term { - abs!(3, app!(Var(1), Var(2), Var(3))) -} - fn p132() -> Term { abs!(3, app!(Var(1), Var(3), Var(2))) } -fn p213() -> Term { - abs!(3, app!(Var(2), Var(1), Var(3))) -} - -fn p231() -> Term { - abs!(3, app!(Var(2), Var(3), Var(1))) -} - -fn p312() -> Term { - abs!(3, app!(Var(3), Var(1), Var(2))) -} - -fn p321() -> Term { - abs!(3, app!(Var(3), Var(2), Var(1))) -} - pub(super) fn test_add(a: usize, b: usize) -> Term { let mut test = parse(r"\eq. \a. \b. \ab. \f. (eq (f a b) ab)", Classic).unwrap(); test = app!( @@ -85,23 +60,6 @@ pub(super) fn test_add(a: usize, b: usize) -> Term { test } -fn test_add_seq(pairs: impl Iterator) -> Term { - let mut test = parse(r"\f. \a. \b. a", Classic).unwrap(); - for (u, v) in pairs { - let gut = parse( - r"\and. \test. \testadd. \f. and (test f) (testadd f)", - Classic, - ) - .unwrap(); - test = app!(gut, and(), test, test_add(u, v)); - } - test.reduce(lambda_calculus::HAP, 0); - let mut comp = app!(test.clone(), add()); - comp.reduce(lambda_calculus::HAP, 0); - assert!(comp.is_isomorphic_to(&boolean::tru())); - test -} - pub(super) fn test_succ(a: usize) -> Term { let mut test = parse(r"\eq. \a. \asucc. \f. (eq (f a) asucc)", Classic).unwrap(); test = app!(test, eq(), a.into_church(), (a + 1).into_church()); @@ -110,23 +68,6 @@ pub(super) fn test_succ(a: usize) -> Term { test } -pub(super) fn test_succ_seq(nums: impl Iterator) -> Term { - let mut test = parse(r"\f. \a. \b. a", Classic).unwrap(); - for u in nums { - let gut = parse( - r"\and. \test. \testscc. \f. and (test f) (testscc f)", - Classic, - ) - .unwrap(); - test = app!(gut, and(), test, test_succ(u)); - } - test.reduce(lambda_calculus::HAP, 0); - let mut comp = app!(test.clone(), succ()); - comp.reduce(lambda_calculus::HAP, 0); - assert!(comp.is_isomorphic_to(&boolean::tru())); - test -} - pub fn test_addtwo(a: usize) -> Term { let mut test = parse(r"\eq. \a. \asucc. \f. (eq (f a) asucc)", Classic).unwrap(); test = app!(test, eq(), a.into_church(), (a + 2).into_church()); @@ -135,28 +76,6 @@ pub fn test_addtwo(a: usize) -> Term { test } -fn generate_sample_for_addsearch(seed: ConfigSeed) -> Vec { - let mut sample = vec![S(); 200]; - sample.append(&mut vec![K(); 100]); - sample.append(&mut vec![I(); 100]); - for size in 5..12 { - let mut gen = BTreeGen::from_config(&config::BTreeGen { - size, - freevar_generation_probability: 0.2, - standardization: crate::generators::Standardization::Prefix, - n_max_free_vars: 6, - seed, - }); - let n_samples = match size { - 5..=7 => 800, - 8..=10 => 400, - _ => 200, - }; - sample.append(&mut gen.generate_n(n_samples)) - } - sample -} - pub(super) fn asymmetric_skip_sample() -> Vec { let mut sample = vec![]; sample.append(&mut vec![S(); 10]); @@ -367,12 +286,11 @@ pub fn succ_search_with_test() { } } +#[cfg(test)] mod tests { - use lambda_calculus::{ - app, data::boolean::tru, data::num::church::add, reduction::Order::HNO, IntoChurchNum, - }; + use lambda_calculus::{app, data::boolean::tru, data::num::church::add, reduction::Order::HNO}; - use crate::experiments::magic_test_function::{addtwo, test_addtwo, test_succ}; + use crate::experiments::magic_test_function::{addtwo, test_addtwo}; use super::test_add; diff --git a/src/experiments/search_by_behavior.rs b/src/experiments/search_by_behavior.rs index 9b26b0c..0d8018d 100644 --- a/src/experiments/search_by_behavior.rs +++ b/src/experiments/search_by_behavior.rs @@ -124,7 +124,7 @@ pub fn look_for_xorset() { i, run_length, polling_interval, - xorset_test + xorset_test, ))); } @@ -153,7 +153,7 @@ pub fn look_for_not_xorset() { i, run_length, polling_interval, - not_xorset_test + not_xorset_test, ))); } diff --git a/src/generators.rs b/src/generators.rs index 13437d1..7cf02fb 100644 --- a/src/generators.rs +++ b/src/generators.rs @@ -166,18 +166,28 @@ pub struct FontanaGen { impl FontanaGen { pub fn new( min_depth: u32, - max_depth: u32, - abs_prob: (f32, f32), - app_prob: (f32, f32), - free_prob: f32, - max_vars: u32, + mut max_depth: u32, + mut abs_prob: (f32, f32), + mut app_prob: (f32, f32), + mut free_prob: f32, + mut max_vars: u32, seed: [u8; 32], ) -> FontanaGen { - let abs_incr = (abs_prob.1 - abs_prob.0) / ((max_depth - 1) as f32); - let app_incr = (app_prob.1 - app_prob.0) / ((max_depth - 1) as f32); + // Sanitise configuration so generation never panics. + max_depth = max_depth.max(1); + max_vars = max_vars.max(1); + free_prob = free_prob.clamp(0.0, 1.0); + abs_prob.0 = abs_prob.0.clamp(0.0, 1.0); + abs_prob.1 = abs_prob.1.clamp(0.0, 1.0); + app_prob.0 = app_prob.0.clamp(0.0, 1.0); + app_prob.1 = app_prob.1.clamp(0.0, 1.0); + + let steps = (max_depth - 1).max(1); + let abs_incr = (abs_prob.1 - abs_prob.0) / (steps as f32); + let app_incr = (app_prob.1 - app_prob.0) / (steps as f32); FontanaGen { - min_depth, + min_depth: min_depth.min(max_depth.saturating_sub(1)), max_depth, abs_prob, app_prob, @@ -192,32 +202,25 @@ impl FontanaGen { pub fn from_config(cfg: &config::FontanaGen) -> FontanaGen { let seed = cfg.seed.get(); - let rng = ChaCha8Rng::from_seed(seed); - - let abs_incr: f32 = ((cfg.abstraction_prob_range.1 - cfg.abstraction_prob_range.0) - / ((cfg.max_depth - 1) as f64)) as f32; - let app_incr: f32 = ((cfg.application_prob_range.1 - cfg.application_prob_range.0) - / ((cfg.max_depth - 1) as f64)) as f32; - - FontanaGen { - min_depth: 0, - max_depth: cfg.max_depth, - - abs_prob: (cfg.abstraction_prob_range.0 as f32, cfg.abstraction_prob_range.1 as f32), - app_prob: (cfg.application_prob_range.0 as f32, cfg.application_prob_range.1 as f32), - - abs_incr, - app_incr, - free_prob: 0.0, - max_vars: cfg.n_max_free_vars, - + FontanaGen::new( + cfg.min_depth, + cfg.max_depth, + ( + cfg.abstraction_prob_range.0 as f32, + cfg.abstraction_prob_range.1 as f32, + ), + ( + cfg.application_prob_range.0 as f32, + cfg.application_prob_range.1 as f32, + ), + cfg.free_variable_probability as f32, + cfg.n_max_free_vars, seed, - rng: ChaCha8Rng::from_seed(seed), - } + ) } - pub fn generate(&mut self) -> Term{ + pub fn generate(&mut self) -> Term { self.rand_lambda(0, self.abs_prob.0, self.app_prob.0) } @@ -234,48 +237,56 @@ impl FontanaGen { } pub fn rand_lambda(&mut self, depth: u32, p_abs: f32, p_app: f32) -> Term { - if depth >= self.max_depth { // over max depth generating leaf - let var = if self.rng.gen_bool(self.free_prob as f64) || depth == 0 { - depth + self.rng.gen_range(1..=self.max_vars) - } else { - self.rng.gen_range(1..=depth) - }; - return Term::Var(var as usize); - } else if depth < self.min_depth { // under min length will not generate leaf - let coin = self.rng.gen_bool(0.5); - if coin { - let n_abs = p_abs + self.abs_incr; - let n_app = p_app + self.app_incr; - return Term::Abs(Box::new(self.rand_lambda(depth + 1, n_abs, n_app))); - } else { - let n_abs = p_abs + self.abs_incr; - let n_app = p_app + self.app_incr; - return Term::App(Box::new(( - self.rand_lambda(depth+1, n_abs, n_app), - self.rand_lambda(depth+1, n_abs, n_app), - ))); - }; - } else { - let coin: f32 = self.rng.gen(); - if coin <= p_abs { - let n_abs = p_abs + self.abs_incr; - let n_app = p_app + self.app_incr; - return Term::Abs(Box::new(self.rand_lambda(depth + 1, n_abs, n_app))); - } else if coin <= p_abs + p_app { - let n_abs = p_abs + self.abs_incr; - let n_app = p_app + self.app_incr; - return Term::App(Box::new(( - self.rand_lambda(depth+1, n_abs, n_app), - self.rand_lambda(depth+1, n_abs, n_app), - ))); - } else { - let var = if self.rng.gen_bool(self.free_prob as f64) || depth == 0 { - depth + self.rng.gen_range(1..=self.max_vars) - } else { - self.rng.gen_range(1..=depth) - }; - return Term::Var(var as usize); + let (p_abs_eff, p_app_eff) = Self::clamp_probabilities(p_abs, p_app); + + if depth >= self.max_depth { + return self.sample_variable(depth); + } + + let next_abs = p_abs + self.abs_incr; + let next_app = p_app + self.app_incr; + + if depth < self.min_depth { + if self.rng.gen_bool(0.5) { + return Term::Abs(Box::new(self.rand_lambda(depth + 1, next_abs, next_app))); } + return Term::App(Box::new(( + self.rand_lambda(depth + 1, next_abs, next_app), + self.rand_lambda(depth + 1, next_abs, next_app), + ))); + } + + let coin: f32 = self.rng.gen(); + if coin <= p_abs_eff { + return Term::Abs(Box::new(self.rand_lambda(depth + 1, next_abs, next_app))); + } + if coin <= p_abs_eff + p_app_eff { + return Term::App(Box::new(( + self.rand_lambda(depth + 1, next_abs, next_app), + self.rand_lambda(depth + 1, next_abs, next_app), + ))); } + + self.sample_variable(depth) + } + + fn clamp_probabilities(p_abs: f32, p_app: f32) -> (f32, f32) { + let abs = p_abs.clamp(0.0, 1.0); + let remaining = 1.0 - abs; + let app = p_app.clamp(0.0, remaining); + (abs, app) + } + + fn sample_variable(&mut self, depth: u32) -> Term { + let free_choice = self.rng.gen_bool(self.free_prob as f64) || depth == 0; + let max_vars = self.max_vars.max(1); + let value = if free_choice { + let offset = self.rng.gen_range(1..=max_vars); + depth.saturating_add(offset) as usize + } else { + let upper = depth.max(1); + self.rng.gen_range(1..=upper) as usize + }; + Term::Var(value) } } diff --git a/src/main.rs b/src/main.rs index 3581afe..d985097 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,7 +4,6 @@ use experiments::{ discovery, distribution, entropy, kinetics, magic_test_function, search_by_behavior, }; use generators::BTreeGen; -use lambda_calculus::Term; use std::fs::{read_to_string, File}; use std::io::Write; @@ -126,8 +125,6 @@ pub fn generate_expressions_and_seed_soup(cfg: &config::Config) -> lambda::recur config::Generator::Fontana(gen_cfg) => { let mut gen = generators::FontanaGen::from_config(gen_cfg); gen.generate_n(cfg.sample_size) - - } }; let mut soup = lambda::recursive::LambdaSoup::from_config(&cfg.reactor_config); @@ -153,9 +150,19 @@ fn main() -> std::io::Result<()> { } if let Some(n) = cli.generate { - let mut gen = BTreeGen::new(); - for _ in 0..n { - println!("{:?}", gen.generate()) + match &config.generator_config { + config::Generator::BTree(gen_cfg) => { + let mut gen = BTreeGen::from_config(gen_cfg); + for _ in 0..n { + println!("{:?}", gen.generate()) + } + } + config::Generator::Fontana(gen_cfg) => { + let mut gen = generators::FontanaGen::from_config(gen_cfg); + for _ in 0..n { + println!("{:?}", gen.generate()) + } + } } return Ok(()); } From 3881abc96cfae85afd774d1c687cd85ab7709a0e Mon Sep 17 00:00:00 2001 From: Joel Joseph <72823288+jjoseph12@users.noreply.github.com> Date: Thu, 16 Oct 2025 14:21:37 -0700 Subject: [PATCH 09/36] Update README with current and resolved issues Added documentation for current issues, resolved problems, and remaining known issues in the README. --- README.md | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..963fa5e --- /dev/null +++ b/README.md @@ -0,0 +1,50 @@ +# Modern AlChemy – Current Issues (fontanagen branch) + +##### How to Run +- **Build:** `cargo build` +- **Run default simulation:** `cargo run` +- **Fontana generator demo:** `cargo run -- --config-file /path/to/fontana_config.json` +- **Dump default config:** `cargo run -- --dump-config` +- **Dump default config with CL:** cargo run -- --config-file /tmp/fontana_cfg.json --generate 3 +- **Run experiments:** `cargo run -- --experiment ` (not sure how to run this part) + +##### Currently Resolved + +##### Fontana generator looks at configuration +- `src/generators.rs`: `FontanaGen::from_config` now uses `min_depth`, `max_depth`, and `free_variable_probability`, clamps probability ranges, checks depth bounds, and removed the unused local RNG binding. +- Added a defensive helpers to avoid divide-by-zero (`max_depth` guard), empty ranges when depth is zero, and runaway probabilities (clamped per depth). Generation can now use the configured `n_max_free_vars` safely. +- CLI `--generate` now obeys the active generator in the loaded config, so Fontana samples can be printed without editing source. + +##### Previous issue: + +- `src/generators.rs:202-217`: `min_depth` and `free_prob` are hard-coded to `0`, ignoring `config::FontanaGen.min_depth` and `.free_variable_probability`. (but it seems like in config.rs it matches the same values? do we hard code or use that?) +- `src/generators.rs:197-200`: the local `rng` binding is never used; we instantiate the struct with `ChaCha8Rng::from_seed(seed)` directly, so this variable only triggers warnings. +- `src/generators.rs:197-200`: division by `cfg.max_depth - 1` won't work if `max_depth <= 1`. +- `src/generators.rs:236-279`: `p_abs`/`p_app` can exceed `1.0`, meaning leaf nodes might never be produced before hitting maximum depth; when `depth == 0`, `rng.gen_range(1..=depth)` is an empty range. +- `src/main.rs:156`: `--generate` always uses `BTreeGen`. Need to do for FontanaGen + + +##### Warning cleanup +- Removed unused helper functions and tightened imports in `src/experiments/magic_test_function.rs`; wrapped tests in `#[cfg(test)]` to avoid dev-build noise. +- Added documentation for `config::FontanaGen`, so the `#[warn(missing_docs)]` lint is satisfied. +- `cargo build` and `cargo test` now run warning-free. + +##### Remaining Known Issues + +##### Postfix standardization unimplemented +- `src/generators.rs:134-136`: `postfix_standardize` calls `unimplemented!`. +- Any config selecting `Standardization::Postfix` crashes. +- Need to add the transformation or disable the option in configs/CLI until ready. + +##### Collision metrics mislabelled +- `src/lambda/recursive.rs:227-232`: tuples are pushed as `(expr, size, reductions)` but collected as reductions → `t.1`, sizes → `t.2`, swapping the data. +- **Does this imapct antyhing? does it affect the analytics and experiment outputs report incorrect reduction counts vs sizes. + +##### Jacard metric incorrect +- `src/analysis.rs:57-68`: calculates `intersection / (|A| + |B|)` +- should be divded by A U B + +##### Documentation +- `scripts/discovery.sh:15` (and similar files): `cd ~/cwd/functional-supercollider`, which doesn’t exist here. + + From 6c4b984e0ffc83705e6139fc12f094ab5be37c83 Mon Sep 17 00:00:00 2001 From: "ridham.patel" Date: Thu, 16 Oct 2025 17:06:12 -0700 Subject: [PATCH 10/36] refactor(pyo3): move Python bindings to src/python.rs and register from lib.rs --- src/lib.rs | 605 +------------------------------------------------- src/python.rs | 219 ++++++++++++++++++ 2 files changed, 226 insertions(+), 598 deletions(-) create mode 100644 src/python.rs diff --git a/src/lib.rs b/src/lib.rs index 9045cfd..35b1f04 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,310 +1,7 @@ -// use pyo3::prelude::*; -// use pyo3::wrap_pyfunction; -// use serde::{Deserialize, Serialize}; - - -// // --- Module Imports --- -// mod config; -// mod supercollider; -// mod experiments; -// mod generators; -// mod utils; -// mod analysis; -// mod lambda; - - -// use lambda_calculus::{parse, term::Notation::Classic, Term}; -// use std::io::{self, BufRead}; - -// pub fn read_inputs() -> impl Iterator { -// io::stdin() -// .lock() -// .lines() -// .filter_map(|line| line.ok()) -// .filter_map(|line| parse(&line, Classic).ok()) -// } - - -// // --- Exposing Core Configurations --- -// use crate::config::{Reactor as RustReactor, ConfigSeed}; -// //use crate::supercollider::Soup; -// use crate::generators::{BTreeGen as RustBTreeGen, FontanaGen as RustFontanaGen, Standardization as RustStandardization}; -// use crate::utils::{decode_hex, encode_hex}; -// //use crate::experiments::{look_for_add, entropy_series, entropy_test, sync_entropy_test}; -// //use crate::lambda::lambda::LambdaCollisionError; - -// use crate::supercollider::Soup as GenericSoup; -// use crate::lambda::recursive::{ -// LambdaParticle, AlchemyCollider, LambdaCollisionOk, LambdaCollisionError, -// }; -// type RustSoup = GenericSoup; - -// // --- Error Handling Wrappers --- -// #[pyclass] -// #[derive(Debug, Clone)] -// pub struct PyReactionError { -// kind: LambdaCollisionError, -// } - -// #[derive(Debug, Clone)] -// pub enum ReactionErrorKind { -// ExceedsReductionLimit, -// NotEnoughExpressions, -// IsIdentity, -// IsParent, -// HasFreeVariables, -// ExceedsDepthLimit, -// } - -// impl From for PyReactionError { -// fn from(error: LambdaCollisionError) -> Self { -// let kind = match error { -// LambdaCollisionError::ExceedsReductionLimit => ReactionErrorKind::ExceedsReductionLimit, -// LambdaCollisionError::NotEnoughExpressions => ReactionErrorKind::NotEnoughExpressions, -// LambdaCollisionError::IsIdentity => ReactionErrorKind::IsIdentity, -// LambdaCollisionError::IsParent => ReactionErrorKind::IsParent, -// LambdaCollisionError::HasFreeVariables => ReactionErrorKind::HasFreeVariables, -// LambdaCollisionError::ExceedsDepthLimit => ReactionErrorKind::ExceedsDepthLimit, -// }; -// PyReactionError { kind } -// } -// } - -// // --- Reactor Wrapper --- -// #[pyclass] -// pub struct PyReactor { -// inner: RustReactor, -// } - -// #[pymethods] -// impl PyReactor { -// #[new] -// fn new() -> Self { -// PyReactor { -// inner: RustReactor::new(), -// } -// } -// } - -// // --- Standardization Wrapper --- -// #[pyclass] -// #[derive(Debug, Clone, Serialize, Deserialize)] -// pub struct PyStandardization { -// standardization: RustStandardization, -// } - -// #[pymethods] -// impl PyStandardization { -// #[new] -// fn new(kind: &str) -> PyResult { -// let standardization = match kind { -// "prefix" => RustStandardization::Prefix, -// "postfix" => RustStandardization::Postfix, -// "none" => RustStandardization::None, -// _ => return Err(pyo3::exceptions::PyValueError::new_err("Invalid standardization type")) -// }; -// Ok(PyStandardization { standardization }) -// } -// } - -// impl From for RustStandardization { -// fn from(py_std: PyStandardization) -> Self { -// py_std.standardization -// } -// } - -// // --- Soup Wrapper --- -// #[pyclass] -// pub struct PySoup { -// inner: Soup, -// } - -// #[pymethods] -// impl PySoup { -// #[new] -// fn new() -> Self { -// PySoup { inner: Soup::new() } -// } - -// #[staticmethod] -// fn from_config(cfg: &PyReactor) -> Self { -// PySoup { inner: Soup::from_config(&cfg.inner) } -// } - -// fn set_limit(&mut self, limit: usize) { -// self.inner.set_limit(limit); -// } - -// fn perturb(&mut self, expressions: Vec) -> PyResult<()> { -// let terms = expressions -// .into_iter() -// .filter_map(|s| parse(&s, Classic).ok()); -// self.inner.perturb(terms); // Pass iterator directly -// Ok(()) -// } - -// fn simulate_for(&mut self, n: usize, log: bool) -> usize { -// self.inner.simulate_for(n, log) -// } - -// fn expressions(&self) -> Vec { -// self.inner -// .expressions() -// .map(|term| term.to_string()) -// .collect() -// } - -// fn len(&self) -> usize { -// self.inner.len() -// } - -// fn collisions(&self) -> usize { -// self.inner.collisions() -// } - -// fn unique_expressions(&self) -> Vec { -// self.inner -// .unique_expressions() -// .into_iter() -// .map(|term| term.to_string()) -// .collect() -// } - -// fn expression_counts(&self) -> Vec<(String, u32)> { -// self.inner -// .expression_counts() -// .into_iter() -// .map(|(term, count)| (term.to_string(), count)) -// .collect() -// } - -// fn population_entropy(&self) -> f32 { -// self.inner.population_entropy() -// } -// } - -// // --- BTreeGen Wrapper --- -// #[pyclass] -// pub struct PyBTreeGen { -// inner: RustBTreeGen, -// } - -// #[pymethods] -// impl PyBTreeGen { -// #[new] -// fn new() -> Self { -// PyBTreeGen { inner: RustBTreeGen::new() } -// } - -// #[staticmethod] -// fn from_config(size: u32, freevar_generation_probability: f64, max_free_vars: u32, std: PyStandardization) -> Self { -// let config = config::BTreeGen { -// size, -// freevar_generation_probability, -// n_max_free_vars: max_free_vars, -// standardization: std.into(), -// seed: ConfigSeed(Some([0; 32])), -// }; -// PyBTreeGen { inner: RustBTreeGen::from_config(&config) } -// } - -// fn generate(&mut self) -> String { -// self.inner.generate().to_string() -// } - -// fn generate_n(&mut self, n: usize) -> Vec { -// self.inner.generate_n(n).into_iter().map(|t| t.to_string()).collect() -// } -// } - -// // --- FontanaGen Wrapper --- -// #[pyclass] -// pub struct PyFontanaGen { -// inner: RustFontanaGen, -// } - -// #[pymethods] -// impl PyFontanaGen { -// #[staticmethod] -// fn from_config(abs_range: (f64, f64), app_range: (f64, f64), max_depth: u32, max_free_vars: u32) -> Self { -// let config = config::FontanaGen { -// abstraction_prob_range: abs_range, -// application_prob_range: app_range, -// max_depth, -// n_max_free_vars: max_free_vars, -// seed: ConfigSeed(Some([0; 32])), -// }; -// PyFontanaGen { inner: RustFontanaGen::from_config(&config) } -// } - -// fn generate(&self) -> Option { -// self.inner.generate().map(|term| term.to_string()) -// } -// } - -// // --- Utilities --- -// #[pyfunction] -// fn decode_hex_py(hex_string: &str) -> PyResult> { -// decode_hex(hex_string).map_err(|e| pyo3::exceptions::PyValueError::new_err(e.to_string())) -// } - -// #[pyfunction] -// fn encode_hex_py(bytes: Vec) -> String { -// encode_hex(&bytes) -// } - -// // --- Experiment Functions --- -// // #[pyfunction] -// // fn run_look_for_add() -> PyResult<()> { -// // async_std::task::block_on(look_for_add()); -// // Ok(()) -// // } - -// // #[pyfunction] -// // fn run_entropy_series() -> PyResult<()> { -// // async_std::task::block_on(entropy_series()); -// // Ok(()) -// // } - -// // #[pyfunction] -// // fn run_entropy_test() -> PyResult<()> { -// // async_std::task::block_on(entropy_test()); -// // Ok(()) -// // } - -// // #[pyfunction] -// // fn run_sync_entropy_test() -> PyResult<()> { -// // sync_entropy_test(); -// // Ok(()) -// // } - -// // --- Python Module Initialization --- -// #[pymodule] -// fn alchemy(py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> { -// // Register classes -// m.add_class::()?; -// m.add_class::()?; -// m.add_class::()?; -// m.add_class::()?; -// m.add_class::()?; -// m.add_class::()?; - -// // Register functions -// m.add_function(wrap_pyfunction!(decode_hex_py, m)?)?; -// m.add_function(wrap_pyfunction!(encode_hex_py, m)?)?; -// // m.add_function(wrap_pyfunction!(run_look_for_add, m)?)?; -// // m.add_function(wrap_pyfunction!(run_entropy_series, m)?)?; -// // m.add_function(wrap_pyfunction!(run_entropy_test, m)?)?; -// // m.add_function(wrap_pyfunction!(run_sync_entropy_test, m)?)?; - -// Ok(()) -// } - +// src/lib.rs use pyo3::prelude::*; -use pyo3::wrap_pyfunction; -use serde::{Deserialize, Serialize}; -// --- Module Imports --- +// Re-export your Rust modules for the CLI and for external users pub mod config; pub mod experiments; pub mod generators; @@ -313,7 +10,10 @@ pub mod analysis; pub mod lambda; pub mod supercollider; +// New Python wrapper module +mod python; +// If you still need this for the CLI, keep it here: use lambda_calculus::{parse, term::Notation::Classic, Term}; use std::io::{self, BufRead}; @@ -325,299 +25,8 @@ pub fn read_inputs() -> impl Iterator { .filter_map(|line| parse(&line, Classic).ok()) } -// --- Exposing Core Configurations --- -use crate::config::{Reactor as RustReactor, ConfigSeed}; -use crate::generators::{ - BTreeGen as RustBTreeGen, FontanaGen as RustFontanaGen, Standardization as RustStandardization, -}; -use crate::utils::{decode_hex, encode_hex}; -// use crate::experiments::{look_for_add, entropy_series, entropy_test, sync_entropy_test}; - -// Bring in the concrete lambda types so we can fully specify Soup. -use crate::lambda::recursive::{ - AlchemyCollider, LambdaCollisionError, LambdaCollisionOk, LambdaParticle, -}; - -// Create a concrete alias for the generic Soup. Without this, the compiler -// complains about missing generic parameters and ambiguous new()/from_config() impls. -use crate::supercollider::Soup as GenericSoup; -type RustSoup = - GenericSoup; - -// --- Error Handling Wrappers --- -#[pyclass] -#[derive(Debug, Clone)] -pub struct PyReactionError { - // Store the kind you map to below rather than LambdaCollisionError itself. - kind: ReactionErrorKind, -} - -#[derive(Debug, Clone)] -pub enum ReactionErrorKind { - ExceedsReductionLimit, - NotEnoughExpressions, - IsIdentity, - IsParent, - HasFreeVariables, - ExceedsDepthLimit, - RecursiveArgument, - BadArgument, -} - -// Map the recursive LambdaCollisionError into your public PyReactionError. -impl From for PyReactionError { - fn from(error: LambdaCollisionError) -> Self { - let kind = match error { - LambdaCollisionError::ExceedsReductionLimit => { - ReactionErrorKind::ExceedsReductionLimit - } - LambdaCollisionError::NotEnoughExpressions => { - ReactionErrorKind::NotEnoughExpressions - } - LambdaCollisionError::IsIdentity => ReactionErrorKind::IsIdentity, - LambdaCollisionError::IsParent => ReactionErrorKind::IsParent, - LambdaCollisionError::HasFreeVariables => ReactionErrorKind::HasFreeVariables, - LambdaCollisionError::ExceedsDepthLimit => ReactionErrorKind::ExceedsDepthLimit, - LambdaCollisionError::RecursiveArgument => ReactionErrorKind::RecursiveArgument, - LambdaCollisionError::BadArgument => ReactionErrorKind::BadArgument, - }; - PyReactionError { kind } - } -} - -// --- Reactor Wrapper --- -#[pyclass] -pub struct PyReactor { - inner: RustReactor, -} - -#[pymethods] -impl PyReactor { - #[new] - fn new() -> Self { - PyReactor { - inner: RustReactor::new(), - } - } -} - -// --- Standardization Wrapper --- -#[pyclass] -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct PyStandardization { - standardization: RustStandardization, -} - -#[pymethods] -impl PyStandardization { - #[new] - fn new(kind: &str) -> PyResult { - let standardization = match kind { - "prefix" => RustStandardization::Prefix, - "postfix" => RustStandardization::Postfix, - "none" => RustStandardization::None, - _ => { - return Err(pyo3::exceptions::PyValueError::new_err( - "Invalid standardization type", - )) - } - }; - Ok(PyStandardization { standardization }) - } -} - -impl From for RustStandardization { - fn from(py_std: PyStandardization) -> Self { - py_std.standardization - } -} - -// --- Soup Wrapper --- -#[pyclass] -pub struct PySoup { - inner: RustSoup, -} - -#[pymethods] -impl PySoup { - #[new] - fn new() -> Self { - PySoup { - inner: RustSoup::new(), - } - } - - #[staticmethod] - fn from_config(cfg: &PyReactor) -> Self { - PySoup { - inner: RustSoup::from_config(&cfg.inner), - } - } - - fn perturb(&mut self, expressions: Vec) -> PyResult<()> { - // Parse incoming strings to Terms… - let terms = expressions - .into_iter() - .filter_map(|s| parse(&s, Classic).ok()); - - // …and let LambdaSoup wrap them into LambdaParticle (recursive = false) - self.inner.add_lambda_expressions(terms); - Ok(()) - } - - fn simulate_for(&mut self, n: usize, log: bool) -> usize { - self.inner.simulate_for(n, log) - } - - fn len(&self) -> usize { - self.inner.len() - } - - fn collisions(&self) -> usize { - self.inner.collisions() - } - - /// Return the list of expressions in the soup in Church notation. - fn expressions(&self) -> Vec { - self.inner - .lambda_expressions() - .map(|term| term.to_string()) - .collect() - } - - /// Return the set of unique expressions (each term converted to a string). - fn unique_expressions(&self) -> Vec { - self.inner - .unique_expressions() - .into_iter() - .map(|term| term.to_string()) - .collect() - } - - /// Return a (term, count) list giving population counts for each unique term. - fn expression_counts(&self) -> Vec<(String, u32)> { - self.inner - .expression_counts() - .into_iter() - .map(|(term, count)| (term.to_string(), count)) - .collect() - } - - /// Return the Shannon-like entropy of the current population (base‑10 logarithm). - fn population_entropy(&self) -> f32 { - self.inner.population_entropy() - } -} - -// --- BTreeGen Wrapper --- -#[pyclass] -pub struct PyBTreeGen { - inner: RustBTreeGen, -} - -#[pymethods] -impl PyBTreeGen { - #[new] - fn new() -> Self { - PyBTreeGen { - inner: RustBTreeGen::new(), - } - } - - #[staticmethod] - fn from_config( - size: u32, - freevar_generation_probability: f64, - max_free_vars: u32, - std: PyStandardization, - ) -> Self { - let config = config::BTreeGen { - size, - freevar_generation_probability, - n_max_free_vars: max_free_vars, - standardization: std.into(), - seed: ConfigSeed(Some([0; 32])), - }; - PyBTreeGen { - inner: RustBTreeGen::from_config(&config), - } - } - - fn generate(&mut self) -> String { - self.inner.generate().to_string() - } - - fn generate_n(&mut self, n: usize) -> Vec { - self.inner - .generate_n(n) - .into_iter() - .map(|t| t.to_string()) - .collect() - } -} - -// --- FontanaGen Wrapper --- -#[pyclass] -pub struct PyFontanaGen { - inner: RustFontanaGen, -} - -#[pymethods] -impl PyFontanaGen { - #[staticmethod] - fn from_config( - abs_range: (f64, f64), - app_range: (f64, f64), - max_depth: u32, - max_free_vars: u32, - ) -> Self { - let config = config::FontanaGen { - abstraction_prob_range: abs_range, - application_prob_range: app_range, - max_depth, - n_max_free_vars: max_free_vars, - seed: ConfigSeed(Some([0; 32])), - }; - PyFontanaGen { - inner: RustFontanaGen::from_config(&config), - } - } - - fn generate(&self) -> Option { - self.inner.generate().map(|term| term.to_string()) - } -} - -// --- Utilities --- -#[pyfunction] -fn decode_hex_py(hex_string: &str) -> PyResult> { - decode_hex(hex_string).map_err(|e| pyo3::exceptions::PyValueError::new_err(e.to_string())) -} - -#[pyfunction] -fn encode_hex_py(bytes: Vec) -> String { - encode_hex(&bytes) -} - -// --- Python Module Initialization --- +// Python module entry point stays tiny and delegates to python::register #[pymodule] fn alchemy(_py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> { - // Register classes - m.add_class::()?; - m.add_class::()?; - m.add_class::()?; - m.add_class::()?; - m.add_class::()?; - m.add_class::()?; - - // Register functions - m.add_function(wrap_pyfunction!(decode_hex_py, m)?)?; - m.add_function(wrap_pyfunction!(encode_hex_py, m)?)?; - // m.add_function(wrap_pyfunction!(run_look_for_add, m)?)?; - // m.add_function(wrap_pyfunction!(run_entropy_series, m)?)?; - // m.add_function(wrap_pyfunction!(run_entropy_test, m)?)?; - // m.add_function(wrap_pyfunction!(run_sync_entropy_test, m)?)?; - - Ok(()) + python::register(m) } - diff --git a/src/python.rs b/src/python.rs new file mode 100644 index 0000000..9ec5119 --- /dev/null +++ b/src/python.rs @@ -0,0 +1,219 @@ +// src/python.rs +use pyo3::prelude::*; +use pyo3::wrap_pyfunction; +use serde::{Deserialize, Serialize}; + +use lambda_calculus::{parse, term::Notation::Classic, Term}; + +use crate::config::{self, ConfigSeed, Reactor as RustReactor}; +use crate::generators::{ + BTreeGen as RustBTreeGen, FontanaGen as RustFontanaGen, Standardization as RustStandardization, +}; +use crate::lambda::recursive::{ + AlchemyCollider, LambdaCollisionError, LambdaCollisionOk, LambdaParticle, +}; +use crate::supercollider::Soup as GenericSoup; +use crate::utils::{decode_hex, encode_hex}; + +// Concrete soup alias for the recursive lambda flavor +type RustSoup = + GenericSoup; + +// ============ Errors exposed to Python ============ + +#[pyclass] +#[derive(Debug, Clone)] +pub struct PyReactionError { + kind: ReactionErrorKind, +} + +#[derive(Debug, Clone)] +pub enum ReactionErrorKind { + ExceedsReductionLimit, + NotEnoughExpressions, + IsIdentity, + IsParent, + HasFreeVariables, + ExceedsDepthLimit, + RecursiveArgument, + BadArgument, +} + +impl From for PyReactionError { + fn from(error: LambdaCollisionError) -> Self { + let kind = match error { + LambdaCollisionError::ExceedsReductionLimit => ReactionErrorKind::ExceedsReductionLimit, + LambdaCollisionError::NotEnoughExpressions => ReactionErrorKind::NotEnoughExpressions, + LambdaCollisionError::IsIdentity => ReactionErrorKind::IsIdentity, + LambdaCollisionError::IsParent => ReactionErrorKind::IsParent, + LambdaCollisionError::HasFreeVariables => ReactionErrorKind::HasFreeVariables, + LambdaCollisionError::ExceedsDepthLimit => ReactionErrorKind::ExceedsDepthLimit, + LambdaCollisionError::RecursiveArgument => ReactionErrorKind::RecursiveArgument, + LambdaCollisionError::BadArgument => ReactionErrorKind::BadArgument, + }; + PyReactionError { kind } + } +} + +// ============ Reactor wrapper ============ + +#[pyclass] +pub struct PyReactor { + pub(crate) inner: RustReactor, +} + +#[pymethods] +impl PyReactor { + #[new] + fn new() -> Self { + PyReactor { inner: RustReactor::new() } + } +} + +// ============ Standardization wrapper ============ + +#[pyclass] +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct PyStandardization { + standardization: RustStandardization, +} + +#[pymethods] +impl PyStandardization { + #[new] + fn new(kind: &str) -> PyResult { + let standardization = match kind { + "prefix" => RustStandardization::Prefix, + "postfix" => RustStandardization::Postfix, + "none" => RustStandardization::None, + _ => return Err(pyo3::exceptions::PyValueError::new_err("Invalid standardization type")), + }; + Ok(PyStandardization { standardization }) + } +} + +impl From for RustStandardization { + fn from(py_std: PyStandardization) -> Self { py_std.standardization } +} + +// ============ Soup wrapper ============ + +#[pyclass] +pub struct PySoup { + inner: RustSoup, +} + +#[pymethods] +impl PySoup { + #[new] + fn new() -> Self { PySoup { inner: RustSoup::new() } } + + #[staticmethod] + fn from_config(cfg: &PyReactor) -> Self { + PySoup { inner: RustSoup::from_config(&cfg.inner) } + } + + fn perturb(&mut self, expressions: Vec) -> PyResult<()> { + let terms = expressions.into_iter().filter_map(|s| parse(&s, Classic).ok()); + self.inner.add_lambda_expressions(terms); + Ok(()) + } + + fn simulate_for(&mut self, n: usize, log: bool) -> usize { self.inner.simulate_for(n, log) } + fn len(&self) -> usize { self.inner.len() } + fn collisions(&self) -> usize { self.inner.collisions() } + + fn expressions(&self) -> Vec { + self.inner.lambda_expressions().map(|t| t.to_string()).collect() + } + fn unique_expressions(&self) -> Vec { + self.inner.unique_expressions().into_iter().map(|t| t.to_string()).collect() + } + fn expression_counts(&self) -> Vec<(String, u32)> { + self.inner.expression_counts().into_iter().map(|(t, c)| (t.to_string(), c)).collect() + } + fn population_entropy(&self) -> f32 { self.inner.population_entropy() } +} + +// ============ Generators ============ + +#[pyclass] +pub struct PyBTreeGen { inner: RustBTreeGen } + +#[pymethods] +impl PyBTreeGen { + #[new] + fn new() -> Self { PyBTreeGen { inner: RustBTreeGen::new() } } + + #[staticmethod] + fn from_config( + size: u32, + freevar_generation_probability: f64, + max_free_vars: u32, + std: PyStandardization, + ) -> Self { + let cfg = config::BTreeGen { + size, + freevar_generation_probability, + n_max_free_vars: max_free_vars, + standardization: std.into(), + seed: ConfigSeed(Some([0; 32])), + }; + PyBTreeGen { inner: RustBTreeGen::from_config(&cfg) } + } + + fn generate(&mut self) -> String { self.inner.generate().to_string() } + + fn generate_n(&mut self, n: usize) -> Vec { + self.inner.generate_n(n).into_iter().map(|t| t.to_string()).collect() + } +} + +#[pyclass] +pub struct PyFontanaGen { inner: RustFontanaGen } + +#[pymethods] +impl PyFontanaGen { + #[staticmethod] + fn from_config( + abs_range: (f64, f64), + app_range: (f64, f64), + max_depth: u32, + max_free_vars: u32, + ) -> Self { + let cfg = config::FontanaGen { + abstraction_prob_range: abs_range, + application_prob_range: app_range, + max_depth, + n_max_free_vars: max_free_vars, + seed: ConfigSeed(Some([0; 32])), + }; + PyFontanaGen { inner: RustFontanaGen::from_config(&cfg) } + } + + fn generate(&self) -> Option { self.inner.generate().map(|t| t.to_string()) } +} + +// ============ Utilities ============ + +#[pyfunction] +fn decode_hex_py(hex_string: &str) -> PyResult> { + decode_hex(hex_string).map_err(|e| pyo3::exceptions::PyValueError::new_err(e.to_string())) +} + +#[pyfunction] +fn encode_hex_py(bytes: Vec) -> String { encode_hex(&bytes) } + +// ============ Public registration hook ============ + +pub fn register(m: &Bound<'_, PyModule>) -> PyResult<()> { + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + m.add_function(wrap_pyfunction!(decode_hex_py, m)?)?; + m.add_function(wrap_pyfunction!(encode_hex_py, m)?)?; + Ok(()) +} From fbdc8c23895c97411c446c923df88bbbb6ca0715 Mon Sep 17 00:00:00 2001 From: "ridham.patel" Date: Thu, 16 Oct 2025 17:27:10 -0700 Subject: [PATCH 11/36] python bindings: split into python.rs; add PyFontanaGen; tests; tidy lib.rs --- .gitignore | 1 + src/lib.rs | 1 + src/python.rs | 8 ++- test_alchemy.py | 175 ++++++++++++++++++++++++++++++------------------ 4 files changed, 117 insertions(+), 68 deletions(-) diff --git a/.gitignore b/.gitignore index a9e7e65..e87b436 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /target /scratchpad /result +.DS_Store diff --git a/src/lib.rs b/src/lib.rs index 35b1f04..e7949d0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -30,3 +30,4 @@ pub fn read_inputs() -> impl Iterator { fn alchemy(_py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> { python::register(m) } + diff --git a/src/python.rs b/src/python.rs index 9ec5119..14ace23 100644 --- a/src/python.rs +++ b/src/python.rs @@ -174,8 +174,9 @@ pub struct PyFontanaGen { inner: RustFontanaGen } #[pymethods] impl PyFontanaGen { + /// Build a Fontana generator from config values #[staticmethod] - fn from_config( + pub fn from_config( abs_range: (f64, f64), app_range: (f64, f64), max_depth: u32, @@ -191,7 +192,10 @@ impl PyFontanaGen { PyFontanaGen { inner: RustFontanaGen::from_config(&cfg) } } - fn generate(&self) -> Option { self.inner.generate().map(|t| t.to_string()) } + /// Generate one lambda term; returns `None` when the generator yields nothing + pub fn generate(&self) -> Option { + self.inner.generate().map(|t| t.to_string()) + } } // ============ Utilities ============ diff --git a/test_alchemy.py b/test_alchemy.py index 4addacd..b7e9a73 100644 --- a/test_alchemy.py +++ b/test_alchemy.py @@ -1,80 +1,123 @@ +#!/usr/bin/env python3 +# test_alchemy.py + import sys -sys.path.append('/Users/ridhamap/Python_wrapper_alchemy/alchemy-reimplemented/alchemy_new/lib/python3.12/site-packages') -import alchemy +import traceback + +def main(): + try: + import alchemy + except Exception as e: + print("❌ Failed to import 'alchemy' Python module.") + traceback.print_exc() + sys.exit(1) + + print("✅ Imported alchemy") + + # ---------- Utilities ---------- + try: + raw = b"\x00\x01\xab\xcd" + hx = alchemy.decode_hex_py("0001abcd") + assert bytes(hx) == raw, f"decode_hex_py mismatch: {hx}" + re_hx = alchemy.encode_hex_py(list(raw)) + assert re_hx.lower() == "0001abcd", f"encode_hex_py mismatch: {re_hx}" + print("✅ Utilities: decode_hex_py / encode_hex_py") + except Exception: + print("❌ Utilities failed") + traceback.print_exc() + sys.exit(1) + + # ---------- Standardization ---------- + try: + std_prefix = alchemy.PyStandardization("prefix") + std_postfix = alchemy.PyStandardization("postfix") + std_none = alchemy.PyStandardization("none") + print("✅ PyStandardization constructed (prefix/postfix/none)") + except Exception: + print("❌ PyStandardization construction failed") + traceback.print_exc() + sys.exit(1) -def test_reactor(): - print("Testing PyReactor:") - reactor = alchemy.PyReactor() - print("PyReactor instance created successfully.") + # ---------- Generators: BTreeGen ---------- + try: + # BTreeGen::from_config(size, freevar_prob, max_free_vars, std) + bt = alchemy.PyBTreeGen.from_config( + size=6, + freevar_generation_probability=0.3, + max_free_vars=3, + std=std_prefix + ) + one = bt.generate() + many = bt.generate_n(5) + assert isinstance(one, str) and len(one) > 0, "BTreeGen.generate must return string term" + assert isinstance(many, list) and len(many) == 5 and all(isinstance(x, str) for x in many) + print("✅ PyBTreeGen.generate / generate_n OK") + except Exception: + print("❌ PyBTreeGen tests failed") + traceback.print_exc() + sys.exit(1) -def test_standardization(): - print("Testing PyStandardization:") + # ---------- Soup: basic lifecycle ---------- try: - std = alchemy.PyStandardization("prefix") - print("Standardization created with 'prefix':", std) - except ValueError as e: - print("Error in creating Standardization:", e) + # Path 1: default config + soup1 = alchemy.PySoup() + assert soup1.len() == 0, "New soup should be empty" -def test_soup(): - print("Testing PySoup:") - soup = alchemy.PySoup() - print("Empty PySoup instance created.") - print("Initial length of soup:", soup.len()) - - valid_expressions = ["λx.x", "λx.λy.xy", "(λx.x) y", "λx.λy.(yx)", "λf.λx.(f (f x))"] - soup.perturb(valid_expressions) - - print("Soup after perturbation:", soup.expressions()) - print("Unique expressions:", soup.unique_expressions()) - print("Population entropy:", soup.population_entropy()) + # Path 2: from reactor config + reactor = alchemy.PyReactor() + soup2 = alchemy.PySoup.from_config(reactor) + assert soup2.len() == 0, "Soup.from_config should start empty" + # Seed soup2 with 10 generated expressions from BTreeGen + exprs = bt.generate_n(10) + soup2.perturb(exprs) + assert soup2.len() == 10, f"After perturb, expected 10 expressions, got {soup2.len()}" -def test_btree_gen(): - print("Testing PyBTreeGen:") - # Change "postfix" to a supported type, such as "prefix" - gen = alchemy.PyBTreeGen.from_config(5, 0.5, 3, alchemy.PyStandardization("prefix")) - print("BTreeGen instance created.") - print("Generated expression:", gen.generate()) - print("Generated 3 expressions:", gen.generate_n(3)) + # Simulate a few steps, ensure the API responds + steps = soup2.simulate_for(25, False) + assert isinstance(steps, int), "simulate_for should return usize (int in Python)" + # Read back data + all_exprs = soup2.expressions() + uniq = soup2.unique_expressions() + counts = soup2.expression_counts() + ent = soup2.population_entropy() + col = soup2.collisions() + ln = soup2.len() -def test_fontana_gen(): - print("Testing PyFontanaGen:") - fontana_gen = alchemy.PyFontanaGen.from_config((0.1, 0.5), (0.2, 0.6), 5, 2) - print("FontanaGen instance created.") - result = fontana_gen.generate() - print("Generated expression from FontanaGen:", result) + assert isinstance(all_exprs, list) + assert isinstance(uniq, list) + assert isinstance(counts, list) and all( + isinstance(p, (tuple, list)) and len(p) == 2 and isinstance(p[0], str) and isinstance(p[1], int) + for p in counts + ) + assert isinstance(ent, float) + assert isinstance(col, int) + assert isinstance(ln, int) + print(f"✅ PySoup lifecycle OK | len={ln}, uniq={len(uniq)}, collisions={col}, entropy={ent:.4f}") + except Exception: + print("❌ PySoup tests failed") + traceback.print_exc() + sys.exit(1) -def test_utilities(): - print("Testing Utilities:") - encoded = alchemy.encode_hex_py([104, 101, 108, 108, 111]) - print("Encoded hex:", encoded) + # ---------- FontanaGen (may legitimately return None) ---------- try: - decoded = alchemy.decode_hex_py(encoded) - print("Decoded hex:", decoded) - except ValueError as e: - print("Error in decoding hex:", e) + fg = alchemy.PyFontanaGen.from_config( + abs_range=(0.2, 0.6), + app_range=(0.2, 0.6), + max_depth=5, + max_free_vars=3 + ) + maybe = fg.generate() + # Current Rust stub returns None; accept either None or str + assert (maybe is None) or isinstance(maybe, str) + print(f"✅ PyFontanaGen.generate OK (returned {type(maybe).__name__})") + except Exception: + print("❌ PyFontanaGen tests failed") + traceback.print_exc() + sys.exit(1) -def test_experiments(): - print("Testing Experiment Functions:") - try: - - #alchemy.run_look_for_add() - #print("run_look_for_add executed successfully.") - alchemy.run_entropy_series() - print("run_entropy_series executed successfully.") - alchemy.run_entropy_test() - print("run_entropy_test executed successfully.") - alchemy.run_sync_entropy_test() - print("run_sync_entropy_test executed successfully.") - except Exception as e: - print("Error in running experiment functions:", e) + print("\n🎉 All python.rs bindings exercised successfully.") if __name__ == "__main__": - test_reactor() - test_standardization() - test_soup() - test_btree_gen() - test_fontana_gen() - test_utilities() - test_experiments() + main() From fe0e5d08f1fe55767d2d75f2665f17a3843526b6 Mon Sep 17 00:00:00 2001 From: "ridham.patel" Date: Thu, 16 Oct 2025 18:30:05 -0700 Subject: [PATCH 12/36] Bindings + config updates: FontanaGen fields, Py wrappers, pyproject dynamic version, tests --- README.md | 31 +++++++++- pyproject.toml | 10 +++- src/config.rs | 42 +++++++------ src/generators.rs | 150 +++++++++++++++++++++++++++++++++++++++------- src/lib.rs | 13 ++-- src/main.rs | 8 +-- src/python.rs | 23 ++++--- test_alchemy.py | 78 ++++++++++++------------ 8 files changed, 258 insertions(+), 97 deletions(-) diff --git a/README.md b/README.md index c6a43e2..830d65f 100644 --- a/README.md +++ b/README.md @@ -30,4 +30,33 @@ The documentation for the configuration file is in the `Config` object. # Interactive Dashboard -Can be found here: https://github.com/mathis-group/Alchemy-Dashboard \ No newline at end of file +Can be found here: https://github.com/mathis-group/Alchemy-Dashboard + + +# Steps to run updated + +# 0) Pick & activate the EXACT Python you will use (avoid shim confusion) +# (choose one) +# — pyenv: +pyenv shell 3.11.9 + +# — OR virtualenv: +# python3.11 -m venv .venv && source .venv/bin/activate + +# 1) Show which Python we’re about to use +python -c 'import sys,platform; print(sys.executable); print(platform.python_version(), platform.machine())' + +# 2) Clean previous artifacts +cargo clean +python -m pip uninstall -y alchemy || true +rm -rf target + +# 3) Build & install the package *editable* via the PEP 517 backend (maturin) +python -m pip install -e . + +# 4) Sanity-check the import (this uses the same interpreter as above) +python - <<'PY' +import alchemy, sys +print("OK :", alchemy.__file__) +print("Py :", sys.executable) +PY diff --git a/pyproject.toml b/pyproject.toml index 08d8da4..9e91d0a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,9 +4,17 @@ build-backend = "maturin" [project] name = "alchemy" +dynamic = ["version"] # ← pull version from Cargo.toml requires-python = ">=3.7" classifiers = [ "Programming Language :: Rust", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", -] \ No newline at end of file +] + +[tool.maturin] +# maturin will use the crate name & version from Cargo.toml +# (Optional) If your Python sources live in ./python/, you can mirror your Cargo metadata: +# python-source = "python" +# (Optional) Explicit module name if you want to force it: +# module-name = "alchemy" diff --git a/src/config.rs b/src/config.rs index 1a4ccde..f45792e 100644 --- a/src/config.rs +++ b/src/config.rs @@ -107,14 +107,20 @@ pub struct BTreeGen { pub standardization: Standardization, } -/// Configuration for Fontana's generator #[warn(missing_docs)] #[derive(Serialize, Deserialize, Debug)] +/// Generator-specific configuration derived from Walter Fontana's original scheme. pub struct FontanaGen { /// The seed for the lambda expression generator. If set to `None`, then a seed is chosen /// randomly. Default: `None` pub seed: ConfigSeed, + /// Minimum depth before leaf nodes can be generated + pub min_depth: u32, + + /// Maximum depth of the generated trees + pub max_depth: u32, + /// Probability range of an abstraction being generated. Linearly changes from start to end, /// varying with depth pub abstraction_prob_range: (f64, f64), @@ -123,13 +129,27 @@ pub struct FontanaGen { /// varying with depth pub application_prob_range: (f64, f64), - /// Maximum depth of the generated trees - pub max_depth: u32, + /// Probability that a leaf vertex is a free variable + pub free_variable_probability: f64, /// Size of the free variable palette pub n_max_free_vars: u32, } +impl GenConfig for FontanaGen { + fn new() -> Self { + FontanaGen { + seed: ConfigSeed(None), + min_depth: 0, + max_depth: 10, + n_max_free_vars: 6, + application_prob_range: (0.3, 0.5), + abstraction_prob_range: (0.5, 0.3), + free_variable_probability: 0.0, + } + } +} + impl Reactor { /// Produce a new `ReactorConfig` struct with default values. pub fn new() -> Self { @@ -169,18 +189,6 @@ impl GenConfig for BTreeGen { } } -impl GenConfig for FontanaGen { - fn new() -> Self { - FontanaGen { - max_depth: 10, - n_max_free_vars: 6, - application_prob_range: (0.3, 0.5), - abstraction_prob_range: (0.5, 0.3), - seed: ConfigSeed(None), - } - } -} - impl Config { /// Create a config object from a string pub fn from_config_str(s: &str) -> Config { @@ -225,7 +233,7 @@ impl Config { /// to ser/de to/from a hex string. #[warn(missing_docs)] #[derive(Debug, Clone, Copy)] -pub struct ConfigSeed(pub Option<[u8; 32]>); +pub struct ConfigSeed(Option<[u8; 32]>); impl ConfigSeed { /// Get the seed item @@ -276,4 +284,4 @@ impl<'de> Deserialize<'de> for ConfigSeed { ConfigSeed::blank() }) } -} +} \ No newline at end of file diff --git a/src/generators.rs b/src/generators.rs index bed0c75..73bb151 100644 --- a/src/generators.rs +++ b/src/generators.rs @@ -82,12 +82,6 @@ pub struct BTreeGen { rng: ChaCha8Rng, } -impl Default for BTreeGen { - fn default() -> BTreeGen { - Self::new() - } -} - impl BTreeGen { pub fn new() -> BTreeGen { BTreeGen::from_config(&config::BTreeGen::new()) @@ -152,33 +146,147 @@ impl BTreeGen { } } -#[allow(dead_code)] pub struct FontanaGen { - abs_range: (f64, f64), - app_range: (f64, f64), - depth_cutoff: u32, - free_vars_count: u32, + min_depth: u32, + max_depth: u32, + + abs_prob: (f32, f32), + app_prob: (f32, f32), + + abs_incr: f32, + app_incr: f32, + + free_prob: f32, + max_vars: u32, seed: [u8; 32], rng: ChaCha8Rng, } impl FontanaGen { + pub fn new( + min_depth: u32, + mut max_depth: u32, + mut abs_prob: (f32, f32), + mut app_prob: (f32, f32), + mut free_prob: f32, + mut max_vars: u32, + seed: [u8; 32], + ) -> FontanaGen { + // Sanitise configuration so generation never panics. + max_depth = max_depth.max(1); + max_vars = max_vars.max(1); + free_prob = free_prob.clamp(0.0, 1.0); + abs_prob.0 = abs_prob.0.clamp(0.0, 1.0); + abs_prob.1 = abs_prob.1.clamp(0.0, 1.0); + app_prob.0 = app_prob.0.clamp(0.0, 1.0); + app_prob.1 = app_prob.1.clamp(0.0, 1.0); + + let steps = (max_depth - 1).max(1); + let abs_incr = (abs_prob.1 - abs_prob.0) / (steps as f32); + let app_incr = (app_prob.1 - app_prob.0) / (steps as f32); + + FontanaGen { + min_depth: min_depth.min(max_depth.saturating_sub(1)), + max_depth, + abs_prob, + app_prob, + abs_incr, + app_incr, + free_prob, + max_vars, + seed, + rng: ChaCha8Rng::from_seed(seed), + } + } + pub fn from_config(cfg: &config::FontanaGen) -> FontanaGen { let seed = cfg.seed.get(); - let rng = ChaCha8Rng::from_seed(seed); - FontanaGen { - abs_range: cfg.abstraction_prob_range, - app_range: cfg.application_prob_range, - depth_cutoff: cfg.max_depth, - free_vars_count: cfg.n_max_free_vars, + FontanaGen::new( + cfg.min_depth, + cfg.max_depth, + ( + cfg.abstraction_prob_range.0 as f32, + cfg.abstraction_prob_range.1 as f32, + ), + ( + cfg.application_prob_range.0 as f32, + cfg.application_prob_range.1 as f32, + ), + cfg.free_variable_probability as f32, + cfg.n_max_free_vars, seed, - rng, + ) + } + + pub fn generate(&mut self) -> Term { // <-- not Option + self.rand_lambda(0, self.abs_prob.0, self.app_prob.0) + } + + pub fn generate_n(&mut self, n: usize) -> Vec { + let mut v = Vec::with_capacity(n); + for _ in 0..n { + v.push(self.generate()) } + v } - pub fn generate(&self) -> Option { - None + pub fn seed(&self) -> [u8; 32] { + self.seed } -} + + pub fn rand_lambda(&mut self, depth: u32, p_abs: f32, p_app: f32) -> Term { + let (p_abs_eff, p_app_eff) = Self::clamp_probabilities(p_abs, p_app); + + if depth >= self.max_depth { + return self.sample_variable(depth); + } + + let next_abs = p_abs + self.abs_incr; + let next_app = p_app + self.app_incr; + + if depth < self.min_depth { + if self.rng.gen_bool(0.5) { + return Term::Abs(Box::new(self.rand_lambda(depth + 1, next_abs, next_app))); + } + return Term::App(Box::new(( + self.rand_lambda(depth + 1, next_abs, next_app), + self.rand_lambda(depth + 1, next_abs, next_app), + ))); + } + + let coin: f32 = self.rng.gen(); + if coin <= p_abs_eff { + return Term::Abs(Box::new(self.rand_lambda(depth + 1, next_abs, next_app))); + } + if coin <= p_abs_eff + p_app_eff { + return Term::App(Box::new(( + self.rand_lambda(depth + 1, next_abs, next_app), + self.rand_lambda(depth + 1, next_abs, next_app), + ))); + } + + self.sample_variable(depth) + } + + fn clamp_probabilities(p_abs: f32, p_app: f32) -> (f32, f32) { + let abs = p_abs.clamp(0.0, 1.0); + let remaining = 1.0 - abs; + let app = p_app.clamp(0.0, remaining); + (abs, app) + } + + fn sample_variable(&mut self, depth: u32) -> Term { + let free_choice = self.rng.gen_bool(self.free_prob as f64) || depth == 0; + let max_vars = self.max_vars.max(1); + let value = if free_choice { + let offset = self.rng.gen_range(1..=max_vars); + depth.saturating_add(offset) as usize + } else { + let upper = depth.max(1); + self.rng.gen_range(1..=upper) as usize + }; + Term::Var(value) + } +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index e7949d0..e1a22b5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,10 +13,17 @@ pub mod supercollider; // New Python wrapper module mod python; +#[pymodule] +fn alchemy(_py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> { + python::register(m) +} + // If you still need this for the CLI, keep it here: use lambda_calculus::{parse, term::Notation::Classic, Term}; use std::io::{self, BufRead}; + + pub fn read_inputs() -> impl Iterator { io::stdin() .lock() @@ -25,9 +32,3 @@ pub fn read_inputs() -> impl Iterator { .filter_map(|line| parse(&line, Classic).ok()) } -// Python module entry point stays tiny and delegates to python::register -#[pymodule] -fn alchemy(_py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> { - python::register(m) -} - diff --git a/src/main.rs b/src/main.rs index 66f9ad6..b886aaa 100644 --- a/src/main.rs +++ b/src/main.rs @@ -117,6 +117,7 @@ fn get_config(cli: &Cli) -> std::io::Result { Ok(config) } +// main.rs pub fn generate_expressions_and_seed_soup(cfg: &config::Config) -> lambda::recursive::LambdaSoup { let expressions = match &cfg.generator_config { config::Generator::BTree(gen_cfg) => { @@ -124,10 +125,8 @@ pub fn generate_expressions_and_seed_soup(cfg: &config::Config) -> lambda::recur gen.generate_n(cfg.sample_size) } config::Generator::Fontana(gen_cfg) => { - let gen = generators::FontanaGen::from_config(gen_cfg); - std::iter::from_fn(move || gen.generate()) - .take(cfg.sample_size) - .collect::>() + let mut gen = generators::FontanaGen::from_config(gen_cfg); + gen.generate_n(cfg.sample_size) // ← returns Vec } }; let mut soup = lambda::recursive::LambdaSoup::from_config(&cfg.reactor_config); @@ -135,6 +134,7 @@ pub fn generate_expressions_and_seed_soup(cfg: &config::Config) -> lambda::recur soup } + fn main() -> std::io::Result<()> { let cli = Cli::parse(); diff --git a/src/python.rs b/src/python.rs index 14ace23..f835a3a 100644 --- a/src/python.rs +++ b/src/python.rs @@ -3,7 +3,7 @@ use pyo3::prelude::*; use pyo3::wrap_pyfunction; use serde::{Deserialize, Serialize}; -use lambda_calculus::{parse, term::Notation::Classic, Term}; +use lambda_calculus::{parse, term::Notation::Classic}; use crate::config::{self, ConfigSeed, Reactor as RustReactor}; use crate::generators::{ @@ -157,7 +157,7 @@ impl PyBTreeGen { freevar_generation_probability, n_max_free_vars: max_free_vars, standardization: std.into(), - seed: ConfigSeed(Some([0; 32])), + seed:ConfigSeed::new([0; 32]), }; PyBTreeGen { inner: RustBTreeGen::from_config(&cfg) } } @@ -174,27 +174,36 @@ pub struct PyFontanaGen { inner: RustFontanaGen } #[pymethods] impl PyFontanaGen { - /// Build a Fontana generator from config values + /// Build a Fontana generator from config values (matches the new struct) #[staticmethod] pub fn from_config( abs_range: (f64, f64), app_range: (f64, f64), + min_depth: u32, max_depth: u32, + free_variable_probability: f64, max_free_vars: u32, ) -> Self { let cfg = config::FontanaGen { abstraction_prob_range: abs_range, application_prob_range: app_range, + min_depth, max_depth, + free_variable_probability, n_max_free_vars: max_free_vars, - seed: ConfigSeed(Some([0; 32])), + seed: ConfigSeed::new([0; 32]), }; PyFontanaGen { inner: RustFontanaGen::from_config(&cfg) } } - /// Generate one lambda term; returns `None` when the generator yields nothing - pub fn generate(&self) -> Option { - self.inner.generate().map(|t| t.to_string()) + /// Generate a single lambda term (now always returns a term) + pub fn generate(&mut self) -> String { + self.inner.generate().to_string() + } + + /// Convenience: generate N terms + pub fn generate_n(&mut self, n: usize) -> Vec { + self.inner.generate_n(n).into_iter().map(|t| t.to_string()).collect() } } diff --git a/test_alchemy.py b/test_alchemy.py index b7e9a73..9f1f03d 100644 --- a/test_alchemy.py +++ b/test_alchemy.py @@ -4,15 +4,23 @@ import sys import traceback + +def die(msg: str): + print(msg) + sys.exit(1) + + def main(): + # ---------- Import ---------- try: import alchemy - except Exception as e: - print("❌ Failed to import 'alchemy' Python module.") - traceback.print_exc() - sys.exit(1) - - print("✅ Imported alchemy") + import inspect + print("✅ Imported alchemy") + print(" module:", getattr(alchemy, "__file__", "")) + print(" PyFontanaGen.from_config signature:", + inspect.signature(alchemy.PyFontanaGen.from_config)) + except Exception: + die("❌ Failed to import 'alchemy'\n" + traceback.format_exc()) # ---------- Utilities ---------- try: @@ -21,63 +29,53 @@ def main(): assert bytes(hx) == raw, f"decode_hex_py mismatch: {hx}" re_hx = alchemy.encode_hex_py(list(raw)) assert re_hx.lower() == "0001abcd", f"encode_hex_py mismatch: {re_hx}" - print("✅ Utilities: decode_hex_py / encode_hex_py") + print("✅ Utilities OK (decode_hex_py / encode_hex_py)") except Exception: - print("❌ Utilities failed") - traceback.print_exc() - sys.exit(1) + die("❌ Utilities failed\n" + traceback.format_exc()) # ---------- Standardization ---------- try: std_prefix = alchemy.PyStandardization("prefix") std_postfix = alchemy.PyStandardization("postfix") std_none = alchemy.PyStandardization("none") + for s in (std_prefix, std_postfix, std_none): + assert s is not None print("✅ PyStandardization constructed (prefix/postfix/none)") except Exception: - print("❌ PyStandardization construction failed") - traceback.print_exc() - sys.exit(1) + die("❌ PyStandardization construction failed\n" + traceback.format_exc()) # ---------- Generators: BTreeGen ---------- try: - # BTreeGen::from_config(size, freevar_prob, max_free_vars, std) bt = alchemy.PyBTreeGen.from_config( size=6, freevar_generation_probability=0.3, max_free_vars=3, - std=std_prefix + std=std_prefix, ) one = bt.generate() many = bt.generate_n(5) - assert isinstance(one, str) and len(one) > 0, "BTreeGen.generate must return string term" - assert isinstance(many, list) and len(many) == 5 and all(isinstance(x, str) for x in many) + assert isinstance(one, str) and len(one) > 0, "BTreeGen.generate must return a non-empty string" + assert isinstance(many, list) and len(many) == 5 and all(isinstance(x, str) and x for x in many) print("✅ PyBTreeGen.generate / generate_n OK") except Exception: - print("❌ PyBTreeGen tests failed") - traceback.print_exc() - sys.exit(1) + die("❌ PyBTreeGen tests failed\n" + traceback.format_exc()) - # ---------- Soup: basic lifecycle ---------- + # ---------- Soup lifecycle ---------- try: - # Path 1: default config soup1 = alchemy.PySoup() assert soup1.len() == 0, "New soup should be empty" - # Path 2: from reactor config reactor = alchemy.PyReactor() soup2 = alchemy.PySoup.from_config(reactor) assert soup2.len() == 0, "Soup.from_config should start empty" - # Seed soup2 with 10 generated expressions from BTreeGen exprs = bt.generate_n(10) soup2.perturb(exprs) assert soup2.len() == 10, f"After perturb, expected 10 expressions, got {soup2.len()}" - # Simulate a few steps, ensure the API responds steps = soup2.simulate_for(25, False) - assert isinstance(steps, int), "simulate_for should return usize (int in Python)" + assert isinstance(steps, int) - # Read back data all_exprs = soup2.expressions() uniq = soup2.unique_expressions() counts = soup2.expression_counts() @@ -96,28 +94,28 @@ def main(): assert isinstance(ln, int) print(f"✅ PySoup lifecycle OK | len={ln}, uniq={len(uniq)}, collisions={col}, entropy={ent:.4f}") except Exception: - print("❌ PySoup tests failed") - traceback.print_exc() - sys.exit(1) + die("❌ PySoup tests failed\n" + traceback.format_exc()) - # ---------- FontanaGen (may legitimately return None) ---------- + # ---------- FontanaGen (new API + always returns str) ---------- try: fg = alchemy.PyFontanaGen.from_config( abs_range=(0.2, 0.6), app_range=(0.2, 0.6), - max_depth=5, - max_free_vars=3 + min_depth=1, # test minimum nesting before allowing variables + max_depth=5, # cap depth growth + free_variable_probability=0.25, + max_free_vars=3, ) - maybe = fg.generate() - # Current Rust stub returns None; accept either None or str - assert (maybe is None) or isinstance(maybe, str) - print(f"✅ PyFontanaGen.generate OK (returned {type(maybe).__name__})") + term = fg.generate() + assert isinstance(term, str) and term, "FontanaGen.generate must return a non-empty string" + terms = fg.generate_n(5) + assert isinstance(terms, list) and len(terms) == 5 and all(isinstance(t, str) and t for t in terms) + print("✅ PyFontanaGen.from_config / generate / generate_n OK") except Exception: - print("❌ PyFontanaGen tests failed") - traceback.print_exc() - sys.exit(1) + die("❌ PyFontanaGen tests failed\n" + traceback.format_exc()) print("\n🎉 All python.rs bindings exercised successfully.") + if __name__ == "__main__": main() From 302f800e7b22550f5faa60053a69c1aedb8b856a Mon Sep 17 00:00:00 2001 From: Cole Mathis Date: Thu, 23 Oct 2025 14:29:35 -0700 Subject: [PATCH 13/36] Remove plotters dependency --- Cargo.lock | 599 ----------------------------------------------------- Cargo.toml | 1 - 2 files changed, 600 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4df5bda..0068620 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -25,7 +25,6 @@ dependencies = [ "clap", "futures", "lambda_calculus", - "plotters", "pyo3", "pyo3-build-config", "rand", @@ -35,15 +34,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - [[package]] name = "anstream" version = "0.6.15" @@ -293,47 +283,18 @@ version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" -[[package]] -name = "bytemuck" -version = "1.23.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3995eaeebcdf32f91f980d360f78732ddc061097ab4e39991ae7a6ace9194677" - [[package]] name = "byteorder" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" -[[package]] -name = "cc" -version = "1.2.38" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80f41ae168f955c12fb8960b057d70d0ca153fb83182b57d86380443527be7e9" -dependencies = [ - "find-msvc-tools", - "shlex", -] - [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -[[package]] -name = "chrono" -version = "0.4.42" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" -dependencies = [ - "iana-time-zone", - "js-sys", - "num-traits", - "wasm-bindgen", - "windows-link", -] - [[package]] name = "clap" version = "4.5.15" @@ -374,12 +335,6 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" -[[package]] -name = "color_quant" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" - [[package]] name = "colorchoice" version = "1.0.2" @@ -395,115 +350,12 @@ dependencies = [ "crossbeam-utils", ] -[[package]] -name = "core-foundation" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" - -[[package]] -name = "core-graphics" -version = "0.23.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081" -dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "core-graphics-types", - "foreign-types", - "libc", -] - -[[package]] -name = "core-graphics-types" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" -dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "libc", -] - -[[package]] -name = "core-text" -version = "20.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9d2790b5c08465d49f8dc05c8bcae9fea467855947db39b0f8145c091aaced5" -dependencies = [ - "core-foundation", - "core-graphics", - "foreign-types", - "libc", -] - -[[package]] -name = "crc32fast" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" -dependencies = [ - "cfg-if", -] - [[package]] name = "crossbeam-utils" version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" -[[package]] -name = "dirs" -version = "6.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" -dependencies = [ - "dirs-sys", -] - -[[package]] -name = "dirs-sys" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" -dependencies = [ - "libc", - "option-ext", - "redox_users", - "windows-sys 0.59.0", -] - -[[package]] -name = "dlib" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" -dependencies = [ - "libloading", -] - -[[package]] -name = "dwrote" -version = "0.11.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20c93d234bac0cdd0e2ac08bc8a5133f8df2169e95b262dfcea5e5cb7855672f" -dependencies = [ - "lazy_static", - "libc", - "winapi", - "wio", -] - [[package]] name = "errno" version = "0.3.9" @@ -556,100 +408,6 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" -[[package]] -name = "fdeflate" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" -dependencies = [ - "simd-adler32", -] - -[[package]] -name = "find-msvc-tools" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ced73b1dacfc750a6db6c0a0c3a3853c8b41997e2e2c563dc90804ae6867959" - -[[package]] -name = "flate2" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d" -dependencies = [ - "crc32fast", - "miniz_oxide", -] - -[[package]] -name = "float-ord" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce81f49ae8a0482e4c55ea62ebbd7e5a686af544c00b9d090bba3ff9be97b3d" - -[[package]] -name = "font-kit" -version = "0.14.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c7e611d49285d4c4b2e1727b72cf05353558885cc5252f93707b845dfcaf3d3" -dependencies = [ - "bitflags 2.6.0", - "byteorder", - "core-foundation", - "core-graphics", - "core-text", - "dirs", - "dwrote", - "float-ord", - "freetype-sys", - "lazy_static", - "libc", - "log", - "pathfinder_geometry", - "pathfinder_simd", - "walkdir", - "winapi", - "yeslogic-fontconfig-sys", -] - -[[package]] -name = "foreign-types" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" -dependencies = [ - "foreign-types-macros", - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-macros" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "foreign-types-shared" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" - -[[package]] -name = "freetype-sys" -version = "0.20.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e7edc5b9669349acfda99533e9e0bcf26a51862ab43b08ee7745c55d28eb134" -dependencies = [ - "cc", - "libc", - "pkg-config", -] - [[package]] name = "futures" version = "0.3.30" @@ -778,16 +536,6 @@ dependencies = [ "wasi", ] -[[package]] -name = "gif" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80792593675e051cf94a4b111980da2ba60d4a83e43e0048c5693baab3977045" -dependencies = [ - "color_quant", - "weezl", -] - [[package]] name = "gimli" version = "0.31.1" @@ -824,44 +572,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" -[[package]] -name = "iana-time-zone" -version = "0.1.64" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "log", - "wasm-bindgen", - "windows-core", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" -dependencies = [ - "cc", -] - -[[package]] -name = "image" -version = "0.24.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5690139d2f55868e080017335e4b94cb7414274c74f1669c84fb5feba2c9f69d" -dependencies = [ - "bytemuck", - "byteorder", - "color_quant", - "jpeg-decoder", - "num-traits", - "png", -] - [[package]] name = "indoc" version = "2.0.6" @@ -911,12 +621,6 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" -[[package]] -name = "jpeg-decoder" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00810f1d8b74be64b13dbf3db89ac67740615d6c891f0e7b6179326533011a07" - [[package]] name = "js-sys" version = "0.3.70" @@ -940,38 +644,12 @@ name = "lambda_calculus" version = "3.3.0" source = "git+https://github.com/agentelement/lambda_calculus?branch=size-feat#8346381fc0df29f3ed5022913d58d3a5244c22e7" -[[package]] -name = "lazy_static" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" - [[package]] name = "libc" version = "0.2.176" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "58f929b4d672ea937a23a1ab494143d968337a5f47e56d0815df1e0890ddf174" -[[package]] -name = "libloading" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" -dependencies = [ - "cfg-if", - "windows-link", -] - -[[package]] -name = "libredox" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb" -dependencies = [ - "bitflags 2.6.0", - "libc", -] - [[package]] name = "linux-raw-sys" version = "0.3.8" @@ -1015,7 +693,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" dependencies = [ "adler2", - "simd-adler32", ] [[package]] @@ -1029,15 +706,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "num-traits" -version = "0.2.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" -dependencies = [ - "autocfg", -] - [[package]] name = "object" version = "0.36.7" @@ -1053,37 +721,12 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" -[[package]] -name = "option-ext" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" - [[package]] name = "parking" version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" -[[package]] -name = "pathfinder_geometry" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b7e7b4ea703700ce73ebf128e1450eb69c3a8329199ffbfb9b2a0418e5ad3" -dependencies = [ - "log", - "pathfinder_simd", -] - -[[package]] -name = "pathfinder_simd" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf9027960355bf3afff9841918474a81a5f972ac6d226d518060bba758b5ad57" -dependencies = [ - "rustc_version", -] - [[package]] name = "pin-project-lite" version = "0.2.14" @@ -1107,71 +750,6 @@ dependencies = [ "futures-io", ] -[[package]] -name = "pkg-config" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" - -[[package]] -name = "plotters" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747" -dependencies = [ - "chrono", - "font-kit", - "image", - "lazy_static", - "num-traits", - "pathfinder_geometry", - "plotters-backend", - "plotters-bitmap", - "plotters-svg", - "ttf-parser", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "plotters-backend" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" - -[[package]] -name = "plotters-bitmap" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72ce181e3f6bf82d6c1dc569103ca7b1bd964c60ba03d7e6cdfbb3e3eb7f7405" -dependencies = [ - "gif", - "image", - "plotters-backend", -] - -[[package]] -name = "plotters-svg" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670" -dependencies = [ - "plotters-backend", -] - -[[package]] -name = "png" -version = "0.17.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526" -dependencies = [ - "bitflags 1.3.2", - "crc32fast", - "fdeflate", - "flate2", - "miniz_oxide", -] - [[package]] name = "polling" version = "2.8.0" @@ -1329,32 +907,12 @@ dependencies = [ "getrandom", ] -[[package]] -name = "redox_users" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" -dependencies = [ - "getrandom", - "libredox", - "thiserror", -] - [[package]] name = "rustc-demangle" version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" -[[package]] -name = "rustc_version" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" -dependencies = [ - "semver", -] - [[package]] name = "rustix" version = "0.37.27" @@ -1388,21 +946,6 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "semver" -version = "1.0.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" - [[package]] name = "serde" version = "1.0.207" @@ -1435,18 +978,6 @@ dependencies = [ "serde", ] -[[package]] -name = "shlex" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" - -[[package]] -name = "simd-adler32" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" - [[package]] name = "slab" version = "0.4.9" @@ -1489,26 +1020,6 @@ version = "0.12.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" -[[package]] -name = "thiserror" -version = "2.0.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "2.0.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "tokio" version = "1.47.1" @@ -1539,12 +1050,6 @@ version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" -[[package]] -name = "ttf-parser" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17f77d76d837a7830fe1d4f12b7b4ba4192c1888001c7164257e4bc6d21d96b4" - [[package]] name = "unicode-ident" version = "1.0.12" @@ -1575,16 +1080,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "317211a0dc0ceedd78fb2ca9a44aed3d7b9b26f81870d485c07122b4350673b7" -[[package]] -name = "walkdir" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" -dependencies = [ - "same-file", - "winapi-util", -] - [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -1668,12 +1163,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "weezl" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a751b3277700db47d3e574514de2eced5e54dc8a5436a3bf7a0b248b2cee16f3" - [[package]] name = "winapi" version = "0.3.9" @@ -1690,80 +1179,12 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" -[[package]] -name = "winapi-util" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" -dependencies = [ - "windows-sys 0.59.0", -] - [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows-core" -version = "0.62.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57fe7168f7de578d2d8a05b07fd61870d2e73b4020e9f49aa00da8471723497c" -dependencies = [ - "windows-implement", - "windows-interface", - "windows-link", - "windows-result", - "windows-strings", -] - -[[package]] -name = "windows-implement" -version = "0.60.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "windows-interface" -version = "0.59.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "windows-link" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65" - -[[package]] -name = "windows-result" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7084dcc306f89883455a206237404d3eaf961e5bd7e0f312f7c91f57eb44167f" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-strings" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7218c655a553b0bed4426cf54b20d7ba363ef543b52d515b3e48d7fd55318dda" -dependencies = [ - "windows-link", -] - [[package]] name = "windows-sys" version = "0.48.0" @@ -1912,26 +1333,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" -[[package]] -name = "wio" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d129932f4644ac2396cb456385cbf9e63b5b30c6e8dc4820bdca4eb082037a5" -dependencies = [ - "winapi", -] - -[[package]] -name = "yeslogic-fontconfig-sys" -version = "6.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "503a066b4c037c440169d995b869046827dbc71263f6e8f3be6d77d4f3229dbd" -dependencies = [ - "dlib", - "once_cell", - "pkg-config", -] - [[package]] name = "zerocopy" version = "0.7.35" diff --git a/Cargo.toml b/Cargo.toml index 94c62ca..b00535a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,6 @@ async-std = "1.12.0" clap = { version = "4.5.4", features = ["derive"] } futures = "0.3.30" lambda_calculus = { git = "https://github.com/agentelement/lambda_calculus", branch = "size-feat" } -plotters = "0.3.6" rand = "0.8" rand_chacha = "0.3.1" serde = { version = "1.0.202", features = ["derive"] } From e17498018b58b98f910ffa696032d9880d919936 Mon Sep 17 00:00:00 2001 From: Cole Mathis Date: Thu, 23 Oct 2025 14:38:38 -0700 Subject: [PATCH 14/36] trying to debug the fmt error --- src/main.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index b886aaa..aa268ba 100644 --- a/src/main.rs +++ b/src/main.rs @@ -134,7 +134,6 @@ pub fn generate_expressions_and_seed_soup(cfg: &config::Config) -> lambda::recur soup } - fn main() -> std::io::Result<()> { let cli = Cli::parse(); From 6b05cfed28d080c4a5f65434dd75692a0368a890 Mon Sep 17 00:00:00 2001 From: Cole Mathis Date: Thu, 23 Oct 2025 14:40:56 -0700 Subject: [PATCH 15/36] Updating lib file, should not have Cli in it --- src/lib.rs | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index e1a22b5..74882e8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,13 +2,13 @@ use pyo3::prelude::*; // Re-export your Rust modules for the CLI and for external users +pub mod analysis; pub mod config; pub mod experiments; pub mod generators; -pub mod utils; -pub mod analysis; pub mod lambda; pub mod supercollider; +pub mod utils; // New Python wrapper module mod python; @@ -18,17 +18,3 @@ fn alchemy(_py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> { python::register(m) } -// If you still need this for the CLI, keep it here: -use lambda_calculus::{parse, term::Notation::Classic, Term}; -use std::io::{self, BufRead}; - - - -pub fn read_inputs() -> impl Iterator { - io::stdin() - .lock() - .lines() - .filter_map(|line| line.ok()) - .filter_map(|line| parse(&line, Classic).ok()) -} - From 140c5b0f56108c283b23a18914c171414ac436c7 Mon Sep 17 00:00:00 2001 From: Cole Mathis Date: Thu, 23 Oct 2025 14:49:36 -0700 Subject: [PATCH 16/36] modify cargo to allow build, this probably breaks maturin --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index b00535a..c6fed8b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" [lib] name = "alchemy" path = "src/lib.rs" -crate-type = ["cdylib", "rlib"] +crate-type = ["rlib"] [package.metadata.maturin] name = "alchemy" From 3e4c3cefc5c0a6c2dc9f74d8cfb80e28199592a2 Mon Sep 17 00:00:00 2001 From: Cole Mathis Date: Thu, 23 Oct 2025 14:51:58 -0700 Subject: [PATCH 17/36] fmt --- src/config.rs | 2 +- src/generators.rs | 5 ++- src/lib.rs | 1 - src/python.rs | 105 ++++++++++++++++++++++++++++++++++++---------- 4 files changed, 86 insertions(+), 27 deletions(-) diff --git a/src/config.rs b/src/config.rs index f45792e..318580c 100644 --- a/src/config.rs +++ b/src/config.rs @@ -284,4 +284,4 @@ impl<'de> Deserialize<'de> for ConfigSeed { ConfigSeed::blank() }) } -} \ No newline at end of file +} diff --git a/src/generators.rs b/src/generators.rs index 73bb151..776aa35 100644 --- a/src/generators.rs +++ b/src/generators.rs @@ -220,7 +220,8 @@ impl FontanaGen { ) } - pub fn generate(&mut self) -> Term { // <-- not Option + pub fn generate(&mut self) -> Term { + // <-- not Option self.rand_lambda(0, self.abs_prob.0, self.app_prob.0) } @@ -289,4 +290,4 @@ impl FontanaGen { }; Term::Var(value) } -} \ No newline at end of file +} diff --git a/src/lib.rs b/src/lib.rs index 74882e8..28b8923 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,4 +17,3 @@ mod python; fn alchemy(_py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> { python::register(m) } - diff --git a/src/python.rs b/src/python.rs index f835a3a..45159d0 100644 --- a/src/python.rs +++ b/src/python.rs @@ -66,7 +66,9 @@ pub struct PyReactor { impl PyReactor { #[new] fn new() -> Self { - PyReactor { inner: RustReactor::new() } + PyReactor { + inner: RustReactor::new(), + } } } @@ -86,14 +88,20 @@ impl PyStandardization { "prefix" => RustStandardization::Prefix, "postfix" => RustStandardization::Postfix, "none" => RustStandardization::None, - _ => return Err(pyo3::exceptions::PyValueError::new_err("Invalid standardization type")), + _ => { + return Err(pyo3::exceptions::PyValueError::new_err( + "Invalid standardization type", + )) + } }; Ok(PyStandardization { standardization }) } } impl From for RustStandardization { - fn from(py_std: PyStandardization) -> Self { py_std.standardization } + fn from(py_std: PyStandardization) -> Self { + py_std.standardization + } } // ============ Soup wrapper ============ @@ -106,44 +114,77 @@ pub struct PySoup { #[pymethods] impl PySoup { #[new] - fn new() -> Self { PySoup { inner: RustSoup::new() } } + fn new() -> Self { + PySoup { + inner: RustSoup::new(), + } + } #[staticmethod] fn from_config(cfg: &PyReactor) -> Self { - PySoup { inner: RustSoup::from_config(&cfg.inner) } + PySoup { + inner: RustSoup::from_config(&cfg.inner), + } } fn perturb(&mut self, expressions: Vec) -> PyResult<()> { - let terms = expressions.into_iter().filter_map(|s| parse(&s, Classic).ok()); + let terms = expressions + .into_iter() + .filter_map(|s| parse(&s, Classic).ok()); self.inner.add_lambda_expressions(terms); Ok(()) } - fn simulate_for(&mut self, n: usize, log: bool) -> usize { self.inner.simulate_for(n, log) } - fn len(&self) -> usize { self.inner.len() } - fn collisions(&self) -> usize { self.inner.collisions() } + fn simulate_for(&mut self, n: usize, log: bool) -> usize { + self.inner.simulate_for(n, log) + } + fn len(&self) -> usize { + self.inner.len() + } + fn collisions(&self) -> usize { + self.inner.collisions() + } fn expressions(&self) -> Vec { - self.inner.lambda_expressions().map(|t| t.to_string()).collect() + self.inner + .lambda_expressions() + .map(|t| t.to_string()) + .collect() } fn unique_expressions(&self) -> Vec { - self.inner.unique_expressions().into_iter().map(|t| t.to_string()).collect() + self.inner + .unique_expressions() + .into_iter() + .map(|t| t.to_string()) + .collect() } fn expression_counts(&self) -> Vec<(String, u32)> { - self.inner.expression_counts().into_iter().map(|(t, c)| (t.to_string(), c)).collect() + self.inner + .expression_counts() + .into_iter() + .map(|(t, c)| (t.to_string(), c)) + .collect() + } + fn population_entropy(&self) -> f32 { + self.inner.population_entropy() } - fn population_entropy(&self) -> f32 { self.inner.population_entropy() } } // ============ Generators ============ #[pyclass] -pub struct PyBTreeGen { inner: RustBTreeGen } +pub struct PyBTreeGen { + inner: RustBTreeGen, +} #[pymethods] impl PyBTreeGen { #[new] - fn new() -> Self { PyBTreeGen { inner: RustBTreeGen::new() } } + fn new() -> Self { + PyBTreeGen { + inner: RustBTreeGen::new(), + } + } #[staticmethod] fn from_config( @@ -157,20 +198,30 @@ impl PyBTreeGen { freevar_generation_probability, n_max_free_vars: max_free_vars, standardization: std.into(), - seed:ConfigSeed::new([0; 32]), + seed: ConfigSeed::new([0; 32]), }; - PyBTreeGen { inner: RustBTreeGen::from_config(&cfg) } + PyBTreeGen { + inner: RustBTreeGen::from_config(&cfg), + } } - fn generate(&mut self) -> String { self.inner.generate().to_string() } + fn generate(&mut self) -> String { + self.inner.generate().to_string() + } fn generate_n(&mut self, n: usize) -> Vec { - self.inner.generate_n(n).into_iter().map(|t| t.to_string()).collect() + self.inner + .generate_n(n) + .into_iter() + .map(|t| t.to_string()) + .collect() } } #[pyclass] -pub struct PyFontanaGen { inner: RustFontanaGen } +pub struct PyFontanaGen { + inner: RustFontanaGen, +} #[pymethods] impl PyFontanaGen { @@ -193,7 +244,9 @@ impl PyFontanaGen { n_max_free_vars: max_free_vars, seed: ConfigSeed::new([0; 32]), }; - PyFontanaGen { inner: RustFontanaGen::from_config(&cfg) } + PyFontanaGen { + inner: RustFontanaGen::from_config(&cfg), + } } /// Generate a single lambda term (now always returns a term) @@ -203,7 +256,11 @@ impl PyFontanaGen { /// Convenience: generate N terms pub fn generate_n(&mut self, n: usize) -> Vec { - self.inner.generate_n(n).into_iter().map(|t| t.to_string()).collect() + self.inner + .generate_n(n) + .into_iter() + .map(|t| t.to_string()) + .collect() } } @@ -215,7 +272,9 @@ fn decode_hex_py(hex_string: &str) -> PyResult> { } #[pyfunction] -fn encode_hex_py(bytes: Vec) -> String { encode_hex(&bytes) } +fn encode_hex_py(bytes: Vec) -> String { + encode_hex(&bytes) +} // ============ Public registration hook ============ From f624dfed4327a03d1d68dd96f2900ccadfdf2e59 Mon Sep 17 00:00:00 2001 From: Cole Mathis Date: Thu, 23 Oct 2025 14:53:11 -0700 Subject: [PATCH 18/36] Fmt experiments files --- src/experiments/kinetics.rs | 9 +++++++-- src/experiments/search_by_behavior.rs | 4 ++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/experiments/kinetics.rs b/src/experiments/kinetics.rs index 6f0087c..5709060 100644 --- a/src/experiments/kinetics.rs +++ b/src/experiments/kinetics.rs @@ -134,8 +134,13 @@ pub fn kinetic_succ_experiment() { let mut futures = FuturesUnordered::new(); let sample_size = 5000; - let good_fracs = [0.0, 0.0002, 0.0004, 0.0008, 0.0016, 0.0032, 0.0064, 0.0128, 0.0256, 0.0512, 0.1024]; - let test_fracs = [0.0, 0.05, 0.10, 0.15, 0.20, 0.25, 0.30, 0.35, 0.40, 0.45, 0.50, 0.55, 0.60, 0.65, 0.70, 0.75, 0.80]; + let good_fracs = [ + 0.0, 0.0002, 0.0004, 0.0008, 0.0016, 0.0032, 0.0064, 0.0128, 0.0256, 0.0512, 0.1024, + ]; + let test_fracs = [ + 0.0, 0.05, 0.10, 0.15, 0.20, 0.25, 0.30, 0.35, 0.40, 0.45, 0.50, 0.55, 0.60, 0.65, 0.70, + 0.75, 0.80, + ]; for (i, good_frac) in good_fracs.iter().enumerate() { for (j, test_frac) in test_fracs.iter().enumerate() { diff --git a/src/experiments/search_by_behavior.rs b/src/experiments/search_by_behavior.rs index 07794f3..e180b28 100644 --- a/src/experiments/search_by_behavior.rs +++ b/src/experiments/search_by_behavior.rs @@ -127,7 +127,7 @@ pub fn look_for_xorset() { i, run_length, polling_interval, - xorset_test + xorset_test, ))); } @@ -156,7 +156,7 @@ pub fn look_for_not_xorset() { i, run_length, polling_interval, - not_xorset_test + not_xorset_test, ))); } From 3fafcbfbf092f683085fc478b1e685e7b7bd2df5 Mon Sep 17 00:00:00 2001 From: "ridham.patel" Date: Thu, 30 Oct 2025 10:53:59 -0700 Subject: [PATCH 19/36] Fix PyReactionError dead_code warning and update Cargo.toml --- Cargo.toml | 2 +- src/python.rs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index c6fed8b..50741a6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" [lib] name = "alchemy" path = "src/lib.rs" -crate-type = ["rlib"] +crate-type = ["rlib","cdylib"] [package.metadata.maturin] name = "alchemy" diff --git a/src/python.rs b/src/python.rs index 45159d0..a7a60a6 100644 --- a/src/python.rs +++ b/src/python.rs @@ -23,6 +23,7 @@ type RustSoup = #[pyclass] #[derive(Debug, Clone)] +#[allow(dead_code)] pub struct PyReactionError { kind: ReactionErrorKind, } From a9799a38bb1e1d90c9092304aae63355526825e6 Mon Sep 17 00:00:00 2001 From: jjoseph12 <72823288+jjoseph12@users.noreply.github.com> Date: Thu, 30 Oct 2025 14:17:53 -0700 Subject: [PATCH 20/36] Fix clippy warnings and restore cargo test --- Cargo.toml | 2 +- build.rs | 3 +++ src/generators.rs | 6 ++++++ src/python.rs | 25 ++++++++++++++++++++++++- uv.lock | 7 +++++++ 5 files changed, 41 insertions(+), 2 deletions(-) create mode 100644 build.rs create mode 100644 uv.lock diff --git a/Cargo.toml b/Cargo.toml index c6fed8b..b00535a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" [lib] name = "alchemy" path = "src/lib.rs" -crate-type = ["rlib"] +crate-type = ["cdylib", "rlib"] [package.metadata.maturin] name = "alchemy" diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..dace4a9 --- /dev/null +++ b/build.rs @@ -0,0 +1,3 @@ +fn main() { + pyo3_build_config::add_extension_module_link_args(); +} diff --git a/src/generators.rs b/src/generators.rs index 776aa35..e5eeada 100644 --- a/src/generators.rs +++ b/src/generators.rs @@ -146,6 +146,12 @@ impl BTreeGen { } } +impl Default for BTreeGen { + fn default() -> Self { + Self::new() + } +} + pub struct FontanaGen { min_depth: u32, max_depth: u32, diff --git a/src/python.rs b/src/python.rs index 45159d0..8df4809 100644 --- a/src/python.rs +++ b/src/python.rs @@ -27,7 +27,7 @@ pub struct PyReactionError { kind: ReactionErrorKind, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Copy)] pub enum ReactionErrorKind { ExceedsReductionLimit, NotEnoughExpressions, @@ -39,6 +39,29 @@ pub enum ReactionErrorKind { BadArgument, } +impl ReactionErrorKind { + fn as_str(&self) -> &'static str { + match self { + ReactionErrorKind::ExceedsReductionLimit => "exceeds_reduction_limit", + ReactionErrorKind::NotEnoughExpressions => "not_enough_expressions", + ReactionErrorKind::IsIdentity => "is_identity", + ReactionErrorKind::IsParent => "is_parent", + ReactionErrorKind::HasFreeVariables => "has_free_variables", + ReactionErrorKind::ExceedsDepthLimit => "exceeds_depth_limit", + ReactionErrorKind::RecursiveArgument => "recursive_argument", + ReactionErrorKind::BadArgument => "bad_argument", + } + } +} + +#[pymethods] +impl PyReactionError { + #[getter] + fn kind(&self) -> &'static str { + self.kind.as_str() + } +} + impl From for PyReactionError { fn from(error: LambdaCollisionError) -> Self { let kind = match error { diff --git a/uv.lock b/uv.lock new file mode 100644 index 0000000..7c4d26c --- /dev/null +++ b/uv.lock @@ -0,0 +1,7 @@ +version = 1 +revision = 3 +requires-python = ">=3.7" + +[[package]] +name = "alchemy" +source = { editable = "." } From d81bb8fba995c4fe4c9434b358febc5a71dfb778 Mon Sep 17 00:00:00 2001 From: jjoseph12 <72823288+jjoseph12@users.noreply.github.com> Date: Thu, 30 Oct 2025 14:35:47 -0700 Subject: [PATCH 21/36] Fix remaining Clippy warnings --- src/config.rs | 6 ++++++ src/python.rs | 4 ++-- src/utils.rs | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/config.rs b/src/config.rs index 318580c..d3acffe 100644 --- a/src/config.rs +++ b/src/config.rs @@ -229,6 +229,12 @@ impl Config { } } +impl Default for Config { + fn default() -> Self { + Self::new() + } +} + /// Represents a seed for serde RNGs in the configuration file. Mostly here because we want /// to ser/de to/from a hex string. #[warn(missing_docs)] diff --git a/src/python.rs b/src/python.rs index 8df4809..1eca2b1 100644 --- a/src/python.rs +++ b/src/python.rs @@ -150,12 +150,11 @@ impl PySoup { } } - fn perturb(&mut self, expressions: Vec) -> PyResult<()> { + fn perturb(&mut self, expressions: Vec) { let terms = expressions .into_iter() .filter_map(|s| parse(&s, Classic).ok()); self.inner.add_lambda_expressions(terms); - Ok(()) } fn simulate_for(&mut self, n: usize, log: bool) -> usize { @@ -289,6 +288,7 @@ impl PyFontanaGen { // ============ Utilities ============ +#[allow(clippy::useless_conversion)] #[pyfunction] fn decode_hex_py(hex_string: &str) -> PyResult> { decode_hex(hex_string).map_err(|e| pyo3::exceptions::PyValueError::new_err(e.to_string())) diff --git a/src/utils.rs b/src/utils.rs index 70c17ef..d844fe2 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -8,7 +8,7 @@ use std::io::{self, BufRead, BufReader, Write}; // This was shamelessly stolen from // https://play.rust-lang.org/?version=stable&mode=debug&edition=2015&gist=e241493d100ecaadac3c99f37d0f766f pub fn decode_hex(s: &str) -> Result, DecodeHexError> { - if s.len() % 2 != 0 { + if s.len() & 1 == 1 { Err(DecodeHexError::OddLength) } else { (0..s.len()) From 29dc1d652dac042a2bbb81c9333172e4e0a79dee Mon Sep 17 00:00:00 2001 From: jjoseph12 <72823288+jjoseph12@users.noreply.github.com> Date: Wed, 5 Nov 2025 14:15:52 -0700 Subject: [PATCH 22/36] Updated message --- build.rs | 4 ++++ src/generators.rs | 6 ++++++ uv.lock | 7 +++++++ 3 files changed, 17 insertions(+) create mode 100644 build.rs create mode 100644 uv.lock diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..033c3cc --- /dev/null +++ b/build.rs @@ -0,0 +1,4 @@ +fn main() { + // Ensure correct linker arguments for PyO3 extension modules on all platforms. + pyo3_build_config::add_extension_module_link_args(); +} diff --git a/src/generators.rs b/src/generators.rs index 776aa35..ed6b3fd 100644 --- a/src/generators.rs +++ b/src/generators.rs @@ -82,6 +82,12 @@ pub struct BTreeGen { rng: ChaCha8Rng, } +impl Default for BTreeGen { + fn default() -> Self { + Self::new() + } +} + impl BTreeGen { pub fn new() -> BTreeGen { BTreeGen::from_config(&config::BTreeGen::new()) diff --git a/uv.lock b/uv.lock new file mode 100644 index 0000000..7c4d26c --- /dev/null +++ b/uv.lock @@ -0,0 +1,7 @@ +version = 1 +revision = 3 +requires-python = ">=3.7" + +[[package]] +name = "alchemy" +source = { editable = "." } From d3446aa52d4700ad4e173ae00b9a38a52fff8c4d Mon Sep 17 00:00:00 2001 From: devyanshnegi Date: Thu, 13 Nov 2025 05:06:53 -0700 Subject: [PATCH 23/36] Added Abs standardization for Fontana Gen and Fixed Clippy Errors --- Cargo.lock | 803 ++++++++++++++-------------------------------- Cargo.toml | 4 +- src/generators.rs | 22 +- 3 files changed, 268 insertions(+), 561 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0068620..fe8f4d6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,21 +2,6 @@ # It is not intended for manual editing. version = 4 -[[package]] -name = "addr2line" -version = "0.24.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler2" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" - [[package]] name = "alchemy" version = "0.1.0" @@ -36,9 +21,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.15" +version = "0.6.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" +checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" dependencies = [ "anstyle", "anstyle-parse", @@ -51,36 +36,37 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.8" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" +checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" [[package]] name = "anstyle-parse" -version = "0.2.5" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.1.1" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" +checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.60.2", ] [[package]] name = "anstyle-wincon" -version = "3.0.4" +version = "3.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" +checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a" dependencies = [ "anstyle", - "windows-sys 0.52.0", + "once_cell_polyfill", + "windows-sys 0.60.2", ] [[package]] @@ -96,9 +82,9 @@ dependencies = [ [[package]] name = "async-channel" -version = "2.3.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a" +checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2" dependencies = [ "concurrent-queue", "event-listener-strategy", @@ -108,14 +94,15 @@ dependencies = [ [[package]] name = "async-executor" -version = "1.13.0" +version = "1.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7ebdfa2ebdab6b1760375fa7d6f382b9f486eac35fc994625a00e89280bdbb7" +checksum = "497c00e0fd83a72a79a39fcbd8e3e2f055d6f6c7e025f3b3d91f4f8e76527fb8" dependencies = [ "async-task", "concurrent-queue", - "fastrand 2.1.0", - "futures-lite 2.3.0", + "fastrand", + "futures-lite", + "pin-project-lite", "slab", ] @@ -125,89 +112,59 @@ version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c" dependencies = [ - "async-channel 2.3.1", + "async-channel 2.5.0", "async-executor", - "async-io 2.3.4", - "async-lock 3.4.0", + "async-io", + "async-lock", "blocking", - "futures-lite 2.3.0", + "futures-lite", "once_cell", ] [[package]] name = "async-io" -version = "1.13.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" +checksum = "456b8a8feb6f42d237746d4b3e9a178494627745c3c56c6ea55d92ba50d026fc" dependencies = [ - "async-lock 2.8.0", "autocfg", "cfg-if", "concurrent-queue", - "futures-lite 1.13.0", - "log", - "parking", - "polling 2.8.0", - "rustix 0.37.27", - "slab", - "socket2", - "waker-fn", -] - -[[package]] -name = "async-io" -version = "2.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "444b0228950ee6501b3568d3c93bf1176a1fdbc3b758dcd9475046d30f4dc7e8" -dependencies = [ - "async-lock 3.4.0", - "cfg-if", - "concurrent-queue", "futures-io", - "futures-lite 2.3.0", + "futures-lite", "parking", - "polling 3.7.3", - "rustix 0.38.34", + "polling", + "rustix", "slab", - "tracing", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] name = "async-lock" -version = "2.8.0" +version = "3.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" +checksum = "5fd03604047cee9b6ce9de9f70c6cd540a0520c813cbd49bae61f33ab80ed1dc" dependencies = [ - "event-listener 2.5.3", -] - -[[package]] -name = "async-lock" -version = "3.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" -dependencies = [ - "event-listener 5.3.1", + "event-listener 5.4.1", "event-listener-strategy", "pin-project-lite", ] [[package]] name = "async-std" -version = "1.12.0" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62565bb4402e926b29953c785397c6dc0391b7b446e45008b0049eb43cec6f5d" +checksum = "2c8e079a4ab67ae52b7403632e4618815d6db36d2a010cfe41b02c1b1578f93b" dependencies = [ "async-channel 1.9.0", "async-global-executor", - "async-io 1.13.0", - "async-lock 2.8.0", + "async-io", + "async-lock", "crossbeam-utils", "futures-channel", "futures-core", "futures-io", - "futures-lite 1.13.0", + "futures-lite", "gloo-timers", "kv-log-macro", "log", @@ -233,73 +190,46 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "autocfg" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" - -[[package]] -name = "backtrace" -version = "0.3.75" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" -dependencies = [ - "addr2line", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", - "windows-targets 0.52.6", -] - -[[package]] -name = "bitflags" -version = "1.3.2" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "bitflags" -version = "2.6.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" [[package]] name = "blocking" -version = "1.6.1" +version = "1.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea" +checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21" dependencies = [ - "async-channel 2.3.1", + "async-channel 2.5.0", "async-task", "futures-io", - "futures-lite 2.3.0", + "futures-lite", "piper", ] [[package]] name = "bumpalo" -version = "3.16.0" +version = "3.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" - -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" [[package]] name = "cfg-if" -version = "1.0.0" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "clap" -version = "4.5.15" +version = "4.5.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11d8838454fda655dafd3accb2b6e2bea645b9e4078abe84a22ceb947235c5cc" +checksum = "4c26d721170e0295f191a69bd9a1f93efcdb0aff38684b61ab5750468972e5f5" dependencies = [ "clap_builder", "clap_derive", @@ -307,9 +237,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.15" +version = "4.5.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "216aec2b177652e3846684cbfe25c9964d18ec45234f0f5da5157b207ed1aab6" +checksum = "75835f0c7bf681bfd05abe44e965760fea999a5286c6eb2d59883634fd02011a" dependencies = [ "anstream", "anstyle", @@ -319,9 +249,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.13" +version = "4.5.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0" +checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671" dependencies = [ "heck", "proc-macro2", @@ -331,15 +261,15 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.2" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" +checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d" [[package]] name = "colorchoice" -version = "1.0.2" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" [[package]] name = "concurrent-queue" @@ -352,18 +282,18 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.20" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "errno" -version = "0.3.9" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -374,9 +304,9 @@ checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" [[package]] name = "event-listener" -version = "5.3.1" +version = "5.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" +checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" dependencies = [ "concurrent-queue", "parking", @@ -385,34 +315,25 @@ dependencies = [ [[package]] name = "event-listener-strategy" -version = "0.5.2" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" +checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" dependencies = [ - "event-listener 5.3.1", + "event-listener 5.4.1", "pin-project-lite", ] [[package]] name = "fastrand" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" -dependencies = [ - "instant", -] - -[[package]] -name = "fastrand" -version = "2.1.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "futures" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" dependencies = [ "futures-channel", "futures-core", @@ -425,9 +346,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ "futures-core", "futures-sink", @@ -435,15 +356,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" [[package]] name = "futures-executor" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" dependencies = [ "futures-core", "futures-task", @@ -452,32 +373,17 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-lite" -version = "1.13.0" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" +checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" dependencies = [ - "fastrand 1.9.0", - "futures-core", - "futures-io", - "memchr", - "parking", - "pin-project-lite", - "waker-fn", -] - -[[package]] -name = "futures-lite" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5" -dependencies = [ - "fastrand 2.1.0", + "fastrand", "futures-core", "futures-io", "parking", @@ -486,9 +392,9 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", @@ -497,21 +403,21 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" [[package]] name = "futures-task" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" [[package]] name = "futures-util" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ "futures-channel", "futures-core", @@ -527,26 +433,20 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", "libc", "wasi", ] -[[package]] -name = "gimli" -version = "0.31.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" - [[package]] name = "gloo-timers" -version = "0.2.6" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" +checksum = "bbb143cf96099802033e0d4f4963b19fd2e0b728bcf076cd9cf7f6634f092994" dependencies = [ "futures-channel", "futures-core", @@ -562,71 +462,38 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hermit-abi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" - -[[package]] -name = "hermit-abi" -version = "0.4.0" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" [[package]] name = "indoc" -version = "2.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c7245a08504955605670dbf141fceab975f15ca21570696aebe9d2e71576bd" - -[[package]] -name = "instant" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "io-lifetimes" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" -dependencies = [ - "hermit-abi 0.3.9", - "libc", - "windows-sys 0.48.0", -] - -[[package]] -name = "io-uring" -version = "0.7.10" +version = "2.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "046fa2d4d00aea763528b4950358d0ead425372445dc8ff86312b3c69ff7727b" +checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706" dependencies = [ - "bitflags 2.6.0", - "cfg-if", - "libc", + "rustversion", ] [[package]] name = "is_terminal_polyfill" -version = "1.70.1" +version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" [[package]] name = "itoa" -version = "1.0.11" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "js-sys" -version = "0.3.70" +version = "0.3.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" +checksum = "b011eec8cc36da2aab2d5cff675ec18454fad408585853910a202391cf9f8e65" dependencies = [ + "once_cell", "wasm-bindgen", ] @@ -646,36 +513,30 @@ source = "git+https://github.com/agentelement/lambda_calculus?branch=size-feat#8 [[package]] name = "libc" -version = "0.2.176" +version = "0.2.177" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58f929b4d672ea937a23a1ab494143d968337a5f47e56d0815df1e0890ddf174" +checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" [[package]] name = "linux-raw-sys" -version = "0.3.8" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" - -[[package]] -name = "linux-raw-sys" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" [[package]] name = "log" -version = "0.4.22" +version = "0.4.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" dependencies = [ "value-bag", ] [[package]] name = "memchr" -version = "2.7.4" +version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" [[package]] name = "memoffset" @@ -687,51 +548,28 @@ dependencies = [ ] [[package]] -name = "miniz_oxide" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" -dependencies = [ - "adler2", -] - -[[package]] -name = "mio" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" -dependencies = [ - "libc", - "wasi", - "windows-sys 0.59.0", -] - -[[package]] -name = "object" -version = "0.36.7" +name = "once_cell" +version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" -dependencies = [ - "memchr", -] +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] -name = "once_cell" -version = "1.19.0" +name = "once_cell_polyfill" +version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" [[package]] name = "parking" -version = "2.2.0" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" [[package]] name = "pin-project-lite" -version = "0.2.14" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" [[package]] name = "pin-utils" @@ -746,39 +584,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066" dependencies = [ "atomic-waker", - "fastrand 2.1.0", + "fastrand", "futures-io", ] [[package]] name = "polling" -version = "2.8.0" +version = "3.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" +checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218" dependencies = [ - "autocfg", - "bitflags 1.3.2", "cfg-if", "concurrent-queue", - "libc", - "log", + "hermit-abi", "pin-project-lite", - "windows-sys 0.48.0", -] - -[[package]] -name = "polling" -version = "3.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc2790cd301dec6cd3b7a025e4815cf825724a51c98dccfe6a3e55f05ffb6511" -dependencies = [ - "cfg-if", - "concurrent-queue", - "hermit-abi 0.4.0", - "pin-project-lite", - "rustix 0.38.34", - "tracing", - "windows-sys 0.59.0", + "rustix", + "windows-sys 0.61.2", ] [[package]] @@ -789,27 +610,27 @@ checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" [[package]] name = "ppv-lite86" -version = "0.2.20" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" dependencies = [ "zerocopy", ] [[package]] name = "proc-macro2" -version = "1.0.101" +version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" +checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" dependencies = [ "unicode-ident", ] [[package]] name = "pyo3" -version = "0.22.6" +version = "0.23.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f402062616ab18202ae8319da13fa4279883a2b8a9d9f83f20dbade813ce1884" +checksum = "7778bffd85cf38175ac1f545509665d0b9b92a198ca7941f131f85f7a4f9a872" dependencies = [ "cfg-if", "indoc", @@ -825,9 +646,9 @@ dependencies = [ [[package]] name = "pyo3-build-config" -version = "0.22.6" +version = "0.23.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b14b5775b5ff446dd1056212d778012cbe8a0fbffd368029fd9e25b514479c38" +checksum = "94f6cbe86ef3bf18998d9df6e0f3fc1050a8c5efa409bf712e661a4366e010fb" dependencies = [ "once_cell", "target-lexicon", @@ -835,9 +656,9 @@ dependencies = [ [[package]] name = "pyo3-ffi" -version = "0.22.6" +version = "0.23.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ab5bcf04a2cdcbb50c7d6105de943f543f9ed92af55818fd17b660390fc8636" +checksum = "e9f1b4c431c0bb1c8fb0a338709859eed0d030ff6daa34368d3b152a63dfdd8d" dependencies = [ "libc", "pyo3-build-config", @@ -845,9 +666,9 @@ dependencies = [ [[package]] name = "pyo3-macros" -version = "0.22.6" +version = "0.23.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fd24d897903a9e6d80b968368a34e1525aeb719d568dba8b3d4bfa5dc67d453" +checksum = "fbc2201328f63c4710f68abdf653c89d8dbc2858b88c5d88b0ff38a75288a9da" dependencies = [ "proc-macro2", "pyo3-macros-backend", @@ -857,9 +678,9 @@ dependencies = [ [[package]] name = "pyo3-macros-backend" -version = "0.22.6" +version = "0.23.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36c011a03ba1e50152b4b394b479826cad97e7a21eb52df179cd91ac411cbfbe" +checksum = "fca6726ad0f3da9c9de093d6f116a93c1a38e417ed73bf138472cf4064f72028" dependencies = [ "heck", "proc-macro2", @@ -870,9 +691,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.36" +version = "1.0.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" dependencies = [ "proc-macro2", ] @@ -907,59 +728,55 @@ dependencies = [ "getrandom", ] -[[package]] -name = "rustc-demangle" -version = "0.1.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" - [[package]] name = "rustix" -version = "0.37.27" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fea8ca367a3a01fe35e6943c400addf443c0f57670e6ec51196f71a4b8762dd2" +checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" dependencies = [ - "bitflags 1.3.2", + "bitflags", "errno", - "io-lifetimes", "libc", - "linux-raw-sys 0.3.8", - "windows-sys 0.48.0", + "linux-raw-sys", + "windows-sys 0.61.2", ] [[package]] -name = "rustix" -version = "0.38.34" +name = "rustversion" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" -dependencies = [ - "bitflags 2.6.0", - "errno", - "libc", - "linux-raw-sys 0.4.14", - "windows-sys 0.52.0", -] +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "ryu" -version = "1.0.18" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "serde" -version = "1.0.207" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5665e14a49a4ea1b91029ba7d3bca9f299e1f7cfa194388ccc20f14743e784f2" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.207" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6aea2634c86b0e8ef2cfdc0c340baede54ec27b1e46febd7f80dffb2aa44a00e" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", @@ -968,34 +785,22 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.124" +version = "1.0.145" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66ad62847a56b3dba58cc891acd13884b9c61138d330c0d7b6181713d4fce38d" +checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" dependencies = [ "itoa", "memchr", "ryu", "serde", + "serde_core", ] [[package]] name = "slab" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" -dependencies = [ - "autocfg", -] - -[[package]] -name = "socket2" -version = "0.4.10" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" -dependencies = [ - "libc", - "winapi", -] +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" [[package]] name = "strsim" @@ -1005,9 +810,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "syn" -version = "2.0.106" +version = "2.0.110" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" +checksum = "a99801b5bd34ede4cf3fc688c5919368fea4e4814a4664359503e6015b280aea" dependencies = [ "proc-macro2", "quote", @@ -1022,39 +827,18 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "tokio" -version = "1.47.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" -dependencies = [ - "backtrace", - "io-uring", - "libc", - "mio", - "pin-project-lite", - "slab", -] - -[[package]] -name = "tracing" -version = "0.1.40" +version = "1.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" dependencies = [ "pin-project-lite", - "tracing-core", ] -[[package]] -name = "tracing-core" -version = "0.1.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" - [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" [[package]] name = "unindent" @@ -1070,65 +854,47 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "value-bag" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a84c137d37ab0142f0f2ddfe332651fdbf252e7b7dbb4e67b6c1f1b2e925101" - -[[package]] -name = "waker-fn" -version = "1.2.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "317211a0dc0ceedd78fb2ca9a44aed3d7b9b26f81870d485c07122b4350673b7" +checksum = "943ce29a8a743eb10d6082545d861b24f9d1b160b7d741e0f2cdf726bec909c5" [[package]] name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" +version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasm-bindgen" -version = "0.2.93" +version = "0.2.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" +checksum = "da95793dfc411fbbd93f5be7715b0578ec61fe87cb1a42b12eb625caa5c5ea60" dependencies = [ "cfg-if", "once_cell", + "rustversion", "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.93" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" -dependencies = [ - "bumpalo", - "log", - "once_cell", - "proc-macro2", - "quote", - "syn", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.43" +version = "0.4.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61e9300f63a621e96ed275155c108eb6f843b6a26d053f122ab69724559dc8ed" +checksum = "551f88106c6d5e7ccc7cd9a16f312dd3b5d36ea8b4954304657d5dfba115d4a0" dependencies = [ "cfg-if", "js-sys", + "once_cell", "wasm-bindgen", "web-sys", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.93" +version = "0.2.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" +checksum = "04264334509e04a7bf8690f2384ef5265f05143a4bff3889ab7a3269adab59c2" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1136,218 +902,139 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.93" +version = "0.2.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" +checksum = "420bc339d9f322e562942d52e115d57e950d12d88983a14c79b86859ee6c7ebc" dependencies = [ + "bumpalo", "proc-macro2", "quote", "syn", - "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.93" +version = "0.2.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" +checksum = "76f218a38c84bcb33c25ec7059b07847d465ce0e0a76b995e134a45adcb6af76" +dependencies = [ + "unicode-ident", +] [[package]] name = "web-sys" -version = "0.3.70" +version = "0.3.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0" +checksum = "3a1f95c0d03a47f4ae1f7a64643a6bb97465d9b740f0fa8f90ea33915c99a9a1" dependencies = [ "js-sys", "wasm-bindgen", ] [[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" +name = "windows-link" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.5", -] +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" [[package]] name = "windows-sys" -version = "0.52.0" +version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" dependencies = [ - "windows-targets 0.52.6", + "windows-targets", ] [[package]] name = "windows-sys" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" -dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-targets" -version = "0.48.5" +version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", + "windows-link", ] [[package]] name = "windows-targets" -version = "0.52.6" +version = "0.53.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" dependencies = [ - "windows_aarch64_gnullvm 0.52.6", - "windows_aarch64_msvc 0.52.6", - "windows_i686_gnu 0.52.6", + "windows-link", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", "windows_i686_gnullvm", - "windows_i686_msvc 0.52.6", - "windows_x86_64_gnu 0.52.6", - "windows_x86_64_gnullvm 0.52.6", - "windows_x86_64_msvc 0.52.6", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] [[package]] name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.6" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" [[package]] name = "windows_aarch64_msvc" -version = "0.48.5" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" [[package]] name = "windows_i686_gnu" -version = "0.48.5" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" [[package]] name = "windows_i686_gnullvm" -version = "0.52.6" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" [[package]] name = "windows_i686_msvc" -version = "0.48.5" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" [[package]] name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" [[package]] name = "windows_x86_64_msvc" -version = "0.52.6" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" [[package]] name = "zerocopy" -version = "0.7.35" +version = "0.8.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" dependencies = [ - "byteorder", "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.35" +version = "0.8.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 50741a6..57b9d35 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,7 @@ name = "alchemy" python-source = "python" [dependencies] -pyo3 = { version = "0.22", features = ["extension-module", "abi3-py37"] } +pyo3 = { version = "0.23.5", features = ["extension-module", "abi3-py37"] } async-std = "1.12.0" clap = { version = "4.5.4", features = ["derive"] } futures = "0.3.30" @@ -26,7 +26,7 @@ serde_json = "1.0.117" tokio = "1.39.2" [build-dependencies] -pyo3-build-config = "0.22" +pyo3-build-config = "0.23.5" [features] extension-module = ["pyo3/extension-module"] diff --git a/src/generators.rs b/src/generators.rs index ed6b3fd..9ba7a27 100644 --- a/src/generators.rs +++ b/src/generators.rs @@ -167,6 +167,8 @@ pub struct FontanaGen { seed: [u8; 32], rng: ChaCha8Rng, + + std: Standardization, } impl FontanaGen { @@ -203,6 +205,7 @@ impl FontanaGen { max_vars, seed, rng: ChaCha8Rng::from_seed(seed), + std: Standardization::Prefix, } } @@ -228,7 +231,13 @@ impl FontanaGen { pub fn generate(&mut self) -> Term { // <-- not Option - self.rand_lambda(0, self.abs_prob.0, self.app_prob.0) + let lambda = self.rand_lambda(0, self.abs_prob.0, self.app_prob.0); + + match self.std { + Standardization::Prefix => Self::prefix_standardize(lambda), + Standardization::Postfix => Self::postfix_standardize(lambda), + Standardization::None => lambda, + } } pub fn generate_n(&mut self, n: usize) -> Vec { @@ -296,4 +305,15 @@ impl FontanaGen { }; Term::Var(value) } + + fn prefix_standardize(mut t: Term) -> Term { + while t.has_free_variables() { + t = Term::Abs(Box::new(t)) + } + t +} + + fn postfix_standardize(_t: Term) -> Term { + unimplemented!("Postfix standardization is unimplemented!!!!") + } } From b1eda985010cbd02150741b33d4a9c2e5144fde2 Mon Sep 17 00:00:00 2001 From: jjoseph12 <72823288+jjoseph12@users.noreply.github.com> Date: Thu, 13 Nov 2025 12:38:04 -0700 Subject: [PATCH 24/36] fix(lambda): correct collision metrics tuple order in nonrecursive colliders (#8) - Store tuple as (expr, reductions, size) to align with collectors - Affects src/lambda/recursive.rs and src/lambda/core.rs --- src/lambda/core.rs | 2 +- src/lambda/recursive.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lambda/core.rs b/src/lambda/core.rs index 186962e..13b8a8e 100644 --- a/src/lambda/core.rs +++ b/src/lambda/core.rs @@ -157,7 +157,7 @@ impl AlchemyCollider { let expr = LambdaParticle { expr }; - collision_results.push((expr, size, n)) + collision_results.push((expr, n, size)) } Ok(LambdaCollisionOk { results: collision_results.iter().map(|t| t.0.clone()).collect(), diff --git a/src/lambda/recursive.rs b/src/lambda/recursive.rs index 5a7404e..76c5016 100644 --- a/src/lambda/recursive.rs +++ b/src/lambda/recursive.rs @@ -224,7 +224,7 @@ impl AlchemyCollider { recursive: false, }; - collision_results.push((expr, size, n)) + collision_results.push((expr, n, size)) } Ok(LambdaCollisionOk { results: collision_results.iter().map(|t| t.0.clone()).collect(), From 1e9dc6c45aef9725557d58281063aa27f1698329 Mon Sep 17 00:00:00 2001 From: jjoseph12 <72823288+jjoseph12@users.noreply.github.com> Date: Thu, 13 Nov 2025 12:38:05 -0700 Subject: [PATCH 25/36] fix(analysis): use multiset Jaccard union for denominator (#9) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Union = |A| + |B| - |A ∩ B| - Guard empty-empty case to return 1.0 --- src/analysis.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/analysis.rs b/src/analysis.rs index 5a08dcf..79b83a3 100644 --- a/src/analysis.rs +++ b/src/analysis.rs @@ -58,12 +58,19 @@ impl LambdaSoup { let selfcounts = self.expression_counts(); let othercounts = other.expression_counts(); - let mut intersection = 0; + let mut intersection: u32 = 0; for (k, v) in selfcounts { if let Some(c) = othercounts.get(&k) { intersection += c.min(&v); } } - (intersection as f32) / ((self.len() + other.len()) as f32) + // Jaccard for multisets: |A ∩ B| / |A ∪ B|, + // where ∩ is sum of mins and ∪ is sum of maxes. + // Using identity: sum(max) = |A| + |B| - sum(min). + let union = self.len() + other.len() - intersection as usize; + if union == 0 { + return 1.0; // both empty → treat as identical + } + (intersection as f32) / (union as f32) } } From 7126d66a4bd9cda046064d5eebbec0767a492dfa Mon Sep 17 00:00:00 2001 From: Cole Mathis Date: Thu, 13 Nov 2025 15:17:56 -0700 Subject: [PATCH 26/36] Clean up duplicated BTreeGen Impl --- src/generators.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/generators.rs b/src/generators.rs index 8891441..b3d64fd 100644 --- a/src/generators.rs +++ b/src/generators.rs @@ -152,11 +152,7 @@ impl BTreeGen { } } -impl Default for BTreeGen { - fn default() -> Self { - Self::new() - } -} + pub struct FontanaGen { min_depth: u32, From 9e2fabce90baa943d617d290e2b621c0fa4bf713 Mon Sep 17 00:00:00 2001 From: Devyansh <67937759+devyanshnegi@users.noreply.github.com> Date: Thu, 13 Nov 2025 16:28:48 -0700 Subject: [PATCH 27/36] Fix Fmt Errors --- src/generators.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/generators.rs b/src/generators.rs index b3d64fd..29a803f 100644 --- a/src/generators.rs +++ b/src/generators.rs @@ -152,8 +152,6 @@ impl BTreeGen { } } - - pub struct FontanaGen { min_depth: u32, max_depth: u32, @@ -309,11 +307,11 @@ impl FontanaGen { } fn prefix_standardize(mut t: Term) -> Term { - while t.has_free_variables() { - t = Term::Abs(Box::new(t)) + while t.has_free_variables() { + t = Term::Abs(Box::new(t)) + } + t } - t -} fn postfix_standardize(_t: Term) -> Term { unimplemented!("Postfix standardization is unimplemented!!!!") From dbc0ac11d024e41d51adde29c77fd9d5791384e3 Mon Sep 17 00:00:00 2001 From: devyanshnegi Date: Thu, 5 Feb 2026 14:06:00 -0700 Subject: [PATCH 28/36] Added postfix standardization and exposed seed in python binding --- src/generators.rs | 30 +++++++++++++++++++++++++---- src/python.rs | 49 ++++++++++++++++++++++++++++++++++++----------- 2 files changed, 64 insertions(+), 15 deletions(-) diff --git a/src/generators.rs b/src/generators.rs index 29a803f..ce1e2a3 100644 --- a/src/generators.rs +++ b/src/generators.rs @@ -137,8 +137,18 @@ impl BTreeGen { self.seed } - fn postfix_standardize(_t: Term) -> Term { - unimplemented!("Postfix standiardization is unimplimented!!!!"); + fn postfix_standardize(mut t: Term) -> Term { + let mut depth = 0; + while t.has_free_variables() { + t = Abs(Box::new(t)); + depth += 1; + } + for _ in 0..depth { + // Identity function: \x. 1 (in De Bruijn, \x. x is Abs(Var(1))) + let identity = Abs(Box::new(Term::Var(1))); + t = Term::App(Box::new((t, identity))); + } + t } /// Add abstractions until the expression has no free variables @@ -313,7 +323,19 @@ impl FontanaGen { t } - fn postfix_standardize(_t: Term) -> Term { - unimplemented!("Postfix standardization is unimplemented!!!!") + fn postfix_standardize(mut t: Term) -> Term { + let mut depth = 0; + while t.has_free_variables() { + t = Term::Abs(Box::new(t)); + depth += 1; + } + + for _ in 0..depth { + // Supply Identity (\x.x) for each bound variable + let identity = Term::Abs(Box::new(Term::Var(1))); + t = Term::App(Box::new((t, identity))); + } + + t } } diff --git a/src/python.rs b/src/python.rs index 5cb120e..4e89ff6 100644 --- a/src/python.rs +++ b/src/python.rs @@ -2,6 +2,7 @@ use pyo3::prelude::*; use pyo3::wrap_pyfunction; use serde::{Deserialize, Serialize}; +use rand::Rng; // Import Rng for random seed generation use lambda_calculus::{parse, term::Notation::Classic}; @@ -19,6 +20,26 @@ use crate::utils::{decode_hex, encode_hex}; type RustSoup = GenericSoup; +// ============ Helper for Seed Parsing ============ + +fn parse_seed(seed_hex: Option) -> PyResult<[u8; 32]> { + match seed_hex { + Some(s) => { + let bytes = decode_hex(&s).map_err(|e| { + pyo3::exceptions::PyValueError::new_err(format!("Invalid hex seed: {}", e)) + })?; + bytes.try_into().map_err(|_| { + pyo3::exceptions::PyValueError::new_err("Seed must be exactly 32 bytes (64 hex chars)") + }) + } + None => { + // Generate random seed if not provided + let mut rng = rand::thread_rng(); + Ok(rng.gen()) + } + } +} + // ============ Errors exposed to Python ============ #[pyclass] @@ -215,17 +236,20 @@ impl PyBTreeGen { freevar_generation_probability: f64, max_free_vars: u32, std: PyStandardization, - ) -> Self { + seed: Option, + ) -> PyResult { + let seed_bytes = parse_seed(seed)?; + let cfg = config::BTreeGen { size, freevar_generation_probability, n_max_free_vars: max_free_vars, standardization: std.into(), - seed: ConfigSeed::new([0; 32]), + seed: ConfigSeed::new(seed_bytes), }; - PyBTreeGen { + Ok(PyBTreeGen { inner: RustBTreeGen::from_config(&cfg), - } + }) } fn generate(&mut self) -> String { @@ -248,7 +272,7 @@ pub struct PyFontanaGen { #[pymethods] impl PyFontanaGen { - /// Build a Fontana generator from config values (matches the new struct) + /// Build a Fontana generator from config values #[staticmethod] pub fn from_config( abs_range: (f64, f64), @@ -257,7 +281,10 @@ impl PyFontanaGen { max_depth: u32, free_variable_probability: f64, max_free_vars: u32, - ) -> Self { + seed: Option, + ) -> PyResult { + let seed_bytes = parse_seed(seed)?; + let cfg = config::FontanaGen { abstraction_prob_range: abs_range, application_prob_range: app_range, @@ -265,14 +292,14 @@ impl PyFontanaGen { max_depth, free_variable_probability, n_max_free_vars: max_free_vars, - seed: ConfigSeed::new([0; 32]), + seed: ConfigSeed::new(seed_bytes), }; - PyFontanaGen { + Ok(PyFontanaGen { inner: RustFontanaGen::from_config(&cfg), - } + }) } - /// Generate a single lambda term (now always returns a term) + /// Generate a single lambda term pub fn generate(&mut self) -> String { self.inner.generate().to_string() } @@ -312,4 +339,4 @@ pub fn register(m: &Bound<'_, PyModule>) -> PyResult<()> { m.add_function(wrap_pyfunction!(decode_hex_py, m)?)?; m.add_function(wrap_pyfunction!(encode_hex_py, m)?)?; Ok(()) -} +} \ No newline at end of file From 6e6f4ff969797376341bf6fd354929927ebe572f Mon Sep 17 00:00:00 2001 From: devyanshnegi Date: Thu, 12 Feb 2026 14:24:53 -0700 Subject: [PATCH 29/36] Fixed Py03 warnings and name collisions --- Cargo.toml | 4 ++++ src/generators.rs | 6 ++++-- src/python.rs | 2 ++ 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 57b9d35..1f6fba9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,10 @@ name = "alchemy" path = "src/lib.rs" crate-type = ["rlib","cdylib"] +[[bin]] +name = "alchemy-cli" # Rename the executable target here +path = "src/main.rs" + [package.metadata.maturin] name = "alchemy" python-source = "python" diff --git a/src/generators.rs b/src/generators.rs index ce1e2a3..0893239 100644 --- a/src/generators.rs +++ b/src/generators.rs @@ -262,6 +262,8 @@ impl FontanaGen { self.seed } + // if depth of tree is not reached with a 50% probability either an abs or app is generated. + // at each level an incremented prob for abs and app exist. pub fn rand_lambda(&mut self, depth: u32, p_abs: f32, p_app: f32) -> Term { let (p_abs_eff, p_app_eff) = Self::clamp_probabilities(p_abs, p_app); @@ -303,6 +305,7 @@ impl FontanaGen { (abs, app) } + // uses De Bruijn indices fn sample_variable(&mut self, depth: u32) -> Term { let free_choice = self.rng.gen_bool(self.free_prob as f64) || depth == 0; let max_vars = self.max_vars.max(1); @@ -310,8 +313,7 @@ impl FontanaGen { let offset = self.rng.gen_range(1..=max_vars); depth.saturating_add(offset) as usize } else { - let upper = depth.max(1); - self.rng.gen_range(1..=upper) as usize + self.rng.gen_range(1..=depth) as usize }; Term::Var(value) } diff --git a/src/python.rs b/src/python.rs index 4e89ff6..93ec266 100644 --- a/src/python.rs +++ b/src/python.rs @@ -231,6 +231,7 @@ impl PyBTreeGen { } #[staticmethod] + #[pyo3(signature = (size, freevar_generation_probability, max_free_vars, std, seed=None))] fn from_config( size: u32, freevar_generation_probability: f64, @@ -274,6 +275,7 @@ pub struct PyFontanaGen { impl PyFontanaGen { /// Build a Fontana generator from config values #[staticmethod] + #[pyo3(signature = (abs_range, app_range, min_depth, max_depth, free_variable_probability, max_free_vars, seed=None))] pub fn from_config( abs_range: (f64, f64), app_range: (f64, f64), From f5fc88b2dafc4411e297e3740cdb9e2f593549db Mon Sep 17 00:00:00 2001 From: devyanshnegi Date: Thu, 2 Apr 2026 14:38:10 -0700 Subject: [PATCH 30/36] Free Variables for Fontana Gen removed --- src/Untitled-1.txt | 3 + src/config.rs | 10 +-- src/generators.rs | 159 +++++++++++++++------------------------------ src/python.rs | 9 ++- 4 files changed, 64 insertions(+), 117 deletions(-) create mode 100644 src/Untitled-1.txt diff --git a/src/Untitled-1.txt b/src/Untitled-1.txt new file mode 100644 index 0000000..16dc3c0 --- /dev/null +++ b/src/Untitled-1.txt @@ -0,0 +1,3 @@ +issue with fontana gen -> sample_var() + +testing seed python binders diff --git a/src/config.rs b/src/config.rs index d3acffe..8295c12 100644 --- a/src/config.rs +++ b/src/config.rs @@ -99,7 +99,7 @@ pub struct BTreeGen { /// Probability that a leaf vertex is a free variable pub freevar_generation_probability: f64, - /// Size of the free variable palette + /// Size of the variable palette pub n_max_free_vars: u32, /// Standardization scheme. Defaults to prefix standardization (this is different from the @@ -130,10 +130,10 @@ pub struct FontanaGen { pub application_prob_range: (f64, f64), /// Probability that a leaf vertex is a free variable - pub free_variable_probability: f64, + // pub free_variable_probability: f64, /// Size of the free variable palette - pub n_max_free_vars: u32, + pub n_max_vars: u32, } impl GenConfig for FontanaGen { @@ -142,10 +142,10 @@ impl GenConfig for FontanaGen { seed: ConfigSeed(None), min_depth: 0, max_depth: 10, - n_max_free_vars: 6, + n_max_vars: 6, application_prob_range: (0.3, 0.5), abstraction_prob_range: (0.5, 0.3), - free_variable_probability: 0.0, + // free_variable_probability: 0.0,a } } } diff --git a/src/generators.rs b/src/generators.rs index 0893239..706e387 100644 --- a/src/generators.rs +++ b/src/generators.rs @@ -165,45 +165,35 @@ impl BTreeGen { pub struct FontanaGen { min_depth: u32, max_depth: u32, - + abs_prob: (f32, f32), app_prob: (f32, f32), - + abs_incr: f32, app_incr: f32, - - free_prob: f32, - max_vars: u32, - + seed: [u8; 32], rng: ChaCha8Rng, - - std: Standardization, } - + impl FontanaGen { pub fn new( min_depth: u32, mut max_depth: u32, mut abs_prob: (f32, f32), mut app_prob: (f32, f32), - mut free_prob: f32, - mut max_vars: u32, seed: [u8; 32], ) -> FontanaGen { - // Sanitise configuration so generation never panics. max_depth = max_depth.max(1); - max_vars = max_vars.max(1); - free_prob = free_prob.clamp(0.0, 1.0); abs_prob.0 = abs_prob.0.clamp(0.0, 1.0); abs_prob.1 = abs_prob.1.clamp(0.0, 1.0); app_prob.0 = app_prob.0.clamp(0.0, 1.0); app_prob.1 = app_prob.1.clamp(0.0, 1.0); - + let steps = (max_depth - 1).max(1); let abs_incr = (abs_prob.1 - abs_prob.0) / (steps as f32); let app_incr = (app_prob.1 - app_prob.0) / (steps as f32); - + FontanaGen { min_depth: min_depth.min(max_depth.saturating_sub(1)), max_depth, @@ -211,17 +201,14 @@ impl FontanaGen { app_prob, abs_incr, app_incr, - free_prob, - max_vars, seed, rng: ChaCha8Rng::from_seed(seed), - std: Standardization::Prefix, } } - + pub fn from_config(cfg: &config::FontanaGen) -> FontanaGen { let seed = cfg.seed.get(); - + FontanaGen::new( cfg.min_depth, cfg.max_depth, @@ -233,111 +220,69 @@ impl FontanaGen { cfg.application_prob_range.0 as f32, cfg.application_prob_range.1 as f32, ), - cfg.free_variable_probability as f32, - cfg.n_max_free_vars, seed, ) } - + pub fn generate(&mut self) -> Term { - // <-- not Option - let lambda = self.rand_lambda(0, self.abs_prob.0, self.app_prob.0); - - match self.std { - Standardization::Prefix => Self::prefix_standardize(lambda), - Standardization::Postfix => Self::postfix_standardize(lambda), - Standardization::None => lambda, - } - } - + self.rand_lambda(0, 0, self.abs_prob.0, self.app_prob.0) +} + pub fn generate_n(&mut self, n: usize) -> Vec { - let mut v = Vec::with_capacity(n); - for _ in 0..n { - v.push(self.generate()) - } - v + (0..n).map(|_| self.generate()).collect() } - + pub fn seed(&self) -> [u8; 32] { self.seed } + + pub fn rand_lambda(&mut self, depth: u32, abs_depth: u32, p_abs: f32, p_app: f32) -> Term { + if depth >= self.max_depth && abs_depth > 0 { + return self.sample_variable(abs_depth); + } - // if depth of tree is not reached with a 50% probability either an abs or app is generated. - // at each level an incremented prob for abs and app exist. - pub fn rand_lambda(&mut self, depth: u32, p_abs: f32, p_app: f32) -> Term { - let (p_abs_eff, p_app_eff) = Self::clamp_probabilities(p_abs, p_app); - - if depth >= self.max_depth { - return self.sample_variable(depth); - } - - let next_abs = p_abs + self.abs_incr; - let next_app = p_app + self.app_incr; + let next_abs = p_abs + self.abs_incr; + let next_app = p_app + self.app_incr; - if depth < self.min_depth { - if self.rng.gen_bool(0.5) { - return Term::Abs(Box::new(self.rand_lambda(depth + 1, next_abs, next_app))); - } - return Term::App(Box::new(( - self.rand_lambda(depth + 1, next_abs, next_app), - self.rand_lambda(depth + 1, next_abs, next_app), - ))); + if abs_depth == 0 || depth < self.min_depth { + if self.rng.gen_bool(0.5) { + return Term::Abs(Box::new( + self.rand_lambda(depth + 1, abs_depth + 1, next_abs, next_app), + )); } + return Term::App(Box::new(( + self.rand_lambda(depth + 1, abs_depth, next_abs, next_app), + self.rand_lambda(depth + 1, abs_depth, next_abs, next_app), + ))); + } - let coin: f32 = self.rng.gen(); - if coin <= p_abs_eff { - return Term::Abs(Box::new(self.rand_lambda(depth + 1, next_abs, next_app))); - } - if coin <= p_abs_eff + p_app_eff { - return Term::App(Box::new(( - self.rand_lambda(depth + 1, next_abs, next_app), - self.rand_lambda(depth + 1, next_abs, next_app), - ))); - } + let (p_abs_eff, p_app_eff) = Self::clamp_probabilities(p_abs, p_app); + let coin: f32 = self.rng.gen(); - self.sample_variable(depth) + if coin <= p_abs_eff { + return Term::Abs(Box::new( + self.rand_lambda(depth + 1, abs_depth + 1, next_abs, next_app), + )); + } + if coin <= p_abs_eff + p_app_eff { + return Term::App(Box::new(( + self.rand_lambda(depth + 1, abs_depth, next_abs, next_app), + self.rand_lambda(depth + 1, abs_depth, next_abs, next_app), + ))); } + self.sample_variable(abs_depth) +} + +fn sample_variable(&mut self, abs_depth: u32) -> Term { + debug_assert!(abs_depth > 0, "sample_variable called with no enclosing abstractions"); + Term::Var(self.rng.gen_range(1..=abs_depth) as usize) +} + fn clamp_probabilities(p_abs: f32, p_app: f32) -> (f32, f32) { let abs = p_abs.clamp(0.0, 1.0); let remaining = 1.0 - abs; let app = p_app.clamp(0.0, remaining); (abs, app) } - - // uses De Bruijn indices - fn sample_variable(&mut self, depth: u32) -> Term { - let free_choice = self.rng.gen_bool(self.free_prob as f64) || depth == 0; - let max_vars = self.max_vars.max(1); - let value = if free_choice { - let offset = self.rng.gen_range(1..=max_vars); - depth.saturating_add(offset) as usize - } else { - self.rng.gen_range(1..=depth) as usize - }; - Term::Var(value) - } - - fn prefix_standardize(mut t: Term) -> Term { - while t.has_free_variables() { - t = Term::Abs(Box::new(t)) - } - t - } - - fn postfix_standardize(mut t: Term) -> Term { - let mut depth = 0; - while t.has_free_variables() { - t = Term::Abs(Box::new(t)); - depth += 1; - } - - for _ in 0..depth { - // Supply Identity (\x.x) for each bound variable - let identity = Term::Abs(Box::new(Term::Var(1))); - t = Term::App(Box::new((t, identity))); - } - - t - } -} +} \ No newline at end of file diff --git a/src/python.rs b/src/python.rs index 93ec266..732e260 100644 --- a/src/python.rs +++ b/src/python.rs @@ -275,14 +275,14 @@ pub struct PyFontanaGen { impl PyFontanaGen { /// Build a Fontana generator from config values #[staticmethod] - #[pyo3(signature = (abs_range, app_range, min_depth, max_depth, free_variable_probability, max_free_vars, seed=None))] + #[pyo3(signature = (abs_range, app_range, min_depth, max_depth, _free_variable_probability, max_vars, seed=None))] pub fn from_config( abs_range: (f64, f64), app_range: (f64, f64), min_depth: u32, max_depth: u32, - free_variable_probability: f64, - max_free_vars: u32, + _free_variable_probability: f64, // TODO: REMOVE THIS + max_vars: u32, seed: Option, ) -> PyResult { let seed_bytes = parse_seed(seed)?; @@ -292,8 +292,7 @@ impl PyFontanaGen { application_prob_range: app_range, min_depth, max_depth, - free_variable_probability, - n_max_free_vars: max_free_vars, + n_max_vars: max_vars, seed: ConfigSeed::new(seed_bytes), }; Ok(PyFontanaGen { From 1d22f60be6320a8191aa56e0afe127a907e8e08b Mon Sep 17 00:00:00 2001 From: devyanshnegi Date: Thu, 2 Apr 2026 16:39:29 -0700 Subject: [PATCH 31/36] More work needed --- src/python.rs | 86 ++++++++++++++++++++++++++++++++++++++++--- src/supercollider.rs | 88 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 168 insertions(+), 6 deletions(-) diff --git a/src/python.rs b/src/python.rs index 93ec266..52fe68c 100644 --- a/src/python.rs +++ b/src/python.rs @@ -2,7 +2,7 @@ use pyo3::prelude::*; use pyo3::wrap_pyfunction; use serde::{Deserialize, Serialize}; -use rand::Rng; // Import Rng for random seed generation +use rand::Rng; use lambda_calculus::{parse, term::Notation::Classic}; @@ -29,11 +29,12 @@ fn parse_seed(seed_hex: Option) -> PyResult<[u8; 32]> { pyo3::exceptions::PyValueError::new_err(format!("Invalid hex seed: {}", e)) })?; bytes.try_into().map_err(|_| { - pyo3::exceptions::PyValueError::new_err("Seed must be exactly 32 bytes (64 hex chars)") + pyo3::exceptions::PyValueError::new_err( + "Seed must be exactly 32 bytes (64 hex chars)", + ) }) } None => { - // Generate random seed if not provided let mut rng = rand::thread_rng(); Ok(rng.gen()) } @@ -100,6 +101,44 @@ impl From for PyReactionError { } } +// ============ Reaction Record exposed to Python ============ + +#[pyclass] +#[derive(Clone)] +pub struct PyReactionRecord { + #[pyo3(get)] + pub step: usize, + #[pyo3(get)] + pub left: String, + #[pyo3(get)] + pub right: String, + #[pyo3(get)] + pub products: Vec, + #[pyo3(get)] + pub success: bool, + #[pyo3(get)] + pub error: Option, +} + +#[pymethods] +impl PyReactionRecord { + fn __repr__(&self) -> String { + if self.success { + format!( + "ReactionRecord(step={}, left=..., right=..., products=[{}])", + self.step, + self.products.len() + ) + } else { + format!( + "ReactionRecord(step={}, FAILED: {})", + self.step, + self.error.as_deref().unwrap_or("unknown") + ) + } + } +} + // ============ Reactor wrapper ============ #[pyclass] @@ -182,6 +221,41 @@ impl PySoup { fn simulate_for(&mut self, n: usize, log: bool) -> usize { self.inner.simulate_for(n, log) } + + /// Simulate for `n` collisions and return a list of ReactionRecord + /// objects capturing every reaction (parents + products). + fn simulate_for_logged(&mut self, n: usize) -> Vec { + self.inner + .simulate_for_logged(n) + .into_iter() + .map(|r| PyReactionRecord { + step: r.step, + left: r.left.to_string(), + right: r.right.to_string(), + products: r.products.iter().map(|p| p.to_string()).collect(), + success: r.success, + error: r.error, + }) + .collect() + } + + /// Simulate for `n` collisions and return only successful reaction records. + /// Uses less memory than `simulate_for_logged` for large n. + fn simulate_for_logged_filtered(&mut self, n: usize) -> Vec { + self.inner + .simulate_for_logged_filtered(n) + .into_iter() + .map(|r| PyReactionRecord { + step: r.step, + left: r.left.to_string(), + right: r.right.to_string(), + products: r.products.iter().map(|p| p.to_string()).collect(), + success: r.success, + error: r.error, + }) + .collect() + } + fn len(&self) -> usize { self.inner.len() } @@ -240,7 +314,7 @@ impl PyBTreeGen { seed: Option, ) -> PyResult { let seed_bytes = parse_seed(seed)?; - + let cfg = config::BTreeGen { size, freevar_generation_probability, @@ -273,7 +347,8 @@ pub struct PyFontanaGen { #[pymethods] impl PyFontanaGen { - /// Build a Fontana generator from config values + /// Build a Fontana generator from config values. + /// No free variable parameters — FontanaGen only produces closed terms. #[staticmethod] #[pyo3(signature = (abs_range, app_range, min_depth, max_depth, free_variable_probability, max_free_vars, seed=None))] pub fn from_config( @@ -335,6 +410,7 @@ pub fn register(m: &Bound<'_, PyModule>) -> PyResult<()> { m.add_class::()?; m.add_class::()?; m.add_class::()?; + m.add_class::()?; m.add_class::()?; m.add_class::()?; m.add_class::()?; diff --git a/src/supercollider.rs b/src/supercollider.rs index 772177d..99bddc1 100644 --- a/src/supercollider.rs +++ b/src/supercollider.rs @@ -27,6 +27,17 @@ where fn count(&self) -> usize; } +/// A single logged reaction event, capturing parents, products, and outcome. +#[derive(Debug, Clone)] +pub struct ReactionRecord { + pub step: usize, + pub left: P, + pub right: P, + pub products: Vec

, + pub success: bool, + pub error: Option, +} + /// The principal AlChemy object. The `Soup` struct contains a set of /// lambda expressions, and rules for composing and filtering them. #[derive(Debug, Clone)] @@ -100,6 +111,81 @@ where result.clone() } + /// Produce one atomic reaction and return a full record of who + /// reacted with whom to produce what. + pub fn react_logged(&mut self, step: usize) -> ReactionRecord

{ + let n_expr = self.expressions.len(); + + let i = self.rng.gen_range(0..n_expr); + let left = self.expressions.swap_remove(i); + + let j = self.rng.gen_range(0..n_expr - 1); + let right = self.expressions.swap_remove(j); + + let result = self.collider.collide(left.clone(), right.clone()); + + let record = match &result { + Ok(ref t) => { + let products: Vec

= t.particles().collect(); + self.perturb(products.iter().cloned()); + + if self.maintain_constant_population_size { + for _ in 0..t.count() { + let k = self.rng.gen_range(0..self.expressions.len()); + self.expressions.swap_remove(k); + } + } + + ReactionRecord { + step, + left: left.clone(), + right: right.clone(), + products, + success: true, + error: None, + } + } + Err(ref e) => ReactionRecord { + step, + left: left.clone(), + right: right.clone(), + products: vec![], + success: false, + error: Some(format!("{}", e)), + }, + }; + + if !self.discard_parents { + self.expressions.push(left); + self.expressions.push(right); + } + + record + } + + /// Simulate for `n` collisions, returning a full reaction log. + /// Note: this allocates a Vec of n records. For very large n, + /// consider using `simulate_for_logged_filtered` instead. + pub fn simulate_for_logged(&mut self, n: usize) -> Vec> { + let mut log = Vec::with_capacity(n); + for step in 0..n { + log.push(self.react_logged(step)); + } + log + } + + /// Simulate for `n` collisions, returning only successful reaction records. + pub fn simulate_for_logged_filtered(&mut self, n: usize) -> Vec> { + let mut log = Vec::new(); + for step in 0..n { + let record = self.react_logged(step); + if record.success { + log.push(record); + } + } + log + } + fn log_message_from_reaction(reaction: &Result) -> String { match reaction { Ok(result) => format!("successful with {}", result), @@ -251,4 +337,4 @@ where pub fn polling_interval(&self) -> usize { self.polling_interval } -} +} \ No newline at end of file From f6de580f5f2d104bf1922b741cfbf31e2cf06de5 Mon Sep 17 00:00:00 2001 From: devyanshnegi Date: Thu, 2 Apr 2026 19:27:57 -0700 Subject: [PATCH 32/36] Avalanche bug with base case generation & added unique expression generation --- src/config.rs | 12 +----- src/generators.rs | 107 +++++++++++++++++++++++++++++++--------------- src/python.rs | 47 +++++++++++++------- 3 files changed, 106 insertions(+), 60 deletions(-) diff --git a/src/config.rs b/src/config.rs index 8295c12..dd1fcdf 100644 --- a/src/config.rs +++ b/src/config.rs @@ -128,12 +128,6 @@ pub struct FontanaGen { /// Probability range of an application being generated. Linearly changes from start to end, /// varying with depth pub application_prob_range: (f64, f64), - - /// Probability that a leaf vertex is a free variable - // pub free_variable_probability: f64, - - /// Size of the free variable palette - pub n_max_vars: u32, } impl GenConfig for FontanaGen { @@ -142,10 +136,8 @@ impl GenConfig for FontanaGen { seed: ConfigSeed(None), min_depth: 0, max_depth: 10, - n_max_vars: 6, - application_prob_range: (0.3, 0.5), - abstraction_prob_range: (0.5, 0.3), - // free_variable_probability: 0.0,a + application_prob_range: (0.5, 0.3), + abstraction_prob_range: (0.3, 0.5), } } } diff --git a/src/generators.rs b/src/generators.rs index 706e387..bc0938c 100644 --- a/src/generators.rs +++ b/src/generators.rs @@ -133,6 +133,25 @@ impl BTreeGen { v } + pub fn generate_n_unique(&mut self, n: usize) -> Vec { + let mut seen: std::collections::HashSet = std::collections::HashSet::new(); + let mut results: Vec = Vec::with_capacity(n); + + let max_attempts = n * 20; + let mut attempts = 0; + + while results.len() < n && attempts < max_attempts { + let term = self.generate(); + let key = term.to_string(); + if seen.insert(key) { + results.push(term); + } + attempts += 1; + } + + results + } + pub fn seed(&self) -> [u8; 32] { self.seed } @@ -225,60 +244,80 @@ impl FontanaGen { } pub fn generate(&mut self) -> Term { - self.rand_lambda(0, 0, self.abs_prob.0, self.app_prob.0) -} + self.rand_lambda(0, 0, self.abs_prob.0, self.app_prob.0) + } pub fn generate_n(&mut self, n: usize) -> Vec { (0..n).map(|_| self.generate()).collect() } + + pub fn generate_n_unique(&mut self, n: usize) -> Vec { + let mut seen: std::collections::HashSet = std::collections::HashSet::new(); + let mut results: Vec = Vec::with_capacity(n); + + let max_attempts = n * 20; + let mut attempts = 0; + + while results.len() < n && attempts < max_attempts { + let term = self.generate(); + let key = term.to_string(); + if seen.insert(key) { + results.push(term); + } + attempts += 1; + } + + results + } pub fn seed(&self) -> [u8; 32] { self.seed } pub fn rand_lambda(&mut self, depth: u32, abs_depth: u32, p_abs: f32, p_app: f32) -> Term { - if depth >= self.max_depth && abs_depth > 0 { - return self.sample_variable(abs_depth); - } + // Terminal case: max depth reached + if depth >= self.max_depth { + if abs_depth > 0 { + return self.sample_variable(abs_depth); + } + return Term::Abs(Box::new(Term::Var(1))); + } - let next_abs = p_abs + self.abs_incr; - let next_app = p_app + self.app_incr; + let next_abs = p_abs + self.abs_incr; + let next_app = p_app + self.app_incr; - if abs_depth == 0 || depth < self.min_depth { - if self.rng.gen_bool(0.5) { + let (p_abs_eff, p_app_eff) = Self::clamp_probabilities(p_abs, p_app); + let coin: f32 = self.rng.gen(); + + if coin <= p_abs_eff { return Term::Abs(Box::new( self.rand_lambda(depth + 1, abs_depth + 1, next_abs, next_app), )); } - return Term::App(Box::new(( - self.rand_lambda(depth + 1, abs_depth, next_abs, next_app), - self.rand_lambda(depth + 1, abs_depth, next_abs, next_app), - ))); - } - let (p_abs_eff, p_app_eff) = Self::clamp_probabilities(p_abs, p_app); - let coin: f32 = self.rng.gen(); + // Below min_depth or no binders: can't emit Var, must pick Abs or App + if abs_depth == 0 || depth < self.min_depth { + return Term::App(Box::new(( + self.rand_lambda(depth + 1, abs_depth, next_abs, next_app), + self.rand_lambda(depth + 1, abs_depth, next_abs, next_app), + ))); + } - if coin <= p_abs_eff { - return Term::Abs(Box::new( - self.rand_lambda(depth + 1, abs_depth + 1, next_abs, next_app), - )); - } - if coin <= p_abs_eff + p_app_eff { - return Term::App(Box::new(( - self.rand_lambda(depth + 1, abs_depth, next_abs, next_app), - self.rand_lambda(depth + 1, abs_depth, next_abs, next_app), - ))); - } + if coin <= p_abs_eff + p_app_eff { + return Term::App(Box::new(( + self.rand_lambda(depth + 1, abs_depth, next_abs, next_app), + self.rand_lambda(depth + 1, abs_depth, next_abs, next_app), + ))); + } - self.sample_variable(abs_depth) -} + self.sample_variable(abs_depth) + } -fn sample_variable(&mut self, abs_depth: u32) -> Term { - debug_assert!(abs_depth > 0, "sample_variable called with no enclosing abstractions"); - Term::Var(self.rng.gen_range(1..=abs_depth) as usize) -} - + fn sample_variable(&mut self, abs_depth: u32) -> Term { + debug_assert!(abs_depth > 0, "sample_variable called with no enclosing abstractions"); + Term::Var(self.rng.gen_range(1..=abs_depth) as usize) + } + fn clamp_probabilities(p_abs: f32, p_app: f32) -> (f32, f32) { let abs = p_abs.clamp(0.0, 1.0); let remaining = 1.0 - abs; diff --git a/src/python.rs b/src/python.rs index 732e260..af82837 100644 --- a/src/python.rs +++ b/src/python.rs @@ -257,12 +257,21 @@ impl PyBTreeGen { self.inner.generate().to_string() } - fn generate_n(&mut self, n: usize) -> Vec { - self.inner - .generate_n(n) - .into_iter() - .map(|t| t.to_string()) - .collect() + #[pyo3(signature = (n, unique=false))] + fn generate_n(&mut self, n: usize, unique: bool) -> Vec { + if unique { + self.inner + .generate_n_unique(n) + .into_iter() + .map(|t| t.to_string()) + .collect() + } else { + self.inner + .generate_n(n) + .into_iter() + .map(|t| t.to_string()) + .collect() + } } } @@ -275,14 +284,12 @@ pub struct PyFontanaGen { impl PyFontanaGen { /// Build a Fontana generator from config values #[staticmethod] - #[pyo3(signature = (abs_range, app_range, min_depth, max_depth, _free_variable_probability, max_vars, seed=None))] + #[pyo3(signature = (abs_range, app_range, min_depth, max_depth, seed=None))] pub fn from_config( abs_range: (f64, f64), app_range: (f64, f64), min_depth: u32, max_depth: u32, - _free_variable_probability: f64, // TODO: REMOVE THIS - max_vars: u32, seed: Option, ) -> PyResult { let seed_bytes = parse_seed(seed)?; @@ -292,7 +299,6 @@ impl PyFontanaGen { application_prob_range: app_range, min_depth, max_depth, - n_max_vars: max_vars, seed: ConfigSeed::new(seed_bytes), }; Ok(PyFontanaGen { @@ -306,12 +312,21 @@ impl PyFontanaGen { } /// Convenience: generate N terms - pub fn generate_n(&mut self, n: usize) -> Vec { - self.inner - .generate_n(n) - .into_iter() - .map(|t| t.to_string()) - .collect() + #[pyo3(signature = (n, unique=false))] + pub fn generate_n(&mut self, n: usize, unique: bool) -> Vec { + if unique { + self.inner + .generate_n_unique(n) + .into_iter() + .map(|t| t.to_string()) + .collect() + } else { + self.inner + .generate_n(n) + .into_iter() + .map(|t| t.to_string()) + .collect() + } } } From 0b3cba271acaefd8496be4cb4d5bd76e9e312cc2 Mon Sep 17 00:00:00 2001 From: devyanshnegi Date: Thu, 2 Apr 2026 21:25:29 -0700 Subject: [PATCH 33/36] Fix Fmt Errors --- src/generators.rs | 39 +++++++++++++++++++++------------------ src/python.rs | 4 ++-- src/supercollider.rs | 2 +- 3 files changed, 24 insertions(+), 21 deletions(-) diff --git a/src/generators.rs b/src/generators.rs index bc0938c..c871163 100644 --- a/src/generators.rs +++ b/src/generators.rs @@ -184,17 +184,14 @@ impl BTreeGen { pub struct FontanaGen { min_depth: u32, max_depth: u32, - abs_prob: (f32, f32), app_prob: (f32, f32), - abs_incr: f32, app_incr: f32, - seed: [u8; 32], rng: ChaCha8Rng, } - + impl FontanaGen { pub fn new( min_depth: u32, @@ -208,11 +205,11 @@ impl FontanaGen { abs_prob.1 = abs_prob.1.clamp(0.0, 1.0); app_prob.0 = app_prob.0.clamp(0.0, 1.0); app_prob.1 = app_prob.1.clamp(0.0, 1.0); - + let steps = (max_depth - 1).max(1); let abs_incr = (abs_prob.1 - abs_prob.0) / (steps as f32); let app_incr = (app_prob.1 - app_prob.0) / (steps as f32); - + FontanaGen { min_depth: min_depth.min(max_depth.saturating_sub(1)), max_depth, @@ -224,10 +221,10 @@ impl FontanaGen { rng: ChaCha8Rng::from_seed(seed), } } - + pub fn from_config(cfg: &config::FontanaGen) -> FontanaGen { let seed = cfg.seed.get(); - + FontanaGen::new( cfg.min_depth, cfg.max_depth, @@ -242,11 +239,11 @@ impl FontanaGen { seed, ) } - + pub fn generate(&mut self) -> Term { self.rand_lambda(0, 0, self.abs_prob.0, self.app_prob.0) } - + pub fn generate_n(&mut self, n: usize) -> Vec { (0..n).map(|_| self.generate()).collect() } @@ -269,11 +266,11 @@ impl FontanaGen { results } - + pub fn seed(&self) -> [u8; 32] { self.seed } - + pub fn rand_lambda(&mut self, depth: u32, abs_depth: u32, p_abs: f32, p_app: f32) -> Term { // Terminal case: max depth reached if depth >= self.max_depth { @@ -290,9 +287,12 @@ impl FontanaGen { let coin: f32 = self.rng.gen(); if coin <= p_abs_eff { - return Term::Abs(Box::new( - self.rand_lambda(depth + 1, abs_depth + 1, next_abs, next_app), - )); + return Term::Abs(Box::new(self.rand_lambda( + depth + 1, + abs_depth + 1, + next_abs, + next_app, + ))); } // Below min_depth or no binders: can't emit Var, must pick Abs or App @@ -314,14 +314,17 @@ impl FontanaGen { } fn sample_variable(&mut self, abs_depth: u32) -> Term { - debug_assert!(abs_depth > 0, "sample_variable called with no enclosing abstractions"); + debug_assert!( + abs_depth > 0, + "sample_variable called with no enclosing abstractions" + ); Term::Var(self.rng.gen_range(1..=abs_depth) as usize) } - + fn clamp_probabilities(p_abs: f32, p_app: f32) -> (f32, f32) { let abs = p_abs.clamp(0.0, 1.0); let remaining = 1.0 - abs; let app = p_app.clamp(0.0, remaining); (abs, app) } -} \ No newline at end of file +} diff --git a/src/python.rs b/src/python.rs index 6d94a42..0bac464 100644 --- a/src/python.rs +++ b/src/python.rs @@ -1,8 +1,8 @@ // src/python.rs use pyo3::prelude::*; use pyo3::wrap_pyfunction; -use serde::{Deserialize, Serialize}; use rand::Rng; +use serde::{Deserialize, Serialize}; use lambda_calculus::{parse, term::Notation::Classic}; @@ -431,4 +431,4 @@ pub fn register(m: &Bound<'_, PyModule>) -> PyResult<()> { m.add_function(wrap_pyfunction!(decode_hex_py, m)?)?; m.add_function(wrap_pyfunction!(encode_hex_py, m)?)?; Ok(()) -} \ No newline at end of file +} diff --git a/src/supercollider.rs b/src/supercollider.rs index 99bddc1..0981144 100644 --- a/src/supercollider.rs +++ b/src/supercollider.rs @@ -337,4 +337,4 @@ where pub fn polling_interval(&self) -> usize { self.polling_interval } -} \ No newline at end of file +} From 94c229a5b903452145356e078458049c5efd6aa0 Mon Sep 17 00:00:00 2001 From: Cole Mathis Date: Thu, 16 Apr 2026 14:58:37 -0700 Subject: [PATCH 34/36] Delete src/Untitled-1.txt --- src/Untitled-1.txt | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 src/Untitled-1.txt diff --git a/src/Untitled-1.txt b/src/Untitled-1.txt deleted file mode 100644 index 16dc3c0..0000000 --- a/src/Untitled-1.txt +++ /dev/null @@ -1,3 +0,0 @@ -issue with fontana gen -> sample_var() - -testing seed python binders From 34c9d0fcd98d11f6dba2b3189709ea4d052e6a42 Mon Sep 17 00:00:00 2001 From: Nadya317 Date: Thu, 1 Oct 2026 14:00:46 -0700 Subject: [PATCH 35/36] Add optional seed to PySoup and PyReactor Python bindings The reactor already supports a seed, but the Python bindings had no way to pass one in, so every soup picked a random seed and collisions were not reproducible from Python. Add an optional hex `seed` argument (same format as the generators). With no seed, behavior is unchanged. --- src/python.rs | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/src/python.rs b/src/python.rs index 0bac464..a80af41 100644 --- a/src/python.rs +++ b/src/python.rs @@ -146,13 +146,24 @@ pub struct PyReactor { pub(crate) inner: RustReactor, } +// Reactor config with an optional hex seed. With no seed, the reactor picks +// a random one, so collisions are not reproducible. +fn reactor_with_seed(seed: Option) -> PyResult { + let mut cfg = RustReactor::new(); + if seed.is_some() { + cfg.seed = ConfigSeed::new(parse_seed(seed)?); + } + Ok(cfg) +} + #[pymethods] impl PyReactor { #[new] - fn new() -> Self { - PyReactor { - inner: RustReactor::new(), - } + #[pyo3(signature = (seed=None))] + fn new(seed: Option) -> PyResult { + Ok(PyReactor { + inner: reactor_with_seed(seed)?, + }) } } @@ -198,10 +209,11 @@ pub struct PySoup { #[pymethods] impl PySoup { #[new] - fn new() -> Self { - PySoup { - inner: RustSoup::new(), - } + #[pyo3(signature = (seed=None))] + fn new(seed: Option) -> PyResult { + Ok(PySoup { + inner: RustSoup::from_config(&reactor_with_seed(seed)?), + }) } #[staticmethod] From ec2a667d24c513da86ce09c24704fa13017a8775 Mon Sep 17 00:00:00 2001 From: Nadya317 Date: Tue, 6 Oct 2026 19:33:36 -0700 Subject: [PATCH 36/36] Expose the resolved seed on soups, reactors and generators When no seed is given, one is now chosen at construction and kept, and PySoup.seed, PyReactor.seed, PyBTreeGen.seed and PyFontanaGen.seed return it as a 64-char hex string (the encode_hex_py format). Passing that value back as `seed=` reproduces the run, so an unseeded run is no longer lost. --- src/python.rs | 41 +++++++++++++++++++++++++++++++++++------ 1 file changed, 35 insertions(+), 6 deletions(-) diff --git a/src/python.rs b/src/python.rs index a80af41..0d0cc53 100644 --- a/src/python.rs +++ b/src/python.rs @@ -146,13 +146,11 @@ pub struct PyReactor { pub(crate) inner: RustReactor, } -// Reactor config with an optional hex seed. With no seed, the reactor picks -// a random one, so collisions are not reproducible. +// Reactor config with an optional hex seed. With no seed, a random one is +// chosen here and stored, so it can be read back and the run reproduced. fn reactor_with_seed(seed: Option) -> PyResult { let mut cfg = RustReactor::new(); - if seed.is_some() { - cfg.seed = ConfigSeed::new(parse_seed(seed)?); - } + cfg.seed = ConfigSeed::new(parse_seed(seed)?); Ok(cfg) } @@ -165,6 +163,12 @@ impl PyReactor { inner: reactor_with_seed(seed)?, }) } + + /// The seed this reactor uses, as a 64-char hex string. + #[getter] + fn seed(&self) -> String { + encode_hex(&self.inner.seed.get()) + } } // ============ Standardization wrapper ============ @@ -204,6 +208,7 @@ impl From for RustStandardization { #[pyclass] pub struct PySoup { inner: RustSoup, + seed: [u8; 32], } #[pymethods] @@ -211,18 +216,30 @@ impl PySoup { #[new] #[pyo3(signature = (seed=None))] fn new(seed: Option) -> PyResult { + let cfg = reactor_with_seed(seed)?; Ok(PySoup { - inner: RustSoup::from_config(&reactor_with_seed(seed)?), + inner: RustSoup::from_config(&cfg), + seed: cfg.seed.get(), }) } #[staticmethod] fn from_config(cfg: &PyReactor) -> Self { + // A PyReactor always holds a resolved seed (see reactor_with_seed), + // so get() returns the same seed the soup is built with. PySoup { inner: RustSoup::from_config(&cfg.inner), + seed: cfg.inner.seed.get(), } } + /// The seed this soup's collisions use, as a 64-char hex string. + /// Pass it back as `PySoup(seed=...)` to reproduce the run. + #[getter] + fn seed(&self) -> String { + encode_hex(&self.seed) + } + fn perturb(&mut self, expressions: Vec) { let terms = expressions .into_iter() @@ -339,6 +356,12 @@ impl PyBTreeGen { }) } + /// The seed this generator uses, as a 64-char hex string. + #[getter] + fn seed(&self) -> String { + encode_hex(&self.inner.seed()) + } + fn generate(&mut self) -> String { self.inner.generate().to_string() } @@ -393,6 +416,12 @@ impl PyFontanaGen { }) } + /// The seed this generator uses, as a 64-char hex string. + #[getter] + fn seed(&self) -> String { + encode_hex(&self.inner.seed()) + } + /// Generate a single lambda term pub fn generate(&mut self) -> String { self.inner.generate().to_string()