Skip to content

Compute runtime of a trace#3186

Closed
msoeken wants to merge 19 commits into
mainfrom
msoeken/depth_time
Closed

Compute runtime of a trace#3186
msoeken wants to merge 19 commits into
mainfrom
msoeken/depth_time

Conversation

@msoeken
Copy link
Copy Markdown
Member

@msoeken msoeken commented Apr 29, 2026

This computes the runtime of a trace using the existing Tetris-based scheduler. Before this functionality was only used in the Trace::estimate function, but can now also be called from the Python API as Trace.runtime.

msoeken and others added 6 commits April 29, 2026 11:57
This PR adds Electron (Node.js) test execution to the VS Code
integration test suites, which previously only ran in the browser via
`@vscode/test-web`. Tests now run in both environments to catch
platform-specific issues.

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This PR resolves #2581 by
separating cases when errors are in the left-hand side and printing the
appropriate span with the error message.

## Examples:

Error is in the left-hand side:
<img width="1012" height="203" alt="image"
src="https://github.com/user-attachments/assets/514ab572-08a9-437d-b4c8-558aaadaa841"
/>

Error is in the right-hand side:
<img width="1012" height="203" alt="image"
src="https://github.com/user-attachments/assets/fa48bef3-a966-4aca-b410-00c848c08d13"
/>
This change adds a new `type` parameter to `qsharp.run` that allows
users to specify the simulation type they want to use, with the default
the existing sparse simulation and a new option of "clifford"
simulation. There is an additional parameter that allows specifying the
number of qubits to use for Clifford simulation.

This is accomplished by updated the `Backend` trait to have most
functions be fallible, returning a `Result` type. This allows Clifford
simulation to fail gracefully on non-Clifford operations and qubit
allocation beyond the configured amount.
swernli and others added 10 commits May 7, 2026 17:26
This applies an optimization to how the expensive H, Rx, Ry gates are
applied in the sprase state vector simulator used by default in QDK. In
testing this showed around 33% perf improvement in (newly added)
targeted gate benchmarks and a range of perf improvements across
existing benchmarks, including 48% improvement in the Bench 5x5
benchmark.
This extends the timeout to hopefully make these parts of the
integration tests more reliable. We've been seeing intermittent timeouts
when waiting for VS code to report the creation of a new cell in a
notebook, but used a timeout of 50 ms that was probably too aggressive
for the (sometimes slow) CI. Fixes #3134
This PR improves the error logging for job submission to azure.
This PR fixes #1536

Previously, runtime errors from the debugger were displayed as
unreadable text in the circuit panel. The errors were being serialized
as plain JS `Error` objects in the WASM layer and the extension was
displaying the raw stack trace instead of the actual error message. This
PR aligns the debugger's error handling with the circuit panel command
by serializing errors as structured `IQSharpError` diagnostics all the
way from WASM through the worker boundary to the VS Code extension,
where they are now formatted with the same `errorsToHtml` function used
by the "Show Circuit" command — including error codes, source locations,
and clickable links to the relevant Q# source files.

## Before:
<img width="1262" height="499" alt="image"
src="https://github.com/user-attachments/assets/5dbddb77-9d46-46ab-be3c-b12e314e71f3"
/>


## Now:
<img width="732" height="485" alt="image"
src="https://github.com/user-attachments/assets/27464c87-6daf-4ed2-9711-16377aabf1e0"
/>
# PR: Migrate `qsharp` pip package → `qdk`

## Summary

This PR migrates all Python source code, Rust native extension code,
tests, and build infrastructure from the `qsharp` pip package
(`source/pip/`) into the `qdk` package (`source/qdk_package/`). After
this change:

- **`qdk`** is the primary Python package containing all source code and
the native Rust extension.
- **`qsharp`** becomes a thin, pure-Python deprecation shim that
re-exports from the `qdk` package.

## Motivation

The repo is being rebranded from `qsharp` to `qdk`. Rather than
maintaining two packages with real code, all functionality consolidates
into `qdk`, and `qsharp` exists solely for backward compatibility during
the transition period.

---

## What Changed

### 1. Rust native extension moved (`source/pip/src/` →
`source/qdk_package/src/`)

The pyo3 native module (`_native`) and all its Rust source files
(interpreter, QIR simulation, noisy simulator bindings, resource
estimator, etc.) were moved from `source/pip/` into
`source/qdk_package/`. The `Cargo.toml` at `source/qdk_package/` now
defines the `qdk` crate (previously `qsharp`), and the root `Cargo.toml`
workspace member was updated accordingly.

**Files moved (all "pure" renames):**
- `src/lib.rs`, `src/interpreter.rs`, `src/qir_simulation.rs`,
`src/noisy_simulator.rs`, `src/qre.rs`, `src/fs.rs`, `src/interop.rs`,
`src/generic_estimator/`, `src/displayable_output/`,
`src/state_*_template.html`, and all sub-modules.

### 2. Python source reorganized (`source/pip/qsharp/` →
`source/qdk_package/qdk/`)

All Python modules were moved from `qsharp.*` to `qdk.*` with import
paths updated throughout.

**Key structural changes:**

| Before (`qsharp.*`) | After (`qdk.*`) | Notes |
|---|---|---|
| `qsharp/__init__.py` | `qdk/__init__.py` | New minimal root; exposes
common utilities |
| `qsharp/_qsharp.py` | `qdk/_interpreter.py` + `qdk/_types.py` | Split:
interpreter functions vs. type definitions |
| `qsharp/_simulation.py` | `qdk/simulation/_simulation.py` | Moved into
`simulation/` subpackage |
| `qsharp/noisy_simulator/` | `qdk/simulation/_noisy_simulator.py` |
Absorbed into `simulation/` subpackage |
| `qsharp/interop/qiskit/` | `qdk/qiskit/` | Promoted from nested
`interop` to top-level subpackage |
| `qsharp/interop/cirq/` | `qdk/cirq/` | Promoted from nested `interop`
to top-level subpackage |
| `qsharp/utils/_utils.py` | *(deleted)* | `dump_operation` moved into
`_interpreter.py` |
| `qsharp/estimator/` | `qdk/estimator/` | Direct move, imports updated
|
| `qsharp/openqasm/` | `qdk/openqasm/` | Direct move, imports updated |
| `qsharp/code/` | `qdk/code/` | Direct move |
| `qsharp/applications/` | `qdk/applications/` | Direct move |
| `qsharp/qre/` | `qdk/qre/` | Direct move, imports updated |
| `qsharp/_device/` | `qdk/_device/` | Direct move, circular import
fixed (see below) |

**New `qdk` public API surface:**
- `qdk.qsharp` — Q# interpreter functions (`init`, `eval`, `run`,
`compile`, `circuit`, `estimate`, etc.)
- `qdk.simulation` — Simulation APIs (`NeutralAtomDevice`,
`NoiseConfig`, noisy simulator types)
- `qdk.qiskit` — Qiskit interop (`QSharpBackend`, `NeutralAtomBackend`,
etc.)
- `qdk.cirq` — Cirq interop
- `qdk.estimator` — Resource estimator
- `qdk.openqasm` — OpenQASM compilation/execution
- `qdk.code` — Code analysis
- `qdk.applications` — Domain applications (magnets, etc.)
- `qdk.qre` — QRE v3

### 3. `qsharp` package converted to deprecation shim (`source/pip/`)

`source/pip/qsharp/__init__.py` now:
1. Emits a `DeprecationWarning` on import.
2. Re-exports the full public API from `qdk._types`, `qdk._interpreter`,
and `qdk._native`.
3. Registers IPython magics from `qdk._ipython`.

All other Python files under `source/pip/qsharp/` are now thin re-export
wrappers that import from their `qdk.*` counterparts. The package
metadata in `source/pip/pyproject.toml` declares `dependencies =
["qdk==0.0.0"]` (version stamped at CI time).

### 4. Tests moved

- **Unit tests** remain at `source/qdk_package/tests/` — import paths
updated from `qsharp.*` to `qdk.*`.
- **Integration tests** moved from `source/pip/tests-integration/` to
`source/qdk_package/tests-integration/` — all imports updated to use
`qdk.*`.

### 5. Build script changes (`build.py`)

- **`--qdk` flag**: Builds the maturin wheel (Rust + Python) and runs
unit tests. Also runs integration tests when `--integration-tests` is
passed.
- **`--pip` flag**: Builds only the pure-Python `qsharp` shim wheel via
setuptools. No longer runs any tests (integration tests moved to
`--qdk`).
- **Removed `install_qsharp_python_package()`**: No longer needed since
integration tests don't depend on the `qsharp` shim.
- Install commands use `--no-deps --no-index` to install from local
wheels without reaching PyPI.

### 6. CI/CD pipeline changes

**GitHub Actions (`ci.yml`)** — No changes needed. The
`integration-tests` job already passed both `--qdk` and
`--integration-tests`.

**Azure DevOps (`publish.yml`)** — Restructured for clean platform
split:

| Job | Before | After |
|---|---|---|
| `Platform_Agnostic_Python` | `--jupyterlab --widgets --qdk` |
`--jupyterlab --widgets --pip` |
| Per-platform matrix | `--pip --integration-tests` | `--qdk
--integration-tests` |

This restructuring reflects the fact that `qdk` is now the
platform-specific package (it contains the native Rust extension), while
`qsharp` is now platform-agnostic (pure-Python shim). Previously it was
the reverse: `qsharp` held the native code and `qdk` was a pure-Python
meta-package. Each of the 6 OS/arch combinations now builds its own
native `qdk` wheel, while the platform-agnostic wheels (`qsharp`,
widgets, jupyterlab) are built once.

### 7. Circular import fix (`_device._atom` ↔ `simulation`)

`simulation/__init__.py` imports `NeutralAtomDevice` from
`_device._atom`, while `_device._atom` needs `NoiseConfig` and
`run_qir_*` from `simulation._simulation`. This was resolved by:
- Using `from __future__ import annotations` in
`_device/_atom/__init__.py`
- Guarding `NoiseConfig` import behind `TYPE_CHECKING`
- Deferring runtime imports of `run_qir_*` into the `simulate()` method
body

### 8. Miscellaneous

- **`.prettierignore`**: Added `source/qdk_package/src/**/*.html` and
`source/qdk_package/tests-integration/**/*.inc` (these files moved from
`source/pip/` which was fully ignored).
- **`.github/CODEOWNERS`**: Updated paths from `source/pip/` to
`source/qdk_package/`.
- **`.github/copilot-instructions.md`**: Updated architecture
documentation.
- **`Cargo.toml` (root)**: Updated workspace member from `source/pip` to
`source/qdk_package`.
- **Sample notebooks**: Updated `import qsharp` to `import qdk` / `from
qdk import qsharp`.

---

## How to Review

Due to the large number of file moves, GitHub's diff may be hard to
follow. Suggested approach:

1. **Start with `build.py`** — understand the new build flow (`--qdk`
builds native + runs tests, `--pip` just builds the shim).
2. **Read `source/qdk_package/qdk/__init__.py`** — see what the new
`qdk` root exposes.
3. **Read `source/pip/qsharp/__init__.py`** — see the deprecation shim
pattern.
4. **Skim `source/qdk_package/qdk/simulation/__init__.py`** and
`source/qdk_package/qdk/_device/_atom/__init__.py` — these have the
circular import fix.
5. **Check `.ado/publish.yml`** — verify the platform-agnostic vs.
per-platform split.
6. **The rest is mostly mechanical** — file renames and `s/qsharp/qdk/g`
import updates. GitHub should detect most as renames (95%+ similarity).

## Testing

- 1,248 unit tests pass (`source/qdk_package/tests/`)
- 338 integration tests pass per Qiskit version
(`source/qdk_package/tests-integration/`), run against both Qiskit v1
(`>=1.3,<2`) and v2 (`>=2,<3`)
- Widgets build successfully
- `qsharp` shim wheel builds successfully

## `qdk` Package Structure

```
qdk_package/
├── Cargo.toml
├── pyproject.toml
├── MANIFEST.in
├── README.md
├── test_requirements.txt
│
├── src/                                # Rust source for _native
│   └── *.rs
│
├── qdk/
│   ├── __init__.py                     # Package root; exposes common utilities
│   │
│   ├── _native.pyd/.so                 # Built by maturin (module-name = "qdk._native")
│   ├── _types.py                       # Pure Python types (PauliNoise, StateDump, etc.)
│   ├── _interpreter.py                 # Interpreter lifecycle & operations
│   ├── _ipython.py                     # %%qsharp cell magic
│   ├── _http.py                        # fetch_github()
│   ├── _fs.py                          # File system callbacks
│   ├── _adaptive_pass.py
│   ├── _adaptive_bytecode.py
│   ├── telemetry.py
│   ├── telemetry_events.py
│   │
│   ├── code/
│   │   └── __init__.py                 # Dynamic Q# callables namespace
│   │
│   ├── estimator/
│   │   └── __init__.py
│   │
│   ├── openqasm/
│   │   └── __init__.py
│   │
│   ├── qiskit/                         # Lifted out of interop/
│   │   ├── __init__.py
│   │   ├── backends/__init__.py
│   │   ├── passes/__init__.py
│   │   ├── jobs/__init__.py
│   │   └── execution/__init__.py
│   │
│   ├── cirq/                           # Lifted out of interop/
│   │   └── __init__.py
│   │
│   ├── _device/
│   │   ├── __init__.py
│   │   ├── _device.py
│   │   └── _atom/
│   │       └── __init__.py             # NeutralAtomDevice
│   │
│   ├── qre/
│   │   ├── __init__.py
│   │   ├── application/__init__.py
│   │   ├── models/__init__.py
│   │   │   ├── qubits/__init__.py
│   │   │   ├── qec/__init__.py
│   │   │   └── factories/__init__.py
│   │   ├── interop/__init__.py
│   │   ├── property_keys.py
│   │   └── instruction_ids.py
│   │
│   ├── applications/
│   │   ├── __init__.py
│   │   └── magnets/
│   │       ├── __init__.py
│   │       ├── utilities/
│   │       ├── trotter/
│   │       ├── models/
│   │       └── geometry/
│   │
│   ├── qsharp.py                       # Re-exports full qsharp-like API from _types + _interpreter
│   │
│   ├── simulation/                     # Simulation facade package
│   │   ├── __init__.py                 # Public API: NeutralAtomDevice, NoiseConfig, run_qir, etc.
│   │   ├── _simulation.py              # QIR simulation implementation (internal)
│   │   ├── _noisy_simulator.py         # Private wrapper for noisy simulator types
│   │   └── _noisy_simulator.pyi        # Type stubs
│   │
│   ├── widgets.py                      # from qsharp_widgets import * (external)
│   │
│   └── azure/                          # Re-exports from azure.quantum
│       ├── __init__.py
│       ├── job.py
│       ├── qiskit.py
│       ├── cirq.py
│       ├── argument_types.py
│       └── target/
│           ├── __init__.py
│           └── rigetti.py
│
├── tests/                              # Unit tests (run with --qdk)
│   ├── conftest.py
│   ├── test_qsharp.py
│   ├── test_interpreter.py
│   ├── test_re.py
│   ├── test_qasm.py
│   ├── ... (30+ test modules)
│   ├── reexports/                      # Re-export verification tests
│   ├── qre/                            # QRE-specific tests
│   └── applications/                   # Application-specific tests
│
└── tests-integration/                  # Integration tests (run with --qdk --integration-tests)
    ├── conftest.py
    ├── utils.py
    ├── test_adaptive_ri_qir.py
    ├── test_adaptive_rif_qir.py
    ├── test_adaptive_rifla_qir.py
    ├── test_base_qir.py
    ├── devices/                        # Device integration tests
    ├── interop_qiskit/                 # Qiskit interop tests
    ├── interop_cirq/                   # Cirq interop tests
    └── resources/                      # Test resource files (QIR, etc.)
```

For a detailed breakdown of every public symbol exported by each `qdk`
submodule, see
[API_SURFACE.md](https://github.com/microsoft/qdk/blob/9df4562c8f5c32eb6e7f24b56d60fa586d57301f/source/qdk_package/API_SURFACE.md).

## Follow-up Work

- **Move noise types to `qdk.simulation`**: The `PauliNoise`,
`DepolarizingNoise`, `BitFlipNoise`, and `PhaseFlipNoise` classes
currently live in `qdk._types` and are re-exported through `qdk.qsharp`.
These are simulation concepts and should canonically live in
`qdk.simulation`, with backward-compatible re-exports from `qdk.qsharp`
and `qdk._types`. Deferred from this PR to avoid additional circular
import complexity.
- **`NoiseConfig` in `qdk.qsharp`**: Similarly, `NoiseConfig` (from
`_native`) is re-exported in `qdk.qsharp.__all__` but semantically
belongs in `qdk.simulation` (where it's already exported). The
`qdk.qsharp` re-export should be removed in a follow-up.
- **Audit and rewrite docstrings**: Module and function docstrings
throughout the package still reference the old `qsharp` import paths and
naming conventions. These need to be audited and updated to reflect the
new `qdk.*` namespace for accurate generated documentation.
Comment on lines +606 to +614
qsharp.eval("""
operation Main() : Result {
use q = Qubit();
T(q);
return MResetZ(q);
}
""")
try:
qsharp.run("Main()", shots=1, type="clifford")
Comment on lines +552 to +560
qsharp.eval("""
operation Main() : Result[] {
import Std.Math.PI;
use qs = Qubit[2];
within {
H(qs[0]);
H(qs[1]);
} apply {
Rzz(PI() / 2.0, qs[0], qs[1]);
Comment on lines +525 to +529
qsharp.eval("""
operation Main() : Result {
import Std.Math.PI;
use q = Qubit();
Rx(PI(), q); // X equivalent
Comment on lines +384 to +390
qsharp.eval(program)

noise = NoiseConfig()
noise.x.set_bitflip(0.001)
noise.x.loss = 0.001

output = qsharp.run("Main()", shots=1000, noise=noise, type="clifford")
Comment on lines +340 to +345
qsharp.eval(program)

noise = NoiseConfig()
noise.x.loss = 0.1

output = qsharp.run("Main()", shots=1000, noise=noise, type="clifford")

def test_1224_clifford_ising():
qsharp.init(target_profile=TargetProfile.Base)
qsharp.eval(read_file_relative("CliffordIsing.qs"))

def test_smoke():
qsharp.init(target_profile=TargetProfile.Base)
qsharp.eval(read_file_relative("CliffordIsing.qs"))
Comment on lines +654 to +660
qsharp.eval("""
operation Main() : Unit {
use qs = Qubit[10];
}
""")
try:
qsharp.run("Main()", shots=1, type="clifford", num_qubits=5)
Comment on lines +638 to +640
qsharp.eval("""
operation Main() : Result {
use q = Qubit();
Comment on lines +622 to +630
qsharp.eval("""
operation Main() : Result {
use q = Qubit();
Adjoint T(q);
return MResetZ(q);
}
""")
try:
qsharp.run("Main()", shots=1, type="clifford")
@msoeken
Copy link
Copy Markdown
Member Author

msoeken commented May 11, 2026

Closing this one and opened #3209 after merging failed.

@msoeken msoeken closed this May 11, 2026
pull Bot pushed a commit to Mattlk13/qsharp that referenced this pull request May 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants