added adaptive profile support for qbraid_qir
Summary of changes
Adaptive Execution Profile The Adaptive Execution Profile (adaptive_profile.json) enables:
- Conditional Execution: Support for if/else statements based on measurement results
- Qubit Reuse: Operations on qubits after measurement (with proper state tracking)
- Advanced Output Recording: Preserves register structure and grouping
- Measurement State Tracking: Per-qubit measurement state management
Key differences from qbraid_qir custom Profile:
- Uses qis.* functions instead of pyqir._native.* for better adaptive support
- Implements measurement state tracking for post-measurement qubit operations
- Provides register-grouped output recording
- Supports configurable barrier emission
Profile Structure Each profile is defined as a JSON file with the following structure:
{
"profile_name": "ProfileName",
"version": "1.0.0",
"description": "Profile description",
"capabilities": {
"feature_name": {
"enabled": true,
"description": "Feature description",
"additional_options": "..."
}
},
"required_functions": {
"quantum_intrinsics": ["function_list"],
"runtime_functions": ["function_list"]
},
"restrictions": {
"restriction_category": {
"setting": "value"
}
},
"validation_rules": {
"rule_category": {
"rule_name": "rule_value"
}
},
"compliance_checks": [
{
"rule_id": "PROFILE_001",
"description": "Rule description",
"severity": "error|warning|info",
"check_type": "check_category"
}
]
}
Compliance Issues Identified
- Function Usage Compliance (CRITICAL) Issue: Custom profile uses pyqir._native.* functions while adaptive profile requires qis.* functions.
Custom Profile
pyqir._native.mz(self._builder, src_id, tgt_id)
pyqir._native.reset(self._builder, qid)
pyqir._native.if_result(self._builder, result, ...)
pyqir._native.barrier(self._builder)
Adaptive Profile:
qis.mz(self._builder, src_id, tgt_id)
qis.reset(self._builder, qid)
qis.if_result(self._builder, result, ...)
qis.barrier(self._builder)
Impact: Native functions don't provide the adaptive execution semantics required for conditional branching and qubit reuse.
- Measurement State Tracking (MEDIUM) Issue: Base profile doesn't track which qubits have been measured.
Adaptive Profile Addition:
self._measured_qubits: dict[int, bool] = {} # Track measured qubits
# In measurement:
qubit_id = pyqir.qubit_id(src_id)
self._measured_qubits[qubit_id] = True
# In reset:
self._measured_qubits[qubit_id] = False
Impact: Without tracking, post-measurement operations can't be properly validated or optimized.
- Output Recording Structure (HIGH) Issue: Custom profile records output bit-by-bit, losing register structure.
Custom Profile:
for i in range(module.qasm_program.num_qubits):
result_ref = pyqir.result(self._llvm_module.context, i)
pyqir.rt.result_record_output(self._builder, result_ref, pyqir.Constant.null(i8p))
Adaptive Profile:
# Record array for each register
for reg_name, reg_size in self._global_creg_size_map.items():
pyqir.rt.array_record_output(self._builder, ...)
# Then record individual results within register
for i in range(reg_size - 1, -1, -1): # Inverted order
# Record individual bits...
Impact: Register structure is lost, making it difficult for adaptive runtimes to process results correctly.
- Qubit Use Validation (LOW) Issue:Custom profile doesn't validate qubit usage patterns for adaptive execution. Adaptive Profile Addition:
def _check_qubit_use_after_measurement(self, qubit_ids: list[pyqir.Constant]) -> None:
"""Check if any qubits have been measured before use (adaptive profile allows this)."""
# In adaptive profile, qubit use after measurement is allowed
# This is a key difference from custom profile
pass
Impact: No validation of post-measurement qubit operations, which may be restricted in some execution environments.
- Barrier Configuration (LOW) Issue: Custom profile always emits barriers when applicable. Adaptive Profile Addition:
def __init__(self, ..., emit_barrier_calls: bool = False):
self._emit_barrier_calls = emit_barrier_calls
# In barrier handling:
if self._emit_barrier_calls:
qis.barrier(self._builder)
Impact: Some adaptive runtimes may not support or need explicit barrier operations.
Codecov Report
Attention: Patch coverage is 89.13043% with 30 lines in your changes missing coverage. Please review.
:loudspeaker: Thoughts on this report? Let us know!
The PR is quite close @feelerx , thanks for the great work! Other than some minor refactoring suggestions, the only major change I'd want is adding test cases for verification.
Besides that, could you also add an entry to the CHANGELOG.md with a link to this PR?
Thank you for the changes @feelerx , they LGTM! Can you please fix the format action so that it can be merged?
@ryanhill1 this lgtm, can you approve as well?
Shouldn't we put this code in a module
qbraid_qir/profiles, notqbraid_qir/qasm3/profiles, since it is not unique to qasm3, and could also eventually be leveraged by the cirq module as well?
core.py in qbraid_qir/qasm3/profiles makes calls to QasmQIRVisitor and QasmQIRModule. Making it unique to qasm3.
@ryanhill1 yeah I think that makes sense, most likely Cirq could benefit from the profiles too.
@ryanhill1 yeah I think that makes sense, most likely Cirq could benefit from the profiles too.
@TheGupta2012 core.py is unique to qasm3. The other 2 json files aren't. Should I move only the 2 of them?
Maybe we want to create abstract QIRVisitor and QIRModule classes at the qbraid_qir top-level which the cirq and qasm3 visitor / module classes inherit from, and then have the AdaptiveProfile and BaseProfile classes reference the abstract visitor / module classes instead of the specific QasmQIRVisitor and QasmQIRModule classes?