Skip to content
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

Adding Cliffords to QuantumCircuits as Operations #7966

Merged
merged 41 commits into from
Aug 10, 2022
Merged
Changes from 1 commit
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
068525a
Adding Clifford to a QuantumCircuit natively; adding some tests; addi…
alexanderivrii Apr 20, 2022
cd8367b
Merge branch 'main' into operation
alexanderivrii Apr 27, 2022
2ab3084
Making Instruction.condition into a property
alexanderivrii Apr 27, 2022
dffc618
improving tests
alexanderivrii Apr 27, 2022
ea56a13
adding release notes
alexanderivrii Apr 27, 2022
f3af0b6
A few changes based on review
alexanderivrii May 10, 2022
2640f5d
Merge branch 'main' into operation
alexanderivrii May 10, 2022
568d8b8
Merge branch 'main' into operation
alexanderivrii May 11, 2022
ec342e5
Merge branch 'main' into operation
alexanderivrii May 15, 2022
c16e85f
Merge branch 'main' into operation
alexanderivrii May 17, 2022
015cbe9
Merge branch 'main' into operation
alexanderivrii May 17, 2022
e35a3d5
resolving merge conflicts
alexanderivrii Jun 7, 2022
15ce409
Removing definition from Operation interface. Instead adding
alexanderivrii Jun 7, 2022
d59dd7e
minor lint fix
alexanderivrii Jun 7, 2022
51aff6d
moving all of broadcast functionality to a separate file; removing br…
alexanderivrii Jun 7, 2022
2c3b908
moving HighLevelSynthesis pass from Decompose to QuantumCircuit.decom…
alexanderivrii Jun 7, 2022
2a87222
Resolving conflcts, reverting broadcasting (temporarily breaks functi…
alexanderivrii Jul 2, 2022
14ddc7b
Fixing broadcasting for Cliffords
alexanderivrii Jul 2, 2022
4949a1d
making sure that transpile decomposes cliffords
alexanderivrii Jul 2, 2022
c1ab860
lint
alexanderivrii Jul 2, 2022
aeef894
As per code review, replacing x._direction by getattr(x, '_direction'…
alexanderivrii Jul 3, 2022
9f019c2
Removing condition from Operation API.
alexanderivrii Jul 5, 2022
c80efa9
pass over condition in transpiler code
alexanderivrii Jul 5, 2022
4b1a246
more refactoring of condition
alexanderivrii Jul 5, 2022
5ae37a5
finishing condition pass
alexanderivrii Jul 5, 2022
4a5f06f
Merge branch 'main' into operation
alexanderivrii Jul 5, 2022
34eca8c
resolving merge conflicts
alexanderivrii Aug 4, 2022
de6110d
resolving merge conflicts
alexanderivrii Aug 4, 2022
7a7b0f1
minor fixes
alexanderivrii Aug 5, 2022
76acd6b
Merge branch 'main' into operation
alexanderivrii Aug 5, 2022
64c8de3
adding OptimizeClifford pass to __init__
alexanderivrii Aug 6, 2022
b2f972a
Improving release notes
alexanderivrii Aug 6, 2022
0d583ed
considering DAG nodes in topological order; adding a simple test to s…
alexanderivrii Aug 6, 2022
bdf9325
typo
alexanderivrii Aug 6, 2022
49bc436
release notes fixes
alexanderivrii Aug 6, 2022
7f525d2
Adding TODO comment to HighLevelSynthesis pass
alexanderivrii Aug 6, 2022
d82e2e0
another attempt to fix docs build
alexanderivrii Aug 6, 2022
07733d6
Fix based on code review
alexanderivrii Aug 9, 2022
ac1c874
Merge branch 'main' into operation
alexanderivrii Aug 10, 2022
ab213f2
Only construction Operator from Instruction (as before) and from Clif…
alexanderivrii Aug 10, 2022
fb57c9d
Merge branch 'operation' of github.com:alexanderivrii/qiskit-terra in…
alexanderivrii Aug 10, 2022
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
Prev Previous commit
Next Next commit
Merge branch 'main' into operation
alexanderivrii authored Aug 10, 2022

Unverified

This user has not yet uploaded their public signing key.
commit ac1c874aa2576a567f509d0f238ea7f1537fcee7
72 changes: 0 additions & 72 deletions qiskit/dagcircuit/dagdependency.py
Original file line number Diff line number Diff line change
@@ -572,75 +572,3 @@ def merge_no_duplicates(*iterables):
if val != last:
last = val
yield val


def _does_commute(node1, node2):
"""Function to verify commutation relation between two nodes in the DAG.

Args:
node1 (DAGnode): first node operation
node2 (DAGnode): second node operation

Return:
bool: True if the nodes commute and false if it is not the case.
"""

# Create set of qubits on which the operation acts
qarg1 = [node1.qargs[i] for i in range(0, len(node1.qargs))]
qarg2 = [node2.qargs[i] for i in range(0, len(node2.qargs))]

# Create set of cbits on which the operation acts
carg1 = [node1.cargs[i] for i in range(0, len(node1.cargs))]
carg2 = [node2.cargs[i] for i in range(0, len(node2.cargs))]

# Commutation for classical conditional gates
# if and only if the qubits are different.
# TODO: qubits can be the same if conditions are identical and
# the non-conditional gates commute.
if node1.type == "op" and node2.type == "op":
if getattr(node1.op, "condition", None) or getattr(node2.op, "condition", None):
intersection = set(qarg1).intersection(set(qarg2))
return not intersection

# Commutation for non-unitary or parameterized or opaque ops
# (e.g. measure, reset, directives or pulse gates)
# if and only if the qubits and clbits are different.
non_unitaries = ["measure", "reset", "initialize", "delay"]

def _unknown_commutator(n):
return (
getattr(n.op, "_directive", False) or n.name in non_unitaries or n.op.is_parameterized()
)

if _unknown_commutator(node1) or _unknown_commutator(node2):
intersection_q = set(qarg1).intersection(set(qarg2))
intersection_c = set(carg1).intersection(set(carg2))
return not (intersection_q or intersection_c)

# Gates over disjoint sets of qubits commute
if not set(qarg1).intersection(set(qarg2)):
return True

# Known non-commuting gates (TODO: add more).
non_commute_gates = [{"x", "y"}, {"x", "z"}]
if qarg1 == qarg2 and ({node1.name, node2.name} in non_commute_gates):
return False

# Create matrices to check commutation relation if no other criteria are matched
qarg = list(set(node1.qargs + node2.qargs))
qbit_num = len(qarg)

qarg1 = [qarg.index(q) for q in node1.qargs]
qarg2 = [qarg.index(q) for q in node2.qargs]

dim = 2**qbit_num
id_op = np.reshape(np.eye(dim), (2, 2) * qbit_num)

op1 = np.reshape(node1.op.to_matrix(), (2, 2) * len(qarg1))
op2 = np.reshape(node2.op.to_matrix(), (2, 2) * len(qarg2))

op = Operator._einsum_matmul(id_op, op1, qarg1)
op12 = Operator._einsum_matmul(op, op2, qarg2, right_mul=False)
op21 = Operator._einsum_matmul(op, op2, qarg2, shift=qbit_num, right_mul=True)

return np.allclose(op12, op21)
86 changes: 0 additions & 86 deletions qiskit/transpiler/passes/optimization/commutation_analysis.py
Original file line number Diff line number Diff line change
@@ -88,89 +88,3 @@ def run(self, dag):

temp_len = len(current_comm_set)
self.property_set["commutation_set"][(current_gate, wire)] = temp_len - 1


_COMMUTE_ID_OP = {}


def _hashable_parameters(params):
"""Convert the parameters of a gate into a hashable format for lookup in a dictionary.

This aims to be fast in common cases, and is not intended to work outside of the lifetime of a
single commutation pass; it does not handle mutable state correctly if the state is actually
changed."""
try:
hash(params)
return params
except TypeError:
pass
if isinstance(params, (list, tuple)):
return tuple(_hashable_parameters(x) for x in params)
if isinstance(params, np.ndarray):
# We trust that the arrays will not be mutated during the commutation pass, since nothing
# would work if they were anyway. Using the id can potentially cause some additional cache
# misses if two UnitaryGate instances are being compared that have been separately
# constructed to have the same underlying matrix, but in practice the cost of string-ifying
# the matrix to get a cache key is far more expensive than just doing a small matmul.
return (np.ndarray, id(params))
# Catch anything else with a slow conversion.
return ("fallback", str(params))


def _commute(node1, node2, cache):
if not isinstance(node1, DAGOpNode) or not isinstance(node2, DAGOpNode):
return False
for nd in [node1, node2]:
if getattr(nd.op, "_directive", False) or nd.name in {"measure", "reset", "delay"}:
return False
if getattr(node1.op, "condition", None) or getattr(node2.op, "condition", None):
return False
if node1.op.is_parameterized() or node2.op.is_parameterized():
return False

# Assign indices to each of the qubits such that all `node1`'s qubits come first, followed by
# any _additional_ qubits `node2` addresses. This helps later when we need to compose one
# operator with the other, since we can easily expand `node1` with a suitable identity.
qarg = {q: i for i, q in enumerate(node1.qargs)}
num_qubits = len(qarg)
for q in node2.qargs:
if q not in qarg:
qarg[q] = num_qubits
num_qubits += 1
qarg1 = tuple(qarg[q] for q in node1.qargs)
qarg2 = tuple(qarg[q] for q in node2.qargs)

node1_key = (node1.op.name, _hashable_parameters(node1.op.params), qarg1)
node2_key = (node2.op.name, _hashable_parameters(node2.op.params), qarg2)
try:
# We only need to try one orientation of the keys, since if we've seen the compound key
# before, we've set it in both orientations.
return cache[node1_key, node2_key]
except KeyError:
pass

operator_1 = Operator(node1.op, input_dims=(2,) * len(qarg1), output_dims=(2,) * len(qarg1))
operator_2 = Operator(node2.op, input_dims=(2,) * len(qarg2), output_dims=(2,) * len(qarg2))

if qarg1 == qarg2:
# Use full composition if possible to get the fastest matmul paths.
op12 = operator_1.compose(operator_2)
op21 = operator_2.compose(operator_1)
else:
# Expand operator_1 to be large enough to contain operator_2 as well; this relies on qargs1
# being the lowest possible indices so the identity can be tensored before it.
extra_qarg2 = num_qubits - len(qarg1)
if extra_qarg2:
try:
id_op = _COMMUTE_ID_OP[extra_qarg2]
except KeyError:
id_op = _COMMUTE_ID_OP[extra_qarg2] = Operator(
np.eye(2**extra_qarg2),
input_dims=(2,) * extra_qarg2,
output_dims=(2,) * extra_qarg2,
)
operator_1 = id_op.tensor(operator_1)
op12 = operator_1.compose(operator_2, qargs=qarg2, front=False)
op21 = operator_1.compose(operator_2, qargs=qarg2, front=True)
cache[node1_key, node2_key] = cache[node2_key, node1_key] = ret = op12 == op21
return ret
You are viewing a condensed version of this merge commit. You can view the full changes here.