Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
6bbf180
Add a key word 'qumode' token in QasmScanner. still need to add qumod…
Katie1harrison Jun 11, 2026
a19dbe9
feat: added qumode keyword and qumode declaraion syntax
Katie1harrison Jun 11, 2026
600d402
doc: added uv; clarified which dir to run conan from
joshmtlau Jun 17, 2026
46176d4
chore: bump flake8 to 7.3.0 for Python 3.12
joshmtlau Jul 14, 2026
75916f3
feat: support angle-array literals as gate parameters
joshmtlau Jul 14, 2026
a318336
test: move snap gate test to qumode subdir
joshmtlau Jul 14, 2026
d3f5498
added comments
Katie1harrison Jul 16, 2026
8e3fca5
qumode fix
Katie1harrison Jul 16, 2026
ca1c243
Merge pull request #2 from paragon-lab/feat/qumode
joshmtlau Jul 16, 2026
996895f
doc: added syntax change markdown
joshmtlau Jul 16, 2026
f01ec17
Allow qumodes as gate operands via opaque CV gates.
joshmtlau Jul 17, 2026
08b68d4
Add builtin disp gate with complex params and controlled forms.
joshmtlau Jul 17, 2026
ee913c7
Merge pull request #6 from paragon-lab/feat/cv-primitive-gates
Katie1harrison Jul 20, 2026
5bc6aca
added the type Unitary
Katie1harrison Jul 21, 2026
4faa27e
added comments for readability
Katie1harrison Jul 21, 2026
d53a324
Basic unitary data-type declaration
Katie1harrison Jul 22, 2026
b30d888
Added unitary as a gate operation
Katie1harrison Jul 22, 2026
2d994f5
Add parser support for unitary attribute syntax
Katie1harrison Jul 28, 2026
a0f4141
fix: gate calls not recognizing subscripted qumode identifiers
joshmtlau Jul 29, 2026
0217278
doc: qumode gates, including displacement, SNAP, and ECD gate syntax …
joshmtlau Jul 29, 2026
6c95334
Merge pull request #8 from paragon-lab/fix/qumode-subscript-op-support
joshmtlau Jul 29, 2026
80efd0d
Add fully-typed gate declarations with call-site type checking.
joshmtlau Jul 30, 2026
17e7a32
Fix duplicate GateQOpList entries under ctrl/negctrl/inv/pow.
joshmtlau Jul 30, 2026
1c7869c
Print typed-gate formals in dumps and fix definition mangling.
joshmtlau Jul 30, 2026
7a816e7
Improve gate AST operand naming and keep complex call expressions.
joshmtlau Jul 30, 2026
927c31e
Store complex Params for typed complex gate formals.
joshmtlau Jul 30, 2026
a460ad9
fix: sign in displacement for ECD gate in cvgates.inc
joshmtlau Jul 30, 2026
89fb8a5
doc: update project status and documentation for qumode gates
joshmtlau Jul 30, 2026
e7b051f
Add uint gate template params with call-site size inference.
joshmtlau Aug 3, 2026
7f0dcba
Add Fock-level ctrl[…] / negctrl[…] with expression levels.
joshmtlau Aug 3, 2026
2651901
Add gate-body for loops and harden typed SNAP definitions.
joshmtlau Aug 3, 2026
299a448
Make typed gate Params type-faithful and freeze the CV call ABI.
joshmtlau Aug 3, 2026
418eb30
Merge pull request #10 from paragon-lab/feat/gate-declaration-fixed-n…
joshmtlau Aug 3, 2026
e2aeb79
chage so we can define a unitary matrix
Katie1harrison Aug 7, 2026
6da7c7d
added a snap gate example
Katie1harrison Aug 11, 2026
ffab91f
added snap example
Katie1harrison Aug 11, 2026
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: 41 additions & 0 deletions .cursor/rules/full-test-before-commit.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
---
description: >-
Always run the full test suite before committing, and after finishing a
feature, so grammar/parser regressions are not missed by subset runs.
alwaysApply: true
---

# Full test suite before commit

## When

- **Before any git commit** (including amend/squash of local WIP), and
- **After a feature is declared complete** (ready to land or squash),

run the **full** CTest suite for this repo — not a subset filtered to the
feature area (e.g. not only `qumode-*`).

## How

From the configured build directory (typically `build/Release`):

```bash
ctest --output-on-failure
```

If the tree was reconfigured or new tests were added, re-run `cmake` in that
build dir first so CTest picks them up.

## Pass / fail

- **All tests must pass** before creating or amending a commit.
- If anything fails: fix, re-run the **full** suite, then commit.
- Do not treat “related tests passed” as sufficient when the change touches
shared surfaces (parser `.y`/`.l`, AST builders used by many productions,
symbol table, etc.).

## Why

Subset runs miss cross-cutting regressions (e.g. gate `ArgsList` changes
breaking function-call / OpenPulse tests). A prior commit shipped with only
qumode checks green while nine unrelated tests failed.
51 changes: 51 additions & 0 deletions .cursor/rules/safe-file-edits.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
---
description: >-
Never overwrite or delete large/important source files without a recoverable
backup. Prefer surgical edits; forbid full-file Write on existing critical
paths unless backed up first.
alwaysApply: true
---

# Safe edits for large / critical files

## Incident this prevents

Using the **Write** tool (or equivalent full-file overwrite) on an existing
large source file replaces the entire contents. That is easy to confuse with
“replace this function,” and recovery may depend on git history, transcripts,
or stale object files — not a reliable backup.

In this repo, high-risk paths include especially:

- `lib/AST/ASTProductionFactory.cpp` (~30k lines)
- `lib/Parser/QasmParser.y`
- Other multi-thousand-line AST / parser / builder translation units

## Required workflow

1. **Prefer `StrReplace`** (or a small, targeted patch) for existing files.
Never use `Write` to “update” a function inside a large existing file.
2. **Before any full-file overwrite** of an existing file (Write with a path
that already exists, or rewrite-via-script that truncates the file):
- Copy the current file to a recoverable backup first, e.g.
`cp path path.bak.$$` or write under `/tmp/` with a clear name, **or**
ensure the content is already committed / stashable and note that.
- Confirm the backup is readable (`wc -l` / size check) before overwriting.
3. **Before deleting** a tracked or substantial untracked source file: ask the
user for permission (project rule). Do not delete “to clean up” mid-task.
4. If a full rewrite is truly required, prefer:
- write the new content to a **new temp path**,
- verify it (size, key symbols, compile if feasible),
- then `mv` over the original **only after** the backup exists.

## After a bad overwrite

- Prefer `git checkout -- <file>` / `git restore` only if losing uncommitted
work on that file is acceptable — check `git status` / `git diff` first.
- Otherwise restore from the backup copy created above, or from agent
transcript / local history — do not invent a partial file and call it done.

## Quick check before Write

Ask: “Does this path already exist, and is it more than a small new file?”
If yes → **do not Write**; use StrReplace or backup-then-replace.
126 changes: 126 additions & 0 deletions .cursor/skills/typed-gate-declarations/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
---
name: typed-gate-declarations
description: >-
Fully-typed OpenQASM gate declarations (qubit/qumode formals + explicit
classical types) and call-site type checking in this fork. Use when editing
gate grammar, ProductionRule_10030, CreateGateCall, ValidateTypedGateCall,
ASTGateType, cvgates.inc, ECD/disp/SNAP typing, or when adding typed gate
call checks.
---

# Typed gate declarations

## Design rules

- **All-untyped** classical + bare quantum formals = legacy OpenQASM 3 (`ProductionRule_1430` / `1431`). No call-site type checks.
- **Any typed classical formals** with **typed quantum operands** = fully typed (`ProductionRule_10030`). No partial typing.
- **Templates (v1):** `gate foo[uint N](array[T, N] …)` — unsigned int templates used as array lengths. Call-site `foo([…])` infers `N` from literal arity or a sized named array’s declared length; `foo[N]([…])` checks. Uninitialized named arrays → “used before assigned.” Call Gate `TemplateParams` carry `<Value>` (NaN on decl, bound int on call); body `N` is not overwritten.
- **Fock ctrl:** `ctrl[3]` / `ctrl[N]` / `ctrl[N-1]` / `negctrl[i]` — level may be int, id, or binary/unary (or parenthesized) expression. Distinct from qubit `ctrl(n)`. See `docs/gates.md`.
- **Gate-body `for`:** `GateForStmt` + nested `GateOpList` → `ASTGateForOpNode` (`ASTTypeGateFor`). SNAP in `cvgates.inc` is `array[angle, N]` with `for i in [0:N] { ctrl[i] @ gphase(thetas[i]) qm; }`.
- **Compiler call ABI:** see `docs/gates.md` § Frozen call-node contract — `disp` / `snap` / `ecd` call fields only; decls and `GateQOpList` are unstable.
- `ctrl` is a reserved token (`TOK_CTRL`); do not use `ctrl` as an operand name.
- **Out of CV-core scope:** `while` in `GateOpList`; int/bool/duration/bit array formals; non-`uint` / non-size template params.

## Grammar / production

| Piece | Location |
|-------|----------|
| Typed quantum list | `GateTypedQuantumOperandList` in `lib/Parser/QasmParser.y` |
| Decl production | `ProductionRule_10030` in `ASTProductionFactory.cpp` |
| Template register | `ProductionRule_10031` + `ASTGateTemplateParamBuilder` |
| Shared AST build | Delegates to `ProductionRule_1430` after validating formals |
| Call entry | `ProductionRule_3500` → `CreateQOpNodeCall` → `CreateGateCall` |
| Compatibility | `ASTGateType` in `include/qasm/AST/ASTGateType.h` |

Decl-time checks in `10030`:
- Classical: `IsExplicitClassicalGateParamType` (includes explicit `angle`,
float/complex/arrays; bare Identifier formals are also angles and allowed
on this path when quantum operands are typed)
- Quantum: polymorphic type must be `ASTTypeQubit` or `ASTTypeQumode`
- Stores `FormalParamTypes`, `FormalParamArraySizes`,
`FormalParamArraySizeTemplateIndices`, `TemplateParams`, `FormalQuantumTypes`

## GateType lattice (call-site)

`FormalParamTypes[i]` (+ optional size) is the type oracle. Carriers (`Params` / `ArrayParams` / `ComplexArrayParams` / `ComplexParams`) are storage only.

`ASTGateType::Compatible(F, A)`:
- Real↔angle array family (`AngleArray` / `FloatArray` / `MPDecimalArray`)
- Real→complex scalar and real-array→complex-array promotion
- Reject complex→real (scalar or array)
- Array size equality when both sides known

Hook: `ASTTypeDiscovery::ValidateTypedGateCall` from `CreateGateCall` **before** `CloneCall`, only if `GN->IsFullyTyped()`.

## AST storage (definition gate)

| Formal kind | Storage | Read for checking |
|-------------|---------|-------------------|
| `complex[…]` | `Params` as `MPComplex` | Prefer `FormalParamTypes[i]` on fully-typed gates |
| `float` / `double` / `mpdecimal` scalars | `Params` as declared type (type-faithful) | Prefer `FormalParamTypes[i]` |
| other non-float scalars (int/bool/…) | still coerced to angle `.gateparam` in `Params` | Must use `FormalParamTypes` |
| `array[float/mpdecimal, N]` | Declared STE in GSTM + same node in `Params` (no `.gatearray`) | `FormalParamTypes` + size |
| `array[angle, N]` | `Params` as `AngleArray` | `FormalParamTypes` + size |
| `array[complex[…], N]` | `Params` as `MPComplexArray` | `FormalParamTypes` + size |
| qubit / qumode | `OperandParams` STE; polymorphic type restored | Prefer `FormalQuantumTypes[i]` |
| `Operands` vector | synthetic GateQubitParam-shaped nodes | **Do not** use for qubit vs qumode |

Array formals share one ctor path in `ASTGates.cpp`: erase LSTM, bind declared STE into GSTM, then `AddParam` with the declared array type (float/mpdecimal/angle/complex).

## Call-site Params (Phase 2)

`ArgumentNodeList` ctor keeps `Params` type-faithful when `FormalParamTypes` say so:

- `FormalWantsComplex` → `MPComplex` (unchanged)
- `FormalWantsFloatArray` + `ProductionRule_10010` AngleArray literal → convert to `MPDecimalArray` (`ast-gate-mpdecimal-array-lit-*`)
- `FormalWantsFloatScalar` → keep float/double/mpdecimal (no angle coercion)
- Angle / complex-array formals → store as AngleArray / MPComplexArray
- Untyped OQ3 calls → still angle-shaped Params

## Literals (`ProductionRule_10010`)

`ASTGateType::ExpressionListHasComplex` decides complex vs angle-array literal. Call-site and builder share that classification. Real array literals stay AngleArray at 10010; float formals convert at call materialization.

Gate call `ArgsList` is `'(' ExprList ')'`. Array literals are `ExprList`
elements (`[ExprList]`), so `foo([a,b], theta)` works alongside `… im`
complex initializers and nested function calls. Do not reintroduce a
separate `GateCallArg` list — it LR-conflicts with function-call parsing.

## Do not

- Check untyped OQ3 gates.
- Treat “has `ComplexParams` alone” as fully typed (classical-only typing + bare qubits is not the fully-typed form).
- Leave stray `std::cerr` / `std::cout` debug prints in production paths.

## Array formals (SNAP-like)

Gate param lists provisionally type identifiers as `ASTTypeAngle`. `ProductionRule_822` must **rebind** Angle/Undefined → array type and allow formals in gate/function contexts (`AllowArrayInCurrentContext` / `IsGateParameterArgument`). Do not leave hard `assert(Id->GetSymbolType() == Ty)` — that SIGABRTs on `array[float[64], N] thetas`.

`GateOpList` includes `for` (`GateForStmt`). After an `array[…]` formal,
`SetCurrentType(ForStatement)` must win over leftover `PreviousType` array
state or induction vars are typed as arrays.

## Tests

- Positive: `tests/src/qumode/gate-for-fock.qasm` (gate-body `for` + Fock `ctrl[i]` + `thetas[i]`)
- Positive: `tests/src/qumode/ecd-mixed.qasm` (`ecd(0.5) qb, qm`)
- Positive: `tests/src/qumode/complex-array-param.qasm` (`array[complex[…], N]` formal + call)
- Positive: `tests/src/qumode/float-array-param.qasm` (`array[float[64], N]` body index + call)
- Positive: `tests/src/qumode/angle-array-param.qasm` (`array[angle, N]` body index + multi-gate ASTM reuse)
- Positive: `tests/src/qumode/multi-angle-array-param.qasm` (two angle arrays, different `N`)
- Positive: `tests/src/qumode/angle-scalar-param.qasm` (`angle` scalar + float array formals)
- Positive: `tests/src/qumode/gate-template-array-param.qasm` (`uint N` template + infer/`[3]`)
- Negative (expect-fail `test $? -ne 0`):
- `ecd-param-reject.qasm` — angle array for complex formal
- `ecd-operand-reject.qasm` — qumode where qubit expected
- `ecd-operand-order-reject.qasm` — swapped qubit/qumode
- `disp-qubit-reject.qasm` — builtin disp pattern
- `array-elem-complex-reject.qasm` — complex element in float array formal
- `array-size-mismatch-reject.qasm` — literal length ≠ formal `N` (param 0)
- `array-size-mismatch-param1-reject.qasm` — size mismatch on second array formal
- `gate-template-size-mismatch-reject.qasm` — `foo[2]([a,b,c])`
- `gate-template-infer-fail-reject.qasm` — uninitialized named array (`used before assigned`)
- `gate-template-conflict-reject.qasm` — conflicting inferred `N` from two arrays
- `gate-template-unused-reject.qasm` — `N` not used as an array size (cannot infer)
- Include: `tests/include/cvgates.inc`
- Manual: `gate-decl.qasm` (`rz3` + `disp3`)
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,9 @@
VERSION.txt
build/
CMakeUserPresets.json

# uv
.venv/
# clangd cache
.cache/
.cache/*
20 changes: 10 additions & 10 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,40 +3,40 @@ repos:
rev: v4.1.0
hooks:
- id: no-commit-to-branch
stages: [commit]
stages: [pre-commit]
args: [--branch, main, --pattern, release/.*, --pattern, .*/release/.*]
- id: check-json
stages: [commit]
stages: [pre-commit]
- id: end-of-file-fixer
stages: [commit]
stages: [pre-commit]
exclude: '.+(\.s[ql][23])$'
- id: trailing-whitespace
stages: [commit]
stages: [pre-commit]
args: [--markdown-linebreak-ext=md]
exclude: '.+(\.s[ql][23])$'
- id: check-merge-conflict
stages: [commit]
stages: [pre-commit]
- id: debug-statements
stages: [commit]
stages: [pre-commit]
- repo: https://github.com/psf/black
rev: 22.3.0
hooks:
- id: black
stages: [commit]
stages: [pre-commit]
args:
- "-l 100"
- repo: https://github.com/pre-commit/mirrors-clang-format
rev: v17.0.5
hooks:
- id: clang-format
stages: [commit]
stages: [pre-commit]
args:
- "--style=file:.clang-format"
- repo: https://github.com/PyCQA/flake8.git
rev: 4.0.1
rev: 7.3.0
hooks:
- id: flake8
stages: [commit]
stages: [pre-commit]
args:
- "--max-line-length=100"
- "--extend-ignore=W503"
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,16 +45,19 @@ Currently the supported platforms are Linux and OSX. It is possible to build on
- Clone this repo: `git clone git@github.com:openqasm/qe-qasm.git`
- Install build dependencies: `pip install -r requirements-dev.txt`
- It is recommended to use a Python virtual environment for this
- If you're using `uv`, `uv sync` also works. Then, prepend every call to `conan` with `uv run` below.
- The package may be built and installed to conan with: `conan create . --build=outdated -pr:h default -pr:b default`
- This will build the conan package and install it locally. The version will be detected automatically from the repo tag.
- If you wish to override the package name, version or remote do so by calling conan with `conan create . <package>/<version>@remote -pr:h default -pr:b default`

#### Building for development and debugging
- If you're using `uv`, make sure you have run `uv sync` first. Then, prepend every call to `conan` with `uv run` below.
- Create a build directory: `mkdir build && cd build/`
- Install package with: `conan install .. --build=outdated -pr:h default -pr:b default` which will install and build all missing dependencies
- Build the package with: `conan build ..`
- The package tests may be run with: `conan build .. --test`

Note that the `conan` commands should be called under the `build/` directory.

### Make

Expand Down
77 changes: 77 additions & 0 deletions conan.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
{
"graph_lock": {
"nodes": {
"0": {
"ref": "qe-qasm/0.1.dev49+gd53a324.d20260722",
"options": "examples=True\nshared=False\nbison:fPIC=True\nflex:fPIC=True\nflex:shared=False\ngmp:disable_assembly=True\ngmp:enable_cxx=True\ngmp:enable_fat=False\ngmp:run_checks=False\ngmp:shared=True\nmpc:shared=True\nmpfr:exact_int=gmp\nmpfr:shared=True",
"requires": [
"1",
"2",
"3",
"4",
"6"
],
"path": "conanfile.py",
"context": "host"
},
"1": {
"ref": "gmp/6.3.0",
"options": "disable_assembly=True\nenable_cxx=True\nenable_fat=False\nrun_checks=False\nshared=True",
"package_id": "fd753028090680f4f67b15fd8f9cf62b7e067d18",
"prev": "0",
"context": "host"
},
"2": {
"ref": "mpfr/4.2.1",
"options": "exact_int=gmp\nshared=True\ngmp:disable_assembly=True\ngmp:enable_cxx=True\ngmp:enable_fat=False\ngmp:run_checks=False\ngmp:shared=True",
"package_id": "6a95df1aab838f59c295854c305f3dee0123b70a",
"prev": "0",
"requires": [
"1"
],
"context": "host"
},
"3": {
"ref": "mpc/1.3.1",
"options": "shared=True\ngmp:disable_assembly=True\ngmp:enable_cxx=True\ngmp:enable_fat=False\ngmp:run_checks=False\ngmp:shared=True\nmpfr:exact_int=gmp\nmpfr:shared=True",
"package_id": "1a1af87cb4792c6f38cdc8a6684c7f3b3289a10a",
"prev": "0",
"requires": [
"1",
"2"
],
"context": "host"
},
"4": {
"ref": "bison/3.8.2",
"options": "fPIC=True",
"package_id": "30a96fe53c3e98a98796c7cef11032c83754d473",
"prev": "0",
"requires": [
"5"
],
"context": "host"
},
"5": {
"ref": "m4/1.4.19",
"options": "",
"package_id": "24647d9fe8ec489125dfbae4b3ebefaf7581674c",
"prev": "0",
"context": "host"
},
"6": {
"ref": "flex/2.6.4",
"options": "fPIC=True\nshared=False",
"package_id": "30a96fe53c3e98a98796c7cef11032c83754d473",
"prev": "0",
"requires": [
"5"
],
"context": "host"
}
},
"revisions_enabled": false
},
"version": "0.4",
"profile_host": "[settings]\narch=x86_64\narch_build=x86_64\nbuild_type=Release\ncompiler=gcc\ncompiler.libcxx=libstdc++\ncompiler.version=8\nos=Linux\nos_build=Linux\n[options]\n[build_requires]\n[env]\n"
}
Loading
Loading