Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
11 changes: 4 additions & 7 deletions source/pip/qsharp/_adaptive_pass.py
Original file line number Diff line number Diff line change
Expand Up @@ -437,15 +437,12 @@ def _resolve_operand(self, value: pyqir.Value) -> IntOperand | FloatOperand | Re
if isinstance(value, pyqir.Instruction):
return self._alloc_reg(value, self._type_tag(value.type))

# Constant expressions (e.g. inttoptr (i64 N to %Qubit*)).
# Constant expressions (e.g. inttoptr (i64 N to ptr)).
if isinstance(value, pyqir.Constant):
# Try extracting as a qubit/result pointer constant.
qid = pyqir.qubit_id(value)
if qid is not None:
return IntOperand(qid)
rid = pyqir.result_id(value)
if rid is not None:
return IntOperand(rid)
pid = pyqir.ptr_id(value)
if pid is not None:
return IntOperand(pid)
# Null pointer
if value.is_null:
reg = self._alloc_reg(value, REG_TYPE_PTR)
Expand Down
15 changes: 7 additions & 8 deletions source/pip/qsharp/_device/_atom/_decomp.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,8 @@
const,
Function,
FunctionType,
PointerType,
Type,
qubit_type,
result_type,
result,
Context,
Linkage,
Expand All @@ -34,7 +33,7 @@ class DecomposeMultiQubitToCZ(QirModuleVisitor):

def _on_module(self, module):
void = Type.void(module.context)
qubit_ty = qubit_type(module.context)
qubit_ty = PointerType(Type.void(module.context))
self.double_ty = Type.double(module.context)
# Find or create all the needed functions.
for func in module.functions:
Expand Down Expand Up @@ -214,7 +213,7 @@ class DecomposeSingleRotationToRz(QirModuleVisitor):

def _on_module(self, module):
void = Type.void(module.context)
qubit_ty = qubit_type(module.context)
qubit_ty = PointerType(Type.void(module.context))
self.double_ty = Type.double(module.context)
# Find or create all the needed functions.
for func in module.functions:
Expand Down Expand Up @@ -290,7 +289,7 @@ class DecomposeSingleQubitToRzSX(QirModuleVisitor):

def _on_module(self, module):
void = Type.void(module.context)
qubit_ty = qubit_type(module.context)
qubit_ty = PointerType(Type.void(module.context))
self.double_ty = Type.double(module.context)
# Find or create all the needed functions.
for func in module.functions:
Expand Down Expand Up @@ -400,7 +399,7 @@ class DecomposeRzAnglesToCliffordGates(QirModuleVisitor):

def _on_module(self, module):
void = Type.void(module.context)
qubit_ty = qubit_type(module.context)
qubit_ty = PointerType(Type.void(module.context))
self.double_ty = Type.double(module.context)
# Find or create all the needed functions.
for func in module.functions:
Expand Down Expand Up @@ -482,8 +481,8 @@ class ReplaceResetWithMResetZ(QirModuleVisitor):
def _on_module(self, module):
self.context = module.context
void = Type.void(self.context)
qubit_ty = qubit_type(self.context)
result_ty = result_type(self.context)
qubit_ty = PointerType(Type.void(self.context))
result_ty = PointerType(Type.void(self.context))
# Find or create the intrinsic mresetz function
for func in module.functions:
match func.name:
Expand Down
39 changes: 19 additions & 20 deletions source/pip/qsharp/_device/_atom/_optimize.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,9 @@
FunctionType,
FloatConstant,
Linkage,
PointerType,
const,
qubit_type,
qubit_id,
result_type,
ptr_id,
is_entry_point,
QirModuleVisitor,
)
Expand All @@ -30,8 +29,8 @@ class OptimizeSingleQubitGates(QirModuleVisitor):

def _on_module(self, module):
void = Type.void(module.context)
qubit_ty = qubit_type(module.context)
result_ty = result_type(module.context)
qubit_ty = PointerType(Type.void(module.context))
result_ty = qubit_ty
self.double_ty = Type.double(module.context)
self.used_qubits = set()
# Find or create the intrinsic gate functions
Expand Down Expand Up @@ -61,7 +60,7 @@ def _drop_ops(self, qubits):
# Since instructions are only removed when they are canceled out by their adjoint or folded with another
# instruction, we can just pop the entries for these qubits so they start fresh with the next gates.
for qubit in qubits:
q = qubit_id(qubit)
q = ptr_id(qubit)
self.qubit_ops.pop(q, None)
self.last_meas.pop(q, None)
self.used_qubits.add(q)
Expand Down Expand Up @@ -201,43 +200,43 @@ def _on_call_instr(self, call):
super()._on_call_instr(call)

def _on_qis_h(self, call, target):
self._schedule_gate(call, qubit_id(target), "h", "h")
self._schedule_gate(call, ptr_id(target), "h", "h")

def _on_qis_s(self, call, target):
self._schedule_gate(call, qubit_id(target), "s", "s_adj")
self._schedule_gate(call, ptr_id(target), "s", "s_adj")

def _on_qis_s_adj(self, call, target):
self._schedule_gate(call, qubit_id(target), "s_adj", "s")
self._schedule_gate(call, ptr_id(target), "s_adj", "s")

def _on_qis_t(self, call, target):
self._schedule_gate(call, qubit_id(target), "t", "t_adj")
self._schedule_gate(call, ptr_id(target), "t", "t_adj")

def _on_qis_t_adj(self, call, target):
self._schedule_gate(call, qubit_id(target), "t_adj", "t")
self._schedule_gate(call, ptr_id(target), "t_adj", "t")

def _on_qis_x(self, call, target):
self._schedule_gate(call, qubit_id(target), "x", "x")
self._schedule_gate(call, ptr_id(target), "x", "x")

def _on_qis_y(self, call, target):
self._schedule_gate(call, qubit_id(target), "y", "y")
self._schedule_gate(call, ptr_id(target), "y", "y")

def _on_qis_z(self, call, target):
self._schedule_gate(call, qubit_id(target), "z", "z")
self._schedule_gate(call, ptr_id(target), "z", "z")

def _on_qis_rx(self, call, angle, target):
self._schedule_rotation(call, qubit_id(target), "rx")
self._schedule_rotation(call, ptr_id(target), "rx")

def _on_qis_rxx(self, call, angle, target1, target2):
self._drop_ops([target1, target2])

def _on_qis_ry(self, call, angle, target):
self._schedule_rotation(call, qubit_id(target), "ry")
self._schedule_rotation(call, ptr_id(target), "ry")

def _on_qis_ryy(self, call, angle, target1, target2):
self._drop_ops([target1, target2])

def _on_qis_rz(self, call, angle, target):
self._schedule_rotation(call, qubit_id(target), "rz")
self._schedule_rotation(call, ptr_id(target), "rz")

def _on_qis_rzz(self, call, angle, target1, target2):
self._drop_ops([target1, target2])
Expand All @@ -259,7 +258,7 @@ def _on_qis_swap(self, call, target1, target2):

def _on_qis_m(self, call, target, result):
self._drop_ops([target])
self.last_meas[qubit_id(target)] = (call, target, result)
self.last_meas[ptr_id(target)] = (call, target, result)

def _on_qis_mz(self, call, target, result):
self._on_qis_m(call, target, result)
Expand All @@ -268,7 +267,7 @@ def _on_qis_mresetz(self, call, target, result):
self._on_qis_m(call, target, result)

def _on_qis_reset(self, call, target):
id = qubit_id(target)
id = ptr_id(target)
if id in self.last_meas:
# Since the last operation on this qubit was a measurement,
# we can combine that measurement with the reset.
Expand All @@ -280,7 +279,7 @@ def _on_qis_reset(self, call, target):
[target, result],
)
call.erase()
self.last_meas[qubit_id(target)] = (new_call, target, result)
self.last_meas[ptr_id(target)] = (new_call, target, result)
elif not id in self.used_qubits:
# This qubit was never used, so we can just erase the reset instruction.
call.erase()
Expand Down
22 changes: 11 additions & 11 deletions source/pip/qsharp/_device/_atom/_scheduler.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@
Function,
QirModuleVisitor,
FunctionType,
PointerType,
Type,
Linkage,
qubit_type,
qubit_id,
ptr_id,
IntType,
Value,
)
Expand Down Expand Up @@ -48,7 +48,7 @@ def __repr__(self):

@property
def qubit_id(self) -> int:
q_id = qubit_id(self.qubit_id_ptr)
q_id = ptr_id(self.qubit_id_ptr)
assert q_id is not None, "Qubit id should be known"
return q_id

Expand All @@ -70,7 +70,7 @@ class PartialMove:

@property
def qubit_id(self) -> int:
q_id = qubit_id(self.qubit_id_ptr)
q_id = ptr_id(self.qubit_id_ptr)
assert q_id is not None, "Qubit id should be known"
return q_id

Expand Down Expand Up @@ -405,15 +405,15 @@ def qubits_to_partial_moves(
partial_moves = []
for elt in qubits_to_move:
if isinstance(elt, tuple):
q_id1 = qubit_id(elt[0])
q_id2 = qubit_id(elt[1])
q_id1 = ptr_id(elt[0])
q_id2 = ptr_id(elt[1])
assert q_id1 is not None
assert q_id2 is not None
mov1 = PartialMove(elt[0], self.device.get_home_loc(q_id1))
mov2 = PartialMove(elt[1], self.device.get_home_loc(q_id2))
partial_moves.append((mov1, mov2))
else:
q_id = qubit_id(elt)
q_id = ptr_id(elt)
assert q_id is not None
mov = PartialMove(elt, self.device.get_home_loc(q_id))
partial_moves.append(mov)
Expand Down Expand Up @@ -630,7 +630,7 @@ def _on_module(self, module):
self.move_func = Function(
FunctionType(
Type.void(module.context),
[qubit_type(module.context), i64_ty, i64_ty],
[PointerType(Type.void(module.context)), i64_ty, i64_ty],
),
Linkage.EXTERNAL,
"__quantum__qis__move__body",
Expand Down Expand Up @@ -676,11 +676,11 @@ def _on_block(self, block):
# If this qubit is involved in pending moves, that implies a CZ or measurement is pending, so flush now.
if any(
(
gate["qubit_args"][0] == qubit_id(q)
gate["qubit_args"][0] == ptr_id(q)
if isinstance(q, QubitId)
else (
gate["qubit_args"][0] == qubit_id(q[0])
or gate["qubit_args"][0] == qubit_id(q[1])
gate["qubit_args"][0] == ptr_id(q[0])
or gate["qubit_args"][0] == ptr_id(q[1])
)
)
for q in self.pending_qubits_to_move
Expand Down
14 changes: 7 additions & 7 deletions source/pip/qsharp/_device/_atom/_trace.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.

from pyqir import QirModuleVisitor, qubit_id, required_num_qubits
from pyqir import QirModuleVisitor, ptr_id, required_num_qubits
from .._device import Device


Expand Down Expand Up @@ -44,33 +44,33 @@ def _on_call_instr(self, call):
def _on_qis_move(self, call, qubit, row, col):
if not self.in_parallel:
self._next_step()
q = qubit_id(qubit)
q = ptr_id(qubit)
self.q_cols[q] = col.value
self.trace["steps"][-1]["ops"].append(f"move({row.value}, {col.value}) {q}")

def _on_qis_sx(self, call, qubit):
if not self.in_parallel:
self._next_step()
q = qubit_id(qubit)
q = ptr_id(qubit)
self.trace["steps"][-1]["ops"].append(f"sx {q}")

def _on_qis_rz(self, call, angle, qubit):
if not self.in_parallel:
self._next_step()
q = qubit_id(qubit)
q = ptr_id(qubit)
self.trace["steps"][-1]["ops"].append(f"rz({angle.value}) {q}")

def _on_qis_cz(self, call, qubit1, qubit2):
if not self.in_parallel:
self._next_step()
q1 = qubit_id(qubit1)
q2 = qubit_id(qubit2)
q1 = ptr_id(qubit1)
q2 = ptr_id(qubit2)
if self.q_cols.get(q1, -1) > self.q_cols.get(q2, -1):
q1, q2 = q2, q1
self.trace["steps"][-1]["ops"].append(f"cz {q1}, {q2}")

def _on_qis_mresetz(self, call, target, result):
if not self.in_parallel:
self._next_step()
q = qubit_id(target)
q = ptr_id(target)
self.trace["steps"][-1]["ops"].append(f"mz {q}")
48 changes: 31 additions & 17 deletions source/pip/qsharp/_device/_atom/_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,34 +5,49 @@
Instruction,
Call,
Constant,
PointerType,
Value,
qubit_id,
is_qubit_type,
result_id,
is_result_type,
ptr_id,
)
from typing import Dict

TOLERANCE: float = 1.1920929e-7 # Machine epsilon for 32-bit IEEE FP numbers.

# QIS gates that consume a measurement result; the value is the 0-based index
# of the result argument. All other pointer-typed arguments of a QIS call are
# qubit arguments.
_RESULT_ARG_INDEX: Dict[str, int] = {
"__quantum__qis__m__body": 1,
"__quantum__qis__mz__body": 1,
"__quantum__qis__mresetz__body": 1,
"__quantum__qis__read_result__body": 0,
}


# If this is a call to a __qis__ gate, return a dict describing the gate and its arguments.
def as_qis_gate(instr: Instruction) -> Dict:
if isinstance(instr, Call) and instr.callee.name.startswith("__quantum__qis__"):
parts = instr.callee.name.split("__")
result_idx = _RESULT_ARG_INDEX.get(instr.callee.name)
qubit_args = []
result_args = []
other_args = []
for i, arg in enumerate(instr.args):
if isinstance(arg.type, PointerType):
pid = ptr_id(arg)
if pid is None:
other_args.append(arg)
elif result_idx is not None and i == result_idx:
result_args.append(pid)
else:
qubit_args.append(pid)
else:
other_args.append(arg)
return {
"gate": parts[3] + ("_adj" if parts[4] == "adj" else ""),
"qubit_args": [
qubit_id(arg) for arg in instr.args if qubit_id(arg) is not None
],
"result_args": [
result_id(arg) for arg in instr.args if result_id(arg) is not None
],
"other_args": [
arg
for arg in instr.args
if qubit_id(arg) is None and result_id(arg) is None
],
"qubit_args": qubit_args,
"result_args": result_args,
"other_args": other_args,
}
return {}

Expand Down Expand Up @@ -72,7 +87,6 @@ def uses_any_value(used_values, existing_values) -> bool:
[
val in existing_values
for val in used_values
if not isinstance(val, Constant)
or (is_qubit_type(val.type) or is_result_type(val.type))
if not isinstance(val, Constant) or isinstance(val.type, PointerType)
]
)
Loading
Loading