Skip to content

Commit 29f8453

Browse files
authored
adding various decoders back from previous branch (#216)
* adding various decoders back from previous branch
1 parent e5eb588 commit 29f8453

File tree

106 files changed

+26034
-61
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

106 files changed

+26034
-61
lines changed

Cargo.lock

Lines changed: 258 additions & 53 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,24 @@ pecos-decoders = { version = "0.1.1", path = "crates/pecos-decoders" }
128128
# ldpc decoder wrapper (https://github.com/quantumgizmos/ldpc)
129129
pecos-ldpc-decoders = { version = "0.1.1", path = "crates/pecos-ldpc-decoders" }
130130

131+
# PyMatching decoder wrapper (https://github.com/oscarhiggott/PyMatching)
132+
pecos-pymatching = { version = "0.1.1", path = "crates/pecos-pymatching" }
133+
134+
# Tesseract decoder wrapper (https://github.com/quantumlib/tesseract-decoder)
135+
pecos-tesseract = { version = "0.1.1", path = "crates/pecos-tesseract" }
136+
137+
# Chromobius decoder wrapper (https://github.com/quantumlib/chromobius)
138+
pecos-chromobius = { version = "0.1.1", path = "crates/pecos-chromobius" }
139+
140+
# Fusion Blossom decoder wrapper (pure Rust MWPM)
141+
pecos-fusion-blossom = { version = "0.1.1", path = "crates/pecos-fusion-blossom" }
142+
143+
# Fusion Blossom library (pure Rust MWPM decoder)
144+
fusion-blossom = "0.2"
145+
146+
# petgraph for graph algorithms (used by pymatching)
147+
petgraph = "0.8"
148+
131149
# QuEST simulator wrapper (https://github.com/quest-kit/QuEST)
132150
pecos-quest = { version = "0.1.1", path = "crates/pecos-quest" }
133151

crates/pecos-build/src/manifest.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -608,7 +608,7 @@ mod tests {
608608
assert_eq!(qulacs_deps.len(), 3); // qulacs, eigen, boost
609609

610610
let ldpc_deps = manifest.get_crate_dependencies("pecos-ldpc-decoders");
611-
assert!(ldpc_deps.len() >= 5);
611+
assert_eq!(ldpc_deps.len(), 3); // ldpc, stim, boost
612612
}
613613

614614
#[test]

crates/pecos-chromobius/Cargo.toml

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
[package]
2+
name = "pecos-chromobius"
3+
version.workspace = true
4+
edition.workspace = true
5+
readme.workspace = true
6+
authors.workspace = true
7+
homepage.workspace = true
8+
repository.workspace = true
9+
license.workspace = true
10+
keywords.workspace = true
11+
categories.workspace = true
12+
description = "Chromobius decoder wrapper for PECOS"
13+
14+
[dependencies]
15+
pecos-decoder-core.workspace = true
16+
ndarray.workspace = true
17+
thiserror.workspace = true
18+
cxx.workspace = true
19+
20+
[build-dependencies]
21+
pecos-build.workspace = true
22+
cxx-build.workspace = true
23+
cc.workspace = true
24+
env_logger.workspace = true
25+
log.workspace = true
26+
27+
[lib]
28+
name = "pecos_chromobius"
29+
30+
[lints]
31+
workspace = true

crates/pecos-chromobius/build.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
//! Build script for pecos-chromobius
2+
3+
mod build_chromobius;
4+
mod build_stim;
5+
mod chromobius_patch;
6+
7+
fn main() {
8+
// Initialize logger for build script
9+
env_logger::init();
10+
11+
// Build Chromobius (download handled inside build_chromobius)
12+
build_chromobius::build().expect("Chromobius build failed");
13+
}
Lines changed: 237 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
1+
//! Build script for Chromobius decoder integration
2+
3+
use log::info;
4+
use pecos_build::{Manifest, Result, ensure_dep_ready, report_cache_config};
5+
use std::env;
6+
use std::fs;
7+
use std::path::{Path, PathBuf};
8+
9+
// Use the shared modules from the parent
10+
use crate::build_stim;
11+
use crate::chromobius_patch;
12+
13+
/// Get the build profile from Cargo's environment
14+
fn get_build_profile() -> String {
15+
if let Ok(out_dir) = env::var("OUT_DIR") {
16+
let parts: Vec<&str> = out_dir.split(std::path::MAIN_SEPARATOR).collect();
17+
if let Some(target_idx) = parts.iter().position(|&p| p == "target")
18+
&& let Some(profile_name) = parts.get(target_idx + 1)
19+
{
20+
return match *profile_name {
21+
"native" => "native",
22+
"release" => "release",
23+
"debug" => "debug",
24+
_ => {
25+
if env::var("PROFILE").as_deref() == Ok("release") {
26+
"release"
27+
} else {
28+
"debug"
29+
}
30+
}
31+
}
32+
.to_string();
33+
}
34+
}
35+
36+
match env::var("PROFILE").as_deref() {
37+
Ok("release") => "release".to_string(),
38+
_ => "debug".to_string(),
39+
}
40+
}
41+
42+
/// Main build function for Chromobius
43+
pub fn build() -> Result<()> {
44+
println!("cargo:rerun-if-changed=build_chromobius.rs");
45+
println!("cargo:rerun-if-changed=src/bridge.rs");
46+
println!("cargo:rerun-if-changed=src/bridge.cpp");
47+
println!("cargo:rerun-if-changed=include/chromobius_bridge.h");
48+
println!("cargo:rerun-if-env-changed=FORCE_REBUILD");
49+
50+
let out_dir = PathBuf::from(env::var("OUT_DIR")?);
51+
52+
// Always emit link directives - Cargo will cache these
53+
println!("cargo:rustc-link-search=native={}", out_dir.display());
54+
println!("cargo:rustc-link-lib=static=chromobius-bridge");
55+
56+
// Get dependencies (downloads to ~/.pecos/cache/, extracts to ~/.pecos/deps/)
57+
let manifest = Manifest::find_and_load_validated()?;
58+
let chromobius_dir = ensure_dep_ready("chromobius", &manifest)?;
59+
let stim_dir = ensure_dep_ready("stim", &manifest)?;
60+
let pymatching_dir = ensure_dep_ready("pymatching", &manifest)?;
61+
62+
// Apply compatibility patches for newer Stim version
63+
chromobius_patch::patch_chromobius_for_newer_stim(&chromobius_dir)?;
64+
65+
// Generate amalgamated stim.h if needed
66+
build_stim::generate_amalgamated_header(&stim_dir)?;
67+
68+
// Build using cxx
69+
build_cxx_bridge(&chromobius_dir, &stim_dir, &pymatching_dir)?;
70+
71+
Ok(())
72+
}
73+
74+
fn build_cxx_bridge(chromobius_dir: &Path, stim_dir: &Path, pymatching_dir: &Path) -> Result<()> {
75+
let chromobius_src_dir = chromobius_dir.join("src");
76+
let stim_src_dir = stim_dir.join("src");
77+
let pymatching_src_dir = pymatching_dir.join("src");
78+
79+
// Find essential source files
80+
let chromobius_files = collect_chromobius_sources(&chromobius_src_dir)?;
81+
let stim_files = build_stim::collect_stim_sources(&stim_src_dir);
82+
let pymatching_files = collect_pymatching_sources(&pymatching_src_dir)?;
83+
84+
// Build the cxx bridge first to generate headers
85+
let mut build = cxx_build::bridge("src/bridge.rs");
86+
87+
let target = env::var("TARGET").unwrap_or_default();
88+
89+
// On macOS, explicitly use system clang to ensure SDK paths are correct.
90+
if target.contains("darwin") && env::var("CXX").is_err() && env::var("CC").is_err() {
91+
build.compiler("/usr/bin/clang++");
92+
}
93+
94+
// Add our bridge implementation
95+
build.file("src/bridge.cpp");
96+
97+
// Add Chromobius core files
98+
for file in chromobius_files {
99+
build.file(file);
100+
}
101+
102+
// Add PyMatching files
103+
for file in pymatching_files {
104+
build.file(file);
105+
}
106+
107+
// Configure build
108+
build
109+
.std("c++20")
110+
.include(&chromobius_src_dir)
111+
.include(&stim_src_dir)
112+
.include(stim_dir) // For amalgamated stim.h
113+
.include(&pymatching_src_dir)
114+
.include("include")
115+
.include("src")
116+
.define("CHROMOBIUS_BRIDGE_EXPORTS", None);
117+
118+
// Report ccache/sccache configuration
119+
report_cache_config();
120+
121+
// Use build profile for optimization settings
122+
let profile = get_build_profile();
123+
match profile.as_str() {
124+
"native" => {
125+
build.flag_if_supported("-O3");
126+
if env::var("CARGO_CFG_TARGET_ARCH").ok() == env::var("HOST_ARCH").ok() {
127+
build.flag_if_supported("-march=native");
128+
}
129+
}
130+
"release" => {
131+
build.flag_if_supported("-O3");
132+
}
133+
_ => {
134+
build.flag_if_supported("-O0");
135+
build.flag_if_supported("-g");
136+
}
137+
}
138+
139+
// Add Stim files to the main build
140+
for file in &stim_files {
141+
build.file(file);
142+
}
143+
144+
// Platform-specific configurations
145+
if cfg!(not(target_env = "msvc")) {
146+
build
147+
.flag("-fvisibility=hidden")
148+
.flag("-fvisibility-inlines-hidden")
149+
.flag("-w")
150+
.flag_if_supported("-fopenmp")
151+
.flag("-fPIC");
152+
153+
if target.contains("darwin") {
154+
build.flag("-stdlib=libc++");
155+
build.flag("-L/usr/lib");
156+
build.flag("-Wl,-search_paths_first");
157+
}
158+
} else {
159+
build
160+
.flag("/W0")
161+
.flag("/MD")
162+
.flag("/EHsc") // Enable C++ exception handling
163+
.flag_if_supported("/permissive-")
164+
.flag_if_supported("/Zc:__cplusplus");
165+
166+
// Force include standard headers that external libraries assume are available
167+
// MSVC is stricter than GCC/Clang about transitive includes
168+
build.flag("/FI").flag("array"); // For std::array
169+
build.flag("/FI").flag("numeric"); // For std::iota (used by PyMatching)
170+
}
171+
172+
build.compile("chromobius-bridge");
173+
174+
// On macOS, link against the system C++ library
175+
if target.contains("darwin") {
176+
println!("cargo:rustc-link-search=native=/usr/lib");
177+
println!("cargo:rustc-link-lib=c++");
178+
println!("cargo:rustc-link-arg=-Wl,-search_paths_first");
179+
}
180+
181+
Ok(())
182+
}
183+
184+
fn collect_chromobius_sources(chromobius_src_dir: &Path) -> Result<Vec<PathBuf>> {
185+
let mut files = Vec::new();
186+
187+
// Collect all non-test, non-perf, non-pybind .cc files
188+
collect_cc_files_filtered(chromobius_src_dir, &mut files)?;
189+
190+
info!("Found {} Chromobius source files", files.len());
191+
Ok(files)
192+
}
193+
194+
fn collect_pymatching_sources(pymatching_src_dir: &Path) -> Result<Vec<PathBuf>> {
195+
let mut files = Vec::new();
196+
197+
// PyMatching sparse_blossom implementation files
198+
let sparse_blossom_dir = pymatching_src_dir.join("pymatching/sparse_blossom");
199+
if sparse_blossom_dir.exists() {
200+
collect_cc_files_filtered(&sparse_blossom_dir, &mut files)?;
201+
}
202+
203+
info!("Found {} PyMatching source files", files.len());
204+
Ok(files)
205+
}
206+
207+
fn collect_cc_files_filtered(dir: &Path, files: &mut Vec<PathBuf>) -> Result<()> {
208+
for entry in fs::read_dir(dir)? {
209+
let entry = entry?;
210+
let path = entry.path();
211+
212+
if path.is_dir() {
213+
// Skip test directories
214+
if let Some(name) = path.file_name().and_then(|n| n.to_str())
215+
&& (name == "test" || name == "tests")
216+
{
217+
continue;
218+
}
219+
collect_cc_files_filtered(&path, files)?;
220+
} else if path.extension().and_then(|s| s.to_str()) == Some("cc") {
221+
let filename = path.file_name().and_then(|n| n.to_str()).unwrap_or("");
222+
// Skip test, perf, pybind, and main files
223+
if filename.contains(".test.")
224+
|| filename.contains(".perf.")
225+
|| filename.contains(".pybind.")
226+
|| filename == "main.cc"
227+
{
228+
continue;
229+
}
230+
if !files.contains(&path) {
231+
files.push(path);
232+
}
233+
}
234+
}
235+
236+
Ok(())
237+
}

0 commit comments

Comments
 (0)