Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
9 changes: 4 additions & 5 deletions .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -90,13 +90,14 @@ jobs:
# empty bin/example harnesses, which is especially expensive with LLVM 21.
run: cargo nextest run --lib --all-features ${{ matrix.nextest_extra_args }}
- uses: astral-sh/setup-uv@v7
if: runner.os != 'Linux'
- name: Rust API example smoke
if: runner.os != 'Linux'
run: cargo run --example rust_api --all-features --profile test
- name: Python API example smoke
if: runner.os != 'Linux'
run: uv run examples/python_api.py
- name: Python main.py regression smoke
run: uv run -- python tests/test_main.py

lint:
runs-on: ubuntu-latest
Expand Down Expand Up @@ -146,10 +147,8 @@ jobs:
cache-on-failure: true
cache-workspace-crates: true
cache-targets: true
- name: Run cargo fmt (check if all code is rustfmt-ed)
run: cargo fmt --all --check
- name: Run cargo clippy (deny warnings)
run: cargo clippy --locked --all-targets --all-features -- -D warnings
- name: Run pre-commit lint
run: uvx prek run --all-files
Comment thread
qartik marked this conversation as resolved.
Comment thread
qartik marked this conversation as resolved.
- name: Verify Python stubs are up to date
if: github.event_name == 'pull_request' && steps.stub_changes.outputs.lib_rs == 'true'
run: |
Expand Down
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ lint:
.PHONY: test
test:
cargo nextest run --all-targets --all-features
$(PYTHON) tests/test_main.py

.PHONY: mutants
mutants:
Expand Down
50 changes: 50 additions & 0 deletions fuzz/fuzz_targets/mutated_fixture_contracts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,18 @@ const FIXTURES: &[&str] = &[
include_str!("../../tests/data/adaptive.ll"),
include_str!("../../tests/data/qir2_base.ll"),
include_str!("../../tests/data/qir2_adaptive.ll"),
include_str!("../../tests/data/dynamic_qubit_alloc.ll"),
include_str!("../../tests/data/dynamic_qubit_alloc_checked.ll"),
include_str!("../../tests/data/dynamic_qubit_array_checked.ll"),
include_str!("../../tests/data/dynamic_qubit_array_ssa.ll"),
include_str!("../../tests/data/dynamic_result_alloc.ll"),
include_str!("../../tests/data/dynamic_result_array.ll"),
include_str!("../../tests/data/dynamic_result_mixed_array_output.ll"),
];
const PROFILE_ALPHABET: &[u8] = b"abcdefghijklmnopqrstuvwxyz_";
const SCHEMA_ALPHABET: &[u8] = b"abcdefghijklmnopqrstuvwxyz_";
const NUMERIC_ALPHABET: &[u8] = b"0123456789";
const BOOL_ALPHABET: &[u8] = b"falserut";

#[derive(Clone)]
struct SpanSpec {
Expand Down Expand Up @@ -41,6 +49,33 @@ fn find_module_flag_value_span(text: &str, flag_name: &str) -> Option<Range<usiz
Some(start..end)
}

fn find_boolean_attr_value_span(text: &str, attr_name: &str) -> Option<Range<usize>> {
let needle = format!("\"{attr_name}\"=\"");
let start = text.find(&needle)? + needle.len();
let end = text[start..]
.find(|ch: char| ch != 't' && ch != 'r' && ch != 'u' && ch != 'e' && ch != 'f' && ch != 'a' && ch != 'l' && ch != 's')
.map_or(text.len(), |idx| start + idx);
Some(start..end)
}

fn find_boolean_module_flag_value_span(text: &str, flag_name: &str) -> Option<Range<usize>> {
let needle = format!("!\"{flag_name}\", i1 ");
let start = text.find(&needle)? + needle.len();
let end = text[start..]
.find(|ch: char| {
ch != 't'
&& ch != 'r'
&& ch != 'u'
&& ch != 'e'
&& ch != 'f'
&& ch != 'a'
&& ch != 'l'
&& ch != 's'
})
.map_or(text.len(), |idx| start + idx);
Some(start..end)
}

fn collect_spans(text: &str) -> Vec<SpanSpec> {
let mut spans = Vec::new();

Expand Down Expand Up @@ -71,6 +106,21 @@ fn collect_spans(text: &str) -> Vec<SpanSpec> {
});
}

for flag_name in [
"dynamic_qubit_management",
"dynamic_result_management",
"arrays",
] {
Comment thread
qartik marked this conversation as resolved.
if let Some(range) = find_boolean_module_flag_value_span(text, flag_name)
.or_else(|| find_boolean_attr_value_span(text, flag_name))
{
spans.push(SpanSpec {
range,
alphabet: BOOL_ALPHABET,
});
}
}

spans
}

Expand Down
7 changes: 2 additions & 5 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,19 +32,16 @@ auditwheel = "repair"
[dependency-groups]
dev = [
"maturin~=1.13.1",
"qir-formatter~=0.1",
"qir-formatter~=0.2",
"rich~=14.0",
"selene-sim~=0.2.11",
"selene-sim~=0.2.15",
]

[tool.uv]
prerelease = "allow"
# Rebuild package when any rust files change
cache-keys = [{ file = "src/**/*.rs" }]

[tool.uv.sources]
# selene-sim = { path = "../selene" }

[tool.deptry]
known_first_party = ["qir_qis"]

Expand Down
31 changes: 31 additions & 0 deletions qtm-qir-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,37 @@ declare void @__quantum__rt__int_record_output(i64, i8*)
declare void @__quantum__rt__double_record_output(double, i8*)
```

### Dynamic Allocation and Arrays

`qir-qis` accepts the optional Adaptive Profile capability flags:

- `"dynamic_qubit_management"`
- `"dynamic_result_management"`
- `"arrays"`

When enabled, the following QIR runtime APIs are accepted and lowered by the compiler:

```llvm
declare ptr @__quantum__rt__qubit_allocate(ptr %out_err)
declare void @__quantum__rt__qubit_release(ptr %qubit)
declare ptr @__quantum__rt__result_allocate(ptr %out_err)
declare void @__quantum__rt__result_release(ptr %result)

declare void @__quantum__rt__qubit_array_allocate(i64 %N, ptr %array, ptr %out_err)
declare void @__quantum__rt__qubit_array_release(i64 %N, ptr %array)
declare void @__quantum__rt__result_array_allocate(i64 %N, ptr %array, ptr %out_err)
declare void @__quantum__rt__result_array_release(i64 %N, ptr %array)
declare void @__quantum__rt__result_array_record_output(i64 %N, ptr %result_array, ptr %tag)
```

Support boundary:

- Fixed-size LLVM pointer arrays are supported via `alloca`, `getelementptr`, `load`, `store`, `extractvalue`, and `insertvalue`.
- `required_num_qubits` is optional when `dynamic_qubit_management=true`.
- `required_num_results` is optional when `dynamic_result_management=true`.
- `__quantum__rt__result_array_record_output` currently requires an array length that fits in `i32` for the downstream `print_bool_arr(...)` ABI.
- Runtime-sized classical buffers remain out of scope.

## Platform Utilities

These QIR functions provide additional runtime capabilities.
Expand Down
Loading
Loading