Skip to content

Commit 0f9fa3f

Browse files
daxfohlCirqBot95-martin-orion
authored
Optimize swap gates (#4169)
* split * Allow config param split_entangled_states * default split to off * ensure consistent_act_on circuits have a qubit. * lint * lint * mps * lint * lint * run sparse by default * fix tests * fix tests * fix tests * mps * tableau * test simulator * test simulator * Update simulator_base.py * Drop mps/join * Fix clifford extract * lint * remove split/join from ch-form * Add default arg for zero qubit circuits * Have last repetition reuse original state repr * Remove cast * Split all pure initial states by default * Detangle on reset channels * docstrings * docstrings * docstrings * docstrings * fix merge * lint * Add unit test for integer states * format * Add tests for splitting and joining * remove unnecessary qubits param * Clean up default args * Fix failing test * Add ActOnArgsContainer * Add ActOnArgsContainer * Clean up tests * Clean up tests * Clean up tests * format * Fix tests and coverage * Add OperationTarget interface * Fix unit tests * mypy, lint, mocks, coverage * coverage * add log to container * fix logs * dead code * EmptyActOnArgs * simplify dummyargs * Optimize swap gate * lint * cover * lint * inplace option * Add [] to actonargs * rename _create_act_on_arg * coverage * coverage * Default sparse sim to split=false * format * Default sparse sim to split=false * Default density matrix sim to split=false * lint * lint * lint * lint * address review comments * lint * Defaults back to split=false * add error if setting state when split is enabled * Unit tests * coverage * coverage * coverage * docs * fix merge * tests * better tests * lint * Update docstrings. Co-authored-by: Cirq Bot <[email protected]> Co-authored-by: Orion Martin <[email protected]>
1 parent fb43b84 commit 0f9fa3f

File tree

4 files changed

+119
-0
lines changed

4 files changed

+119
-0
lines changed

cirq-core/cirq/sim/act_on_args.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
# limitations under the License.
1414
"""Objects and methods for acting efficiently on a state tensor."""
1515
import abc
16+
import copy
1617
from typing import (
1718
Any,
1819
Iterable,
@@ -139,6 +140,64 @@ def log_of_measurement_results(self) -> Dict[str, Any]:
139140
def qubits(self) -> Tuple['cirq.Qid', ...]:
140141
return self._qubits
141142

143+
def swap(self, q1: 'cirq.Qid', q2: 'cirq.Qid', *, inplace=False):
144+
"""Swaps two qubits.
145+
146+
This only affects the index, and does not modify the underlying
147+
state.
148+
149+
Args:
150+
q1: The first qubit to swap.
151+
q2: The second qubit to swap.
152+
inplace: True to swap the qubits in the current object, False to
153+
create a copy with the qubits swapped.
154+
155+
Returns:
156+
The original object with the qubits swapped if inplace is
157+
requested, or a copy of the original object with the qubits swapped
158+
otherwise.
159+
160+
Raises:
161+
ValueError if the qubits are of different dimensionality."""
162+
if q1.dimension != q2.dimension:
163+
raise ValueError(f'Cannot swap different dimensions: q1={q1}, q2={q2}')
164+
165+
args = self if inplace else copy.copy(self)
166+
i1 = self.qubits.index(q1)
167+
i2 = self.qubits.index(q2)
168+
qubits = list(args.qubits)
169+
qubits[i1], qubits[i2] = qubits[i2], qubits[i1]
170+
args._qubits = tuple(qubits)
171+
args.qubit_map = {q: i for i, q in enumerate(qubits)}
172+
return args
173+
174+
def rename(self, q1: 'cirq.Qid', q2: 'cirq.Qid', *, inplace=False):
175+
"""Renames `q1` to `q2`.
176+
177+
Args:
178+
q1: The qubit to rename.
179+
q2: The new name.
180+
inplace: True to rename the qubit in the current object, False to
181+
create a copy with the qubit renamed.
182+
183+
Returns:
184+
The original object with the qubits renamed if inplace is
185+
requested, or a copy of the original object with the qubits renamed
186+
otherwise.
187+
188+
Raises:
189+
ValueError if the qubits are of different dimensionality."""
190+
if q1.dimension != q2.dimension:
191+
raise ValueError(f'Cannot rename to different dimensions: q1={q1}, q2={q2}')
192+
193+
args = self if inplace else copy.copy(self)
194+
i1 = self.qubits.index(q1)
195+
qubits = list(args.qubits)
196+
qubits[i1] = q2
197+
args._qubits = tuple(qubits)
198+
args.qubit_map = {q: i for i, q in enumerate(qubits)}
199+
return args
200+
142201
def __getitem__(self: TSelf, item: Optional['cirq.Qid']) -> TSelf:
143202
if item not in self.qubit_map:
144203
raise IndexError(f'{item} not in {self.qubits}')

cirq-core/cirq/sim/act_on_args_container.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,18 @@ def apply_operation(
8181
self,
8282
op: 'cirq.Operation',
8383
):
84+
gate = op.gate
85+
if isinstance(gate, ops.SwapPowGate) and gate.exponent % 2 == 1 and gate.global_shift == 0:
86+
q0, q1 = op.qubits
87+
args0 = self.args[q0]
88+
args1 = self.args[q1]
89+
if args0 is args1:
90+
args0.swap(q0, q1, inplace=True)
91+
else:
92+
self.args[q0] = args1.rename(q1, q0, inplace=True)
93+
self.args[q1] = args0.rename(q0, q1, inplace=True)
94+
return
95+
8496
# Go through the op's qubits and join any disparate ActOnArgs states
8597
# into a new combined state.
8698
op_args_opt: Optional[TActOnArgs] = None

cirq-core/cirq/sim/act_on_args_container_test.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,3 +172,35 @@ def test_merge_succeeds():
172172
args = create_container(qs2, False)
173173
merged = args.create_merged_state()
174174
assert merged.qubits == (q0, q1)
175+
176+
177+
def test_swap_does_not_merge():
178+
args = create_container(qs2)
179+
old_q0 = args[q0]
180+
old_q1 = args[q1]
181+
args.apply_operation(cirq.SWAP(q0, q1))
182+
assert len(set(args.values())) == 3
183+
assert args[q0] is not old_q0
184+
assert args[q1] is old_q0
185+
assert args[q1] is not old_q1
186+
assert args[q0] is old_q1
187+
assert args[q0].qubits == (q0,)
188+
assert args[q1].qubits == (q1,)
189+
190+
191+
def test_half_swap_does_merge():
192+
args = create_container(qs2)
193+
args.apply_operation(cirq.SWAP(q0, q1) ** 0.5)
194+
assert len(set(args.values())) == 2
195+
assert args[q0] is args[q1]
196+
197+
198+
def test_swap_after_entangle_reorders():
199+
args = create_container(qs2)
200+
args.apply_operation(cirq.CX(q0, q1))
201+
assert len(set(args.values())) == 2
202+
assert args[q0].qubits == (q0, q1)
203+
args.apply_operation(cirq.SWAP(q0, q1))
204+
assert len(set(args.values())) == 2
205+
assert args[q0] is args[q1]
206+
assert args[q0].qubits == (q1, q0)

cirq-core/cirq/sim/act_on_args_test.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,3 +59,19 @@ def test_mapping():
5959
assert args is r1
6060
with pytest.raises(IndexError):
6161
_ = args[cirq.LineQubit(2)]
62+
63+
64+
def test_swap_bad_dimensions():
65+
q0 = cirq.LineQubit(0)
66+
q1 = cirq.LineQid(1, 3)
67+
args = DummyArgs()
68+
with pytest.raises(ValueError, match='Cannot swap different dimensions'):
69+
args.swap(q0, q1)
70+
71+
72+
def test_rename_bad_dimensions():
73+
q0 = cirq.LineQubit(0)
74+
q1 = cirq.LineQid(1, 3)
75+
args = DummyArgs()
76+
with pytest.raises(ValueError, match='Cannot rename to different dimensions'):
77+
args.rename(q0, q1)

0 commit comments

Comments
 (0)