Skip to content

Commit e3819a5

Browse files
committed
Clean up PR review issues: remove hardcoded paths, deduplicate unified_sim, optimize CI
1 parent 13890e7 commit e3819a5

4 files changed

Lines changed: 102 additions & 179 deletions

File tree

.github/workflows/python-release.yml

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -297,10 +297,13 @@ jobs:
297297
LLVM_SYS_140_PREFIX=$HOME/.pecos/deps/llvm
298298
MACOSX_DEPLOYMENT_TARGET=13.2
299299
CIBW_BEFORE_ALL_MACOS: |
300-
curl -sSf https://sh.rustup.rs | sh -s -- -y
301-
source $HOME/.cargo/env
302-
rustup update
303-
cargo run --release -p pecos --features cli -- install llvm --force
300+
source $HOME/.cargo/env 2>/dev/null || { curl -sSf https://sh.rustup.rs | sh -s -- -y && source $HOME/.cargo/env; }
301+
if [ ! -d "$HOME/.pecos/deps/llvm/bin" ]; then
302+
rustup update
303+
cargo run --release -p pecos --features cli -- install llvm --force
304+
else
305+
echo "LLVM already installed from pecos-rslib build, skipping"
306+
fi
304307
mkdir -p $HOME/.pecos/bin
305308
printf '#!/bin/bash\nunset DYLD_LIBRARY_PATH\nexec /usr/bin/codesign "$@"\n' > $HOME/.pecos/bin/codesign
306309
chmod +x $HOME/.pecos/bin/codesign
@@ -312,7 +315,7 @@ jobs:
312315
LLVM_SYS_140_PREFIX="C:\\Users\\runneradmin\\.pecos\\deps\\llvm"
313316
CIBW_BEFORE_ALL_WINDOWS: >
314317
rustup update &&
315-
cargo run --release -p pecos --features cli -- install llvm --force
318+
if not exist "C:\Users\runneradmin\.pecos\deps\llvm\bin" (cargo run --release -p pecos --features cli -- install llvm --force) else (echo LLVM already installed from pecos-rslib build)
316319
CIBW_BEFORE_BUILD_WINDOWS: >
317320
pip install delvewheel &&
318321
python -c "import delvewheel._dll_list as d,inspect,re as r;p=inspect.getfile(d);c=open(p).read();n=chr(10);open(p,'w').write(c.replace(r\"re.compile('api-.*'),\",r\"re.compile('api-.*'),\"+n+r\" re.compile('ext-.*'),\")) if 'ext-.*' not in c else None"

.github/workflows/rust-test.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ on:
1414
- 'examples/**'
1515
- 'julia/pecos-julia-ffi/**'
1616
- 'python/pecos-rslib-cuda/**'
17+
- 'python/pecos-rslib/**'
1718
- 'python/pecos-rslib-llvm/**'
1819
- 'Cargo.toml'
1920
- '.github/workflows/rust-test.yml'
@@ -25,6 +26,7 @@ on:
2526
- 'examples/**'
2627
- 'julia/pecos-julia-ffi/**'
2728
- 'python/pecos-rslib-cuda/**'
29+
- 'python/pecos-rslib/**'
2830
- 'python/pecos-rslib-llvm/**'
2931
- 'Cargo.toml'
3032
- '.github/workflows/rust-test.yml'

crates/pecos/src/unified_sim.rs

Lines changed: 69 additions & 161 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,22 @@ use pecos_engines::{ClassicalControlEngineBuilder, MonteCarloEngine, SimBuilder,
88
use pecos_programs::Program;
99
use pecos_qasm::qasm_engine;
1010
#[cfg(feature = "qis")]
11-
use pecos_qis::qis_engine;
11+
use pecos_qis::{IntoQisInterface, qis_engine};
12+
13+
/// Set up a QIS engine with Selene runtime and Helios interface for the given program.
14+
#[cfg(feature = "qis")]
15+
fn build_qis_engine<P: IntoQisInterface + 'static>(
16+
program: P,
17+
) -> Result<pecos_qis::QisEngineBuilder, PecosError> {
18+
let selene_runtime = crate::selene_simple_runtime()
19+
.map_err(|e| PecosError::Generic(format!("Failed to load Selene runtime: {e}")))?;
20+
let helios_builder = crate::helios_interface_builder();
21+
qis_engine()
22+
.runtime(selene_runtime)
23+
.interface(helios_builder)
24+
.try_program(program)
25+
.map_err(|e| PecosError::Generic(format!("Failed to load program: {e}")))
26+
}
1227

1328
/// Extension trait for `SimBuilder` to add program-based methods
1429
pub trait SimBuilderExt {
@@ -43,184 +58,77 @@ pub struct ProgrammedSimBuilder {
4358
}
4459

4560
impl ProgrammedSimBuilder {
61+
/// Auto-select the classical engine based on program type, returning a configured `SimBuilder`.
62+
fn configure_engine(self) -> Result<SimBuilder, PecosError> {
63+
if self.override_classical {
64+
return Ok(self.base_builder);
65+
}
66+
67+
match self.program {
68+
Program::Qasm(qasm) => Ok(self.base_builder.classical(qasm_engine().program(qasm))),
69+
Program::Qis(qis) => {
70+
#[cfg(feature = "qis")]
71+
{
72+
let engine_builder = build_qis_engine(qis)?;
73+
Ok(self.base_builder.classical(engine_builder))
74+
}
75+
#[cfg(not(feature = "qis"))]
76+
{
77+
let _ = qis;
78+
Err(PecosError::Generic(
79+
"QIS programs require Selene and LLVM support. Please rebuild with --features selene,llvm".to_string()
80+
))
81+
}
82+
}
83+
Program::Hugr(hugr) => {
84+
#[cfg(feature = "qis")]
85+
{
86+
let engine_builder = build_qis_engine(hugr)?;
87+
Ok(self.base_builder.classical(engine_builder))
88+
}
89+
#[cfg(not(feature = "qis"))]
90+
{
91+
let _ = hugr;
92+
Err(PecosError::Generic(
93+
"HUGR programs require Selene and LLVM support. Please rebuild with --features selene,llvm".to_string()
94+
))
95+
}
96+
}
97+
Program::Wasm(_) => Err(PecosError::Input(
98+
"WASM programs are not yet supported in unified simulation".to_string(),
99+
)),
100+
Program::Wat(_) => Err(PecosError::Input(
101+
"WAT programs are not yet supported in unified simulation".to_string(),
102+
)),
103+
Program::PhirJson(_) => Err(PecosError::Input(
104+
"PHIR JSON programs are not yet supported in unified simulation".to_string(),
105+
)),
106+
Program::SeleneInterface(_) => Err(PecosError::Input(
107+
"SeleneInterface programs are not yet supported in unified simulation".to_string(),
108+
)),
109+
}
110+
}
111+
46112
/// Build the simulation with automatic engine selection
47113
///
48-
/// This selects an engine based on the program type and builds the simulation,
49-
/// unless a classical engine was already explicitly set.
50-
///
51114
/// # Errors
52115
///
53116
/// Returns an error if:
54117
/// - The program type is not yet supported (WASM, WAT, PHIR JSON, `SeleneInterface`)
55118
/// - Engine building fails
56119
pub fn build(self) -> Result<MonteCarloEngine, PecosError> {
57-
if self.override_classical {
58-
// Classical engine was already set, just build
59-
self.base_builder.build()
60-
} else {
61-
// Auto-select engine based on program type
62-
match self.program {
63-
Program::Qasm(qasm) => self
64-
.base_builder
65-
.classical(qasm_engine().program(qasm))
66-
.build(),
67-
Program::Qis(qis) => {
68-
// Use Selene runtime and Helios interface
69-
#[cfg(feature = "qis")]
70-
{
71-
let selene_runtime = crate::selene_simple_runtime().map_err(|e| {
72-
PecosError::Generic(format!("Failed to load Selene runtime: {e}"))
73-
})?;
74-
let helios_builder = crate::helios_interface_builder();
75-
let engine_builder = qis_engine()
76-
.runtime(selene_runtime)
77-
.interface(helios_builder)
78-
.try_program(qis)
79-
.map_err(|e| {
80-
PecosError::Generic(format!("Failed to load QIS program: {e}"))
81-
})?;
82-
83-
self.base_builder.classical(engine_builder).build()
84-
}
85-
#[cfg(not(feature = "qis"))]
86-
{
87-
let _ = qis; // Mark as used to avoid warning
88-
Err(PecosError::Generic(
89-
"QIS programs require Selene and LLVM support. Please rebuild with --features selene,llvm".to_string()
90-
))
91-
}
92-
}
93-
Program::Hugr(hugr) => {
94-
// Use Selene runtime and Helios interface for HUGR programs
95-
#[cfg(feature = "qis")]
96-
{
97-
let selene_runtime = crate::selene_simple_runtime().map_err(|e| {
98-
PecosError::Generic(format!("Failed to load Selene runtime: {e}"))
99-
})?;
100-
let helios_builder = crate::helios_interface_builder();
101-
let engine_builder = qis_engine()
102-
.runtime(selene_runtime)
103-
.interface(helios_builder)
104-
.try_program(hugr)
105-
.map_err(|e| {
106-
PecosError::Generic(format!("Failed to load HUGR program: {e}"))
107-
})?;
108-
109-
self.base_builder.classical(engine_builder).build()
110-
}
111-
#[cfg(not(feature = "qis"))]
112-
{
113-
let _ = hugr; // Mark as used to avoid warning
114-
Err(PecosError::Generic(
115-
"HUGR programs require Selene and LLVM support. Please rebuild with --features selene,llvm".to_string()
116-
))
117-
}
118-
}
119-
Program::Wasm(_) => Err(PecosError::Input(
120-
"WASM programs are not yet supported in unified simulation".to_string(),
121-
)),
122-
Program::Wat(_) => Err(PecosError::Input(
123-
"WAT programs are not yet supported in unified simulation".to_string(),
124-
)),
125-
Program::PhirJson(_) => Err(PecosError::Input(
126-
"PHIR JSON programs are not yet supported in unified simulation".to_string(),
127-
)),
128-
Program::SeleneInterface(_) => Err(PecosError::Input(
129-
"SeleneInterface programs are not yet supported in unified simulation"
130-
.to_string(),
131-
)),
132-
}
133-
}
120+
self.configure_engine()?.build()
134121
}
135122

136123
/// Build and run the simulation with automatic engine selection
137124
///
138-
/// This selects an engine based on the program type and runs the simulation,
139-
/// unless a classical engine was already explicitly set.
140-
///
141125
/// # Errors
142126
///
143127
/// Returns an error if:
144128
/// - The program type is not yet supported (WASM, WAT, PHIR JSON, `SeleneInterface`)
145129
/// - Engine building or running fails
146130
pub fn run(self, shots: usize) -> Result<pecos_engines::shot_results::ShotVec, PecosError> {
147-
if self.override_classical {
148-
// Classical engine was already set, just run
149-
self.base_builder.run(shots)
150-
} else {
151-
// Auto-select engine based on program type
152-
match self.program {
153-
Program::Qasm(qasm) => self
154-
.base_builder
155-
.classical(qasm_engine().program(qasm))
156-
.run(shots),
157-
Program::Qis(qis) => {
158-
// Use Selene runtime and Helios interface
159-
#[cfg(feature = "qis")]
160-
{
161-
let selene_runtime = crate::selene_simple_runtime().map_err(|e| {
162-
PecosError::Generic(format!("Failed to load Selene runtime: {e}"))
163-
})?;
164-
let helios_builder = crate::helios_interface_builder();
165-
let engine_builder = qis_engine()
166-
.runtime(selene_runtime)
167-
.interface(helios_builder)
168-
.try_program(qis)
169-
.map_err(|e| {
170-
PecosError::Generic(format!("Failed to load QIS program: {e}"))
171-
})?;
172-
173-
self.base_builder.classical(engine_builder).run(shots)
174-
}
175-
#[cfg(not(feature = "qis"))]
176-
{
177-
let _ = qis; // Mark as used to avoid warning
178-
Err(PecosError::Generic(
179-
"QIS programs require Selene and LLVM support. Please rebuild with --features selene,llvm".to_string()
180-
))
181-
}
182-
}
183-
Program::Hugr(hugr) => {
184-
// Use Selene runtime and Helios interface for HUGR programs
185-
#[cfg(feature = "qis")]
186-
{
187-
let selene_runtime = crate::selene_simple_runtime().map_err(|e| {
188-
PecosError::Generic(format!("Failed to load Selene runtime: {e}"))
189-
})?;
190-
let helios_builder = crate::helios_interface_builder();
191-
let engine_builder = qis_engine()
192-
.runtime(selene_runtime)
193-
.interface(helios_builder)
194-
.try_program(hugr)
195-
.map_err(|e| {
196-
PecosError::Generic(format!("Failed to load HUGR program: {e}"))
197-
})?;
198-
199-
self.base_builder.classical(engine_builder).run(shots)
200-
}
201-
#[cfg(not(feature = "qis"))]
202-
{
203-
let _ = hugr; // Mark as used to avoid warning
204-
Err(PecosError::Generic(
205-
"HUGR programs require Selene and LLVM support. Please rebuild with --features selene,llvm".to_string()
206-
))
207-
}
208-
}
209-
Program::Wasm(_) => Err(PecosError::Input(
210-
"WASM programs are not yet supported in unified simulation".to_string(),
211-
)),
212-
Program::Wat(_) => Err(PecosError::Input(
213-
"WAT programs are not yet supported in unified simulation".to_string(),
214-
)),
215-
Program::PhirJson(_) => Err(PecosError::Input(
216-
"PHIR JSON programs are not yet supported in unified simulation".to_string(),
217-
)),
218-
Program::SeleneInterface(_) => Err(PecosError::Input(
219-
"SeleneInterface programs are not yet supported in unified simulation"
220-
.to_string(),
221-
)),
222-
}
223-
}
131+
self.configure_engine()?.run(shots)
224132
}
225133

226134
/// Override the classical engine selection

python/pecos-rslib/src/lib.rs

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -173,32 +173,42 @@ fn pecos_rslib(_py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> {
173173

174174
log::debug!("Unix detected, attempting Selene runtime preload...");
175175

176-
// Try to find libselene_simple_runtime.so
177-
let possible_paths = [
178-
"/home/ciaranra/Repos/cl_projects/gup/selene/target/debug/libselene_simple_runtime.so",
179-
"/home/ciaranra/Repos/cl_projects/gup/selene/target/release/libselene_simple_runtime.so",
180-
"../selene/target/debug/libselene_simple_runtime.so",
181-
"../selene/target/release/libselene_simple_runtime.so",
182-
];
176+
// Build search paths for libselene_simple_runtime.so:
177+
// 1. PECOS_SELENE_PRELOAD env var (explicit override)
178+
// 2. ~/.pecos/lib/
179+
// 3. Relative development paths (target/debug, target/release)
180+
let mut possible_paths: Vec<std::path::PathBuf> = Vec::new();
181+
182+
if let Ok(path) = std::env::var("PECOS_SELENE_PRELOAD") {
183+
possible_paths.push(std::path::PathBuf::from(path));
184+
}
185+
186+
if let Some(home) = dirs::home_dir() {
187+
possible_paths.push(home.join(".pecos/lib/libselene_simple_runtime.so"));
188+
}
189+
190+
possible_paths.push("target/debug/libselene_simple_runtime.so".into());
191+
possible_paths.push("target/release/libselene_simple_runtime.so".into());
183192

184193
log::debug!("Checking for Selene runtime libraries...");
185194
for path in &possible_paths {
186-
log::trace!("Checking path: {path}");
187-
if std::path::Path::new(path).exists() {
188-
log::debug!("Found Selene runtime! Attempting to preload: {path}");
195+
let path_str = path.to_string_lossy();
196+
log::trace!("Checking path: {path_str}");
197+
if path.exists() {
198+
log::debug!("Found Selene runtime! Attempting to preload: {path_str}");
189199

190200
unsafe {
191-
let path_cstr = CString::new(path.as_bytes()).unwrap();
201+
let path_cstr = CString::new(path_str.as_bytes()).unwrap();
192202
let handle = libc::dlopen(path_cstr.as_ptr(), RTLD_LAZY | RTLD_GLOBAL);
193203
if handle.is_null() {
194204
let error_ptr = libc::dlerror();
195205
if !error_ptr.is_null() {
196206
let error = std::ffi::CStr::from_ptr(error_ptr).to_string_lossy();
197-
log::warn!("Failed to preload {path}: {error}");
207+
log::warn!("Failed to preload {path_str}: {error}");
198208
}
199209
} else {
200210
log::info!(
201-
"Successfully preloaded Selene runtime with RTLD_GLOBAL from: {path}"
211+
"Successfully preloaded Selene runtime with RTLD_GLOBAL from: {path_str}"
202212
);
203213
break;
204214
}

0 commit comments

Comments
 (0)