From e9254f11e3969063085e51afac68ee9e212c385d Mon Sep 17 00:00:00 2001 From: Ciaran Ryan-Anderson Date: Sat, 12 Jul 2025 13:44:11 -0600 Subject: [PATCH 1/6] Minimal Rust wrapper for PCG --- Cargo.lock | 8 +++ Cargo.toml | 2 + .../tests/simple_determinism_test.rs | 2 +- crates/pecos-clib-pcg/Cargo.toml | 20 ++++++++ crates/pecos-clib-pcg/README.md | 33 ++++++++++++ crates/pecos-clib-pcg/build.rs | 29 +++++++++++ crates/pecos-clib-pcg/src/lib.rs | 51 +++++++++++++++++++ crates/pecos/Cargo.toml | 1 + crates/pecos/src/prelude.rs | 6 +++ python/pecos-rslib/rust/src/lib.rs | 2 + python/pecos-rslib/rust/src/pcg_bindings.rs | 49 ++++++++++++++++++ .../src/pecos/engines/cvm/rng_model.py | 9 +++- 12 files changed, 209 insertions(+), 3 deletions(-) create mode 100644 crates/pecos-clib-pcg/Cargo.toml create mode 100644 crates/pecos-clib-pcg/README.md create mode 100644 crates/pecos-clib-pcg/build.rs create mode 100644 crates/pecos-clib-pcg/src/lib.rs create mode 100644 python/pecos-rslib/rust/src/pcg_bindings.rs diff --git a/Cargo.lock b/Cargo.lock index 7e2697500..86686d115 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1167,6 +1167,7 @@ name = "pecos" version = "0.1.1" dependencies = [ "log", + "pecos-clib-pcg", "pecos-core", "pecos-engines", "pecos-phir", @@ -1189,6 +1190,13 @@ dependencies = [ "serde_json", ] +[[package]] +name = "pecos-clib-pcg" +version = "0.1.1" +dependencies = [ + "cc", +] + [[package]] name = "pecos-core" version = "0.1.1" diff --git a/Cargo.toml b/Cargo.toml index 48ea11978..a64deadf9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,6 +49,7 @@ tempfile = "3" assert_cmd = "2" wasmtime = "33" serial_test = "3" +cc = "1" pecos-core = { version = "0.1.1", path = "crates/pecos-core" } pecos-qsim = { version = "0.1.1", path = "crates/pecos-qsim" } @@ -57,6 +58,7 @@ pecos-phir = { version = "0.1.1", path = "crates/pecos-phir" } pecos-engines = { version = "0.1.1", path = "crates/pecos-engines" } pecos-qir = { version = "0.1.1", path = "crates/pecos-qir" } pecos-qec = { version = "0.1.1", path = "crates/pecos-qec" } +pecos-clib-pcg = { version = "0.1.1", path = "crates/pecos-clib-pcg" } pecos = { version = "0.1.1", path = "crates/pecos" } pecos-cli = { version = "0.1.1", path = "crates/pecos-cli" } pecos-rslib = { version = "0.1.1", path = "python/pecos-rslib/rust" } diff --git a/crates/pecos-cli/tests/simple_determinism_test.rs b/crates/pecos-cli/tests/simple_determinism_test.rs index e315e4aa5..f7330d3c7 100644 --- a/crates/pecos-cli/tests/simple_determinism_test.rs +++ b/crates/pecos-cli/tests/simple_determinism_test.rs @@ -254,7 +254,7 @@ fn test_noise_impact_on_determinism() -> Result<(), Box> /// /// NOTE: Currently skipped as worker count determinism is an open issue in PECOS #[test] -#[ignore] +#[ignore = "worker count determinism is an open issue in PECOS"] fn test_worker_count_consistency() -> Result<(), Box> { let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); let phir_path = manifest_dir.join("../../examples/phir/simple_test.json"); diff --git a/crates/pecos-clib-pcg/Cargo.toml b/crates/pecos-clib-pcg/Cargo.toml new file mode 100644 index 000000000..efa86a6b1 --- /dev/null +++ b/crates/pecos-clib-pcg/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "pecos-clib-pcg" +version.workspace = true +edition.workspace = true +authors.workspace = true +homepage.workspace = true +repository.workspace = true +license.workspace = true +keywords.workspace = true +categories.workspace = true +description = "PCG RNG C library wrapper for PECOS" +readme = "README.md" + +[dependencies] + +[build-dependencies] +cc.workspace = true + +[lints] +workspace = true diff --git a/crates/pecos-clib-pcg/README.md b/crates/pecos-clib-pcg/README.md new file mode 100644 index 000000000..7d359a4e0 --- /dev/null +++ b/crates/pecos-clib-pcg/README.md @@ -0,0 +1,33 @@ +# pecos-clib-pcg + +Rust wrapper for the PCG C library used in PECOS. + +This crate provides safe Rust bindings to the PCG random number generator implementation in C. + +## Features + +- `pcg32_random()` - Generate a 32-bit random number +- `pcg32_boundedrand(bound)` - Generate a random number in range [0, bound) +- `pcg32_frandom()` - Generate a random floating-point number in [0, 1) +- `pcg32_srandom(seed)` - Set the random seed + +## Usage + +This crate is primarily used internally by PECOS and exposed through the main `pecos` crate's prelude. + +```rust +use pecos_clib_pcg::{random, boundedrand, frandom, srandom}; + +// Set seed for reproducibility +srandom(12345); + +// Generate random values +let r1 = random(); // 32-bit random number +let r2 = boundedrand(100); // Random number in [0, 100) +let r3 = frandom(); // Random float in [0, 1) +``` + +## Implementation + +This crate uses the `cc` build dependency to compile the existing C implementation from `clibs/pecos-rng/src/rng_pcg.c` +and provides safe Rust wrappers around the unsafe FFI functions. diff --git a/crates/pecos-clib-pcg/build.rs b/crates/pecos-clib-pcg/build.rs new file mode 100644 index 000000000..e06496d1a --- /dev/null +++ b/crates/pecos-clib-pcg/build.rs @@ -0,0 +1,29 @@ +use cc::Build; +use std::env; +use std::path::PathBuf; + +fn main() { + let clib_path = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()) + .parent() + .unwrap() + .parent() + .unwrap() + .join("clibs") + .join("pecos-rng"); + + let src_path = clib_path.join("src"); + + println!( + "cargo:rerun-if-changed={}", + src_path.join("rng_pcg.c").display() + ); + println!( + "cargo:rerun-if-changed={}", + src_path.join("rng_pcg.h").display() + ); + + Build::new() + .file(src_path.join("rng_pcg.c")) + .include(&src_path) + .compile("pecos_pcg"); +} diff --git a/crates/pecos-clib-pcg/src/lib.rs b/crates/pecos-clib-pcg/src/lib.rs new file mode 100644 index 000000000..f44696de9 --- /dev/null +++ b/crates/pecos-clib-pcg/src/lib.rs @@ -0,0 +1,51 @@ +// FFI bindings to the C PCG library +unsafe extern "C" { + fn pcg32_random() -> u32; + fn pcg32_boundedrand(bound: u32) -> u32; + fn pcg32_frandom() -> f64; + fn pcg32_srandom(seq: u64); +} + +// Rust wrapper functions with safe interfaces +#[must_use] +pub fn random() -> u32 { + unsafe { pcg32_random() } +} + +#[must_use] +pub fn boundedrand(bound: u32) -> u32 { + unsafe { pcg32_boundedrand(bound) } +} + +#[must_use] +pub fn frandom() -> f64 { + unsafe { pcg32_frandom() } +} + +pub fn srandom(seq: u64) { + unsafe { pcg32_srandom(seq) } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_pcg_functions() { + // Set seed + srandom(12345); + + // Test basic random + let r1 = random(); + assert!(r1 > 0); + + // Test bounded random + let bound = 100; + let r2 = boundedrand(bound); + assert!(r2 < bound); + + // Test float random + let r3 = frandom(); + assert!((0.0..1.0).contains(&r3)); + } +} diff --git a/crates/pecos/Cargo.toml b/crates/pecos/Cargo.toml index 8f47df46c..8aa18be77 100644 --- a/crates/pecos/Cargo.toml +++ b/crates/pecos/Cargo.toml @@ -22,6 +22,7 @@ pecos-engines.workspace = true pecos-qasm.workspace = true pecos-phir.workspace = true pecos-qir.workspace = true +pecos-clib-pcg.workspace = true log.workspace = true serde_json.workspace = true diff --git a/crates/pecos/src/prelude.rs b/crates/pecos/src/prelude.rs index 129de05da..cd15fed83 100644 --- a/crates/pecos/src/prelude.rs +++ b/crates/pecos/src/prelude.rs @@ -65,3 +65,9 @@ pub use pecos_qir::setup_qir_engine; // Re-export run_sim from pecos-engines pub use pecos_engines::run_sim; + +// Re-export PCG RNG functions +pub use pecos_clib_pcg::{ + boundedrand as pcg32_boundedrand, frandom as pcg32_frandom, random as pcg32_random, + srandom as pcg32_srandom, +}; diff --git a/python/pecos-rslib/rust/src/lib.rs b/python/pecos-rslib/rust/src/lib.rs index 41d5644d4..1afde6542 100644 --- a/python/pecos-rslib/rust/src/lib.rs +++ b/python/pecos-rslib/rust/src/lib.rs @@ -18,6 +18,7 @@ mod byte_message_bindings; mod engine_bindings; +mod pcg_bindings; pub mod phir_bridge; mod sparse_sim; mod sparse_stab_bindings; @@ -43,5 +44,6 @@ fn _pecos_rslib(_py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> { m.add_class::()?; m.add_class::()?; m.add_class::()?; + pcg_bindings::create_pcg_module(m)?; Ok(()) } diff --git a/python/pecos-rslib/rust/src/pcg_bindings.rs b/python/pecos-rslib/rust/src/pcg_bindings.rs new file mode 100644 index 000000000..04e977022 --- /dev/null +++ b/python/pecos-rslib/rust/src/pcg_bindings.rs @@ -0,0 +1,49 @@ +// Copyright 2025 The PECOS Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except +// in compliance with the License.You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software distributed under the License +// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express +// or implied. See the License for the specific language governing permissions and limitations under +// the License. + +use pecos::prelude::*; +use pyo3::prelude::*; + +#[pyfunction] +#[pyo3(name = "pcg32_random")] +pub fn py_pcg32_random() -> u32 { + pcg32_random() +} + +#[pyfunction] +#[pyo3(name = "pcg32_boundedrand")] +pub fn py_pcg32_boundedrand(bound: u32) -> u32 { + pcg32_boundedrand(bound) +} + +#[pyfunction] +#[pyo3(name = "pcg32_frandom")] +pub fn py_pcg32_frandom() -> f64 { + pcg32_frandom() +} + +#[pyfunction] +#[pyo3(name = "pcg32_srandom")] +pub fn py_pcg32_srandom(seq: u64) { + pcg32_srandom(seq); +} + +/// Create a submodule for PCG functions +pub fn create_pcg_module(m: &Bound<'_, PyModule>) -> PyResult<()> { + let pcg_module = PyModule::new(m.py(), "pcg")?; + pcg_module.add_function(wrap_pyfunction!(py_pcg32_random, &pcg_module)?)?; + pcg_module.add_function(wrap_pyfunction!(py_pcg32_boundedrand, &pcg_module)?)?; + pcg_module.add_function(wrap_pyfunction!(py_pcg32_frandom, &pcg_module)?)?; + pcg_module.add_function(wrap_pyfunction!(py_pcg32_srandom, &pcg_module)?)?; + m.add_submodule(&pcg_module)?; + Ok(()) +} diff --git a/python/quantum-pecos/src/pecos/engines/cvm/rng_model.py b/python/quantum-pecos/src/pecos/engines/cvm/rng_model.py index de760a639..dfd08a5cd 100644 --- a/python/quantum-pecos/src/pecos/engines/cvm/rng_model.py +++ b/python/quantum-pecos/src/pecos/engines/cvm/rng_model.py @@ -1,10 +1,15 @@ """This Module is responsible for keeping track of the state for generating a sequence of random numbers. -It handles RNG platform function calls that that are handled by the pcg_rng library. +It handles RNG platform function calls that are handled by the pcg_rng library. """ -from pecos_pcg import pecos_rng +from __future__ import annotations + +try: + from pecos_pcg import pecos_rng +except ImportError: + from pecos_rslib._pecos_rslib import pcg as pecos_rng from pecos.engines.cvm.binarray import BinArray From cfac02b76120f8d1d4afa37ad7eff81cafb4bc20 Mon Sep 17 00:00:00 2001 From: Ciaran Ryan-Anderson Date: Sat, 12 Jul 2025 13:45:54 -0600 Subject: [PATCH 2/6] Add TODO to the rng_model.py --- python/quantum-pecos/src/pecos/engines/cvm/rng_model.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/python/quantum-pecos/src/pecos/engines/cvm/rng_model.py b/python/quantum-pecos/src/pecos/engines/cvm/rng_model.py index dfd08a5cd..028acc8e7 100644 --- a/python/quantum-pecos/src/pecos/engines/cvm/rng_model.py +++ b/python/quantum-pecos/src/pecos/engines/cvm/rng_model.py @@ -4,6 +4,8 @@ """ +# TODO: A Rust version of this RNGModel should be created for Rust side usage and then exposed via pecos-rslib/PyO3 + from __future__ import annotations try: From fdda8ea55a52c2bf908cd6542034754536a3bee7 Mon Sep 17 00:00:00 2001 From: Ciaran Ryan-Anderson Date: Sat, 12 Jul 2025 13:52:03 -0600 Subject: [PATCH 3/6] Renaming clibs/ to clib/ since a library is already a collection of things --- Makefile | 38 +++++++++---------- {clibs => clib}/pecos-rng/CMakeLists.txt | 0 {clibs => clib}/pecos-rng/README.md | 0 .../pecos-rng/pecos_pcg/__init__.py | 0 {clibs => clib}/pecos-rng/pyproject.toml | 0 {clibs => clib}/pecos-rng/src/rng_pcg.c | 0 {clibs => clib}/pecos-rng/src/rng_pcg.h | 0 {clibs => clib}/pecos-rng/src/wrapper.cpp | 0 crates/pecos-clib-pcg/README.md | 2 +- crates/pecos-clib-pcg/build.rs | 2 +- ruff.toml | 2 +- 11 files changed, 22 insertions(+), 22 deletions(-) rename {clibs => clib}/pecos-rng/CMakeLists.txt (100%) rename {clibs => clib}/pecos-rng/README.md (100%) rename {clibs => clib}/pecos-rng/pecos_pcg/__init__.py (100%) rename {clibs => clib}/pecos-rng/pyproject.toml (100%) rename {clibs => clib}/pecos-rng/src/rng_pcg.c (100%) rename {clibs => clib}/pecos-rng/src/rng_pcg.h (100%) rename {clibs => clib}/pecos-rng/src/wrapper.cpp (100%) diff --git a/Makefile b/Makefile index d94150ed7..144fdf4b0 100644 --- a/Makefile +++ b/Makefile @@ -23,7 +23,7 @@ installreqs: ## Install Python project requirements to root .venv buildrng: @echo "Building and installing RNG library..." uv pip install nanobind - cd clibs/pecos-rng && CC=gcc CXX=g++ uv pip install --python $(shell uv run which python) -e . + cd clib/pecos-rng && CC=gcc CXX=g++ uv pip install --python $(shell uv run which python) -e . # Building development environments # --------------------------------- @@ -155,12 +155,12 @@ clean-unix: @find . -type d -name "junit" -exec rm -rf {} + @find python -name "*.so" -delete @find python -name "*.pyd" -delete - @# Clean clibs build artifacts - @find clibs -type d -name "build" -exec rm -rf {} + - @find clibs -type d -name "dist" -exec rm -rf {} + - @find clibs -type d -name "*.egg-info" -exec rm -rf {} + - @find clibs -type d -name ".venv" -exec rm -rf {} + - @find clibs -name "uv.lock" -delete + @# Clean clib build artifacts + @find clib -type d -name "build" -exec rm -rf {} + + @find clib -type d -name "dist" -exec rm -rf {} + + @find clib -type d -name "*.egg-info" -exec rm -rf {} + + @find clib -type d -name ".venv" -exec rm -rf {} + + @find clib -name "uv.lock" -delete @# Clean all target directories in crates (in case they were built independently) @find crates -type d -name "target" -exec rm -rf {} + @find python -type d -name "target" -exec rm -rf {} + @@ -182,12 +182,12 @@ clean-windows-ps: @powershell -Command "Get-ChildItem -Path . -Recurse -Directory -Filter '.hypothesis' | Remove-Item -Recurse -Force -ErrorAction SilentlyContinue" @powershell -Command "Get-ChildItem -Path . -Recurse -Directory -Filter 'junit' | Remove-Item -Recurse -Force -ErrorAction SilentlyContinue" @powershell -Command "Get-ChildItem -Path python -Recurse -File -Include '*.so','*.pyd' | Remove-Item -Force -ErrorAction SilentlyContinue" - @# Clean clibs build artifacts - @powershell -Command "Get-ChildItem -Path clibs -Recurse -Directory -Filter 'build' | Remove-Item -Recurse -Force -ErrorAction SilentlyContinue" - @powershell -Command "Get-ChildItem -Path clibs -Recurse -Directory -Filter 'dist' | Remove-Item -Recurse -Force -ErrorAction SilentlyContinue" - @powershell -Command "Get-ChildItem -Path clibs -Recurse -Directory -Filter '*.egg-info' | Remove-Item -Recurse -Force -ErrorAction SilentlyContinue" - @powershell -Command "Get-ChildItem -Path clibs -Recurse -Directory -Filter '.venv' | Remove-Item -Recurse -Force -ErrorAction SilentlyContinue" - @powershell -Command "Get-ChildItem -Path clibs -Recurse -File -Filter 'uv.lock' | Remove-Item -Force -ErrorAction SilentlyContinue" + @# Clean clib build artifacts + @powershell -Command "Get-ChildItem -Path clib -Recurse -Directory -Filter 'build' | Remove-Item -Recurse -Force -ErrorAction SilentlyContinue" + @powershell -Command "Get-ChildItem -Path clib -Recurse -Directory -Filter 'dist' | Remove-Item -Recurse -Force -ErrorAction SilentlyContinue" + @powershell -Command "Get-ChildItem -Path clib -Recurse -Directory -Filter '*.egg-info' | Remove-Item -Recurse -Force -ErrorAction SilentlyContinue" + @powershell -Command "Get-ChildItem -Path clib -Recurse -Directory -Filter '.venv' | Remove-Item -Recurse -Force -ErrorAction SilentlyContinue" + @powershell -Command "Get-ChildItem -Path clib -Recurse -File -Filter 'uv.lock' | Remove-Item -Force -ErrorAction SilentlyContinue" @# Clean all target directories in crates @powershell -Command "Get-ChildItem -Path crates -Recurse -Directory -Filter 'target' | Remove-Item -Recurse -Force -ErrorAction SilentlyContinue" @powershell -Command "Get-ChildItem -Path python -Recurse -Directory -Filter 'target' | Remove-Item -Recurse -Force -ErrorAction SilentlyContinue" @@ -208,12 +208,12 @@ clean-windows-cmd: -@for /f "delims=" %%d in ('dir /s /b /ad .hypothesis 2^>nul') do @rd /s /q "%%d" 2>nul -@for /f "delims=" %%d in ('dir /s /b /ad junit 2^>nul') do @rd /s /q "%%d" 2>nul -@for /f "delims=" %%f in ('dir /s /b python\*.so python\*.pyd 2^>nul') do @del "%%f" 2>nul - -@REM Clean clibs build artifacts - -@for /f "delims=" %%d in ('dir /s /b /ad clibs\build 2^>nul') do @rd /s /q "%%d" 2>nul - -@for /f "delims=" %%d in ('dir /s /b /ad clibs\dist 2^>nul') do @rd /s /q "%%d" 2>nul - -@for /f "delims=" %%d in ('dir /s /b /ad clibs\*.egg-info 2^>nul') do @rd /s /q "%%d" 2>nul - -@for /f "delims=" %%d in ('dir /s /b /ad clibs\.venv 2^>nul') do @rd /s /q "%%d" 2>nul - -@for /f "delims=" %%f in ('dir /s /b clibs\uv.lock 2^>nul') do @del "%%f" 2>nul + -@REM Clean clib build artifacts + -@for /f "delims=" %%d in ('dir /s /b /ad clib\build 2^>nul') do @rd /s /q "%%d" 2>nul + -@for /f "delims=" %%d in ('dir /s /b /ad clib\dist 2^>nul') do @rd /s /q "%%d" 2>nul + -@for /f "delims=" %%d in ('dir /s /b /ad clib\*.egg-info 2^>nul') do @rd /s /q "%%d" 2>nul + -@for /f "delims=" %%d in ('dir /s /b /ad clib\.venv 2^>nul') do @rd /s /q "%%d" 2>nul + -@for /f "delims=" %%f in ('dir /s /b clib\uv.lock 2^>nul') do @del "%%f" 2>nul -@REM Clean all target directories in crates -@for /f "delims=" %%d in ('dir /s /b /ad crates\target 2^>nul') do @rd /s /q "%%d" 2>nul -@for /f "delims=" %%d in ('dir /s /b /ad python\target 2^>nul') do @rd /s /q "%%d" 2>nul diff --git a/clibs/pecos-rng/CMakeLists.txt b/clib/pecos-rng/CMakeLists.txt similarity index 100% rename from clibs/pecos-rng/CMakeLists.txt rename to clib/pecos-rng/CMakeLists.txt diff --git a/clibs/pecos-rng/README.md b/clib/pecos-rng/README.md similarity index 100% rename from clibs/pecos-rng/README.md rename to clib/pecos-rng/README.md diff --git a/clibs/pecos-rng/pecos_pcg/__init__.py b/clib/pecos-rng/pecos_pcg/__init__.py similarity index 100% rename from clibs/pecos-rng/pecos_pcg/__init__.py rename to clib/pecos-rng/pecos_pcg/__init__.py diff --git a/clibs/pecos-rng/pyproject.toml b/clib/pecos-rng/pyproject.toml similarity index 100% rename from clibs/pecos-rng/pyproject.toml rename to clib/pecos-rng/pyproject.toml diff --git a/clibs/pecos-rng/src/rng_pcg.c b/clib/pecos-rng/src/rng_pcg.c similarity index 100% rename from clibs/pecos-rng/src/rng_pcg.c rename to clib/pecos-rng/src/rng_pcg.c diff --git a/clibs/pecos-rng/src/rng_pcg.h b/clib/pecos-rng/src/rng_pcg.h similarity index 100% rename from clibs/pecos-rng/src/rng_pcg.h rename to clib/pecos-rng/src/rng_pcg.h diff --git a/clibs/pecos-rng/src/wrapper.cpp b/clib/pecos-rng/src/wrapper.cpp similarity index 100% rename from clibs/pecos-rng/src/wrapper.cpp rename to clib/pecos-rng/src/wrapper.cpp diff --git a/crates/pecos-clib-pcg/README.md b/crates/pecos-clib-pcg/README.md index 7d359a4e0..dbed9a341 100644 --- a/crates/pecos-clib-pcg/README.md +++ b/crates/pecos-clib-pcg/README.md @@ -29,5 +29,5 @@ let r3 = frandom(); // Random float in [0, 1) ## Implementation -This crate uses the `cc` build dependency to compile the existing C implementation from `clibs/pecos-rng/src/rng_pcg.c` +This crate uses the `cc` build dependency to compile the existing C implementation from `clib/pecos-rng/src/rng_pcg.c` and provides safe Rust wrappers around the unsafe FFI functions. diff --git a/crates/pecos-clib-pcg/build.rs b/crates/pecos-clib-pcg/build.rs index e06496d1a..551a248f1 100644 --- a/crates/pecos-clib-pcg/build.rs +++ b/crates/pecos-clib-pcg/build.rs @@ -8,7 +8,7 @@ fn main() { .unwrap() .parent() .unwrap() - .join("clibs") + .join("clib") .join("pecos-rng"); let src_path = clib_path.join("src"); diff --git a/ruff.toml b/ruff.toml index 36fffc649..dab0d7f43 100644 --- a/ruff.toml +++ b/ruff.toml @@ -91,7 +91,7 @@ ignore = [ "ANN", "D", ] -"clibs/pecos-rng/pecos_pcg/__init__.py" = ["TID252"] +"clib/pecos-rng/pecos_pcg/__init__.py" = ["TID252"] [lint.pycodestyle] From 95a6ddbdf85ad7bcf8b9133aa2552f3f1ae7da84 Mon Sep 17 00:00:00 2001 From: Ciaran Ryan-Anderson Date: Sat, 12 Jul 2025 14:16:54 -0600 Subject: [PATCH 4/6] Fixing more clibs to clib references --- .github/workflows/python-release.yml | 4 ++-- .github/workflows/python-test.yml | 2 +- .gitignore | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/python-release.yml b/.github/workflows/python-release.yml index be09eca8b..09ba7451d 100644 --- a/.github/workflows/python-release.yml +++ b/.github/workflows/python-release.yml @@ -204,7 +204,7 @@ jobs: - name: Build PECOS RNG run: | - cd clibs/pecos-rng && mkdir build && cd build/ && cmake .. + cd clib/pecos-rng && mkdir build && cd build/ && cmake .. make && cd .. && pip install . env: NANOBIND_DIR: python -m nanobind --include_dir @@ -261,7 +261,7 @@ jobs: - name: Build PECOS RNG run: | - cd clibs/pecos-rng && mkdir build && cd build/ && cmake .. + cd clib/pecos-rng && mkdir build && cd build/ && cmake .. make && cd .. && pip install . env: NANOBIND_DIR: python -m nanobind --include_dir diff --git a/.github/workflows/python-test.yml b/.github/workflows/python-test.yml index b374ea298..f3515f73f 100644 --- a/.github/workflows/python-test.yml +++ b/.github/workflows/python-test.yml @@ -77,7 +77,7 @@ jobs: run: | export NANOBIND_CMAKE_DIR=$(uv run -- python -m nanobind --cmake_dir) export NANOBIND_DIR=$(uv run -- python -m nanobind --include_dir) - cd clibs/pecos-rng && mkdir build && cd build/ && cmake .. -DCMAKE_PREFIX_PATH=$NANOBIND_CMAKE_DIR + cd clib/pecos-rng && mkdir build && cd build/ && cmake .. -DCMAKE_PREFIX_PATH=$NANOBIND_CMAKE_DIR cmake --build . && cd .. uv pip install . diff --git a/.gitignore b/.gitignore index 147a22cfd..f68be8703 100644 --- a/.gitignore +++ b/.gitignore @@ -178,5 +178,5 @@ cython_debug/ .idea/ # Prevent subdirectory virtual environments -clibs/*/.venv/ -clibs/*/uv.lock +clib/*/.venv/ +clib/*/uv.lock From ae26acfed0cbb5cc07f9d07a6ad96b739615e1a1 Mon Sep 17 00:00:00 2001 From: Ciaran Ryan-Anderson Date: Sat, 12 Jul 2025 14:20:20 -0600 Subject: [PATCH 5/6] Resolving maturin build failure due to not including C files for sdist --- python/pecos-rslib/pyproject.toml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/python/pecos-rslib/pyproject.toml b/python/pecos-rslib/pyproject.toml index 51cff5022..733ebe8f5 100644 --- a/python/pecos-rslib/pyproject.toml +++ b/python/pecos-rslib/pyproject.toml @@ -22,6 +22,11 @@ features = ["pyo3/extension-module"] python-source = "src" module-name = "pecos_rslib._pecos_rslib" manifest-path = "rust/Cargo.toml" +sdist-include = [ + "../../clib/**/*", + "../../crates/**/*", + "../../Cargo.toml", +] [tool.uv.sources] pecos-rslib = { workspace = true } From 2942c70d1a1b94d251eddec59ba77a20dc4b5b9f Mon Sep 17 00:00:00 2001 From: Ciaran Ryan-Anderson Date: Sat, 12 Jul 2025 15:52:12 -0600 Subject: [PATCH 6/6] Remove sdist building for pecos-rslib due to external C source issues. Users can just use the repo to build from source. --- .github/workflows/python-release.yml | 57 +-- Cargo.lock | 428 ++++++++++++++++++++++- crates/pecos-clib-pcg/Cargo.toml | 1 + crates/pecos-clib-pcg/README.md | 7 +- crates/pecos-clib-pcg/build.rs | 56 ++- crates/pecos-clib-pcg/tests/pcg_tests.rs | 89 +++++ python/pecos-rslib/pyproject.toml | 5 - 7 files changed, 559 insertions(+), 84 deletions(-) create mode 100644 crates/pecos-clib-pcg/tests/pcg_tests.rs diff --git a/.github/workflows/python-release.yml b/.github/workflows/python-release.yml index 09ba7451d..9f5cabe93 100644 --- a/.github/workflows/python-release.yml +++ b/.github/workflows/python-release.yml @@ -51,59 +51,6 @@ jobs: echo "run=false" >> $GITHUB_OUTPUT fi - build_sdist_pecos_rslib: - needs: check_pr_push - if: | - always() && - (needs.check_pr_push.result == 'success' && needs.check_pr_push.outputs.run == 'true' || needs.check_pr_push.result == 'skipped') && - (github.event_name != 'pull_request' || github.event.pull_request.merged == true || github.event.action == 'opened' || github.event.action == 'synchronize') - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - ref: ${{ inputs.sha || github.sha }} - - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: ${{ env.PYTHON_VERSION }} - - - name: Remove conflicting README.md - run: | - if [ -f crates/pecos-python/README.md ]; then - mv crates/pecos-python/README.md crates/pecos-python/README.md.bak - echo "Moved conflicting README.md to README.md.bak" - else - echo "No conflicting README.md found" - fi - - - name: Build pecos-rslib SDist - uses: PyO3/maturin-action@v1 - with: - command: sdist - args: --out dist - working-directory: python/pecos-rslib - - - name: Restore README.md - if: always() - run: | - if [ -f crates/pecos-python/README.md.bak ]; then - mv crates/pecos-python/README.md.bak crates/pecos-python/README.md - echo "Restored README.md from backup" - else - echo "No README.md backup found" - fi - - - name: Test pecos-rslib SDist - run: | - pip install --force-reinstall --verbose python/pecos-rslib/dist/*.tar.gz - python -c 'import pecos_rslib; print(pecos_rslib.__version__)' - - - name: Upload pecos-rslib SDist - uses: actions/upload-artifact@v4 - with: - name: sdist-pecos-rslib - path: python/pecos-rslib/dist/*.tar.gz build_wheels_pecos_rslib: needs: check_pr_push @@ -173,7 +120,7 @@ jobs: path: python/pecos-rslib/dist/*.whl build_sdist_quantum_pecos: - needs: [check_pr_push, build_sdist_pecos_rslib, build_wheels_pecos_rslib] + needs: [check_pr_push, build_wheels_pecos_rslib] if: | always() && (needs.check_pr_push.result == 'success' && needs.check_pr_push.outputs.run == 'true' || needs.check_pr_push.result == 'skipped') && @@ -226,7 +173,7 @@ jobs: path: python/quantum-pecos/dist/*.tar.gz build_wheels_quantum_pecos: - needs: [check_pr_push, build_wheels_pecos_rslib, build_sdist_quantum_pecos] + needs: [check_pr_push, build_wheels_pecos_rslib] if: | always() && (needs.check_pr_push.result == 'success' && needs.check_pr_push.outputs.run == 'true' || needs.check_pr_push.result == 'skipped') && diff --git a/Cargo.lock b/Cargo.lock index 86686d115..5db70821e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11,6 +11,12 @@ dependencies = [ "gimli", ] +[[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.3" @@ -68,7 +74,7 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" dependencies = [ - "windows-sys", + "windows-sys 0.59.0", ] [[package]] @@ -79,7 +85,7 @@ checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" dependencies = [ "anstyle", "once_cell", - "windows-sys", + "windows-sys 0.59.0", ] [[package]] @@ -600,6 +606,17 @@ dependencies = [ "winapi", ] +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "doc-comment" version = "0.3.3" @@ -675,7 +692,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.59.0", ] [[package]] @@ -690,12 +707,31 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" +[[package]] +name = "flate2" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + [[package]] name = "foldhash" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + [[package]] name = "funty" version = "2.0.0" @@ -877,12 +913,119 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" +[[package]] +name = "icu_collections" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" +dependencies = [ + "displaydoc", + "potential_utf", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" + +[[package]] +name = "icu_properties" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "potential_utf", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" + +[[package]] +name = "icu_provider" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" +dependencies = [ + "displaydoc", + "icu_locale_core", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + [[package]] name = "id-arena" version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005" +[[package]] +name = "idna" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + [[package]] name = "indexmap" version = "2.9.0" @@ -1028,6 +1171,12 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" +[[package]] +name = "litemap" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" + [[package]] name = "lock_api" version = "0.4.12" @@ -1077,6 +1226,15 @@ dependencies = [ "autocfg", ] +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", +] + [[package]] name = "num-bigint" version = "0.4.6" @@ -1195,6 +1353,7 @@ name = "pecos-clib-pcg" version = "0.1.1" dependencies = [ "cc", + "ureq", ] [[package]] @@ -1295,6 +1454,12 @@ dependencies = [ "pyo3-build-config", ] +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + [[package]] name = "pest" version = "2.8.0" @@ -1404,6 +1569,15 @@ dependencies = [ "serde", ] +[[package]] +name = "potential_utf" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" +dependencies = [ + "zerovec", +] + [[package]] name = "ppv-lite86" version = "0.2.20" @@ -1670,6 +1844,20 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.16", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + [[package]] name = "rustc-demangle" version = "0.1.24" @@ -1692,7 +1880,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.4.15", - "windows-sys", + "windows-sys 0.59.0", ] [[package]] @@ -1705,7 +1893,42 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.9.4", - "windows-sys", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustls" +version = "0.23.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2491382039b29b9b11ff08b76ff6c97cf287671dbb74f0be44bda389fffe9bd1" +dependencies = [ + "log", + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pki-types" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" +dependencies = [ + "zeroize", +] + +[[package]] +name = "rustls-webpki" +version = "0.103.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a17884ae0c1b773f1ccd2bd4a8c72f16da897310a98b0e84bf349ad5ead92fc" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", ] [[package]] @@ -1878,6 +2101,12 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + [[package]] name = "syn" version = "2.0.98" @@ -1889,6 +2118,17 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "tap" version = "1.0.1" @@ -1912,7 +2152,7 @@ dependencies = [ "getrandom 0.3.1", "once_cell", "rustix 0.38.44", - "windows-sys", + "windows-sys 0.59.0", ] [[package]] @@ -1970,6 +2210,16 @@ dependencies = [ "syn", ] +[[package]] +name = "tinystr" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" +dependencies = [ + "displaydoc", + "zerovec", +] + [[package]] name = "tinytemplate" version = "1.2.1" @@ -2068,6 +2318,45 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7264e107f553ccae879d21fbea1d6724ac785e8c3bfc762137959b5802826ef3" +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "ureq" +version = "2.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02d1a66277ed75f640d608235660df48c8e3c19f3b4edb6a263315626cc3c01d" +dependencies = [ + "base64", + "flate2", + "log", + "once_cell", + "rustls", + "rustls-pki-types", + "url", + "webpki-roots 0.26.11", +] + +[[package]] +name = "url" +version = "2.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + [[package]] name = "utf8parse" version = "0.2.2" @@ -2287,7 +2576,7 @@ dependencies = [ "wasmtime-versioned-export-macros", "wasmtime-winch", "wat", - "windows-sys", + "windows-sys 0.59.0", ] [[package]] @@ -2315,7 +2604,7 @@ dependencies = [ "serde_derive", "sha2", "toml", - "windows-sys", + "windows-sys 0.59.0", "zstd", ] @@ -2405,7 +2694,7 @@ dependencies = [ "rustix 1.0.7", "wasmtime-asm-macros", "wasmtime-versioned-export-macros", - "windows-sys", + "windows-sys 0.59.0", ] [[package]] @@ -2429,7 +2718,7 @@ dependencies = [ "anyhow", "cfg-if", "libc", - "windows-sys", + "windows-sys 0.59.0", ] [[package]] @@ -2519,6 +2808,24 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "webpki-roots" +version = "0.26.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" +dependencies = [ + "webpki-roots 1.0.1", +] + +[[package]] +name = "webpki-roots" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8782dd5a41a24eed3a4f40b606249b3e236ca61adf1f25ea4d45c73de122b502" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "winapi" version = "0.3.9" @@ -2541,7 +2848,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys", + "windows-sys 0.59.0", ] [[package]] @@ -2569,6 +2876,15 @@ dependencies = [ "wasmtime-environ", ] +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + [[package]] name = "windows-sys" version = "0.59.0" @@ -2678,6 +2994,12 @@ dependencies = [ "wasmparser 0.229.0", ] +[[package]] +name = "writeable" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" + [[package]] name = "wyz" version = "0.5.1" @@ -2687,6 +3009,30 @@ dependencies = [ "tap", ] +[[package]] +name = "yoke" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + [[package]] name = "zerocopy" version = "0.7.35" @@ -2728,6 +3074,66 @@ dependencies = [ "syn", ] +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" + +[[package]] +name = "zerotrie" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "zstd" version = "0.13.3" diff --git a/crates/pecos-clib-pcg/Cargo.toml b/crates/pecos-clib-pcg/Cargo.toml index efa86a6b1..6bda9b5b6 100644 --- a/crates/pecos-clib-pcg/Cargo.toml +++ b/crates/pecos-clib-pcg/Cargo.toml @@ -15,6 +15,7 @@ readme = "README.md" [build-dependencies] cc.workspace = true +ureq = "2.9" [lints] workspace = true diff --git a/crates/pecos-clib-pcg/README.md b/crates/pecos-clib-pcg/README.md index dbed9a341..868c39dbf 100644 --- a/crates/pecos-clib-pcg/README.md +++ b/crates/pecos-clib-pcg/README.md @@ -29,5 +29,8 @@ let r3 = frandom(); // Random float in [0, 1) ## Implementation -This crate uses the `cc` build dependency to compile the existing C implementation from `clib/pecos-rng/src/rng_pcg.c` -and provides safe Rust wrappers around the unsafe FFI functions. +This crate uses the `cc` build dependency to compile the C implementation of PCG32 (64-bit state, 32-bit output). +When building from the PECOS workspace, it uses the local C source files. When used as a dependency from crates.io, +it automatically downloads the C source files from the PECOS GitHub repository. + +The PCG implementation is based on the work by Melissa E. O'Neill. For more information, visit http://www.pcg-random.org diff --git a/crates/pecos-clib-pcg/build.rs b/crates/pecos-clib-pcg/build.rs index 551a248f1..7bf049bd1 100644 --- a/crates/pecos-clib-pcg/build.rs +++ b/crates/pecos-clib-pcg/build.rs @@ -1,9 +1,15 @@ use cc::Build; use std::env; -use std::path::PathBuf; +use std::fs; +use std::path::{Path, PathBuf}; +// TODO: Should probably just vendor the C code into the Rust crate... fn main() { - let clib_path = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()) + let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); + let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()); + + // Try local path first (for development) + let local_clib_path = manifest_dir .parent() .unwrap() .parent() @@ -11,19 +17,47 @@ fn main() { .join("clib") .join("pecos-rng"); - let src_path = clib_path.join("src"); + let src_path = if local_clib_path.exists() { + // Development: use local files + let src = local_clib_path.join("src"); + println!("cargo:rerun-if-changed={}", src.join("rng_pcg.c").display()); + println!("cargo:rerun-if-changed={}", src.join("rng_pcg.h").display()); + src + } else { + // Published crate: download from GitHub + let pcg_dir = out_dir.join("pcg"); + fs::create_dir_all(&pcg_dir).unwrap(); + + let commit = "95a6ddbdf85ad7bcf8b9133aa2552f3f1ae7da84"; + let base_url = format!( + "https://raw.githubusercontent.com/PECOS-packages/PECOS/{commit}/clib/pecos-rng/src" + ); - println!( - "cargo:rerun-if-changed={}", - src_path.join("rng_pcg.c").display() - ); - println!( - "cargo:rerun-if-changed={}", - src_path.join("rng_pcg.h").display() - ); + // Download files if they don't exist + download_if_needed(&pcg_dir.join("rng_pcg.c"), &format!("{base_url}/rng_pcg.c")); + download_if_needed(&pcg_dir.join("rng_pcg.h"), &format!("{base_url}/rng_pcg.h")); + + pcg_dir + }; Build::new() .file(src_path.join("rng_pcg.c")) .include(&src_path) .compile("pecos_pcg"); } + +fn download_if_needed(path: &Path, url: &str) { + if !path.exists() { + println!("cargo:warning=Downloading {} to {}", url, path.display()); + + let response = ureq::get(url) + .call() + .unwrap_or_else(|e| panic!("Failed to download {url}: {e}")); + + let mut file = fs::File::create(path) + .unwrap_or_else(|e| panic!("Failed to create {}: {}", path.display(), e)); + + std::io::copy(&mut response.into_reader(), &mut file) + .unwrap_or_else(|e| panic!("Failed to write {}: {}", path.display(), e)); + } +} diff --git a/crates/pecos-clib-pcg/tests/pcg_tests.rs b/crates/pecos-clib-pcg/tests/pcg_tests.rs new file mode 100644 index 000000000..f687ef5b3 --- /dev/null +++ b/crates/pecos-clib-pcg/tests/pcg_tests.rs @@ -0,0 +1,89 @@ +use pecos_clib_pcg::{boundedrand, frandom, random, srandom}; + +#[test] +fn test_pcg_random_generates_values() { + // Test that random() generates different values + let val1 = random(); + let val2 = random(); + + // It's extremely unlikely that two consecutive calls return the same value + // (probability is 1 in 2^32) + assert_ne!( + val1, val2, + "Two consecutive random() calls should generate different values" + ); +} + +#[test] +fn test_pcg_bounded_random() { + // Test bounded random with various bounds + let bounds = [1, 2, 10, 100, 1000]; + + for bound in bounds { + for _ in 0..10 { + let val = boundedrand(bound); + assert!( + val < bound, + "boundedrand({bound}) returned {val}, which is >= {bound}" + ); + } + } +} + +#[test] +fn test_pcg_frandom_range() { + // Test that frandom returns values in [0.0, 1.0) + for _ in 0..100 { + let val = frandom(); + assert!(val >= 0.0, "frandom() returned {val}, which is < 0.0"); + assert!(val < 1.0, "frandom() returned {val}, which is >= 1.0"); + } +} + +#[test] +fn test_pcg_seeding() { + // Test that seeding produces deterministic sequences + // Note: PCG uses global state, so we test determinism directly + + // Test that the same seed produces the same first few values + srandom(12345); + let first_val_1 = random(); + let second_val_1 = random(); + + srandom(12345); + let first_val_2 = random(); + let second_val_2 = random(); + + assert_eq!( + first_val_1, first_val_2, + "First value after seeding should be deterministic" + ); + assert_eq!( + second_val_1, second_val_2, + "Second value after seeding should be deterministic" + ); + + // Test that different seeds produce different values + srandom(54321); + let different_first = random(); + + assert_ne!( + first_val_1, different_first, + "Different seeds should produce different values" + ); +} + +#[test] +fn test_pcg_deterministic_behavior() { + // Test that the RNG is deterministic after seeding + srandom(999); + let first_value = random(); + + srandom(999); + let second_value = random(); + + assert_eq!( + first_value, second_value, + "First value after seeding should be deterministic" + ); +} diff --git a/python/pecos-rslib/pyproject.toml b/python/pecos-rslib/pyproject.toml index 733ebe8f5..51cff5022 100644 --- a/python/pecos-rslib/pyproject.toml +++ b/python/pecos-rslib/pyproject.toml @@ -22,11 +22,6 @@ features = ["pyo3/extension-module"] python-source = "src" module-name = "pecos_rslib._pecos_rslib" manifest-path = "rust/Cargo.toml" -sdist-include = [ - "../../clib/**/*", - "../../crates/**/*", - "../../Cargo.toml", -] [tool.uv.sources] pecos-rslib = { workspace = true }