-
Notifications
You must be signed in to change notification settings - Fork 18
Feature/Implement Qubit Register Consolidation #222
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Feature/Implement Qubit Register Consolidation #222
Conversation
- Added a new parameter `device_qubits` to `QasmVisitor` and `QasmModule` to manage qubit consolidation. - Introduced methods for qubit index mapping and register consolidation to ensure compatibility with device constraints. - Updated measurement, barrier, and gate operations to utilize the new device qubit logic. - Added unit tests to validate the functionality of qubit consolidation and error handling for exceeding device qubit limits.
Codecov ReportAttention: Patch coverage is
📢 Thoughts on this report? Let us know! |
@@ -57,6 +57,7 @@ def __init__(self, name: str, program: Program): | |||
self._unrolled_ast = Program(statements=[]) | |||
self._external_gates: list[str] = [] | |||
self._decompose_native_gates: Optional[bool] = None | |||
self._device_qubits: Optional[int] = 0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
self._device_qubits: Optional[int] = 0 | |
self._device_qubits: Optional[int] = None |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, let's make this a kwarg
of the load(s)
method, eg. -
module = pyqasm.loads(qasm, device_qubits = 23)
The idea is that a module will be defined using a specific number of device qubits but the users can "choose to consolidate" the qasm according to the device specification. Then, the corresponding argument in unroll
becomes consolidate_qubits
or equivalent.
NOTE: this will also help us implicitly validate the total qubit count according to the total num of device qubits. We can add this check inside the validate
functionality
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @vinayswamik , the changes look good!
A point to consider for the design -
Consolidation of qubits inside the qasm operations should be done as a "finalize" step. Presently, you are updating the qubit identifiers directly during the unroll / validate phase and subsequently adding the __PYQASM_QUBITS__
declaration. While it works for the test cases, this transformation logic should be separated in a new step.
A cleaner approach would be to refactor the transformation of statements (which you have done inside each of reset
, barrier
, etc.) as part of the _qubit_register_consolidation
method. It should read the unrolled statements and transform the qubits for each of them, possibly utilizing the Qasm3Transformer
module. I only see self._get_pyqasm_device_qubit_index
being utilized in your changes and can be decoupled from the visitor code.
@@ -86,6 +86,7 @@ def __init__( # pylint: disable=too-many-arguments | |||
external_gates: list[str] | None = None, | |||
unroll_barriers: bool = True, | |||
max_loop_iters: int = int(1e9), | |||
device_qubits: int = 0, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Derive from module
, use something like a _consolidate_qubits
flag to check and perform the consolidation
""" | ||
_offsets: dict[str, int] = {} | ||
_offset = 0 | ||
for name, n_qubits in self._global_qreg_size_map.items(): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be done once as the offsets will not change after all the declarations are registered with the module.
I believe we should update the offsets whenever a new qubit declaration is encountered in the module, and save it as an attribute for future use.
f"Total qubits '({total_qubits})' exceed device qubits '({self._device_qubits})'." | ||
) | ||
|
||
if "__PYQASM_QUBITS__" in self._global_qreg_size_map: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
also check the variable scope, no variables should be declared with this name
for stmt in unrolled_stmts: | ||
if isinstance(stmt, qasm3_ast.QubitDeclaration): | ||
removable_statements.append(stmt) | ||
for r_stmt in removable_statements: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is better to construct a new list of valid_statements
which do not contain the original QubitDeclaration
s. You can just append any statement which is not of type QubitDeclaration
.
The reason is that .remove(...)
method works in O(n)
time, n
being the size of our list. For a large number of qubit declarations, say k
, this will scale as O(n*k)
.
pyqasm_reg_id = qasm3_ast.Identifier("__PYQASM_QUBITS__") | ||
pyqasm_reg_size = qasm3_ast.IntegerLiteral(self._device_qubits) | ||
pyqasm_reg_stmt = qasm3_ast.QubitDeclaration(pyqasm_reg_id, pyqasm_reg_size) | ||
unrolled_stmts.insert(1, pyqasm_reg_stmt) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How did you derive 1
to be the insert position?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for any valid qasm3 code first stmt is include(stdgates) in unrolled statements. but there might be other include stmts in future. one better way would be below approach, which costs only O(n).
pyqasm_reg_id = qasm3_ast.Identifier("__PYQASM_QUBITS__")
pyqasm_reg_size = qasm3_ast.IntegerLiteral(self._device_qubits)
pyqasm_reg_stmt = qasm3_ast.QubitDeclaration(pyqasm_reg_id, pyqasm_reg_size)
valid_statements = []
_inserted_pyqasm_reg = False
for stmt in unrolled_stmts:
if isinstance(stmt, qasm3_ast.QubitDeclaration):
continue
if not _inserted_pyqasm_reg and not isinstance(stmt, qasm3_ast.Include):
valid_statements.append(pyqasm_reg_stmt)
_inserted_pyqasm_reg = True
valid_statements.append(stmt)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On second thoughts, I believe declaring the __PYQASM_QUBITS__
as the first statement and ignoring all subsequent QubitDeclaration
s would be the simplest way forward.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sounds good.. 👍.
Yeah,I thought about doing all the changes in My suggestion would be move all the current code in ( And also we can do consolidation with/without providing |
Yea, that's a good point, the branches might exist in the code even after |
Summary of changes
Implement device qubit(
__PYQASM_QUBITS__
) support in QasmVisitor and QasmModuledevice_qubits
toQasmVisitor
andQasmModule
to manage qubit consolidation._get_pyqasm_device_qubit_index
method for qubit index mapping and_qubit_register_consolidation
to ensure compatibility with device constraints.enhance a little bit wihtout changing anuthing
Closes #187