Skip to content
Merged
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
26 changes: 12 additions & 14 deletions .github/workflows/nightly.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
name: Nightly SDK Breakage Detection

# Installs the newest releases of every dependency and fails visibly when the
# install, dependency resolution or tests break. Do not add continue-on-error,
# "|| true" or "|| echo" here: a red nightly run is the signal.
on:
schedule:
- cron: "0 2 * * *"
Expand All @@ -9,27 +12,22 @@ jobs:
nightly:
name: Test against latest SDK versions
runs-on: ubuntu-latest
continue-on-error: true
steps:
- uses: actions/checkout@v4
- name: Checkout WestQuant Core
- name: Checkout WestQuant Core main
uses: actions/checkout@v4
with:
repository: WestQuantOpen/westquant-core
path: _westquant-core
- uses: actions/setup-python@v5
with:
python-version: "3.10"
- name: Install latest SDKs
python-version: "3.12"
- name: Install latest dependencies
run: |
pip install --upgrade pip
pip install -e ./_westquant-core[test]
pip install --upgrade qiskit pytket pennylane pulser 2>/dev/null || true
pip install -e .[test] 2>/dev/null || pip install -e .
python -m pip install --upgrade pip
pip install -e ./_westquant-core
pip install --upgrade --upgrade-strategy eager -e ".[test]"
pip check
pip freeze
- name: Run tests
run: |
pytest tests/ -v || echo "Nightly tests failed — SDK breakage detected"
- name: Report status
if: always()
run: |
echo "Nightly run completed. Check for SDK breakage above."
run: pytest tests/ -v
59 changes: 40 additions & 19 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -1,34 +1,55 @@
name: Publish to PyPI

# Single publication path. PyPI Trusted Publishing is bound to this file name
# (publish.yml) and the "pypi" environment. Tests must pass and the release tag
# must match the pyproject version before anything is uploaded.
on:
release:
types: [published]
workflow_dispatch:
inputs:
confirm:
description: 'Type "publish" to confirm'
required: true
default: 'publish'

permissions:
contents: read

jobs:
publish:
name: Build and publish
test:
uses: ./.github/workflows/tests.yml

build:
needs: test
runs-on: ubuntu-latest
environment: pypi
permissions:
id-token: write
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.10"
- name: Build package
python-version: "3.12"
- name: Check release tag matches package version
if: github.event_name == 'release'
run: |
pip install build
python -m build
- name: Check package
version=$(python -c "import tomllib; print(tomllib.load(open('pyproject.toml', 'rb'))['project']['version'])")
if [ "${GITHUB_REF_NAME#v}" != "$version" ]; then
echo "::error::Release tag $GITHUB_REF_NAME does not match pyproject version $version"
exit 1
fi
- name: Build and check distributions
run: |
pip install twine
twine check dist/*
- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
python -m pip install --upgrade build twine
python -m build
twine check --strict dist/*
- uses: actions/upload-artifact@v4
with:
name: python-package-distributions
path: dist/

publish:
needs: build
runs-on: ubuntu-latest
environment: pypi
permissions:
id-token: write
steps:
- uses: actions/download-artifact@v4
with:
name: python-package-distributions
path: dist/
- uses: pypa/gh-action-pypi-publish@release/v1
66 changes: 0 additions & 66 deletions .github/workflows/release.yml

This file was deleted.

6 changes: 4 additions & 2 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
name: Tests

on:
workflow_call:
push:
branches: [main]
pull_request:
Expand All @@ -12,6 +13,7 @@ jobs:
strategy:
matrix:
python-version: ["3.10", "3.11", "3.12"]
qiskit-version: ["2.5.2", "2.6.1"]
steps:
- uses: actions/checkout@v4
- name: Set up Python
Expand All @@ -20,8 +22,8 @@ jobs:
python-version: ${{ matrix.python-version }}
- name: Install
run: |
pip install westquant-core>=0.2.0a1
pip install "qiskit~=2.5.2"
pip install "westquant-core>=0.2.0a1"
pip install "qiskit==${{ matrix.qiskit-version }}"
pip install -e .[test]
- name: Test
run: pytest tests/ -v
25 changes: 25 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Changelog

## 0.1.2

- Fix: the `westquant` layout, routing and optimization stage plugins crashed
on Qiskit 2.x (`QuantumCircuit.from_instructions(dag)` with a `DAGCircuit`)
and the candidate path read a private `tqc._layout.dag` attribute. The stages
are reimplemented as best-of selections over Qiskit built-in strategies that
set `layout` / `final_layout` correctly and use Qiskit's standard embed and
routing stage wrappers.
- Candidate results, including failures, are recorded in
`property_set["westquant_selection"]` and failures are logged; if every
candidate fails a `TranspilerError` is raised instead of silently returning
the input.
- `optimization_level=0` is no longer replaced by 2 inside the plugin.
- Brings the BackendV2 `Target` support in `noise_aware_metrics` that shipped in
the 0.1.1 wheel into the source repository.
- Tests: plugin discovery, unitary equivalence, measurement distribution,
coupling-map compliance, selected-candidate application and all-fail error.

## 0.1.1

Published to PyPI on 2026-09-26 from a working tree that was not committed;
its only source difference from 0.1.0a4 was BackendV2 support in
`adapter.noise_aware_metrics`.
29 changes: 24 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,11 +75,30 @@ the full Pareto front and all losing candidates remain in the dataset.

## Qiskit integration

The package also exposes alpha stage entry points for Qiskit's native
transpiler-plugin mechanism. The plugin stages still delegate to Qiskit
baselines in this Wave 1 build. The deterministic search engine is the first
real WestQuant search implementation and will be wired into stage plugins after
benchmarking and recursion guards are complete.
The package registers `westquant` as a Qiskit transpiler stage plugin for the
`layout`, `routing` and `optimization` stages:

```python
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager

pm = generate_preset_pass_manager(
optimization_level=2, backend=backend, seed_transpiler=7,
layout_method="westquant", routing_method="westquant", optimization_method="westquant",
)
tqc = pm.run(qc)
print(pm.property_set["westquant_selection"])
```

Each stage is a deterministic best-of selection among Qiskit's own built-in
strategies (layout: trivial/dense/sabre; routing: SabreSwap/BasicSwap/
LookaheadSwap; optimization: levels 0-3), scored by two-qubit cost (SWAP = 3)
and then depth. Every candidate, including failures and their error messages,
is recorded in `property_set["westquant_selection"]`; if every candidate fails a
`TranspilerError` is raised. The routing stage only runs when the circuit is
not already mapped to the device. Tests check unitary equivalence, ideal output
distributions with measurements, coupling-map compliance and that the selected
candidate is the one returned. This is a best-of wrapper over Qiskit, not the
full sequential policy search described below.

## Sequential policy search

Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "westquant-qiskit"
version = "0.1.0a4"
version = "0.1.2"
description = "Deterministic representation search, RepGraph tracing, and WQT-ready data generation for Qiskit"
readme = "README.md"
requires-python = ">=3.10"
Expand Down
2 changes: 1 addition & 1 deletion src/westquant_qiskit/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from .verification import QiskitEquivalenceVerifier, VerificationReport
from .sequential import SequentialSearchEngine, SequentialSearchResult, SequentialSearchSpace

__version__ = "0.1.0a4"
__version__ = "0.1.2"

__all__ = [
"QiskitAdapter",
Expand Down
Loading
Loading