From 234581fb7f34ab467fe51582de4c454a52aa93d7 Mon Sep 17 00:00:00 2001 From: Lukas Simon Date: Wed, 28 Oct 2026 14:19:28 +0100 Subject: [PATCH] Keep standalone reducer construction under the package license scope --- rust/loop-reduction/src/runtime/reduction.rs | 2 + .../loop-reduction/tests/runtime_reduction.rs | 44 +++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/rust/loop-reduction/src/runtime/reduction.rs b/rust/loop-reduction/src/runtime/reduction.rs index c5754724..00dc98b4 100644 --- a/rust/loop-reduction/src/runtime/reduction.rs +++ b/rust/loop-reduction/src/runtime/reduction.rs @@ -329,6 +329,8 @@ impl PolynomialPlan { precision: Option, engine: &str, ) -> ExecutionResult { + // Standalone generation callers also parse Symbolica expressions here. + let _unlock = pyamplicol::unlock(); let (arithmetic, bits) = arithmetic_mode(arithmetic, precision)?; if !matches!(engine, "generic" | "fast" | "fast-tensor") { return Err(error("engine must be generic, fast or fast-tensor")); diff --git a/rust/loop-reduction/tests/runtime_reduction.rs b/rust/loop-reduction/tests/runtime_reduction.rs index 390097dc..004c91df 100644 --- a/rust/loop-reduction/tests/runtime_reduction.rs +++ b/rust/loop-reduction/tests/runtime_reduction.rs @@ -3,6 +3,50 @@ //! Real scalar masters and epsilon assembly are available without PyO3. use rusticol_loop_reduction::runtime::{Batch, PolynomialPlan}; +#[test] +fn standalone_reducer_construction_keeps_package_scope() { + assert!( + pyamplicol::is_active(), + "pyAmpliCol's package key must be active" + ); + let ready = std::sync::Barrier::new(2); + std::thread::scope(|scope| { + let workers: Vec<_> = (0..2) + .map(|_| { + scope.spawn(|| { + let mut plans = Vec::new(); + for (arithmetic, precision, engine) in [ + ("double", None, "generic"), + ("double", None, "fast"), + ("double", None, "fast-tensor"), + ("double-double", None, "generic"), + ("arbitrary", Some(60), "generic"), + ] { + plans.push( + PolynomialPlan::new( + 1, + ["1".into(), "0".into(), "0".into()], + vec![], + arithmetic, + precision, + engine, + ) + .unwrap(), + ); + } + // Keep both calling threads alive: an unguarded constructor + // consumes a restricted permit and makes the other abort. + ready.wait(); + assert_eq!(plans.len(), 5); + }) + }) + .collect(); + for worker in workers { + worker.join().unwrap(); + } + }); +} + #[test] fn standalone_reduction_preserves_epsilon_order_and_sparse_results() { for (arithmetic, precision, engine) in [