From fc98c71e2c97501ac1bf513402d3412f89a3549b Mon Sep 17 00:00:00 2001 From: MathisWellmann Date: Tue, 28 Apr 2026 20:41:21 +0200 Subject: [PATCH 01/16] symbiont: add `test_wrap_bodies_in_catch_unwind()` --- symbiont/src/unwind.rs | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/symbiont/src/unwind.rs b/symbiont/src/unwind.rs index 08d7669..075d0dd 100644 --- a/symbiont/src/unwind.rs +++ b/symbiont/src/unwind.rs @@ -98,3 +98,41 @@ pub unsafe fn __symbiont_take_panic(buf: *mut u8, buf_len: usize) -> usize { } } "#; + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_wrap_bodies_in_catch_unwind() { + let mut file: syn::File = syn::parse_str( + " + fn step(counter: &mut usize) { + panic!() + } + ", + ) + .expect("Can parse"); + wrap_bodies_in_catch_unwind(&mut file); + assert_eq!( + &prettyplease::unparse(&file), + r#"fn step(counter: &mut usize) { + match ::std::panic::catch_unwind(::std::panic::AssertUnwindSafe(|| { panic!() })) { + Ok(__symbiont_val) => __symbiont_val, + Err(__symbiont_err) => { + let __symbiont_msg = if let Some(s) = __symbiont_err.downcast_ref::<&str>() { + *s + } else if let Some(s) = __symbiont_err.downcast_ref::() { + s.as_str() + } else { + "unknown panic" + }; + __symbiont_store_panic(__symbiont_msg); + unsafe { ::core::mem::zeroed() } + } + } +} +"# + ); + } +} From c7a972b44cad3cc2462a944fd58a59ca6a5878c8 Mon Sep 17 00:00:00 2001 From: MathisWellmann Date: Tue, 28 Apr 2026 00:21:06 +0200 Subject: [PATCH 02/16] Add `quantize-example` --- .gitignore | 2 + Cargo.lock | 71 ++++++ Cargo.toml | 1 + README.md | 2 +- examples/quantize/Cargo.toml | 16 ++ examples/quantize/src/main.rs | 401 ++++++++++++++++++++++++++++++++++ 6 files changed, 492 insertions(+), 1 deletion(-) create mode 100644 examples/quantize/Cargo.toml create mode 100644 examples/quantize/src/main.rs diff --git a/.gitignore b/.gitignore index a631419..9938a8f 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,5 @@ .direnv/ .devenv/ .env +mutants.out/ +mutants.out.old/ diff --git a/Cargo.lock b/Cargo.lock index 1950d41..65f8f0a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -231,6 +231,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "convert_case" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "core-foundation" version = "0.9.4" @@ -368,6 +377,29 @@ version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" +[[package]] +name = "derive_more" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn 2.0.117", + "unicode-xid", +] + [[package]] name = "digest" version = "0.10.7" @@ -1283,6 +1315,18 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "quantize-example" +version = "0.1.0" +dependencies = [ + "derive_more", + "rig-core", + "romu", + "symbiont", + "tokio", + "tracing", +] + [[package]] name = "quinn" version = "0.11.9" @@ -1608,6 +1652,15 @@ version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + [[package]] name = "rustls" version = "0.23.39" @@ -1761,6 +1814,12 @@ dependencies = [ "libc", ] +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" + [[package]] name = "serde" version = "1.0.228" @@ -2310,6 +2369,18 @@ version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" +[[package]] +name = "unicode-segmentation" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + [[package]] name = "untrusted" version = "0.9.0" diff --git a/Cargo.toml b/Cargo.toml index 95db683..d291801 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,7 @@ exclude = ["assets", "website"] members = [ "examples/counter", "examples/fizzbuzz", + "examples/quantize", "examples/rastrigin", "examples/sort", "examples/tictactoe", diff --git a/README.md b/README.md index f2b834b..8bc5807 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ is appended to the prompt and the LLM retries automatically until it produces co ## Quick start -```rust +```rust,ignore symbiont::evolvable! { fn step(counter: &mut usize) { // default implementation body, entirely evolved by the LLM diff --git a/examples/quantize/Cargo.toml b/examples/quantize/Cargo.toml new file mode 100644 index 0000000..99c3d7a --- /dev/null +++ b/examples/quantize/Cargo.toml @@ -0,0 +1,16 @@ +[package] +edition = "2024" +license = "MPL-2.0" +name = "quantize-example" +publish = false +version = "0.1.0" + +[dependencies] +symbiont = { path = "../../symbiont" } + +rig-core.workspace = true +romu.workspace = true +tokio.workspace = true +tracing.workspace = true + +derive_more = { version = "2", features = ["full"] } diff --git a/examples/quantize/src/main.rs b/examples/quantize/src/main.rs new file mode 100644 index 0000000..046b3e7 --- /dev/null +++ b/examples/quantize/src/main.rs @@ -0,0 +1,401 @@ +// SPDX-License-Identifier: MPL-2.0 +//! Optimal quantization: discover the best compression–fidelity trade-off. +//! +//! The LLM must implement a function that quantizes f64 values into fewer +//! distinct levels, minimizing reconstruction error (MSE) while using as +//! few distinct output values as possible. +//! +//! The default implementation copies the input unchanged — perfect fidelity +//! but zero compression. The LLM must discover quantization schemes that +//! reduce the number of distinct output values while keeping MSE low. +//! +//! Each round: +//! 1. Call the evolvable `quantize` function on data from multiple distributions. +//! 2. Measure MSE (reconstruction error) and count distinct output values. +//! 3. Update the Pareto frontier of (num_levels, MSE) trade-offs. +//! 4. Feed the frontier and per-distribution results back to the LLM. +//! +//! The Pareto frontier tracks non-dominated (num_levels, MSE) pairs across +//! all rounds, showing the LLM which trade-offs have been achieved and +//! challenging it to push the frontier further. + +use std::collections::HashSet; + +use romu::Rng; +use symbiont::Runtime; +use tracing::{ + info, + warn, +}; + +/// Number of values per distribution. +const SAMPLE_LEN: usize = 10_000; + +// Default: identity copy — zero error, zero compression. +// The LLM must evolve this into an actual quantization scheme. +symbiont::evolvable! { + fn quantize(input: &[f64], len: usize, output: &mut [f64]) { + for i in 0..len { + output[i] = input[i]; + } + } +} + +// -- Data distributions ------------------------------------------------------ + +/// The specific input distribution to create a quantization for. +#[derive(Debug, Clone, Copy, derive_more::Display)] +#[expect( + dead_code, + reason = "Leave this in to manually play with input distributions." +)] +enum Distribution { + Uniform, + Gaussian, + Bimodal, + Laplacian, + LogNormal, +} + +impl Distribution { + fn generate(&self, len: usize, rng: &Rng) -> Vec { + use Distribution::*; + match self { + Uniform => Vec::from_iter((0..len).map(|_| rng.f64() * 2.0 - 1.0)), + Gaussian => Vec::from_iter((0..len).map(|_| { + let u1 = rng.f64().max(1e-15); + let u2 = rng.f64(); + (-2.0 * u1.ln()).sqrt() * (2.0 * std::f64::consts::PI * u2).cos() + })), + Bimodal => Vec::from_iter((0..len).map(|_| { + let u1 = rng.f64().max(1e-15); + let u2 = rng.f64(); + let z = (-2.0 * u1.ln()).sqrt() * (2.0 * std::f64::consts::PI * u2).cos(); + if rng.f64() < 0.5 { + -2.0 + 0.5 * z + } else { + 2.0 + 0.5 * z + } + })), + Laplacian => Vec::from_iter((0..len).map(|_| { + let u = rng.f64() - 0.5; + -u.signum() * (1.0 - 2.0 * u.abs()).max(1e-15).ln() + })), + LogNormal => Vec::from_iter((0..len).map(|_| { + let u1 = rng.f64().max(1e-15); + let u2 = rng.f64(); + let z = (-2.0 * u1.ln()).sqrt() * (2.0 * std::f64::consts::PI * u2).cos(); + z.exp() + })), + } + } +} + +// -- Evaluation -------------------------------------------------------------- + +/// Result of evaluating the quantize function on one distribution. +struct EvalResult { + distr: Distribution, + mse: f64, + num_distinct: usize, + bits_per_value: f64, + panic: Option, +} + +fn count_distinct(data: &[f64]) -> usize { + let mut seen = HashSet::with_capacity(data.len()); + for &v in data { + seen.insert(v.to_bits()); + } + seen.len() +} + +fn evaluate(runtime: &Runtime, input: &[f64], distr: Distribution) -> EvalResult { + let mut output = vec![0.0f64; input.len()]; + quantize(input, input.len(), &mut output); + match runtime.take_panic() { + None => { + let mse = input + .iter() + .zip(output.iter()) + .map(|(a, b)| (a - b) * (a - b)) + .sum::() + / input.len() as f64; + let num_distinct = count_distinct(&output); + let bits_per_value = if num_distinct <= 1 { + 0.0 + } else { + (num_distinct as f64).log2().ceil() + }; + EvalResult { + distr, + mse, + num_distinct, + bits_per_value, + panic: None, + } + } + Some(msg) => EvalResult { + distr, + mse: f64::INFINITY, + num_distinct: 0, + bits_per_value: 0.0, + panic: Some(msg), + }, + } +} + +// -- Pareto frontier --------------------------------------------------------- + +/// A point on the Pareto frontier: (num_distinct, mse) from a given round. +#[derive(Clone)] +struct ParetoPoint { + round: usize, + num_distinct: usize, + mse: f64, +} + +/// Maintains the Pareto frontier of (num_distinct, mse) trade-offs. +/// +/// A point is non-dominated if no other point has both fewer (or equal) +/// distinct values AND lower (or equal) MSE. +struct ParetoFrontier { + points: Vec, +} + +impl ParetoFrontier { + fn new() -> Self { + Self { points: Vec::new() } + } + + /// Add a candidate point. Returns true if it was added to the frontier + /// (i.e. it is non-dominated). + fn add(&mut self, point: ParetoPoint) -> bool { + // Check if dominated by any existing point. + let dominated = self.points.iter().any(|p| { + p.num_distinct <= point.num_distinct + && p.mse <= point.mse + && (p.num_distinct < point.num_distinct || p.mse < point.mse) + }); + if dominated { + return false; + } + + // Remove points dominated by the new one. + self.points.retain(|p| { + !(point.num_distinct <= p.num_distinct + && point.mse <= p.mse + && (point.num_distinct < p.num_distinct || point.mse < p.mse)) + }); + + self.points.push(point); + self.points.sort_by_key(|p| p.num_distinct); + true + } + + fn format_table(&self) -> String { + if self.points.is_empty() { + return String::from("(no valid points yet)\n"); + } + let mut table = String::from( + "| Distinct levels | Bits/value | MSE | Round |\n\ + |-----------------|------------|------------|-------|\n", + ); + for p in &self.points { + let bits = if p.num_distinct <= 1 { + 0.0 + } else { + (p.num_distinct as f64).log2().ceil() + }; + table.push_str(&format!( + "| {:>15} | {:>10.1} | {:>10.4e} | {:>5} |\n", + p.num_distinct, bits, p.mse, p.round, + )); + } + table + } +} + +// -- Reporting --------------------------------------------------------------- + +fn format_results(r: &EvalResult) -> String { + let mut report = String::from( + "| Distribution | Distinct | Bits/val | MSE |\n\ + |--------------|----------|----------|------------|\n", + ); + + if r.panic.is_some() { + report.push_str(&format!( + "| {:<12} | PANIC | | |\n", + r.distr.to_string() + )); + } else { + report.push_str(&format!( + "| {:<12} | {:>8} | {:>8.1} | {:>10.4e} |\n", + r.distr.to_string(), + r.num_distinct, + r.bits_per_value, + r.mse, + )); + } + + if r.panic.is_some() { + report.push_str(&format!( + "\nPANIC on '{}': {}\n", + r.distr.to_string(), + r.panic.as_deref().expect("filtered for panic"), + )); + } + + report +} + +// -- Main -------------------------------------------------------------------- + +#[tokio::main] +async fn main() -> symbiont::Result<()> { + symbiont::init_tracing(); + + let runtime = Runtime::init(SYMBIONT_DECLS, symbiont::Profile::Release).await?; + let fn_sigs = runtime.fn_sigs(); + info!("fn_sigs: {fn_sigs:?}"); + + let agent = symbiont::inference::init_agent()?; + + // Fixed test data — identical across rounds for fair comparison. + let rng = Rng::from_seed_with_64bit(42); + let distr = Distribution::Laplacian; + let dist_data = distr.generate(SAMPLE_LEN, &rng); + + let mut frontier = ParetoFrontier::new(); + // Aggregate frontier (worst-case distinct across distributions, mean MSE). + let mut aggregate_frontier = ParetoFrontier::new(); + + // -- Round 0: evaluate the default (identity copy) ----------------------- + println!("\n=== Round 0: default implementation (identity copy) ==="); + let mut result: EvalResult = evaluate(runtime, &dist_data, distr); + let mut report = format_results(&result); + println!("{report}"); + + // Seed frontiers with round 0 data. + if result.panic.is_none() { + frontier.add(ParetoPoint { + round: 0, + num_distinct: result.num_distinct, + mse: result.mse, + }); + } + + // -- Evolution loop ------------------------------------------------------ + let max_rounds = 10; + let mut prev_code = String::new(); + + for round in 1..=max_rounds { + println!("\n=== Round {round}: evolving via LLM ==="); + + let prev_impl_section = if prev_code.is_empty() { + String::new() + } else { + format!("Your previous implementation:\n```rust\n{prev_code}```\n\n") + }; + + let prompt = if let Some(panic_msg) = result.panic { + let mut panic_report = String::new(); + panic_report.push_str(&format!("- {panic_msg}\n")); + format!( + "Implement this function that quantizes `len` input values, writing \ + the reconstructed (quantized) values into `output`:\n\ + ```\n{sig}\n```\n\n\ + {prev_impl_section}\ + Runtime panics:\n{panic_report}\n\ + Fix the panic. The function must not crash for any input. Code only.", + sig = fn_sigs[0], + ) + } else { + let frontier_section = { + let mut s = String::from( + "Pareto frontier of best (distinct_levels, MSE) trade-offs across all rounds:\n", + ); + s.push_str(&aggregate_frontier.format_table()); + s.push_str("\nPer-distribution frontiers:\n"); + s.push_str(&format!("\n{distr}:\n")); + s.push_str(&frontier.format_table()); + s + }; + + format!( + "Implement this function that quantizes `len` input values, writing \ + the reconstructed (quantized) values into `output`:\n\ + ```\n{sig}\n```\n\n\ + Goal: minimize the number of distinct values in `output` (compression) \ + while minimizing reconstruction error (MSE between input and output).\n\ + - Fewer distinct output values = better compression (fewer bits per value)\n\ + - Lower MSE = better reconstruction quality\n\ + - The ideal solution adaptively chooses quantization bin boundaries \ + based on the input data distribution\n\n\ + {prev_impl_section}\ + Current evaluation ({SAMPLE_LEN} values per distribution):\n\ + {report}\n\ + {frontier_section}\n\ + Push the Pareto frontier: find solutions with fewer distinct levels at \ + the same or lower MSE, or lower MSE at the same number of levels. Code only.", + sig = fn_sigs[0], + ) + }; + + runtime + .evolve(&agent, &prompt) + .await + .expect("evolution should succeed"); + + prev_code = runtime + .read_clean_code() + .expect("failed to read generated code"); + + result = evaluate(runtime, &dist_data, distr); + report = format_results(&result); + println!("{report}"); + + // Update frontier + let mut frontier_updated = false; + if result.panic.is_none() + && frontier.add(ParetoPoint { + round, + num_distinct: result.num_distinct, + mse: result.mse, + }) + { + frontier_updated = true; + } + if aggregate_frontier.add(ParetoPoint { + round, + num_distinct: result.num_distinct, + mse: result.mse, + }) { + frontier_updated = true; + } + + if frontier_updated { + info!("Pareto frontier updated in round {round}"); + println!( + "Aggregate Pareto frontier:\n{}", + aggregate_frontier.format_table() + ); + } else { + warn!("No improvement to Pareto frontier in round {round}"); + } + } + + // -- Summary ------------------------------------------------------------- + println!("\nEvolution complete after {max_rounds} rounds."); + println!("Final aggregate Pareto frontier:"); + println!("{}", aggregate_frontier.format_table()); + println!("{distr} frontier:"); + println!("{}", frontier.format_table()); + + if !prev_code.is_empty() { + println!("Last implementation:\n```rust\n{prev_code}```",); + } + + Ok(()) +} From 581a936d63a7c5bebea2dacad4e60094bc2fa034 Mon Sep 17 00:00:00 2001 From: MathisWellmann Date: Tue, 28 Apr 2026 18:31:14 +0200 Subject: [PATCH 03/16] quantize-example: plot pareto progression over time. --- Cargo.lock | 846 +++++++++++++++++++++++++++++++++- examples/quantize/Cargo.toml | 7 + examples/quantize/src/main.rs | 135 ++++++ 3 files changed, 981 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 65f8f0a..b487197 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,28 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "ab_glyph" +version = "0.2.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01c0457472c38ea5bd1c3b5ada5e368271cb550be7a4ca4a0b4634e9913f6cc2" +dependencies = [ + "ab_glyph_rasterizer", + "owned_ttf_parser", +] + +[[package]] +name = "ab_glyph_rasterizer" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618" + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + [[package]] name = "aho-corasick" version = "1.1.4" @@ -11,24 +33,89 @@ dependencies = [ "memchr", ] +[[package]] +name = "aligned" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee4508988c62edf04abd8d92897fca0c2995d907ce1dfeaf369dac3716a40685" +dependencies = [ + "as-slice", +] + +[[package]] +name = "aligned-vec" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc890384c8602f339876ded803c97ad529f3842aba97f6392b3dba0dd171769b" +dependencies = [ + "equator", +] + [[package]] name = "anes" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" +[[package]] +name = "ansi_colours" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14eec43e0298190790f41679fe69ef7a829d2a2ddd78c8c00339e84710e435fe" +dependencies = [ + "rgb", +] + [[package]] name = "anstyle" version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" +[[package]] +name = "anyhow" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" + +[[package]] +name = "arbitrary" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" + +[[package]] +name = "arg_enum_proc_macro" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + [[package]] name = "as-any" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b0f477b951e452a0b6b4a10b53ccd569042d1d01729b519e02074a9c0958a063" +[[package]] +name = "as-slice" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "516b6b4f0e40d50dcda9365d53964ec74560ad4284da2e7fc97122cd83174516" +dependencies = [ + "stable_deref_trait", +] + [[package]] name = "async-stream" version = "0.3.6" @@ -63,6 +150,49 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +[[package]] +name = "av-scenechange" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f321d77c20e19b92c39e7471cf986812cbb46659d2af674adc4331ef3f18394" +dependencies = [ + "aligned", + "anyhow", + "arg_enum_proc_macro", + "arrayvec", + "log", + "num-rational", + "num-traits", + "pastey", + "rayon", + "thiserror 2.0.18", + "v_frame", + "y4m", +] + +[[package]] +name = "av1-grain" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cfddb07216410377231960af4fcab838eaa12e013417781b78bd95ee22077f8" +dependencies = [ + "anyhow", + "arrayvec", + "log", + "nom 8.0.0", + "num-rational", + "v_frame", +] + +[[package]] +name = "avif-serialize" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "375082f007bd67184fb9c0374614b29f9aaa604ec301635f72338bb65386a53d" +dependencies = [ + "arrayvec", +] + [[package]] name = "aws-lc-rs" version = "1.16.3" @@ -91,12 +221,33 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "bit_field" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e4b40c7323adcfc0a41c4b88143ed58346ff65a288fc144329c5c45e05d70c6" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + [[package]] name = "bitflags" version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" +[[package]] +name = "bitstream-io" +version = "4.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7eff00be299a18769011411c9def0d827e8f2d7bf0c3dbf53633147a8867fd1f" +dependencies = [ + "no_std_io2", +] + [[package]] name = "block-buffer" version = "0.10.4" @@ -106,18 +257,36 @@ dependencies = [ "generic-array", ] +[[package]] +name = "built" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4ad8f11f288f48ca24471bbd51ac257aaeaaa07adae295591266b792902ae64" + [[package]] name = "bumpalo" version = "3.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" +[[package]] +name = "bytemuck" +version = "1.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" + [[package]] name = "byteorder" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" +[[package]] +name = "byteorder-lite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" + [[package]] name = "bytes" version = "1.11.1" @@ -221,6 +390,12 @@ dependencies = [ "cc", ] +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + [[package]] name = "combine" version = "4.6.7" @@ -231,6 +406,17 @@ dependencies = [ "memchr", ] +[[package]] +name = "console" +version = "0.16.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d64e8af5551369d19cf50138de61f1c42074ab970f74e99be916646777f8fc87" +dependencies = [ + "encode_unicode", + "libc", + "windows-sys 0.61.2", +] + [[package]] name = "convert_case" version = "0.10.0" @@ -284,6 +470,15 @@ dependencies = [ "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 = "criterion" version = "0.5.1" @@ -296,7 +491,7 @@ dependencies = [ "clap", "criterion-plot", "is-terminal", - "itertools", + "itertools 0.10.5", "num-traits", "once_cell", "oorandom", @@ -317,7 +512,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" dependencies = [ "cast", - "itertools", + "itertools 0.10.5", ] [[package]] @@ -345,6 +540,29 @@ version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" +[[package]] +name = "crossterm" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b" +dependencies = [ + "bitflags 2.11.1", + "crossterm_winapi", + "document-features", + "parking_lot", + "rustix", + "winapi", +] + +[[package]] +name = "crossterm_winapi" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" +dependencies = [ + "winapi", +] + [[package]] name = "crunchy" version = "0.2.4" @@ -421,6 +639,15 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "document-features" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" +dependencies = [ + "litrs", +] + [[package]] name = "dunce" version = "1.0.5" @@ -439,6 +666,12 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +[[package]] +name = "encode_unicode" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" + [[package]] name = "encoding_rs" version = "0.8.35" @@ -448,6 +681,26 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "equator" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4711b213838dfee0117e3be6ac926007d7f433d7bbe33595975d4190cb07e6fc" +dependencies = [ + "equator-macro", +] + +[[package]] +name = "equator-macro" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "equivalent" version = "1.0.2" @@ -471,16 +724,45 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74fef4569247a5f429d9156b9d0a2599914385dd189c539334c625d8099d90ab" dependencies = [ "futures-core", - "nom", + "nom 7.1.3", "pin-project-lite", ] +[[package]] +name = "exr" +version = "1.74.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4300e043a56aa2cb633c01af81ca8f699a321879a7854d3896a0ba89056363be" +dependencies = [ + "bit_field", + "half", + "lebe", + "miniz_oxide", + "smallvec", + "zune-inflate", +] + [[package]] name = "fastrand" version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" +[[package]] +name = "fax" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caf1079563223d5d59d83c85886a56e586cfd5c1a26292e971a0fa266531ac5a" + +[[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.9" @@ -497,6 +779,16 @@ dependencies = [ "tracing", ] +[[package]] +name = "flate2" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + [[package]] name = "fnv" version = "1.0.7" @@ -649,6 +941,16 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "gif" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee8cfcc411d9adbbaba82fb72661cc1bcca13e8bba98b364e62b2dba8f960159" +dependencies = [ + "color_quant", + "weezl", +] + [[package]] name = "glob" version = "0.3.3" @@ -900,6 +1202,59 @@ dependencies = [ "icu_properties", ] +[[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 0.17.16", +] + +[[package]] +name = "image" +version = "0.25.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85ab80394333c02fe689eaf900ab500fbd0c2213da414687ebf995a65d5a6104" +dependencies = [ + "bytemuck", + "byteorder-lite", + "color_quant", + "exr", + "gif", + "image-webp", + "moxcms", + "num-traits", + "png 0.18.1", + "qoi", + "ravif", + "rgb", + "tiff", + "zune-core", + "zune-jpeg", +] + +[[package]] +name = "image-webp" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525e9ff3e1a4be2fbea1fdf0e98686a6d98b4d8f937e1bf7402245af1909e8c3" +dependencies = [ + "byteorder-lite", + "quick-error", +] + +[[package]] +name = "imgref" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c5cedc30da3a610cac6b4ba17597bdf7152cf974e8aab3afb3d54455e371c8" + [[package]] name = "indexmap" version = "2.14.0" @@ -910,6 +1265,17 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "interpolate_name" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "ipnet" version = "2.12.0" @@ -946,6 +1312,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.18" @@ -1006,6 +1381,12 @@ dependencies = [ "libc", ] +[[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.95" @@ -1024,12 +1405,28 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +[[package]] +name = "lebe" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a79a3332a6609480d7d0c9eab957bca6b455b91bb84e66d19f5ff66294b85b8" + [[package]] name = "libc" version = "0.2.186" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" +[[package]] +name = "libfuzzer-sys" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f12a681b7dd8ce12bff52488013ba614b869148d54dd79836ab85aafdd53f08d" +dependencies = [ + "arbitrary", + "cc", +] + [[package]] name = "libloading" version = "0.8.9" @@ -1040,12 +1437,24 @@ dependencies = [ "windows-link", ] +[[package]] +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + [[package]] name = "litemap" version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" +[[package]] +name = "litrs" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" + [[package]] name = "lock_api" version = "0.4.14" @@ -1061,6 +1470,15 @@ version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" +[[package]] +name = "loop9" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fae87c125b03c1d2c0150c90365d7d6bcc53fb73a9acaef207d2d065860f062" +dependencies = [ + "imgref", +] + [[package]] name = "lru-slab" version = "0.1.2" @@ -1076,6 +1494,15 @@ dependencies = [ "regex-automata", ] +[[package]] +name = "maybe-rayon" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea1f30cedd69f0a2954655f7188c6a834246d2bcf1e315e2ac40c4b24dc9519" +dependencies = [ + "cfg-if", +] + [[package]] name = "memchr" version = "2.8.0" @@ -1104,6 +1531,16 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" +[[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 = "minstant" version = "0.1.7" @@ -1125,6 +1562,16 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "moxcms" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb85c154ba489f01b25c0d36ae69a87e4a1c73a72631fc6c0eb6dde34a73e44b" +dependencies = [ + "num-traits", + "pxfm", +] + [[package]] name = "nanoid" version = "0.4.0" @@ -1134,6 +1581,21 @@ dependencies = [ "rand 0.8.6", ] +[[package]] +name = "new_debug_unreachable" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" + +[[package]] +name = "no_std_io2" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b51ed7824b6e07d354605f4abb3d9d300350701299da96642ee084f5ce631550" +dependencies = [ + "memchr", +] + [[package]] name = "nom" version = "7.1.3" @@ -1144,6 +1606,21 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "nom" +version = "8.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405" +dependencies = [ + "memchr", +] + +[[package]] +name = "noop_proc_macro" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8" + [[package]] name = "nu-ansi-term" version = "0.50.3" @@ -1153,6 +1630,47 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -1189,6 +1707,15 @@ dependencies = [ "num-traits", ] +[[package]] +name = "owned_ttf_parser" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36820e9051aca1014ddc75770aab4d68bc1e9e632f0f5627c4086bc216fb583b" +dependencies = [ + "ttf-parser", +] + [[package]] name = "owo-colors" version = "4.3.0" @@ -1218,6 +1745,18 @@ dependencies = [ "windows-link", ] +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pastey" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35fb2e5f958ec131621fdd531e9fc186ed768cbe395337403ae56c17a74c68ec" + [[package]] name = "percent-encoding" version = "2.3.2" @@ -1256,8 +1795,11 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747" dependencies = [ + "ab_glyph", "num-traits", + "once_cell", "plotters-backend", + "plotters-bitmap", "plotters-svg", "wasm-bindgen", "web-sys", @@ -1269,6 +1811,16 @@ 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 = [ + "image 0.24.9", + "plotters-backend", +] + [[package]] name = "plotters-svg" version = "0.3.7" @@ -1278,6 +1830,32 @@ 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 = "png" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60769b8b31b2a9f263dae2776c37b1b28ae246943cf719eb6946a1db05128a61" +dependencies = [ + "bitflags 2.11.1", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + [[package]] name = "potential_utf" version = "0.1.5" @@ -1315,18 +1893,60 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "profiling" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3eb8486b569e12e2c32ad3e204dbaba5e4b5b216e9367044f25f1dba42341773" +dependencies = [ + "profiling-procmacros", +] + +[[package]] +name = "profiling-procmacros" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52717f9a02b6965224f95ca2a81e2e0c5c43baacd28ca057577988930b6c3d5b" +dependencies = [ + "quote", + "syn 2.0.117", +] + +[[package]] +name = "pxfm" +version = "0.1.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0c5ccf5294c6ccd63a74f1565028353830a9c2f5eb0c682c355c471726a6e3f" + +[[package]] +name = "qoi" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001" +dependencies = [ + "bytemuck", +] + [[package]] name = "quantize-example" version = "0.1.0" dependencies = [ "derive_more", + "plotters", "rig-core", "romu", "symbiont", "tokio", "tracing", + "viuer", ] +[[package]] +name = "quick-error" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" + [[package]] name = "quinn" version = "0.11.9" @@ -1468,6 +2088,55 @@ dependencies = [ "tracing", ] +[[package]] +name = "rav1e" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43b6dd56e85d9483277cde964fd1bdb0428de4fec5ebba7540995639a21cb32b" +dependencies = [ + "aligned-vec", + "arbitrary", + "arg_enum_proc_macro", + "arrayvec", + "av-scenechange", + "av1-grain", + "bitstream-io", + "built", + "cfg-if", + "interpolate_name", + "itertools 0.14.0", + "libc", + "libfuzzer-sys", + "log", + "maybe-rayon", + "new_debug_unreachable", + "noop_proc_macro", + "num-derive", + "num-traits", + "paste", + "profiling", + "rand 0.9.4", + "rand_chacha 0.9.0", + "simd_helpers", + "thiserror 2.0.18", + "v_frame", + "wasm-bindgen", +] + +[[package]] +name = "ravif" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e52310197d971b0f5be7fe6b57530dcd27beb35c1b013f29d66c1ad73fbbcc45" +dependencies = [ + "avif-serialize", + "imgref", + "loop9", + "quick-error", + "rav1e", + "rgb", +] + [[package]] name = "rayon" version = "1.12.0" @@ -1494,7 +2163,7 @@ version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags", + "bitflags 2.11.1", ] [[package]] @@ -1590,6 +2259,15 @@ dependencies = [ "web-sys", ] +[[package]] +name = "rgb" +version = "0.8.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b34b781b31e5d73e9fbc8689c70551fd1ade9a19e3e28cfec8580a79290cc4" +dependencies = [ + "bytemuck", +] + [[package]] name = "rig-core" version = "0.35.0" @@ -1661,6 +2339,19 @@ dependencies = [ "semver", ] +[[package]] +name = "rustix" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +dependencies = [ + "bitflags 2.11.1", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.61.2", +] + [[package]] name = "rustls" version = "0.23.39" @@ -1797,7 +2488,7 @@ version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" dependencies = [ - "bitflags", + "bitflags 2.11.1", "core-foundation 0.10.1", "core-foundation-sys", "libc", @@ -1910,6 +2601,21 @@ dependencies = [ "libc", ] +[[package]] +name = "simd-adler32" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" + +[[package]] +name = "simd_helpers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95890f873bec569a0362c235787f3aca6e1e887302ba4840839bcc6459c42da6" +dependencies = [ + "quote", +] + [[package]] name = "slab" version = "0.4.12" @@ -2031,7 +2737,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" dependencies = [ - "bitflags", + "bitflags 2.11.1", "core-foundation 0.9.4", "system-configuration-sys", ] @@ -2046,6 +2752,28 @@ dependencies = [ "libc", ] +[[package]] +name = "tempfile" +version = "3.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" +dependencies = [ + "fastrand", + "getrandom 0.3.4", + "once_cell", + "rustix", + "windows-sys 0.61.2", +] + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + [[package]] name = "thiserror" version = "1.0.69" @@ -2105,6 +2833,20 @@ dependencies = [ "tracing", ] +[[package]] +name = "tiff" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b63feaf3343d35b6ca4d50483f94843803b0f51634937cc2ec519fc32232bc52" +dependencies = [ + "fax", + "flate2", + "half", + "quick-error", + "weezl", + "zune-jpeg", +] + [[package]] name = "tinystr" version = "0.8.3" @@ -2228,7 +2970,7 @@ version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" dependencies = [ - "bitflags", + "bitflags 2.11.1", "bytes", "futures-util", "http", @@ -2331,6 +3073,12 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" +[[package]] +name = "ttf-parser" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31" + [[package]] name = "tungstenite" version = "0.23.0" @@ -2411,6 +3159,17 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" +[[package]] +name = "v_frame" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "666b7727c8875d6ab5db9533418d7c764233ac9c0cff1d469aec8fa127597be2" +dependencies = [ + "aligned-vec", + "num-traits", + "wasm-bindgen", +] + [[package]] name = "valuable" version = "0.1.1" @@ -2423,6 +3182,21 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "viuer" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da3a696d8afebb0cae4b263ff8cae3286b20ea88df0a82187b13ee6fe8398dce" +dependencies = [ + "ansi_colours", + "base64", + "console", + "crossterm", + "image 0.25.10", + "tempfile", + "termcolor", +] + [[package]] name = "walkdir" version = "2.5.0" @@ -2572,6 +3346,28 @@ dependencies = [ "rustls-pki-types", ] +[[package]] +name = "weezl" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88" + +[[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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + [[package]] name = "winapi-util" version = "0.1.11" @@ -2581,6 +3377,12 @@ dependencies = [ "windows-sys 0.61.2", ] +[[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-link" version = "0.2.1" @@ -2850,6 +3652,12 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" +[[package]] +name = "y4m" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5a4b21e1a62b67a2970e6831bc091d7b87e119e7f9791aef9702e3bef04448" + [[package]] name = "yoke" version = "0.8.2" @@ -2958,3 +3766,27 @@ name = "zmij" version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" + +[[package]] +name = "zune-core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb8a0807f7c01457d0379ba880ba6322660448ddebc890ce29bb64da71fb40f9" + +[[package]] +name = "zune-inflate" +version = "0.2.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "zune-jpeg" +version = "0.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27bc9d5b815bc103f142aa054f561d9187d191692ec7c2d1e2b4737f8dbd7296" +dependencies = [ + "zune-core", +] diff --git a/examples/quantize/Cargo.toml b/examples/quantize/Cargo.toml index 99c3d7a..7fe77e7 100644 --- a/examples/quantize/Cargo.toml +++ b/examples/quantize/Cargo.toml @@ -14,3 +14,10 @@ tokio.workspace = true tracing.workspace = true derive_more = { version = "2", features = ["full"] } +plotters = { version = "0.3", default-features = false, features = [ + "ab_glyph", + "bitmap_backend", + "bitmap_encoder", + "line_series", +] } +viuer = { version = "0.11", features = ["print-file"] } diff --git a/examples/quantize/src/main.rs b/examples/quantize/src/main.rs index 046b3e7..5c12bc7 100644 --- a/examples/quantize/src/main.rs +++ b/examples/quantize/src/main.rs @@ -21,6 +21,7 @@ use std::collections::HashSet; +use plotters::prelude::*; use romu::Rng; use symbiont::Runtime; use tracing::{ @@ -193,6 +194,11 @@ impl ParetoFrontier { true } + /// Snapshot the current frontier as (num_distinct, mse) pairs. + fn snapshot(&self) -> Vec<(f64, f64)> { + Vec::from_iter(self.points.iter().map(|p| (p.num_distinct as f64, p.mse))) + } + fn format_table(&self) -> String { if self.points.is_empty() { return String::from("(no valid points yet)\n"); @@ -250,6 +256,123 @@ fn format_results(r: &EvalResult) -> String { report } +// -- Plotting ---------------------------------------------------------------- + +/// Palette of colours for successive generations. +const PALETTE: &[RGBColor] = &[ + RGBColor(31, 119, 180), + RGBColor(255, 127, 14), + RGBColor(44, 160, 44), + RGBColor(214, 39, 40), + RGBColor(148, 103, 189), + RGBColor(140, 86, 75), + RGBColor(227, 119, 194), + RGBColor(127, 127, 127), + RGBColor(188, 189, 34), + RGBColor(23, 190, 207), + RGBColor(174, 199, 232), +]; + +/// Render the Pareto frontier progression to a PNG and display it with `viuer`. +fn plot_frontier_progression( + history: &[(usize, Vec<(f64, f64)>)], +) -> Result<(), Box> { + // Collect all points to determine axis ranges. + let all_points = + Vec::<(f64, f64)>::from_iter(history.iter().flat_map(|(_, pts)| pts.iter().copied())); + if all_points.is_empty() { + println!("No Pareto frontier data to plot."); + return Ok(()); + } + + let x_min = all_points.iter().map(|p| p.0).fold(f64::INFINITY, f64::min); + let x_max = all_points.iter().map(|p| p.0).fold(0.0f64, f64::max); + let y_min = all_points + .iter() + .map(|p| p.1) + .filter(|y| y.is_finite()) + .fold(f64::INFINITY, f64::min); + let y_max = all_points + .iter() + .map(|p| p.1) + .filter(|y| y.is_finite()) + .fold(0.0f64, f64::max); + + // Add some padding to the ranges. + let x_pad = (x_max - x_min).max(1.0) * 0.08; + let y_pad = (y_max - y_min).max(1e-10) * 0.08; + + let path = std::env::temp_dir().join("quantize_pareto.png"); + { + let root = BitMapBackend::new(&path, (2048, 2048)).into_drawing_area(); + root.fill(&WHITE)?; + + let mut chart = ChartBuilder::on(&root) + .caption( + "Pareto Frontier Progression", + ("sans-serif", 22).into_font().with_color(BLACK), + ) + .margin(10) + .x_label_area_size(40) + .y_label_area_size(60) + .build_cartesian_2d( + (x_min - x_pad)..(x_max + x_pad), + (y_min - y_pad)..(y_max + y_pad), + )?; + + chart + .configure_mesh() + .x_desc("Distinct levels") + .y_desc("MSE") + .draw()?; + + for (round, pts) in history { + let color = PALETTE[*round % PALETTE.len()]; + let label = format!("Round {round}"); + + // Draw the frontier line + points for this generation. + let mut sorted = pts.clone(); + sorted.sort_by(|a, b| a.0.partial_cmp(&b.0).expect("finite")); + + chart + .draw_series(LineSeries::new( + sorted.iter().copied(), + color.stroke_width(2), + ))? + .label(&label) + .legend(move |(x, y)| { + Rectangle::new([(x, y - 4), (x + 14, y + 4)], color.filled()) + }); + + chart.draw_series( + sorted + .iter() + .map(|&(x, y)| Circle::new((x, y), 4, color.filled())), + )?; + } + + chart + .configure_series_labels() + .background_style(WHITE.mix(0.8)) + .border_style(BLACK) + .position(SeriesLabelPosition::UpperRight) + .draw()?; + + root.present()?; + } + + // Display in terminal via viuer. + let conf = viuer::Config { + width: Some(80), + absolute_offset: false, + ..Default::default() + }; + viuer::print_from_file(&path, &conf)?; + + println!("Plot saved to: {}", path.display()); + Ok(()) +} + // -- Main -------------------------------------------------------------------- #[tokio::main] @@ -271,6 +394,9 @@ async fn main() -> symbiont::Result<()> { // Aggregate frontier (worst-case distinct across distributions, mean MSE). let mut aggregate_frontier = ParetoFrontier::new(); + // Frontier snapshots per round for the final progression plot. + let mut frontier_history: Vec<(usize, Vec<(f64, f64)>)> = Vec::new(); + // -- Round 0: evaluate the default (identity copy) ----------------------- println!("\n=== Round 0: default implementation (identity copy) ==="); let mut result: EvalResult = evaluate(runtime, &dist_data, distr); @@ -284,6 +410,7 @@ async fn main() -> symbiont::Result<()> { num_distinct: result.num_distinct, mse: result.mse, }); + frontier_history.push((0, frontier.snapshot())); } // -- Evolution loop ------------------------------------------------------ @@ -375,6 +502,9 @@ async fn main() -> symbiont::Result<()> { frontier_updated = true; } + // Always snapshot the frontier state after each round. + frontier_history.push((round, frontier.snapshot())); + if frontier_updated { info!("Pareto frontier updated in round {round}"); println!( @@ -397,5 +527,10 @@ async fn main() -> symbiont::Result<()> { println!("Last implementation:\n```rust\n{prev_code}```",); } + // Plot the Pareto frontier progression across generations. + if let Err(e) = plot_frontier_progression(&frontier_history) { + warn!("Failed to render Pareto frontier plot: {e}"); + } + Ok(()) } From 5c41a5fe4374ff7c38b3b278ee40ae74d1aab049 Mon Sep 17 00:00:00 2001 From: MathisWellmann Date: Tue, 28 Apr 2026 18:40:59 +0200 Subject: [PATCH 04/16] runtime: Include the generated code in the error, so the LLM has it in its context. --- symbiont/src/compiler.rs | 52 ++++++++++++++++++++++++++++++++-------- symbiont/src/error.rs | 4 ++-- symbiont/src/runtime.rs | 37 ++++++---------------------- 3 files changed, 51 insertions(+), 42 deletions(-) diff --git a/symbiont/src/compiler.rs b/symbiont/src/compiler.rs index 5b47180..90968df 100644 --- a/symbiont/src/compiler.rs +++ b/symbiont/src/compiler.rs @@ -2,12 +2,22 @@ use std::path::Path; use minstant::Instant; +use prettyplease::unparse; use tokio::process::Command; -use tracing::info; +use tracing::{ + debug, + info, +}; -use crate::error::{ - Error, - Result, +use crate::{ + error::{ + Error, + Result, + }, + unwind::{ + PANIC_PREAMBLE, + wrap_bodies_in_catch_unwind, + }, }; /// Compilation profile for the evolvable dylib. @@ -38,16 +48,32 @@ impl std::fmt::Display for Profile { /// Runs `cargo build --manifest-path /Cargo.toml`, /// adding `--release` when the profile is [`Profile::Release`]. /// Blocks (async) until compilation finishes. -pub(crate) async fn compile_dylib(crate_dir: &Path, profile: Profile) -> Result<()> { +pub(crate) async fn compile_dylib( + crate_dir: &Path, + profile: Profile, + clean_ast: &mut syn::File, +) -> Result<()> { let t0 = Instant::now(); - let manifest_path = crate_dir.join("Cargo.toml"); + let clean_code = unparse(&clean_ast); + debug!("clean_code: {clean_code}"); + let clean_path = crate_dir.join("src").join("clean.rs"); + std::fs::write(&clean_path, &clean_code) + .map_err(|e| Error::WriteLib(format!("Failed to write clean.rs: {e}")))?; + + // Wrap function bodies in catch_unwind so panics stay inside the dylib. + wrap_bodies_in_catch_unwind(clean_ast); + // Write final lib.rs (preamble + wrapped code) for compilation. + let formatted = format!("{PANIC_PREAMBLE}\n{}", unparse(&clean_ast)); + std::fs::write(crate_dir.join("src").join("lib.rs"), formatted)?; + info!("Created temp dylib crate at {}", crate_dir.display()); + + let manifest_path = crate_dir.join("Cargo.toml"); info!( "Compiling evolvable dylib ({profile}) at {}...", manifest_path.display() ); - let manifest_str = manifest_path.to_string_lossy(); let mut args = vec!["build", "--manifest-path", &manifest_str]; if profile == Profile::Release { @@ -58,7 +84,10 @@ pub(crate) async fn compile_dylib(crate_dir: &Path, profile: Profile) -> Result< .args(&args) .output() .await - .map_err(|e| Error::CompilationFailed(format!("Failed to spawn cargo: {e}")))?; + .map_err(|e| Error::CompilationFailed { + code: clean_code.to_string(), + err: format!("Failed to spawn cargo: {e}"), + })?; if output.status.success() { info!( @@ -67,7 +96,10 @@ pub(crate) async fn compile_dylib(crate_dir: &Path, profile: Profile) -> Result< ); Ok(()) } else { - let stderr = String::from_utf8_lossy(&output.stderr).to_string(); - Err(Error::CompilationFailed(stderr)) + let err = String::from_utf8_lossy(&output.stderr).to_string(); + Err(Error::CompilationFailed { + code: clean_code.to_string(), + err, + }) } } diff --git a/symbiont/src/error.rs b/symbiont/src/error.rs index 234191e..01e40af 100644 --- a/symbiont/src/error.rs +++ b/symbiont/src/error.rs @@ -33,8 +33,8 @@ pub enum Error { got: String, }, - #[error("Compilation failed:\n{0}")] - CompilationFailed(String), + #[error("Compilation failed:\n{err}")] + CompilationFailed { code: String, err: String }, #[error("No evolvable functions found. Use the evolvable! macro to declare at least one.")] NoEvolvableFunctions, diff --git a/symbiont/src/runtime.rs b/symbiont/src/runtime.rs index f2c8b91..eb1ebeb 100644 --- a/symbiont/src/runtime.rs +++ b/symbiont/src/runtime.rs @@ -55,10 +55,6 @@ use crate::{ Result, }, parser::parse_rust_code, - unwind::{ - PANIC_PREAMBLE, - wrap_bodies_in_catch_unwind, - }, validation::validate_generated_ast, }; @@ -199,11 +195,10 @@ impl Runtime { // Write src/lib.rs from all default_source entries let lib_rs = generate_lib_rs(decls); - std::fs::write(crate_dir.join("src").join("lib.rs"), &lib_rs)?; - info!("Created temp dylib crate at {}", crate_dir.display()); + let mut ast = syn::parse_str(&lib_rs)?; // Compile - compile_dylib(&crate_dir, profile).await?; + compile_dylib(&crate_dir, profile, &mut ast).await?; // Find and load the .so let so_path = find_so(&crate_dir, profile)?; @@ -269,33 +264,15 @@ impl Runtime { let llm_time = t0.elapsed().as_millis(); info!("llm_response: {}", llm_response.blue()); - let t0 = Instant::now(); // Parse Rust from markdown fences let mut ast = parse_rust_code(&llm_response).map_err(|_| Error::CouldNotParseRust)?; // Validate signatures match declarations validate_generated_ast(&mut ast, &self.fn_sigs)?; - // Save the clean (unwrapped) LLM code for display / prompt feedback. - let clean_code = prettyplease::unparse(&ast); - let clean_path = self.crate_dir.join("src").join("clean.rs"); - std::fs::write(&clean_path, &clean_code) - .map_err(|e| Error::WriteLib(format!("Failed to write clean.rs: {e}")))?; - - // Wrap function bodies in catch_unwind so panics stay inside the dylib. - wrap_bodies_in_catch_unwind(&mut ast); - - // Write final lib.rs (preamble + wrapped code) for compilation. - let lib_rs_path = self.crate_dir.join("src").join("lib.rs"); - let formatted = format!("{PANIC_PREAMBLE}\n{}", prettyplease::unparse(&ast)); - std::fs::write(&lib_rs_path, &formatted) - .map_err(|e| Error::WriteLib(format!("Failed to write lib.rs: {e}")))?; - info!("Wrote evolved lib.rs to {}", lib_rs_path.display()); - let validation_time = t0.elapsed().as_millis(); - // Recompile let t0 = Instant::now(); - compile_dylib(&self.crate_dir, self.profile).await?; + compile_dylib(&self.crate_dir, self.profile, &mut ast).await?; let compile_time = t0.elapsed().as_millis(); // Copy .so to versioned path to defeat dlopen caching @@ -321,7 +298,7 @@ impl Runtime { *self.library.lock().expect("library Mutex poisoned") = Some(new_lib); info!( - "Hot-reloaded evolvable dylib (version {version}). Timings: LLM generation: {llm_time}ms, validation: {validation_time}ms, compilation: {compile_time}ms.", + "Hot-reloaded evolvable dylib (version {version}). Timings: LLM generation: {llm_time}ms, compilation: {compile_time}ms.", ); Ok(()) @@ -369,8 +346,8 @@ impl Runtime { } => write!(prompt, " Generated function signature miss-match. Expected ```{expected}```, Got ```{}```", got.red() ).expect("Can write to prompt"), - CompilationFailed(ref stderr) => write!(prompt, - " The generated code failed to compile. Compiler output:\n```\n{}\n```\nPlease fix the compilation errors.", stderr.red() + CompilationFailed{code, err} => write!(prompt, + " Your generated code ```{}``` failed to compile. Compiler output:\n```\n{}\n```\nPlease fix the compilation errors.", code.blue(), err.red() ).expect("Can write to prompt"), e => { warn!("Unhandled error: {e}"); @@ -441,7 +418,7 @@ panic = "unwind" } fn generate_lib_rs(decls: &[EvolvableDecl]) -> String { - let mut src = PANIC_PREAMBLE.to_string(); + let mut src = String::with_capacity(1_000); for d in decls { src.push_str(d.default_source); src.push_str("\n\n"); From 8401d843e766cba014ec18159b969c8f357cf809 Mon Sep 17 00:00:00 2001 From: MathisWellmann Date: Tue, 28 Apr 2026 18:42:38 +0200 Subject: [PATCH 05/16] flake: include `fontconfig` and `pkg-config` dependencies. --- examples/quantize/src/main.rs | 2 +- flake.nix | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/examples/quantize/src/main.rs b/examples/quantize/src/main.rs index 5c12bc7..521ec77 100644 --- a/examples/quantize/src/main.rs +++ b/examples/quantize/src/main.rs @@ -360,6 +360,7 @@ fn plot_frontier_progression( root.present()?; } + println!("Plot saved to: {}", path.display()); // Display in terminal via viuer. let conf = viuer::Config { @@ -369,7 +370,6 @@ fn plot_frontier_progression( }; viuer::print_from_file(&path, &conf)?; - println!("Plot saved to: {}", path.display()); Ok(()) } diff --git a/flake.nix b/flake.nix index 902369b..2e600b7 100644 --- a/flake.nix +++ b/flake.nix @@ -32,7 +32,9 @@ } ); - buildInputs = [ + buildInputs = with pkgs; [ + fontconfig + pkg-config rust ]; lsps = with pkgs; [ From fe95f7380fba17fa84e845894ea3c2c46a2e6eb2 Mon Sep 17 00:00:00 2001 From: MathisWellmann Date: Tue, 28 Apr 2026 18:48:49 +0200 Subject: [PATCH 06/16] quantize: remove aggregate frontier. --- examples/quantize/src/main.rs | 30 +++--------------------------- flake.nix | 1 + 2 files changed, 4 insertions(+), 27 deletions(-) diff --git a/examples/quantize/src/main.rs b/examples/quantize/src/main.rs index 521ec77..dbee7f3 100644 --- a/examples/quantize/src/main.rs +++ b/examples/quantize/src/main.rs @@ -391,8 +391,6 @@ async fn main() -> symbiont::Result<()> { let dist_data = distr.generate(SAMPLE_LEN, &rng); let mut frontier = ParetoFrontier::new(); - // Aggregate frontier (worst-case distinct across distributions, mean MSE). - let mut aggregate_frontier = ParetoFrontier::new(); // Frontier snapshots per round for the final progression plot. let mut frontier_history: Vec<(usize, Vec<(f64, f64)>)> = Vec::new(); @@ -443,7 +441,6 @@ async fn main() -> symbiont::Result<()> { let mut s = String::from( "Pareto frontier of best (distinct_levels, MSE) trade-offs across all rounds:\n", ); - s.push_str(&aggregate_frontier.format_table()); s.push_str("\nPer-distribution frontiers:\n"); s.push_str(&format!("\n{distr}:\n")); s.push_str(&frontier.format_table()); @@ -484,42 +481,21 @@ async fn main() -> symbiont::Result<()> { println!("{report}"); // Update frontier - let mut frontier_updated = false; - if result.panic.is_none() - && frontier.add(ParetoPoint { + if result.panic.is_none() { + frontier.add(ParetoPoint { round, num_distinct: result.num_distinct, mse: result.mse, - }) - { - frontier_updated = true; - } - if aggregate_frontier.add(ParetoPoint { - round, - num_distinct: result.num_distinct, - mse: result.mse, - }) { - frontier_updated = true; + }); } // Always snapshot the frontier state after each round. frontier_history.push((round, frontier.snapshot())); - - if frontier_updated { - info!("Pareto frontier updated in round {round}"); - println!( - "Aggregate Pareto frontier:\n{}", - aggregate_frontier.format_table() - ); - } else { - warn!("No improvement to Pareto frontier in round {round}"); - } } // -- Summary ------------------------------------------------------------- println!("\nEvolution complete after {max_rounds} rounds."); println!("Final aggregate Pareto frontier:"); - println!("{}", aggregate_frontier.format_table()); println!("{distr} frontier:"); println!("{}", frontier.format_table()); diff --git a/flake.nix b/flake.nix index 2e600b7..7fa5dc4 100644 --- a/flake.nix +++ b/flake.nix @@ -34,6 +34,7 @@ buildInputs = with pkgs; [ fontconfig + liberation_ttf pkg-config rust ]; From 81cff648830872e1b39a6c176d3b4c45deba7f39 Mon Sep 17 00:00:00 2001 From: MathisWellmann Date: Tue, 28 Apr 2026 18:56:27 +0200 Subject: [PATCH 07/16] runtime: improve validation, allowing for more flexibility when more function signatures were generated. Only check that all required signatures are found, ignore the others. --- symbiont/src/error.rs | 8 ++------ symbiont/src/runtime.rs | 5 ++--- symbiont/src/validation.rs | 38 ++++++++++++++------------------------ 3 files changed, 18 insertions(+), 33 deletions(-) diff --git a/symbiont/src/error.rs b/symbiont/src/error.rs index 01e40af..ad253e5 100644 --- a/symbiont/src/error.rs +++ b/symbiont/src/error.rs @@ -26,12 +26,8 @@ pub enum Error { #[error("Failed to write lib.rs: {0}")] WriteLib(String), - #[error("Validation failed: signature mismatch for '{name}'. Expected: {expected}. Got: {got}")] - SignatureMismatch { - name: String, - expected: String, - got: String, - }, + #[error("Validation failed: signature mismatch. Expected: {expected}")] + SignatureMismatch { code: String, expected: String }, #[error("Compilation failed:\n{err}")] CompilationFailed { code: String, err: String }, diff --git a/symbiont/src/runtime.rs b/symbiont/src/runtime.rs index eb1ebeb..e0e6111 100644 --- a/symbiont/src/runtime.rs +++ b/symbiont/src/runtime.rs @@ -340,11 +340,10 @@ impl Runtime { ), WriteLib(_) => todo!(), SignatureMismatch { - name: _, + code, expected, - got, } => write!(prompt, - " Generated function signature miss-match. Expected ```{expected}```, Got ```{}```", got.red() + " Generated function signature miss-match. Expected ```{expected}```, Got Code ```{code}```", ).expect("Can write to prompt"), CompilationFailed{code, err} => write!(prompt, " Your generated code ```{}``` failed to compile. Compiler output:\n```\n{}\n```\nPlease fix the compilation errors.", code.blue(), err.red() diff --git a/symbiont/src/validation.rs b/symbiont/src/validation.rs index 4a8e97a..8ba8915 100644 --- a/symbiont/src/validation.rs +++ b/symbiont/src/validation.rs @@ -1,3 +1,4 @@ +use prettyplease::unparse; // SPDX-License-Identifier: MPL-2.0 use quote::ToTokens; use syn::{ @@ -28,7 +29,7 @@ pub(crate) fn validate_generated_ast(file: &mut syn::File, expected_sigs: &[Stri return Ok(()); } - let mut found_sigs: Vec = Vec::new(); + let mut found_sigs: Vec = Vec::with_capacity(4); for item in &mut file.items { if let syn::Item::Fn(item_fn) = item { @@ -50,31 +51,14 @@ pub(crate) fn validate_generated_ast(file: &mut syn::File, expected_sigs: &[Stri let sig = format_signature(&item_fn.sig) .unwrap_or_else(|| format!("fn {}(...)", item_fn.sig.ident)); found_sigs.push(sig.clone()); - - if !expected_sigs.contains(&sig) { - let expected = expected_sigs.join(", "); - return Err(Error::SignatureMismatch { - name, - expected, - got: sig, - }); - } } } - // Ensure all expected signatures were found for expected in expected_sigs { if !found_sigs.contains(expected) { - // Try to extract function name for a better error message - let name = expected - .trim_start_matches("fn ") - .split('(') - .next() - .unwrap_or("unknown"); return Err(Error::SignatureMismatch { - name: name.to_string(), + code: unparse(file), expected: expected.clone(), - got: "not found".to_string(), }); } } @@ -213,11 +197,17 @@ pub fn add(a: i32, b: i32) -> i32 { let mut file = parse_rust_code(input).unwrap(); let expected = vec!["fn step(counter: &mut usize)".to_string()]; let err = validate_generated_ast(&mut file, &expected).unwrap_err(); - let msg = format!("{err}"); - assert!( - msg.contains("step") && msg.contains("add"), - "expected signature mismatch error, got: {msg}" - ); + dbg!(&err); + match err { + Error::SignatureMismatch { code, expected } => { + assert_eq!( + &code, + "#[unsafe(no_mangle)]\npub fn add(a: i32, b: i32) -> i32 {\n a + b\n}\n" + ); + assert_eq!(expected, "fn step(counter: &mut usize)"); + } + _ => panic!("Invalid error"), + } } #[test] From e8a53f2d52d5694b2e6c0e7518dc9bda23ae7b2d Mon Sep 17 00:00:00 2001 From: MathisWellmann Date: Tue, 28 Apr 2026 21:20:24 +0200 Subject: [PATCH 08/16] quantize: use `impl Display for EvalResult`, which is just more natural. --- examples/quantize/src/main.rs | 78 +++++++++++++++++++---------------- 1 file changed, 42 insertions(+), 36 deletions(-) diff --git a/examples/quantize/src/main.rs b/examples/quantize/src/main.rs index dbee7f3..e85accf 100644 --- a/examples/quantize/src/main.rs +++ b/examples/quantize/src/main.rs @@ -19,7 +19,10 @@ //! all rounds, showing the LLM which trade-offs have been achieved and //! challenging it to push the frontier further. -use std::collections::HashSet; +use std::{ + collections::HashSet, + fmt::Display, +}; use plotters::prelude::*; use romu::Rng; @@ -224,36 +227,41 @@ impl ParetoFrontier { // -- Reporting --------------------------------------------------------------- -fn format_results(r: &EvalResult) -> String { - let mut report = String::from( - "| Distribution | Distinct | Bits/val | MSE |\n\ - |--------------|----------|----------|------------|\n", - ); - - if r.panic.is_some() { - report.push_str(&format!( - "| {:<12} | PANIC | | |\n", - r.distr.to_string() - )); - } else { - report.push_str(&format!( - "| {:<12} | {:>8} | {:>8.1} | {:>10.4e} |\n", - r.distr.to_string(), - r.num_distinct, - r.bits_per_value, - r.mse, - )); - } +impl Display for EvalResult { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "| Distribution | Distinct | Bits/val | MSE |\n\ + |--------------|----------|----------|------------|\n", + )?; + + if self.panic.is_some() { + write!( + f, + "| {:<12} | PANIC | | |\n", + self.distr.to_string() + )?; + } else { + write!( + f, + "| {:<12} | {:>8} | {:>8.1} | {:>10.4e} |\n", + self.distr.to_string(), + self.num_distinct, + self.bits_per_value, + self.mse, + )?; + } - if r.panic.is_some() { - report.push_str(&format!( - "\nPANIC on '{}': {}\n", - r.distr.to_string(), - r.panic.as_deref().expect("filtered for panic"), - )); + if self.panic.is_some() { + write!( + f, + "\nPANIC on '{}': {}\n", + self.distr.to_string(), + self.panic.as_deref().expect("filtered for panic"), + )?; + } + Ok(()) } - - report } // -- Plotting ---------------------------------------------------------------- @@ -398,8 +406,7 @@ async fn main() -> symbiont::Result<()> { // -- Round 0: evaluate the default (identity copy) ----------------------- println!("\n=== Round 0: default implementation (identity copy) ==="); let mut result: EvalResult = evaluate(runtime, &dist_data, distr); - let mut report = format_results(&result); - println!("{report}"); + println!("{result}"); // Seed frontiers with round 0 data. if result.panic.is_none() { @@ -458,11 +465,11 @@ async fn main() -> symbiont::Result<()> { - The ideal solution adaptively chooses quantization bin boundaries \ based on the input data distribution\n\n\ {prev_impl_section}\ - Current evaluation ({SAMPLE_LEN} values per distribution):\n\ - {report}\n\ + Current evaluation on {SAMPLE_LEN} samples:\n\ + {result}\n\ {frontier_section}\n\ Push the Pareto frontier: find solutions with fewer distinct levels at \ - the same or lower MSE, or lower MSE at the same number of levels. Code only.", + the same or lower MSE, or lower MSE at the same number of levels. Give Rust Code only.", sig = fn_sigs[0], ) }; @@ -477,8 +484,7 @@ async fn main() -> symbiont::Result<()> { .expect("failed to read generated code"); result = evaluate(runtime, &dist_data, distr); - report = format_results(&result); - println!("{report}"); + println!("{result}"); // Update frontier if result.panic.is_none() { From 250f70461896466d54a98f9bf8a76d47a89fd9c6 Mon Sep 17 00:00:00 2001 From: MathisWellmann Date: Tue, 28 Apr 2026 21:50:20 +0200 Subject: [PATCH 09/16] quatize: fix plotting. --- Cargo.lock | 253 ++++++++++++++++++++++++++++++---- examples/quantize/Cargo.toml | 2 +- examples/quantize/src/main.rs | 11 ++ 3 files changed, 236 insertions(+), 30 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b487197..0859b2f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,22 +2,6 @@ # It is not intended for manual editing. version = 4 -[[package]] -name = "ab_glyph" -version = "0.2.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01c0457472c38ea5bd1c3b5ada5e368271cb550be7a4ca4a0b4634e9913f6cc2" -dependencies = [ - "ab_glyph_rasterizer", - "owned_ttf_parser", -] - -[[package]] -name = "ab_glyph_rasterizer" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618" - [[package]] name = "adler2" version = "2.0.1" @@ -452,6 +436,42 @@ 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 0.9.4", + "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 0.9.4", + "libc", +] + +[[package]] +name = "core-text" +version = "20.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9d2790b5c08465d49f8dc05c8bcae9fea467855947db39b0f8145c091aaced5" +dependencies = [ + "core-foundation 0.9.4", + "core-graphics", + "foreign-types", + "libc", +] + [[package]] name = "counter-example" version = "0.1.0" @@ -628,6 +648,27 @@ dependencies = [ "crypto-common", ] +[[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.60.2", +] + [[package]] name = "displaydoc" version = "0.2.5" @@ -639,6 +680,15 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "dlib" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab8ecd87370524b461f8557c119c405552c396ed91fc0a8eec68679eab26f94a" +dependencies = [ + "libloading", +] + [[package]] name = "document-features" version = "0.2.12" @@ -654,6 +704,18 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" +[[package]] +name = "dwrote" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b35532432acc8b19ceed096e35dfa088d3ea037fe4f3c085f1f97f33b4d02" +dependencies = [ + "lazy_static", + "libc", + "winapi", + "wio", +] + [[package]] name = "dyn-clone" version = "1.0.20" @@ -789,12 +851,70 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "float-ord" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce81f49ae8a0482e4c55ea62ebbd7e5a686af544c00b9d090bba3ff9be97b3d" + [[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "font-kit" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c7e611d49285d4c4b2e1727b72cf05353558885cc5252f93707b845dfcaf3d3" +dependencies = [ + "bitflags 2.11.1", + "byteorder", + "core-foundation 0.9.4", + "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 2.0.117", +] + +[[package]] +name = "foreign-types-shared" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" + [[package]] name = "form_urlencoded" version = "1.2.2" @@ -804,6 +924,17 @@ dependencies = [ "percent-encoding", ] +[[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 = "fs_extra" version = "1.3.0" @@ -1437,6 +1568,15 @@ dependencies = [ "windows-link", ] +[[package]] +name = "libredox" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e02f3bb43d335493c96bf3fd3a321600bf6bd07ed34bc64118e9293bdffea46c" +dependencies = [ + "libc", +] + [[package]] name = "linux-raw-sys" version = "0.12.1" @@ -1698,6 +1838,12 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + [[package]] name = "ordered-float" version = "5.3.0" @@ -1707,15 +1853,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "owned_ttf_parser" -version = "0.25.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36820e9051aca1014ddc75770aab4d68bc1e9e632f0f5627c4086bc216fb583b" -dependencies = [ - "ttf-parser", -] - [[package]] name = "owo-colors" version = "4.3.0" @@ -1757,6 +1894,25 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "35fb2e5f958ec131621fdd531e9fc186ed768cbe395337403ae56c17a74c68ec" +[[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.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4500030c302e4af1d423f36f3b958d1aecb6c04184356ed5a833bf6b60435777" +dependencies = [ + "rustc_version", +] + [[package]] name = "percent-encoding" version = "2.3.2" @@ -1789,18 +1945,26 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" +[[package]] +name = "pkg-config" +version = "0.3.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" + [[package]] name = "plotters" version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747" dependencies = [ - "ab_glyph", + "font-kit", + "lazy_static", "num-traits", - "once_cell", + "pathfinder_geometry", "plotters-backend", "plotters-bitmap", "plotters-svg", + "ttf-parser", "wasm-bindgen", "web-sys", ] @@ -2166,6 +2330,17 @@ dependencies = [ "bitflags 2.11.1", ] +[[package]] +name = "redox_users" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" +dependencies = [ + "getrandom 0.2.17", + "libredox", + "thiserror 2.0.18", +] + [[package]] name = "ref-cast" version = "1.0.25" @@ -3075,9 +3250,9 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "ttf-parser" -version = "0.25.1" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31" +checksum = "17f77d76d837a7830fe1d4f12b7b4ba4192c1888001c7164257e4bc6d21d96b4" [[package]] name = "tungstenite" @@ -3640,6 +3815,15 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" +[[package]] +name = "wio" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d129932f4644ac2396cb456385cbf9e63b5b30c6e8dc4820bdca4eb082037a5" +dependencies = [ + "winapi", +] + [[package]] name = "wit-bindgen" version = "0.57.1" @@ -3658,6 +3842,17 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a5a4b21e1a62b67a2970e6831bc091d7b87e119e7f9791aef9702e3bef04448" +[[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 = "yoke" version = "0.8.2" diff --git a/examples/quantize/Cargo.toml b/examples/quantize/Cargo.toml index 7fe77e7..b7d5808 100644 --- a/examples/quantize/Cargo.toml +++ b/examples/quantize/Cargo.toml @@ -15,7 +15,7 @@ tracing.workspace = true derive_more = { version = "2", features = ["full"] } plotters = { version = "0.3", default-features = false, features = [ - "ab_glyph", + "ttf", "bitmap_backend", "bitmap_encoder", "line_series", diff --git a/examples/quantize/src/main.rs b/examples/quantize/src/main.rs index e85accf..86dce76 100644 --- a/examples/quantize/src/main.rs +++ b/examples/quantize/src/main.rs @@ -516,3 +516,14 @@ async fn main() -> symbiont::Result<()> { Ok(()) } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_plot_frontier() { + let history = vec![(0, vec![(1.0, 2.0)])]; + plot_frontier_progression(&history).expect("Can plot") + } +} From 24f9bf62a047b17d2564fefa39300c194f00b474 Mon Sep 17 00:00:00 2001 From: MathisWellmann Date: Tue, 28 Apr 2026 22:08:03 +0200 Subject: [PATCH 10/16] quantize: max x-axis log scale (base 2) --- examples/quantize/src/main.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/examples/quantize/src/main.rs b/examples/quantize/src/main.rs index 86dce76..3efd3ef 100644 --- a/examples/quantize/src/main.rs +++ b/examples/quantize/src/main.rs @@ -307,7 +307,8 @@ fn plot_frontier_progression( .fold(0.0f64, f64::max); // Add some padding to the ranges. - let x_pad = (x_max - x_min).max(1.0) * 0.08; + let x_lo = (x_min / 1.5).max(1.0); + let x_hi = x_max * 1.5; let y_pad = (y_max - y_min).max(1e-10) * 0.08; let path = std::env::temp_dir().join("quantize_pareto.png"); @@ -324,7 +325,7 @@ fn plot_frontier_progression( .x_label_area_size(40) .y_label_area_size(60) .build_cartesian_2d( - (x_min - x_pad)..(x_max + x_pad), + (x_lo..x_hi).log_scale().base(2.0), (y_min - y_pad)..(y_max + y_pad), )?; @@ -419,7 +420,7 @@ async fn main() -> symbiont::Result<()> { } // -- Evolution loop ------------------------------------------------------ - let max_rounds = 10; + let max_rounds = 20; let mut prev_code = String::new(); for round in 1..=max_rounds { From 8e84505f7c87df5bbd32d8738b876b3daa0ce96c Mon Sep 17 00:00:00 2001 From: MathisWellmann Date: Tue, 28 Apr 2026 22:20:25 +0200 Subject: [PATCH 11/16] quantize: new prompting strategy which include the best code in the pareto points. --- examples/quantize/src/main.rs | 112 ++++++++++++++++++++++------------ 1 file changed, 74 insertions(+), 38 deletions(-) diff --git a/examples/quantize/src/main.rs b/examples/quantize/src/main.rs index 3efd3ef..13f021c 100644 --- a/examples/quantize/src/main.rs +++ b/examples/quantize/src/main.rs @@ -126,11 +126,7 @@ fn evaluate(runtime: &Runtime, input: &[f64], distr: Distribution) -> EvalResult .sum::() / input.len() as f64; let num_distinct = count_distinct(&output); - let bits_per_value = if num_distinct <= 1 { - 0.0 - } else { - (num_distinct as f64).log2().ceil() - }; + let bits_per_value = required_bits(num_distinct); EvalResult { distr, mse, @@ -151,12 +147,23 @@ fn evaluate(runtime: &Runtime, input: &[f64], distr: Distribution) -> EvalResult // -- Pareto frontier --------------------------------------------------------- -/// A point on the Pareto frontier: (num_distinct, mse) from a given round. +/// A point on the Pareto frontier: (num_distinct, mse) from a given round, +/// together with the source code that produced it. #[derive(Clone)] struct ParetoPoint { round: usize, num_distinct: usize, mse: f64, + code: String, +} + +#[inline] +fn required_bits(num_distinct: usize) -> f64 { + if num_distinct <= 1 { + 0.0 + } else { + (num_distinct as f64).log2().ceil() + } } /// Maintains the Pareto frontier of (num_distinct, mse) trade-offs. @@ -223,6 +230,23 @@ impl ParetoFrontier { } table } + + /// Format each frontier entry with its source code so the LLM can see + /// *how* each trade-off was achieved. + fn format_with_code(&self) -> String { + if self.points.is_empty() { + return String::from("(no valid points yet)\n"); + } + let mut s = String::new(); + for p in &self.points { + let bits = required_bits(p.num_distinct); + s.push_str(&format!( + "### {} distinct | {:.1} bits/value | MSE {:.4e} (round {})\n```rust\n{}\n```\n\n", + p.num_distinct, bits, p.mse, p.round, p.code, + )); + } + s + } } // -- Reporting --------------------------------------------------------------- @@ -415,6 +439,7 @@ async fn main() -> symbiont::Result<()> { round: 0, num_distinct: result.num_distinct, mse: result.mse, + code: "for i in 0..len {\n output[i] = input[i];\n}".into(), }); frontier_history.push((0, frontier.snapshot())); } @@ -426,52 +451,62 @@ async fn main() -> symbiont::Result<()> { for round in 1..=max_rounds { println!("\n=== Round {round}: evolving via LLM ==="); - let prev_impl_section = if prev_code.is_empty() { - String::new() - } else { - format!("Your previous implementation:\n```rust\n{prev_code}```\n\n") - }; + let sig = &fn_sigs[0]; - let prompt = if let Some(panic_msg) = result.panic { - let mut panic_report = String::new(); - panic_report.push_str(&format!("- {panic_msg}\n")); + let prompt = if let Some(ref panic_msg) = result.panic { + let last_attempt = if prev_code.is_empty() { + String::new() + } else { + format!("## Last Attempt\n```rust\n{prev_code}\n```\n\n") + }; format!( - "Implement this function that quantizes `len` input values, writing \ + "## Task\n\ + Implement this function that quantizes `len` input values, writing \ the reconstructed (quantized) values into `output`:\n\ ```\n{sig}\n```\n\n\ - {prev_impl_section}\ - Runtime panics:\n{panic_report}\n\ - Fix the panic. The function must not crash for any input. Code only.", - sig = fn_sigs[0], + ## Constraints\n\ + - No external crates — only `std` and built-in operations.\n\ + - `input` and `output` are the same length; `len` gives the count.\n\ + - The function must not panic or crash for any input.\n\n\ + {last_attempt}\ + ## Runtime Panic\n\ + - {panic_msg}\n\n\ + Fix the panic. Rust code only.", ) } else { - let frontier_section = { - let mut s = String::from( - "Pareto frontier of best (distinct_levels, MSE) trade-offs across all rounds:\n", - ); - s.push_str("\nPer-distribution frontiers:\n"); - s.push_str(&format!("\n{distr}:\n")); - s.push_str(&frontier.format_table()); - s + let last_attempt = if prev_code.is_empty() { + String::new() + } else { + format!( + "## Last Attempt\n\ + ```rust\n{prev_code}\n```\n\ + Result: {} distinct | MSE {:.4e}\n\n", + result.num_distinct, result.mse, + ) }; - format!( - "Implement this function that quantizes `len` input values, writing \ + "## Task\n\ + Implement this function that quantizes `len` input values, writing \ the reconstructed (quantized) values into `output`:\n\ ```\n{sig}\n```\n\n\ - Goal: minimize the number of distinct values in `output` (compression) \ - while minimizing reconstruction error (MSE between input and output).\n\ + ## Constraints\n\ + - No external crates — only `std` and built-in operations.\n\ + - `input` and `output` are the same length; `len` gives the count.\n\n\ + ## Goal\n\ + Minimize the number of distinct values in `output` (compression) while \ + minimizing reconstruction error (MSE).\n\ - Fewer distinct output values = better compression (fewer bits per value)\n\ - Lower MSE = better reconstruction quality\n\ - The ideal solution adaptively chooses quantization bin boundaries \ based on the input data distribution\n\n\ - {prev_impl_section}\ - Current evaluation on {SAMPLE_LEN} samples:\n\ - {result}\n\ - {frontier_section}\n\ + ## Current Frontier ({distr}, {SAMPLE_LEN} samples)\n\ + {frontier}\n\ + {last_attempt}\ + ## Direction\n\ Push the Pareto frontier: find solutions with fewer distinct levels at \ - the same or lower MSE, or lower MSE at the same number of levels. Give Rust Code only.", - sig = fn_sigs[0], + the same or lower MSE, or lower MSE at the same number of levels. \ + Rust code only.", + frontier = frontier.format_with_code(), ) }; @@ -487,12 +522,13 @@ async fn main() -> symbiont::Result<()> { result = evaluate(runtime, &dist_data, distr); println!("{result}"); - // Update frontier + // Update frontier. if result.panic.is_none() { frontier.add(ParetoPoint { round, num_distinct: result.num_distinct, mse: result.mse, + code: prev_code.clone(), }); } From e9e196c3c9cd16823feff6c5bb4147794ca6693e Mon Sep 17 00:00:00 2001 From: MathisWellmann Date: Tue, 28 Apr 2026 22:44:17 +0200 Subject: [PATCH 12/16] quantize: more prompt changes. --- examples/quantize/src/main.rs | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/examples/quantize/src/main.rs b/examples/quantize/src/main.rs index 13f021c..2d9d1e6 100644 --- a/examples/quantize/src/main.rs +++ b/examples/quantize/src/main.rs @@ -276,14 +276,6 @@ impl Display for EvalResult { )?; } - if self.panic.is_some() { - write!( - f, - "\nPANIC on '{}': {}\n", - self.distr.to_string(), - self.panic.as_deref().expect("filtered for panic"), - )?; - } Ok(()) } } @@ -480,8 +472,7 @@ async fn main() -> symbiont::Result<()> { format!( "## Last Attempt\n\ ```rust\n{prev_code}\n```\n\ - Result: {} distinct | MSE {:.4e}\n\n", - result.num_distinct, result.mse, + Result: {result}\n\n", ) }; format!( From ac4861abd2996cf927108d48dbb85bfa5bad8a63 Mon Sep 17 00:00:00 2001 From: MathisWellmann Date: Tue, 28 Apr 2026 22:58:17 +0200 Subject: [PATCH 13/16] quantize: add `README.md` with some info. --- examples/quantize/README.md | 41 ++++++++++++++++++++++++++ examples/quantize/pareto_frontier.png | Bin 0 -> 257909 bytes examples/quantize/src/main.rs | 2 +- 3 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 examples/quantize/README.md create mode 100644 examples/quantize/pareto_frontier.png diff --git a/examples/quantize/README.md b/examples/quantize/README.md new file mode 100644 index 0000000..3bc5b2c --- /dev/null +++ b/examples/quantize/README.md @@ -0,0 +1,41 @@ +# Quantize Example + +Optimal quantization via LLM-driven evolutionary search. +The LLM must implement a function that quantizes `f64` values into fewer +distinct levels, minimizing reconstruction error (MSE) while using as few +distinct output values as possible. + +## How it works + +1. An evolvable `quantize(input, len, output)` function starts as an identity + copy — perfect fidelity, zero compression. +2. Each round the LLM receives the function signature, the current Pareto + frontier (with the source code that produced each point), and the last + attempt's result. +3. The runtime compiles the new implementation into a shared library and + hot-swaps it in, then evaluates MSE and distinct-level count on fixed + test data. +4. Non-dominated (num_levels, MSE) pairs are tracked on a Pareto frontier + that grows across rounds. + +The prompt is structured into clear sections (Task, Constraints, Goal, +Current Frontier, Last Attempt, Direction) so the LLM can make informed +trade-offs rather than searching blindly. + +## Pareto frontier progression + +The plot below shows how the frontier evolves over 20 rounds on a Laplacian +distribution with 10,000 samples. Each coloured line is the frontier snapshot +after that round. The x-axis (log2) is the number of distinct output levels; +the y-axis is MSE. + +![Pareto Frontier Progression](pareto_frontier.png) + +## Running + +```sh +cargo run -p quantize-example +``` + +Requires `API_KEY`, `BASE_URL`, and `MODEL` environment variables (or a +`.env` file) for the LLM backend. diff --git a/examples/quantize/pareto_frontier.png b/examples/quantize/pareto_frontier.png new file mode 100644 index 0000000000000000000000000000000000000000..b0a92e8b4991ebac1d176e2a556bde27ea6dddf1 GIT binary patch literal 257909 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 oAON>-56d?T&*_OuoOe{`%3XyGE;2xS6~{Iyk- zPqGwO>BtlbAqR?Qh|#{FkHuO;;~k@q-LlbW8bP{E4@hhBf@1XQ)93~LI|8TXb-n)% zch*qMJVWne_Ht)?1x`)2#00Ylxb?uPV4S|8sVM?(aAysjLb-KDSY>PO>~$kRvBgGS z+wr6$3X4N^+kWUqvV_c=*R#IK;sz2)eh)7WAKsk)?5~dh`+-*e6%o}3c4rM(_|NEd zTCYo3lP&R2x0fbq$)+tD+*!jVto8QNMt8Q(=+0W$64u>WYlu0|K1%U~;KeNB19H>? zNoFRhqL7D~!`^f)(2IT-y^m=DEzzxl=IG(#O?VpCpd}1gAU^`<-P{Z6rGnTV5fogs z<^}b-gf-mVS&yhbusa*CS2YFCv8se;iR|BPOMGHsOZ?b~D%&oBCT(nG7Pv6zLN2ylNs5moi0+U>{|9r13q3#XS@_l33w)CeF zSbBeKF+Ap-zhy_A_d?DqNCJC4dQ~hk?mt>^66h4^m&V>ir9OaX`~fcF{PF^P>-dNR zJ-+tXmjASp+~6}26d^A@>EB;+AG`$ zWRZ6kWxKud{2BS=!92w_p2AVo>Sz~nckYOtkD#_>rP^0m?Mr!xoxn9|a?q3w6!`mp zb3<64EUb?c*2NuZ|C{5ix;LZh&h8mFa(nqeQS(5i(p^`HWbeE0o&Z9-M0YnzcXz(1 zeZCm!;eY~;+C9igO*!#lP3c|Lv4U!w?JE@LxSDfF3gm^{oy$G@&P23ZibkTc77s}V z(TS0+mCw!gYoK58Okv#u_t2|H9Xh^Dtf!tSO<@@|VY)IqU0L3NDU_q%_lRe2+!APS z2(;JSwb!CS_k7U3OlHH*ItJaWL~SmjK)FnnD^B~ zvYN3%+pt2S*6$tAi`qKbvC z(y*q>F7iCv!F^FYsZUaD``89K>JB7wY?O-})$8!NBMQ303$Rz*Lo))ak+$iY*%#Yz zJ9-c?xHk=)n*77+Mlh6=QA|5%cHIcg>_SMWXqXmTD9erX{Kv-)FL= z%2+9JbLhs6?cbr+0v{E%j1aX{@N^ZFbid?C>&LS2%d*ND>8Nt7z&V^+jIT>%o)yQL zm<(0CRE6x`or1Hq$B4!6-W|lzRSR_0+R`R%1`6k*Yuc%B616CW{=juj_wm~e@E4^S z!A@!#g3GjUr7aij#jPL5rB(Z7yfAriR(wWXd}o9xYlutQIs3G8ql0dvPM|j(^+-b2 z-N_bdvIQs|h;ywPFHs(pG zduZhyA0Zc+*FKlmzLejw^mdcQM{dy`X%tb6Vz*1F;$zS~jo&scMK0G|mZ!

2fMB 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 QgZ~!HTl`w$ub037e}xQ1rvLx| literal 0 HcmV?d00001 diff --git a/examples/quantize/src/main.rs b/examples/quantize/src/main.rs index 2d9d1e6..075a0ad 100644 --- a/examples/quantize/src/main.rs +++ b/examples/quantize/src/main.rs @@ -10,7 +10,7 @@ //! reduce the number of distinct output values while keeping MSE low. //! //! Each round: -//! 1. Call the evolvable `quantize` function on data from multiple distributions. +//! 1. Call the evolvable `quantize` function on data from a specific distribution. //! 2. Measure MSE (reconstruction error) and count distinct output values. //! 3. Update the Pareto frontier of (num_levels, MSE) trade-offs. //! 4. Feed the frontier and per-distribution results back to the LLM. From fe35cb2d92517ee848b7ca1b791e27c87aea7a66 Mon Sep 17 00:00:00 2001 From: MathisWellmann Date: Tue, 28 Apr 2026 23:07:04 +0200 Subject: [PATCH 14/16] quantize: use `colorgradient` for a better look. --- Cargo.lock | 91 ++++++++++++++++++++++++++++++++++- examples/quantize/Cargo.toml | 1 + examples/quantize/src/main.rs | 20 ++------ 3 files changed, 95 insertions(+), 17 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0859b2f..a0c1626 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -380,6 +380,16 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" +[[package]] +name = "colorgrad" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aee94de557db6ddae3ca7b37b5fbe77ed6f159219f1815a8c2c1a9854c73087e" +dependencies = [ + "csscolorparser", + "libm", +] + [[package]] name = "combine" version = "4.6.7" @@ -599,6 +609,17 @@ dependencies = [ "typenum", ] +[[package]] +name = "csscolorparser" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "199f851bd3cb5004c09474252c7f74e7c047441ed0979bf3688a7106a13da952" +dependencies = [ + "num-traits", + "phf", + "serde", +] + [[package]] name = "ctor" version = "0.1.26" @@ -666,7 +687,7 @@ dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -1568,6 +1589,12 @@ dependencies = [ "windows-link", ] +[[package]] +name = "libm" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" + [[package]] name = "libredox" version = "0.1.16" @@ -1818,6 +1845,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", + "libm", ] [[package]] @@ -1919,6 +1947,51 @@ version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" +[[package]] +name = "phf" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf" +dependencies = [ + "phf_macros", + "phf_shared", + "serde", +] + +[[package]] +name = "phf_generator" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "135ace3a761e564ec88c03a77317a7c6b80bb7f7135ef2544dbe054243b89737" +dependencies = [ + "fastrand", + "phf_shared", +] + +[[package]] +name = "phf_macros" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812f032b54b1e759ccd5f8b6677695d5268c588701effba24601f6932f8269ef" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro2", + "quote", + "syn 2.0.117", + "uncased", +] + +[[package]] +name = "phf_shared" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e57fef6bc5981e38c2ce2d63bfa546861309f875b8a75f092d1d54ae2d64f266" +dependencies = [ + "siphasher", + "uncased", +] + [[package]] name = "pin-project" version = "1.1.11" @@ -2095,6 +2168,7 @@ dependencies = [ name = "quantize-example" version = "0.1.0" dependencies = [ + "colorgrad", "derive_more", "plotters", "rig-core", @@ -2791,6 +2865,12 @@ dependencies = [ "quote", ] +[[package]] +name = "siphasher" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e" + [[package]] name = "slab" version = "0.4.12" @@ -3280,6 +3360,15 @@ version = "1.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de" +[[package]] +name = "uncased" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1b88fcfe09e89d3866a5c11019378088af2d24c3fbd4f0543f96b479ec90697" +dependencies = [ + "version_check", +] + [[package]] name = "unicase" version = "2.9.0" diff --git a/examples/quantize/Cargo.toml b/examples/quantize/Cargo.toml index b7d5808..26ec536 100644 --- a/examples/quantize/Cargo.toml +++ b/examples/quantize/Cargo.toml @@ -21,3 +21,4 @@ plotters = { version = "0.3", default-features = false, features = [ "line_series", ] } viuer = { version = "0.11", features = ["print-file"] } +colorgrad = { version = "0.8", features = ["preset"] } diff --git a/examples/quantize/src/main.rs b/examples/quantize/src/main.rs index 075a0ad..4868150 100644 --- a/examples/quantize/src/main.rs +++ b/examples/quantize/src/main.rs @@ -24,6 +24,7 @@ use std::{ fmt::Display, }; +use colorgrad::Gradient; use plotters::prelude::*; use romu::Rng; use symbiont::Runtime; @@ -282,21 +283,6 @@ impl Display for EvalResult { // -- Plotting ---------------------------------------------------------------- -/// Palette of colours for successive generations. -const PALETTE: &[RGBColor] = &[ - RGBColor(31, 119, 180), - RGBColor(255, 127, 14), - RGBColor(44, 160, 44), - RGBColor(214, 39, 40), - RGBColor(148, 103, 189), - RGBColor(140, 86, 75), - RGBColor(227, 119, 194), - RGBColor(127, 127, 127), - RGBColor(188, 189, 34), - RGBColor(23, 190, 207), - RGBColor(174, 199, 232), -]; - /// Render the Pareto frontier progression to a PNG and display it with `viuer`. fn plot_frontier_progression( history: &[(usize, Vec<(f64, f64)>)], @@ -351,8 +337,10 @@ fn plot_frontier_progression( .y_desc("MSE") .draw()?; + let grad = colorgrad::preset::turbo(); for (round, pts) in history { - let color = PALETTE[*round % PALETTE.len()]; + let color = grad.at(*round as f32 / history.len() as f32).to_rgba8(); + let color = RGBColor(color[0], color[1], color[2]); let label = format!("Round {round}"); // Draw the frontier line + points for this generation. From eff544d7e06c4ed204ec8823fce86f2629c813f0 Mon Sep 17 00:00:00 2001 From: MathisWellmann Date: Tue, 28 Apr 2026 23:36:00 +0200 Subject: [PATCH 15/16] quantize: include run 2 with new colors. --- .env | 2 +- README.md | 9 +- examples/quantize/README.md | 165 +++++++++++++++++++++++-- examples/quantize/run-2-new-colors.png | Bin 0 -> 207030 bytes examples/quantize/src/main.rs | 16 +-- 5 files changed, 164 insertions(+), 28 deletions(-) create mode 100644 examples/quantize/run-2-new-colors.png diff --git a/.env b/.env index f786bcd..909d742 100644 --- a/.env +++ b/.env @@ -1,3 +1,3 @@ BASE_URL="http://127.0.0.1:8001/v1" API_KEY="" -MODEL="unsloth/Qwen3.6-35B-A3B-GGUF:UD-Q4_K_M" +MODEL="unsloth/Qwen3.6-27B-GGUF:BF16" diff --git a/README.md b/README.md index 8bc5807..56e7280 100644 --- a/README.md +++ b/README.md @@ -122,13 +122,14 @@ while `symbiont`'s constrained generation loop ensures the LLM output always com ## Use cases - Typed function body search (e.g., find an implementation that satisfies a test suite). - - See [fizzbuzz-example](examples/fizzbuzz/src/main.rs) - - See [rastrigin-example](examples/rastrigin/src/main.rs) + - See [fizzbuzz-example](examples/fizzbuzz/README.md) + - See [rastrigin-example](examples/rastrigin/README.md) - Performance Optimization under functional equivalence - - See [sort-example](examples/sort/src/main.rs) + - See [sort-example](examples/sort/README.md) - Game AI / strategic reasoning through evolved code - - See [tictactoe-example](examples/tictactoe/src/main.rs) + - See [tictactoe-example](examples/tictactoe/README.md) - Auto-research workflows with native-speed evaluation. + - See [quatize-example](examples/quantize/README.md) - Black-box optimization of inputs that produce desired outputs, e.g. Parameter Search. - Self-evolving feature processing pipelines. - Agentic code evolution generally. diff --git a/examples/quantize/README.md b/examples/quantize/README.md index 3bc5b2c..334df5f 100644 --- a/examples/quantize/README.md +++ b/examples/quantize/README.md @@ -1,20 +1,20 @@ # Quantize Example -Optimal quantization via LLM-driven evolutionary search. +Optimal quantization via LLM-driven agentic search. The LLM must implement a function that quantizes `f64` values into fewer distinct levels, minimizing reconstruction error (MSE) while using as few distinct output values as possible. ## How it works -1. An evolvable `quantize(input, len, output)` function starts as an identity - copy — perfect fidelity, zero compression. +1. An evolvable `fn quantize(input: &[f64], len: usize, output: &mut [f64])` + function starts as an identity copy — perfect fidelity, zero compression. 2. Each round the LLM receives the function signature, the current Pareto frontier (with the source code that produced each point), and the last attempt's result. -3. The runtime compiles the new implementation into a shared library and +3. The runtime compiles the new implementation into a shared library in `--release` mode and hot-swaps it in, then evaluates MSE and distinct-level count on fixed - test data. + test data, which is a laplacian distribution, samples 10_000 times, just to make it interesting. 4. Non-dominated (num_levels, MSE) pairs are tracked on a Pareto frontier that grows across rounds. @@ -22,20 +22,163 @@ The prompt is structured into clear sections (Task, Constraints, Goal, Current Frontier, Last Attempt, Direction) so the LLM can make informed trade-offs rather than searching blindly. +## Running + +```sh +cargo run -p quantize-example +``` + +Requires `API_KEY`, `BASE_URL`, and `MODEL` environment variables (or a +`.env` file) for the LLM backend. + +## Possible Improvements (Left as an excercise to the reader): +- Compare against some baseline algorithms (Uniform, Symmetric, Asymmetric, etc, who knows) +- Run on different input distributions to get specialized schemes. +- Prompting changes / context engineering might help the Agent reach better implementations. +- Try different SOTA models. `unsloth/Qwen3.6-35B-A3B-GGUF:UD-Q4_K_M` might be sub-optimal here. +- Run for more than 20 generations. +- Store the pareto frontier code somewhere for later retrieval (Not just printing it to stdout.) +- More rigorous evaluation procedure (Runtime is dominated by inference latency). +- Better progression plots, etc. + ## Pareto frontier progression The plot below shows how the frontier evolves over 20 rounds on a Laplacian distribution with 10,000 samples. Each coloured line is the frontier snapshot after that round. The x-axis (log2) is the number of distinct output levels; the y-axis is MSE. +Model used is `unsloth/Qwen3.6-35B-A3B-GGUF:UD-Q4_K_M` running with `llama-cpp` on an `RTX Pro 6000 Blackwell (96GB)` ![Pareto Frontier Progression](pareto_frontier.png) -## Running +## Another Run (color, prompting and model change) -```sh -cargo run -p quantize-example -``` +Now with `unsloth/Qwen3.6-27B-GGUF:BF16` -Requires `API_KEY`, `BASE_URL`, and `MODEL` environment variables (or a -`.env` file) for the LLM backend. +![Pareto Frontier Progression 2](run-2-new-colors.png) + +### Output: + +Evolution complete after 20 rounds. +Final aggregate Pareto frontier: +Laplacian frontier: +| Distinct levels | Bits/value | MSE | Round | +|-----------------|------------|------------|-------| +| 64 | 6.0 | 5.6879e-3 | 18 | +| 128 | 7.0 | 4.3793e-3 | 5 | +| 162 | 8.0 | 5.0992e-4 | 3 | +| 10000 | 14.0 | 0.0000e0 | 0 | + +Last implementation: +```rust +#[unsafe(no_mangle)] +pub fn quantize(input: &[f64], len: usize, output: &mut [f64]) { + if len == 0 { + return; + } + if len == 1 { + output[0] = input[0]; + return; + } + let mut data: Vec<(f64, usize)> = input + .iter() + .take(len) + .enumerate() + .map(|(i, &v)| (v, i)) + .collect(); + data.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap_or(std::cmp::Ordering::Equal)); + let k = 64; + let k = if len < k { len } else { k }; + if k <= 1 { + let mean = data.iter().map(|&(v, _)| v).sum::() / len as f64; + for &(_, idx) in &data { + output[idx] = mean; + } + return; + } + let min_v = data[0].0; + let max_v = data[len - 1].0; + let range = max_v - min_v; + if range.abs() < f64::EPSILON { + for &(_, idx) in &data { + output[idx] = min_v; + } + return; + } + let mut centroids: Vec = Vec::with_capacity(k); + let chunk = len / k; + for i in 0..k { + let start = i * chunk; + let end = if i == k - 1 { len } else { (i + 1) * chunk }; + let mut sum = 0.0_f64; + for j in start..end { + sum += data[j].0; + } + centroids.push(sum / (end - start) as f64); + } + let eps = range.abs().max(1.0) * 1e-12; + centroids.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal)); + for i in 1..k { + if centroids[i] <= centroids[i - 1] { + centroids[i] = centroids[i - 1] + eps; + } + } + let mut counts = vec![0usize; k]; + let mut sums = vec![0.0_f64; k]; + let mut empty_bins: Vec = Vec::with_capacity(k); + let tol = range.abs() * 1e-14; + for _ in 0..200 { + counts.fill(0); + sums.fill(0.0); + empty_bins.clear(); + let mut bin = 0; + for i in 0..len { + let v = data[i].0; + while bin < k - 1 && v >= (centroids[bin] + centroids[bin + 1]) * 0.5 { + bin += 1; + } + counts[bin] += 1; + sums[bin] += v; + } + let mut max_shift = 0.0; + for i in 0..k { + if counts[i] > 0 { + let new_c = sums[i] / counts[i] as f64; + let shift = (new_c - centroids[i]).abs(); + if shift > max_shift { + max_shift = shift; + } + centroids[i] = new_c; + } else { + empty_bins.push(i); + } + } + for &i in &empty_bins { + if i == 0 { + centroids[i] = centroids[1] - eps; + } else if i == k - 1 { + centroids[i] = centroids[k - 2] + eps; + } else { + centroids[i] = (centroids[i - 1] + centroids[i + 1]) * 0.5; + } + } + centroids.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal)); + for i in 1..k { + if centroids[i] <= centroids[i - 1] { + centroids[i] = centroids[i - 1] + eps; + } + } + if max_shift <= tol { + break; + } + } + let mut bin = 0; + for i in 0..len { + let v = data[i].0; + while bin < k - 1 && v >= (centroids[bin] + centroids[bin + 1]) * 0.5 { + bin += 1; + } + output[data[i].1] = centroids[bin]; + } +} +``` diff --git a/examples/quantize/run-2-new-colors.png b/examples/quantize/run-2-new-colors.png new file mode 100644 index 0000000000000000000000000000000000000000..37ad30457164bb645b0f845b0169955723148c9e GIT binary patch literal 207030 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 E4VGp(CIA2c literal 0 HcmV?d00001 diff --git a/examples/quantize/src/main.rs b/examples/quantize/src/main.rs index 4868150..3b043c6 100644 --- a/examples/quantize/src/main.rs +++ b/examples/quantize/src/main.rs @@ -295,18 +295,10 @@ fn plot_frontier_progression( return Ok(()); } - let x_min = all_points.iter().map(|p| p.0).fold(f64::INFINITY, f64::min); - let x_max = all_points.iter().map(|p| p.0).fold(0.0f64, f64::max); - let y_min = all_points - .iter() - .map(|p| p.1) - .filter(|y| y.is_finite()) - .fold(f64::INFINITY, f64::min); - let y_max = all_points - .iter() - .map(|p| p.1) - .filter(|y| y.is_finite()) - .fold(0.0f64, f64::max); + let x_min = 16_f64; + let x_max = 10_000_f64; + let y_min = 0_f64; + let y_max = 0.035; // Add some padding to the ranges. let x_lo = (x_min / 1.5).max(1.0); From 0b4ad321cdce656e9186ddc489d799021d44188e Mon Sep 17 00:00:00 2001 From: MathisWellmann Date: Wed, 29 Apr 2026 10:38:17 +0200 Subject: [PATCH 16/16] resolve `cargo clippy` warnings. --- examples/quantize/Cargo.toml | 4 ++-- examples/quantize/src/main.rs | 8 ++++---- symbiont/src/compiler.rs | 8 ++++---- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/examples/quantize/Cargo.toml b/examples/quantize/Cargo.toml index 26ec536..675a4f0 100644 --- a/examples/quantize/Cargo.toml +++ b/examples/quantize/Cargo.toml @@ -13,12 +13,12 @@ romu.workspace = true tokio.workspace = true tracing.workspace = true +colorgrad = { version = "0.8", features = ["preset"] } derive_more = { version = "2", features = ["full"] } plotters = { version = "0.3", default-features = false, features = [ - "ttf", "bitmap_backend", "bitmap_encoder", "line_series", + "ttf", ] } viuer = { version = "0.11", features = ["print-file"] } -colorgrad = { version = "0.8", features = ["preset"] } diff --git a/examples/quantize/src/main.rs b/examples/quantize/src/main.rs index 3b043c6..998a610 100644 --- a/examples/quantize/src/main.rs +++ b/examples/quantize/src/main.rs @@ -261,15 +261,15 @@ impl Display for EvalResult { )?; if self.panic.is_some() { - write!( + writeln!( f, - "| {:<12} | PANIC | | |\n", + "| {:<12} | PANIC | | |", self.distr.to_string() )?; } else { - write!( + writeln!( f, - "| {:<12} | {:>8} | {:>8.1} | {:>10.4e} |\n", + "| {:<12} | {:>8} | {:>8.1} | {:>10.4e} |", self.distr.to_string(), self.num_distinct, self.bits_per_value, diff --git a/symbiont/src/compiler.rs b/symbiont/src/compiler.rs index 90968df..c6b621a 100644 --- a/symbiont/src/compiler.rs +++ b/symbiont/src/compiler.rs @@ -55,7 +55,7 @@ pub(crate) async fn compile_dylib( ) -> Result<()> { let t0 = Instant::now(); - let clean_code = unparse(&clean_ast); + let clean_code = unparse(clean_ast); debug!("clean_code: {clean_code}"); let clean_path = crate_dir.join("src").join("clean.rs"); std::fs::write(&clean_path, &clean_code) @@ -65,7 +65,7 @@ pub(crate) async fn compile_dylib( wrap_bodies_in_catch_unwind(clean_ast); // Write final lib.rs (preamble + wrapped code) for compilation. - let formatted = format!("{PANIC_PREAMBLE}\n{}", unparse(&clean_ast)); + let formatted = format!("{PANIC_PREAMBLE}\n{}", unparse(clean_ast)); std::fs::write(crate_dir.join("src").join("lib.rs"), formatted)?; info!("Created temp dylib crate at {}", crate_dir.display()); @@ -85,7 +85,7 @@ pub(crate) async fn compile_dylib( .output() .await .map_err(|e| Error::CompilationFailed { - code: clean_code.to_string(), + code: clean_code.clone(), err: format!("Failed to spawn cargo: {e}"), })?; @@ -98,7 +98,7 @@ pub(crate) async fn compile_dylib( } else { let err = String::from_utf8_lossy(&output.stderr).to_string(); Err(Error::CompilationFailed { - code: clean_code.to_string(), + code: clean_code.clone(), err, }) }