Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 40 additions & 1 deletion .ado/stages/_platform_setup_steps.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ parameters:

steps:
- script: |
sudo tdnf install binutils glibc-devel kernel-headers patchelf gcc python3-3.12.9 python3-pip-24.2 python3-devel-3.12.9 -y
sudo tdnf install binutils glibc-devel kernel-headers patchelf gcc python3-3.12.9 python3-pip-24.2 python3-devel-3.12.9 boost-devel -y
displayName: Install build tools and Python on Linux aarch64
condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux'), eq(variables['arch'], 'aarch64'))

Expand Down Expand Up @@ -61,6 +61,17 @@ steps:
fi
cmake --version

# Boost: header-only library required by the Tesseract decoder C++ bridge.
if ! test -f /usr/include/boost/dynamic_bitset.hpp && ! test -f /usr/local/include/boost/dynamic_bitset.hpp; then
if command -v apt-get >/dev/null 2>&1; then
sudo apt-get update && sudo apt-get install -y --no-install-recommends libboost-dev
elif command -v tdnf >/dev/null 2>&1; then
sudo tdnf install -y boost-devel
elif command -v brew >/dev/null 2>&1; then
brew install boost
fi
fi

# protoc: download directly from the protobuf GitHub release so we
# get a known version that bundles the well-known proto sources
# (google/protobuf/empty.proto and friends). The distro packages
Expand Down Expand Up @@ -128,6 +139,34 @@ steps:
}
cmake --version

# Boost: header-only library required by the Tesseract decoder C++ bridge.
# Check well-known pre-installed locations first (some agents ship Boost);
# fall back to downloading from the GitHub release (accessible on 1ES agents).
$boostRoot = $null
foreach ($candidate in @($env:BOOST_ROOT, "C:\local\boost", "C:\Boost", "C:\tools\boost")) {
if ($candidate -and (Test-Path (Join-Path $candidate "boost\dynamic_bitset.hpp"))) {
$boostRoot = $candidate
break
}
}
if (-not $boostRoot) {
$boostVersion = "1.83.0"
$boostUrl = "https://github.com/boostorg/boost/releases/download/boost-$boostVersion/boost-$boostVersion.zip"
$boostZip = Join-Path $tempDir "boost.zip"
$boostExtract = Join-Path $tempDir "boost_extract"
Write-Host "Downloading Boost $boostVersion from $boostUrl"
Invoke-WebRequest -Uri $boostUrl -OutFile $boostZip
Write-Host "Extracting Boost headers..."
Expand-Archive -Path $boostZip -DestinationPath $boostExtract -Force
$boostRoot = Get-ChildItem -Path $boostExtract -Directory |
Where-Object { Test-Path (Join-Path $_.FullName "boost\dynamic_bitset.hpp") } |
Select-Object -First 1 -ExpandProperty FullName
if (-not $boostRoot) { throw "Boost extraction failed: boost/dynamic_bitset.hpp not found" }
}
Write-Host "##vso[task.setvariable variable=DEQ_BOOST_ROOT]$boostRoot"
$env:DEQ_BOOST_ROOT = $boostRoot
Write-Host "Boost configured at: $boostRoot"

# protoc: download directly from the protobuf GitHub release.
# The win64.zip is x86_64; Windows ARM64 runs it transparently
# under x64 emulation.
Expand Down
33 changes: 29 additions & 4 deletions .ado/stages/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -90,17 +90,33 @@ stages:
python3 -m venv .venv
source .venv/bin/activate
pip install --upgrade pip
pip install maturin pytest hypothesis more-itertools numpy
maturin_args=()
case "$(uname -s)" in
Linux)
pip install 'maturin[zig]' pytest hypothesis more-itertools numpy
maturin_args=(--zig --manylinux manylinux_2_34)
;;
Darwin)
pip install maturin pytest hypothesis more-itertools numpy
;;
*)
echo "Unsupported Unix platform: $(uname -s)" >&2
exit 1
;;
esac

for crate in binar paulimer; do
pushd "$crate/bindings/python"
maturin build --release --strip --out ../../../target/wheels
maturin build --release --strip "${maturin_args[@]}" --out ../../../target/wheels
popd
pip install --force-reinstall --no-deps target/wheels/${crate/-/_}*.whl
python -c "import ${crate/-/_}; print('${crate} imported successfully')"
python -m pytest "$crate/bindings/python/tests/" -v
done

if [[ "$(uname -s)" == "Linux" ]]; then
ls target/wheels/*manylinux_2_34*.whl
fi
ls -la target/wheels/
displayName: Build + test binar/paulimer wheels (Unix)
condition: ne(variables['Agent.OS'], 'Windows_NT')
Expand Down Expand Up @@ -197,12 +213,21 @@ stages:
python3 -m venv .venv
source .venv/bin/activate
pip install --upgrade pip
pip install maturin
if [[ "$(uname -s)" == "Linux" ]]; then
pip install 'maturin[zig]'
maturin_args=(--zig --manylinux manylinux_2_34)
else
pip install maturin
maturin_args=()
fi

cd deq/deq_runtime
maturin build --release --strip --out ../../target/wheels
maturin build --release --strip "${maturin_args[@]}" --out ../../target/wheels
cd ../..

if [[ "$(uname -s)" == "Linux" ]]; then
ls target/wheels/deq_runtime-*manylinux_2_34*.whl
fi
ls -la target/wheels/
displayName: Build deq-runtime wheel (Unix)
condition: ne(variables['Agent.OS'], 'Windows_NT')
Expand Down
22 changes: 19 additions & 3 deletions .ado/templates/build-wheels-steps.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,29 @@ steps:
displayName: Install Rust toolchain

- bash: |
python -m pip install --upgrade maturin
maturin_args=()
case "$(uname -s)" in
Linux)
python -m pip install --upgrade 'maturin[zig]'
maturin_args=(--zig --manylinux manylinux_2_34)
;;
Darwin)
python -m pip install --upgrade maturin
;;
*)
echo "Unsupported Unix platform: $(uname -s)" >&2
exit 1
;;
esac
mkdir -p target/wheels
cd binar/bindings/python
maturin build --release --out ../../../target/wheels
maturin build --release "${maturin_args[@]}" --out ../../../target/wheels
cd ../../..
cd paulimer/bindings/python
maturin build --release --out ../../../target/wheels
maturin build --release "${maturin_args[@]}" --out ../../../target/wheels
if [ "$(uname -s)" = "Linux" ]; then
ls ../../../target/wheels/*manylinux_2_34*.whl
fi
displayName: Build Python wheels (Linux/Mac)
condition: ne( variables['Agent.OS'], 'Windows_NT')

Expand Down
10 changes: 10 additions & 0 deletions .github/workflows/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,16 @@ jobs:
- name: Install cmake
uses: lukka/get-cmake@latest

- name: Install Boost (Linux/macOS)
if: runner.os != 'Windows'
shell: bash
run: |
if [ "$(uname -s)" = "Linux" ]; then
sudo apt-get update && sudo apt-get install -y --no-install-recommends libboost-dev
elif [ "$(uname -s)" = "Darwin" ]; then
brew install boost
fi

- name: Install cbindgen
run: cargo install cbindgen --locked

Expand Down
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Changed
- Linux native Python wheels are now built with a `manylinux_2_34` baseline via `maturin --zig`, improving compatibility with glibc 2.35 systems for `binar`, `paulimer`, and `deq-runtime`.

## [0.1.0] - 2026-01-23

### Added
Expand Down
4 changes: 2 additions & 2 deletions binar/src/matrix/aligned_bitmatrix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -811,9 +811,9 @@ impl AlignedBitMatrix {
///
/// Will panic if matrix is not invertible
pub fn inverted(&self) -> AlignedBitMatrix {
assert!(self.column_count() == self.row_count());
assert_eq!(self.column_count(), self.row_count());
let echelon_form = EchelonForm::new(self.clone());
assert!(echelon_form.pivots.len() == self.row_count());
assert_eq!(echelon_form.pivots.len(), self.row_count());
debug_assert_eq!(
self * &echelon_form.transform,
AlignedBitMatrix::identity(self.row_count())
Expand Down
4 changes: 2 additions & 2 deletions binar/src/matrix/bitmatrix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -842,7 +842,7 @@ impl BitMatrix {
///
/// Panics if `left.len() != self.row_count()`.
pub fn right_multiply(&self, left: &BitView) -> BitVec {
assert!(left.len() == self.row_count());
assert_eq!(left.len(), self.row_count());
BitVec::from_aligned(self.column_count(), self.aligned.right_multiply(&left.bits))
}

Expand Down Expand Up @@ -988,7 +988,7 @@ impl Mul<&BitView<'_>> for &BitMatrix {
type Output = BitVec;

fn mul(self, right: &BitView) -> Self::Output {
assert!(right.len() == self.column_count());
assert_eq!(right.len(), self.column_count());
BitVec::from_aligned(self.row_count(), &self.aligned * &right.bits)
}
}
Expand Down
71 changes: 69 additions & 2 deletions deq/deq_runtime/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,26 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
}
}

let mut include_dirs = vec![PROTO_DIR.to_string()];
// When using a system-installed protoc that does not embed well-known
// proto types (e.g. Ubuntu's `protobuf-compiler`), we need to pass the
// system include directory so that imports like
// `google/protobuf/empty.proto` can be resolved. The release binary
// downloaded by CI embeds these, so no extra path is needed there.
// Skip this probe if the caller already set PROTOC_INCLUDE, which
// prost-build will forward to protoc on its own.
if std::env::var_os("PROTOC_INCLUDE").is_none() {
for candidate in ["/usr/include", "/usr/local/include"] {
if Path::new(candidate)
.join("google/protobuf/empty.proto")
.exists()
{
include_dirs.push(candidate.to_string());
break;
}
}
}

tonic_prost_build::configure()
.build_server(true)
.client_mod_attribute(".", "#[cfg(feature = \"cli\")]")
Expand All @@ -39,7 +59,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
"deq.coordinator.window_coordinator.Event.event",
"#[allow(clippy::large_enum_variant)]",
)
.compile_protos(&proto_files, &[PROTO_DIR.to_string()])?;
.compile_protos(&proto_files, &include_dirs)?;

Ok(())
}
Expand All @@ -59,7 +79,7 @@ fn build_tesseract_bridge() {
println!("cargo::rerun-if-changed=cpp/tesseract/tesseract_bridge.cc");
println!("cargo::rerun-if-changed=src/decoder/tesseract_ffi.rs");

let boost_dir = download_boost(&out_dir);
let boost_dir = find_boost(&out_dir);

let mut build = cxx_build::bridge("src/decoder/tesseract_ffi.rs");
build
Expand All @@ -76,6 +96,53 @@ fn build_tesseract_bridge() {
build.compile("tesseract-bridge");
}

// ── Boost include path resolution ───────────────────────────────────
//
// Preference order:
// 1. DEQ_BOOST_ROOT env var (explicit override, e.g. from CI or developer)
// 2. BOOST_ROOT env var (set automatically on GitHub-hosted Windows runners)
// 3. pkg-config (Linux/macOS with libboost-dev / Homebrew boost)
// 4. Well-known system include directories
// 5. Fall back to downloading the headers at build time

#[cfg(feature = "tesseract")]
fn find_boost(out_dir: &std::path::Path) -> std::path::PathBuf {
use std::path::PathBuf;

for var in ["DEQ_BOOST_ROOT", "BOOST_ROOT"] {
if let Ok(val) = std::env::var(var) {
let p = PathBuf::from(&val);
if p.join("boost").join("dynamic_bitset.hpp").exists() {
eprintln!("cargo:warning=Using Boost from ${var}: {}", p.display());
return p;
}
}
}

if let Ok(output) = std::process::Command::new("pkg-config")
.args(["--variable=includedir", "boost"])
.output()
&& output.status.success()
{
let s = String::from_utf8_lossy(&output.stdout);
let p = PathBuf::from(s.trim());
if p.join("boost").join("dynamic_bitset.hpp").exists() {
eprintln!("cargo:warning=Using Boost from pkg-config: {}", p.display());
return p;
}
}

for candidate in ["/usr/include", "/usr/local/include", "/opt/homebrew/include"] {
let p = PathBuf::from(candidate);
if p.join("boost").join("dynamic_bitset.hpp").exists() {
eprintln!("cargo:warning=Using Boost from system path: {}", p.display());
return p;
}
}

download_boost(out_dir)
}

// ── Boost download (header-only, for dynamic_bitset) ────────────────

#[cfg(feature = "tesseract")]
Expand Down
2 changes: 1 addition & 1 deletion paulimer/src/clifford/clifford_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1375,7 +1375,7 @@ impl<const WORD_COUNT: usize, const QUBIT_COUNT: usize> MutablePreImages

#[allow(clippy::similar_names)]
fn preimage_xz_views_mut_distinct(&mut self, index: (usize, usize)) -> crate::Tuple2x2<Self::PreImageViewMut<'_>> {
debug_assert!(index.0 != index.1);
debug_assert_ne!(index.0, index.1);
unsafe {
let (xx, xz, zx, zz) = get_quad_mut_unsafe(&mut self.preimages);
let (xx0, xx1) = get_tuple_mut_unsafe(xx, index);
Expand Down
2 changes: 1 addition & 1 deletion paulimer/src/clifford/generic_algos.rs
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ where
for<'life> <CliffordLike as MutablePreImages>::PreImageViewMut<'life>:
PauliBinaryOps<<CliffordLike as Clifford>::DensePauli>,
{
assert!(left.num_qubits() == right.num_qubits());
assert_eq!(left.num_qubits(), right.num_qubits());
let mut result = CliffordLike::zero(left.num_qubits());
for qubit_index in 0..left.num_qubits() {
result
Expand Down
Binary file added stdin
Binary file not shown.
Loading