Line | Branch | Decision | Exec | Source |
---|---|---|---|---|
1 | // Copyright 2019-2022 Cambridge Quantum Computing | |||
2 | // | |||
3 | // Licensed under the Apache License, Version 2.0 (the "License"); | |||
4 | // you may not use this file except in compliance with the License. | |||
5 | // You may obtain a copy of the License at | |||
6 | // | |||
7 | // http://www.apache.org/licenses/LICENSE-2.0 | |||
8 | // | |||
9 | // Unless required by applicable law or agreed to in writing, software | |||
10 | // distributed under the License is distributed on an "AS IS" BASIS, | |||
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
12 | // See the License for the specific language governing permissions and | |||
13 | // limitations under the License. | |||
14 | ||||
15 | #include "CliffordReductionPass.hpp" | |||
16 | ||||
17 | #include "Circuit/DAGDefs.hpp" | |||
18 | #include "PauliGraph/ConjugatePauliFunctions.hpp" | |||
19 | ||||
20 | namespace tket { | |||
21 | ||||
22 | /** | |||
23 | * Finds Clifford circuit C such that | |||
24 | * R[p](a); R[q](b) = C; RZ(a); RZ(b); C^\dagger if p==q or | |||
25 | * C; RZ(a); RY(b); C^\dagger if p!=q | |||
26 | */ | |||
27 | static const std::map<std::pair<Pauli, Pauli>, std::list<OpType>> | |||
28 | mapping_to_zz_or_zy_lut{ | |||
29 | {{Pauli::X, Pauli::X}, {OpType::H}}, | |||
30 | {{Pauli::X, Pauli::Y}, {OpType::H, OpType::Z}}, | |||
31 | {{Pauli::X, Pauli::Z}, {OpType::H, OpType::S}}, | |||
32 | {{Pauli::Y, Pauli::X}, {OpType::V, OpType::S}}, | |||
33 | {{Pauli::Y, Pauli::Y}, {OpType::V}}, | |||
34 | {{Pauli::Y, Pauli::Z}, {OpType::V, OpType::Z}}, | |||
35 | {{Pauli::Z, Pauli::X}, {OpType::S}}, | |||
36 | {{Pauli::Z, Pauli::Y}, {}}, | |||
37 | {{Pauli::Z, Pauli::Z}, {}}, | |||
38 | }; | |||
39 | ||||
40 | static const std::map<Pauli, OpType> pauli_to_pauli_gate_lut{ | |||
41 | {Pauli::X, OpType::X}, | |||
42 | {Pauli::Y, OpType::Y}, | |||
43 | {Pauli::Z, OpType::Z}, | |||
44 | }; | |||
45 | ||||
46 | /** | |||
47 | * Consider an interaction of R[p0, p1](+-0.5); R[q0, q1](+-0.5) | |||
48 | * where p0, p1, q0, q1 in {X, Y, Z}. | |||
49 | * Returns the equivalent replacement circuit with fewer 2qb interactions. | |||
50 | */ | |||
51 | 333 | static Circuit interaction_replacement(const InteractionMatch &match) { | ||
52 | 333 | const Pauli &p0 = match.point0.p; | ||
53 | 333 | const Pauli &p1 = match.point1.p; | ||
54 | 333 | const Pauli &q0 = match.rev0.p; | ||
55 | 333 | const Pauli &q1 = match.rev1.p; | ||
56 |
1/2✓ Branch 2 taken 333 times.
✗ Branch 3 not taken.
|
333 | Circuit replacement(2); | |
57 |
2/2✓ Branch 0 taken 100 times.
✓ Branch 1 taken 233 times.
|
2/2✓ Decision 'true' taken 100 times.
✓ Decision 'false' taken 233 times.
|
333 | if (match.point0.phase ^ match.point1.phase) { |
58 |
3/6✓ Branch 3 taken 100 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 100 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 100 times.
✗ Branch 10 not taken.
|
100 | replacement.add_op<unsigned>(pauli_to_pauli_gate_lut.at(p0), {0}); | |
59 |
3/6✓ Branch 3 taken 100 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 100 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 100 times.
✗ Branch 10 not taken.
|
100 | replacement.add_op<unsigned>(pauli_to_pauli_gate_lut.at(p1), {1}); | |
60 |
2/4✓ Branch 1 taken 100 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 100 times.
✗ Branch 5 not taken.
|
100 | replacement.add_phase(0.5); | |
61 | } | |||
62 |
2/2✓ Branch 0 taken 257 times.
✓ Branch 1 taken 76 times.
|
2/2✓ Decision 'true' taken 257 times.
✓ Decision 'false' taken 76 times.
|
333 | if (p0 == q0) { |
63 |
2/2✓ Branch 0 taken 136 times.
✓ Branch 1 taken 121 times.
|
2/2✓ Decision 'true' taken 136 times.
✓ Decision 'false' taken 121 times.
|
257 | if (p1 == q1) { |
64 | // R[p0, p1](1) = R[p0, I](1); R[I, p1](1) | |||
65 |
1/2✓ Branch 1 taken 136 times.
✗ Branch 2 not taken.
|
136 | OpType op0 = pauli_to_pauli_gate_lut.at(p0); | |
66 |
1/2✓ Branch 1 taken 136 times.
✗ Branch 2 not taken.
|
136 | OpType op1 = pauli_to_pauli_gate_lut.at(p1); | |
67 |
2/4✓ Branch 3 taken 136 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 136 times.
✗ Branch 7 not taken.
|
136 | replacement.add_op<unsigned>(op0, {0}); | |
68 |
2/4✓ Branch 3 taken 136 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 136 times.
✗ Branch 7 not taken.
|
136 | replacement.add_op<unsigned>(op1, {1}); | |
69 |
2/4✓ Branch 1 taken 136 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 136 times.
✗ Branch 5 not taken.
|
136 | replacement.add_phase(-0.5); | |
70 | } else { | |||
71 | // Map to R[Z, Z](0.5); R[Z, Y](0.5) | |||
72 |
1/2✓ Branch 2 taken 121 times.
✗ Branch 3 not taken.
|
121 | Circuit basis_change(2); | |
73 |
2/4✓ Branch 2 taken 121 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 121 times.
✗ Branch 6 not taken.
|
121 | std::list<OpType> ops0 = mapping_to_zz_or_zy_lut.at({p0, q0}); | |
74 |
2/4✓ Branch 2 taken 121 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 121 times.
✗ Branch 6 not taken.
|
121 | std::list<OpType> ops1 = mapping_to_zz_or_zy_lut.at({p1, q1}); | |
75 |
1/2✗ Branch 4 not taken.
✓ Branch 5 taken 121 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 121 times.
|
121 | for (OpType op : ops0) { |
76 | ✗ | basis_change.add_op<unsigned>(op, {0}); | ||
77 | } | |||
78 |
2/2✓ Branch 4 taken 210 times.
✓ Branch 5 taken 121 times.
|
2/2✓ Decision 'true' taken 210 times.
✓ Decision 'false' taken 121 times.
|
331 | for (OpType op : ops1) { |
79 |
2/4✓ Branch 3 taken 210 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 210 times.
✗ Branch 7 not taken.
|
210 | basis_change.add_op<unsigned>(op, {1}); | |
80 | } | |||
81 |
1/2✓ Branch 1 taken 121 times.
✗ Branch 2 not taken.
|
121 | replacement.append(basis_change); | |
82 |
2/4✓ Branch 3 taken 121 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 121 times.
✗ Branch 7 not taken.
|
121 | replacement.add_op<unsigned>(OpType::V, {1}); | |
83 |
2/4✓ Branch 3 taken 121 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 121 times.
✗ Branch 7 not taken.
|
121 | replacement.add_op<unsigned>(OpType::ZZMax, {0, 1}); | |
84 |
2/4✓ Branch 1 taken 121 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 121 times.
✗ Branch 5 not taken.
|
121 | replacement.append(basis_change.dagger()); | |
85 | 121 | } | ||
86 | } else { | |||
87 |
2/2✓ Branch 0 taken 41 times.
✓ Branch 1 taken 35 times.
|
2/2✓ Decision 'true' taken 41 times.
✓ Decision 'false' taken 35 times.
|
76 | if (p1 == q1) { |
88 | // Map to R[Z, Z](0.5); R[Y, Z](0.5) | |||
89 |
1/2✓ Branch 2 taken 41 times.
✗ Branch 3 not taken.
|
41 | Circuit basis_change(2); | |
90 |
2/4✓ Branch 2 taken 41 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 41 times.
✗ Branch 6 not taken.
|
41 | std::list<OpType> ops0 = mapping_to_zz_or_zy_lut.at({p0, q0}); | |
91 |
2/4✓ Branch 2 taken 41 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 41 times.
✗ Branch 6 not taken.
|
41 | std::list<OpType> ops1 = mapping_to_zz_or_zy_lut.at({p1, q1}); | |
92 |
2/2✓ Branch 4 taken 76 times.
✓ Branch 5 taken 41 times.
|
2/2✓ Decision 'true' taken 76 times.
✓ Decision 'false' taken 41 times.
|
117 | for (OpType op : ops0) { |
93 |
2/4✓ Branch 3 taken 76 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 76 times.
✗ Branch 7 not taken.
|
76 | basis_change.add_op<unsigned>(op, {0}); | |
94 | } | |||
95 |
2/2✓ Branch 4 taken 38 times.
✓ Branch 5 taken 41 times.
|
2/2✓ Decision 'true' taken 38 times.
✓ Decision 'false' taken 41 times.
|
79 | for (OpType op : ops1) { |
96 |
2/4✓ Branch 3 taken 38 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 38 times.
✗ Branch 7 not taken.
|
38 | basis_change.add_op<unsigned>(op, {1}); | |
97 | } | |||
98 |
1/2✓ Branch 1 taken 41 times.
✗ Branch 2 not taken.
|
41 | replacement.append(basis_change); | |
99 |
2/4✓ Branch 3 taken 41 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 41 times.
✗ Branch 7 not taken.
|
41 | replacement.add_op<unsigned>(OpType::V, {0}); | |
100 |
2/4✓ Branch 3 taken 41 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 41 times.
✗ Branch 7 not taken.
|
41 | replacement.add_op<unsigned>(OpType::ZZMax, {0, 1}); | |
101 |
2/4✓ Branch 1 taken 41 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 41 times.
✗ Branch 5 not taken.
|
41 | replacement.append(basis_change.dagger()); | |
102 | 41 | } else { | ||
103 | // Map to R[Z, Z](0.5); R[Y, Y](0.5) | |||
104 |
1/2✓ Branch 2 taken 35 times.
✗ Branch 3 not taken.
|
35 | Circuit basis_change(2); | |
105 |
2/4✓ Branch 2 taken 35 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 35 times.
✗ Branch 6 not taken.
|
35 | std::list<OpType> ops0 = mapping_to_zz_or_zy_lut.at({p0, q0}); | |
106 |
2/4✓ Branch 2 taken 35 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 35 times.
✗ Branch 6 not taken.
|
35 | std::list<OpType> ops1 = mapping_to_zz_or_zy_lut.at({p1, q1}); | |
107 |
2/2✓ Branch 4 taken 70 times.
✓ Branch 5 taken 35 times.
|
2/2✓ Decision 'true' taken 70 times.
✓ Decision 'false' taken 35 times.
|
105 | for (OpType op : ops0) { |
108 |
2/4✓ Branch 3 taken 70 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 70 times.
✗ Branch 7 not taken.
|
70 | basis_change.add_op<unsigned>(op, {0}); | |
109 | } | |||
110 |
2/2✓ Branch 4 taken 44 times.
✓ Branch 5 taken 35 times.
|
2/2✓ Decision 'true' taken 44 times.
✓ Decision 'false' taken 35 times.
|
79 | for (OpType op : ops1) { |
111 |
2/4✓ Branch 3 taken 44 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 44 times.
✗ Branch 7 not taken.
|
44 | basis_change.add_op<unsigned>(op, {1}); | |
112 | } | |||
113 |
1/2✓ Branch 1 taken 35 times.
✗ Branch 2 not taken.
|
35 | replacement.append(basis_change); | |
114 |
2/4✓ Branch 3 taken 35 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 35 times.
✗ Branch 7 not taken.
|
35 | replacement.add_op<unsigned>(OpType::H, {0}); | |
115 |
2/4✓ Branch 3 taken 35 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 35 times.
✗ Branch 7 not taken.
|
35 | replacement.add_op<unsigned>(OpType::H, {1}); | |
116 |
2/4✓ Branch 3 taken 35 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 35 times.
✗ Branch 7 not taken.
|
35 | replacement.add_op<unsigned>(OpType::Z, {0}); | |
117 |
2/4✓ Branch 3 taken 35 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 35 times.
✗ Branch 7 not taken.
|
35 | replacement.add_op<unsigned>(OpType::Z, {1}); | |
118 |
2/4✓ Branch 3 taken 35 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 35 times.
✗ Branch 7 not taken.
|
35 | replacement.add_op<unsigned>(OpType::ZZMax, {0, 1}); | |
119 |
2/4✓ Branch 3 taken 35 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 35 times.
✗ Branch 7 not taken.
|
35 | replacement.add_op<unsigned>(OpType::H, {0}); | |
120 |
2/4✓ Branch 3 taken 35 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 35 times.
✗ Branch 7 not taken.
|
35 | replacement.add_op<unsigned>(OpType::H, {1}); | |
121 |
2/4✓ Branch 3 taken 35 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 35 times.
✗ Branch 7 not taken.
|
35 | replacement.add_op<unsigned>(OpType::SWAP, {0, 1}); | |
122 |
2/4✓ Branch 1 taken 35 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 35 times.
✗ Branch 5 not taken.
|
35 | replacement.add_phase(0.25); | |
123 |
2/4✓ Branch 1 taken 35 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 35 times.
✗ Branch 5 not taken.
|
35 | replacement.append(basis_change.dagger()); | |
124 | 35 | } | ||
125 | } | |||
126 |
2/2✓ Branch 0 taken 28 times.
✓ Branch 1 taken 305 times.
|
2/2✓ Decision 'true' taken 28 times.
✓ Decision 'false' taken 305 times.
|
333 | if (match.rev0.phase ^ match.rev1.phase) { |
127 |
3/6✓ Branch 3 taken 28 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 28 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 28 times.
✗ Branch 10 not taken.
|
28 | replacement.add_op<unsigned>(pauli_to_pauli_gate_lut.at(q0), {0}); | |
128 |
3/6✓ Branch 3 taken 28 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 28 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 28 times.
✗ Branch 10 not taken.
|
28 | replacement.add_op<unsigned>(pauli_to_pauli_gate_lut.at(q1), {1}); | |
129 |
2/4✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 28 times.
✗ Branch 5 not taken.
|
28 | replacement.add_phase(0.5); | |
130 | } | |||
131 | 333 | return replacement; | ||
132 | } | |||
133 | ||||
134 | /** | |||
135 | * Given a 2qb Clifford gate, returns just the local operations applied around | |||
136 | * the maximally-entangling gadget. | |||
137 | */ | |||
138 | 666 | static Circuit local_cliffords(OpType op) { | ||
139 |
1/2✓ Branch 2 taken 666 times.
✗ Branch 3 not taken.
|
666 | Circuit locals(2); | |
140 |
4/5✓ Branch 0 taken 621 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 42 times.
✗ Branch 4 not taken.
|
666 | switch (op) { | |
141 |
1/1✓ Decision 'true' taken 621 times.
|
621 | case OpType::CX: { | |
142 |
2/4✓ Branch 3 taken 621 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 621 times.
✗ Branch 7 not taken.
|
621 | locals.add_op<unsigned>(OpType::Sdg, {0}); | |
143 |
2/4✓ Branch 3 taken 621 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 621 times.
✗ Branch 7 not taken.
|
621 | locals.add_op<unsigned>(OpType::Vdg, {1}); | |
144 | 621 | break; | ||
145 | } | |||
146 |
1/1✓ Decision 'true' taken 1 times.
|
1 | case OpType::CZ: { | |
147 |
2/4✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
|
1 | locals.add_op<unsigned>(OpType::Sdg, {0}); | |
148 |
2/4✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
|
1 | locals.add_op<unsigned>(OpType::Sdg, {1}); | |
149 |
2/4✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
|
1 | locals.add_phase(0.25); | |
150 | 1 | break; | ||
151 | } | |||
152 |
1/1✓ Decision 'true' taken 2 times.
|
2 | case OpType::CY: { | |
153 |
2/4✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 2 times.
✗ Branch 7 not taken.
|
2 | locals.add_op<unsigned>(OpType::Sdg, {0}); | |
154 |
2/4✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 2 times.
✗ Branch 7 not taken.
|
2 | locals.add_op<unsigned>(OpType::V, {1}); | |
155 |
2/4✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 2 times.
✗ Branch 7 not taken.
|
2 | locals.add_op<unsigned>(OpType::Sdg, {1}); | |
156 |
2/4✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 2 times.
✗ Branch 7 not taken.
|
2 | locals.add_op<unsigned>(OpType::Vdg, {1}); | |
157 |
2/4✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
|
2 | locals.add_phase(0.25); | |
158 | 2 | break; | ||
159 | } | |||
160 |
1/1✓ Decision 'true' taken 42 times.
|
42 | case OpType::ZZMax: { | |
161 | 42 | break; | ||
162 | } | |||
163 |
0/1✗ Decision 'true' not taken.
|
✗ | default: { | |
164 | ✗ | throw CircuitInvalidity( | ||
165 | "Attempting to replace non-Clifford gate with Clifford " | |||
166 | ✗ | "optimisation"); | ||
167 | break; | |||
168 | } | |||
169 | } | |||
170 | 666 | return locals; | ||
171 | } | |||
172 | ||||
173 | 5411 | void CliffordReductionPass::insert_interaction_point(InteractionPoint ip) { | ||
174 |
1/2✓ Branch 1 taken 5411 times.
✗ Branch 2 not taken.
|
5411 | itable.insert(ip); | |
175 |
1/2✓ Branch 1 taken 5411 times.
✗ Branch 2 not taken.
|
5411 | Vertex next = circ.target(ip.e); | |
176 |
1/2✓ Branch 1 taken 5411 times.
✗ Branch 2 not taken.
|
5411 | port_t next_p = circ.get_target_port(ip.e); | |
177 | 5411 | bool commute = true; | ||
178 |
2/2✓ Branch 0 taken 11945 times.
✓ Branch 1 taken 5411 times.
|
2/2✓ Decision 'true' taken 11945 times.
✓ Decision 'false' taken 5411 times.
|
17356 | while (commute) { |
179 |
3/4✓ Branch 2 taken 11945 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 4697 times.
✓ Branch 6 taken 7248 times.
|
2/2✓ Decision 'true' taken 4697 times.
✓ Decision 'false' taken 7248 times.
|
11945 | if (v_to_depth.find(next) == v_to_depth.end()) { |
180 | 4697 | commute = false; | ||
181 | 5411 | continue; | ||
182 | } | |||
183 |
1/2✓ Branch 1 taken 7248 times.
✗ Branch 2 not taken.
|
7248 | Op_ptr op = circ.get_Op_ptr_from_Vertex(next); | |
184 |
3/6✓ Branch 2 taken 7248 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 7248 times.
✗ Branch 6 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 7248 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 7248 times.
|
7248 | if (!op->get_desc().is_gate()) { |
185 | ✗ | commute = false; | ||
186 | ✗ | continue; | ||
187 | } | |||
188 | 7248 | OpType type = op->get_type(); | ||
189 |
3/3✓ Branch 0 taken 5332 times.
✓ Branch 1 taken 83 times.
✓ Branch 2 taken 1833 times.
|
7248 | switch (type) { | |
190 | 5332 | case OpType::H: | ||
191 | case OpType::S: | |||
192 | case OpType::Sdg: | |||
193 | case OpType::V: | |||
194 | case OpType::Vdg: | |||
195 | case OpType::X: | |||
196 | case OpType::Y: | |||
197 | case OpType::Z: { | |||
198 |
1/2✓ Branch 1 taken 5332 times.
✗ Branch 2 not taken.
|
5332 | std::pair<Pauli, bool> new_basis = conjugate_Pauli(type, ip.p, true); | |
199 | 5332 | ip.p = new_basis.first; | ||
200 | 5332 | ip.phase ^= new_basis.second; | ||
201 | 5332 | break; | ||
202 | } | |||
203 |
1/1✓ Decision 'true' taken 83 times.
|
83 | case OpType::SWAP: { | |
204 | 83 | next_p = 1 - next_p; | ||
205 | 83 | break; | ||
206 | } | |||
207 |
1/1✓ Decision 'true' taken 1833 times.
|
1833 | default: { | |
208 |
3/4✓ Branch 2 taken 1833 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 714 times.
✓ Branch 5 taken 1119 times.
|
2/2✓ Decision 'true' taken 714 times.
✓ Decision 'false' taken 1119 times.
|
1833 | if (!circ.commutes_with_basis(next, ip.p, PortType::Target, next_p)) { |
209 | 714 | commute = false; | ||
210 | 714 | continue; | ||
211 | } | |||
212 | 1119 | break; | ||
213 | } | |||
214 | } | |||
215 |
1/2✓ Branch 1 taken 6534 times.
✗ Branch 2 not taken.
|
6534 | ip.e = circ.get_nth_out_edge(next, next_p); | |
216 |
1/2✓ Branch 1 taken 6534 times.
✗ Branch 2 not taken.
|
6534 | auto inserted = itable.insert(ip); | |
217 | 6534 | commute = inserted.second; | ||
218 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 6534 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 6534 times.
|
6534 | if (!commute) { |
219 | // Now `inserted.first` points to the element of the table that | |||
220 | // blocked insertion, i.e. had the same source/edge combination. | |||
221 | // Check that its `p` and `phase` are correct: if not, something has | |||
222 | // gone wrong. | |||
223 | ✗ | auto blocker = inserted.first; | ||
224 | TKET_ASSERT(blocker->p == ip.p && blocker->phase == ip.phase); | |||
225 | } | |||
226 |
1/2✓ Branch 1 taken 6534 times.
✗ Branch 2 not taken.
|
6534 | next = circ.target(ip.e); | |
227 |
1/2✓ Branch 1 taken 6534 times.
✗ Branch 2 not taken.
|
6534 | next_p = circ.get_target_port(ip.e); | |
228 |
2/2✓ Branch 1 taken 6534 times.
✓ Branch 2 taken 714 times.
|
7248 | } | |
229 | 5411 | } | ||
230 | ||||
231 | 2173 | std::optional<InteractionMatch> CliffordReductionPass::search_back_for_match( | ||
232 | const RevInteractionPoint &rip0, const RevInteractionPoint &rip1) const { | |||
233 |
3/4✓ Branch 1 taken 4346 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 4346 times.
✓ Branch 4 taken 2173 times.
|
6519 | RevInteractionPoint point[2]; | |
234 | 2173 | point[0] = rip0; | ||
235 | 2173 | point[1] = rip1; | ||
236 | 2173 | std::map<Edge, RevInteractionPoint> point_lookup; | ||
237 |
1/2✓ Branch 1 taken 2173 times.
✗ Branch 2 not taken.
|
2173 | IndexMap im = circ.index_map(); | |
238 | ||||
239 | // interactions met when commuting back; point lists are in causal order of | |||
240 | // circuit: | |||
241 |
2/2✓ Branch 1 taken 4346 times.
✓ Branch 2 taken 2173 times.
|
13038 | std::map<IVertex, std::list<InteractionPoint>> candidates[2]; | |
242 | ||||
243 |
2/2✓ Branch 0 taken 4346 times.
✓ Branch 1 taken 2173 times.
|
2/2✓ Decision 'true' taken 4346 times.
✓ Decision 'false' taken 2173 times.
|
6519 | for (unsigned i = 0; i < 2; ++i) { |
244 | // Commute edge i back as far as possible | |||
245 | 4346 | bool commute = true; | ||
246 |
2/2✓ Branch 0 taken 23111 times.
✓ Branch 1 taken 4346 times.
|
2/2✓ Decision 'true' taken 23111 times.
✓ Decision 'false' taken 4346 times.
|
27457 | while (commute) { |
247 |
1/2✓ Branch 2 taken 23111 times.
✗ Branch 3 not taken.
|
23111 | point_lookup.insert({point[i].e, point[i]}); | |
248 |
1/2✓ Branch 2 taken 23111 times.
✗ Branch 3 not taken.
|
23111 | auto r = itable.get<TagEdge>().equal_range(point[i].e); | |
249 |
3/4✓ Branch 1 taken 40853 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 17742 times.
✓ Branch 4 taken 23111 times.
|
0/1? Decision couldn't be analyzed.
|
40853 | for (auto it = r.first; it != r.second; ++it) { |
250 |
1/2✓ Branch 1 taken 17742 times.
✗ Branch 2 not taken.
|
17742 | Vertex v = it->source; | |
251 |
5/10✓ Branch 1 taken 17742 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 17742 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 17742 times.
✗ Branch 9 not taken.
✓ Branch 11 taken 17742 times.
✗ Branch 12 not taken.
✓ Branch 14 taken 17742 times.
✗ Branch 15 not taken.
|
17742 | candidates[i][{im.at(v), v}].push_front(*it); | |
252 | } | |||
253 |
1/2✓ Branch 1 taken 23111 times.
✗ Branch 2 not taken.
|
23111 | Vertex pred = circ.source(point[i].e); | |
254 |
1/2✓ Branch 1 taken 23111 times.
✗ Branch 2 not taken.
|
23111 | port_t pred_port = circ.get_source_port(point[i].e); | |
255 |
1/2✓ Branch 1 taken 23111 times.
✗ Branch 2 not taken.
|
23111 | Op_ptr pred_op = circ.get_Op_ptr_from_Vertex(pred); | |
256 |
4/6✓ Branch 2 taken 23111 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 23111 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 847 times.
✓ Branch 9 taken 22264 times.
|
2/2✓ Decision 'true' taken 847 times.
✓ Decision 'false' taken 22264 times.
|
23111 | if (!pred_op->get_desc().is_gate()) { |
257 | 847 | commute = false; | ||
258 | 847 | continue; | ||
259 | } | |||
260 | 22264 | OpType type = pred_op->get_type(); | ||
261 |
3/3✓ Branch 0 taken 16764 times.
✓ Branch 1 taken 93 times.
✓ Branch 2 taken 5407 times.
|
22264 | switch (type) { | |
262 | 16764 | case OpType::H: | ||
263 | case OpType::S: | |||
264 | case OpType::Sdg: | |||
265 | case OpType::V: | |||
266 | case OpType::Vdg: | |||
267 | case OpType::X: | |||
268 | case OpType::Y: | |||
269 | case OpType::Z: { | |||
270 |
1/2✓ Branch 1 taken 16764 times.
✗ Branch 2 not taken.
|
16764 | std::pair<Pauli, bool> new_basis = conjugate_Pauli(type, point[i].p); | |
271 | 16764 | point[i].p = new_basis.first; | ||
272 | 16764 | point[i].phase ^= new_basis.second; | ||
273 | 16764 | break; | ||
274 | } | |||
275 |
1/1✓ Decision 'true' taken 93 times.
|
93 | case OpType::SWAP: { | |
276 | 93 | pred_port = 1 - pred_port; | ||
277 | 93 | break; | ||
278 | } | |||
279 |
1/1✓ Decision 'true' taken 5407 times.
|
5407 | default: { | |
280 |
1/2✓ Branch 1 taken 5407 times.
✗ Branch 2 not taken.
|
5407 | commute = circ.commutes_with_basis( | |
281 | 5407 | pred, point[i].p, PortType::Source, pred_port); | ||
282 | 5407 | break; | ||
283 | } | |||
284 | } | |||
285 |
1/2✓ Branch 1 taken 22264 times.
✗ Branch 2 not taken.
|
22264 | point[i].e = circ.get_nth_in_edge(pred, pred_port); | |
286 |
2/2✓ Branch 1 taken 22264 times.
✓ Branch 2 taken 847 times.
|
23111 | } | |
287 | } | |||
288 | // Check for matching interactions | |||
289 | 2173 | for (const std::pair<const IVertex, std::list<InteractionPoint>> &pair : | ||
290 |
2/2✓ Branch 5 taken 1841 times.
✓ Branch 6 taken 1840 times.
|
5854 | candidates[0]) { | |
291 |
1/2✓ Branch 1 taken 1841 times.
✗ Branch 2 not taken.
|
1841 | auto found = candidates[1].find(pair.first); | |
292 |
2/2✓ Branch 2 taken 401 times.
✓ Branch 3 taken 1440 times.
|
2/2✓ Decision 'true' taken 401 times.
✓ Decision 'false' taken 1440 times.
|
1841 | if (found != candidates[1].end()) { |
293 | std::optional<std::pair<InteractionPoint, InteractionPoint>> | |||
294 |
1/2✓ Branch 2 taken 401 times.
✗ Branch 3 not taken.
|
401 | insert_point = valid_insertion_point(pair.second, found->second); | |
295 |
2/2✓ Branch 1 taken 394 times.
✓ Branch 2 taken 7 times.
|
2/2✓ Decision 'true' taken 394 times.
✓ Decision 'false' taken 7 times.
|
401 | if (insert_point) { |
296 | InteractionMatch match = { | |||
297 | 394 | insert_point->first, insert_point->second, | ||
298 |
1/2✓ Branch 1 taken 394 times.
✗ Branch 2 not taken.
|
394 | point_lookup.at(insert_point->first.e), | |
299 |
1/2✓ Branch 4 taken 394 times.
✗ Branch 5 not taken.
|
788 | point_lookup.at(insert_point->second.e)}; | |
300 |
2/2✓ Branch 0 taken 282 times.
✓ Branch 1 taken 112 times.
|
2/2✓ Decision 'true' taken 282 times.
✓ Decision 'false' taken 112 times.
|
394 | if (!allow_swaps) { |
301 |
4/4✓ Branch 0 taken 87 times.
✓ Branch 1 taken 195 times.
✓ Branch 2 taken 61 times.
✓ Branch 3 taken 26 times.
|
2/2✓ Decision 'true' taken 61 times.
✓ Decision 'false' taken 221 times.
|
282 | if (match.point0.p != match.rev0.p && match.point1.p != match.rev1.p) |
302 | 61 | continue; | ||
303 | } | |||
304 | 333 | return match; | ||
305 | } | |||
306 | } | |||
307 | } | |||
308 | 1840 | return std::nullopt; | ||
309 |
2/4✓ Branch 0 taken 4346 times.
✓ Branch 1 taken 2173 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
|
8692 | } | |
310 | ||||
311 | 1976 | void CliffordReductionPass::process_new_interaction(const Vertex &inter) { | ||
312 | // Process the vertex as well as any new 2qb Cliffords that get inserted | |||
313 | // while doing so (and so on until there are none left to process). | |||
314 |
1/2✓ Branch 2 taken 1976 times.
✗ Branch 3 not taken.
|
1976 | std::list<Vertex> to_process = {inter}; | |
315 |
2/2✓ Branch 1 taken 2173 times.
✓ Branch 2 taken 1976 times.
|
2/2✓ Decision 'true' taken 2173 times.
✓ Decision 'false' taken 1976 times.
|
4149 | while (!to_process.empty()) { |
316 | 2173 | Vertex v = to_process.front(); | ||
317 | 2173 | to_process.pop_front(); | ||
318 |
1/2✓ Branch 1 taken 2173 times.
✗ Branch 2 not taken.
|
2173 | Op_ptr op = circ.get_Op_ptr_from_Vertex(v); | |
319 |
1/2✓ Branch 2 taken 2173 times.
✗ Branch 3 not taken.
|
2173 | Pauli basis0 = *op->commuting_basis(0); | |
320 |
1/2✓ Branch 2 taken 2173 times.
✗ Branch 3 not taken.
|
2173 | Pauli basis1 = *op->commuting_basis(1); | |
321 |
1/2✓ Branch 1 taken 2173 times.
✗ Branch 2 not taken.
|
2173 | EdgeVec ins = circ.get_in_edges(v); | |
322 |
1/2✓ Branch 1 taken 2173 times.
✗ Branch 2 not taken.
|
2173 | RevInteractionPoint rip0 = {ins.at(0), basis0, false}; | |
323 |
1/2✓ Branch 1 taken 2173 times.
✗ Branch 2 not taken.
|
2173 | RevInteractionPoint rip1 = {ins.at(1), basis1, false}; | |
324 |
1/2✓ Branch 1 taken 2173 times.
✗ Branch 2 not taken.
|
2173 | std::optional<InteractionMatch> match = search_back_for_match(rip0, rip1); | |
325 |
2/2✓ Branch 1 taken 333 times.
✓ Branch 2 taken 1840 times.
|
2/2✓ Decision 'true' taken 333 times.
✓ Decision 'false' taken 1840 times.
|
2173 | if (match) { |
326 |
1/2✓ Branch 2 taken 333 times.
✗ Branch 3 not taken.
|
333 | Circuit replacement = interaction_replacement(*match); | |
327 | 333 | Subcircuit site; | ||
328 |
2/4✓ Branch 3 taken 333 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 333 times.
✗ Branch 7 not taken.
|
333 | site.q_in_hole = site.q_out_hole = {match->point0.e, match->point1.e}; | |
329 |
1/2✓ Branch 1 taken 333 times.
✗ Branch 2 not taken.
|
333 | Subcircuit inserted = substitute(replacement, site); | |
330 | 333 | const Vertex &source = match->point0.source; | ||
331 | Circuit source_locals = | |||
332 |
2/4✓ Branch 1 taken 333 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 333 times.
✗ Branch 5 not taken.
|
333 | local_cliffords(circ.get_OpType_from_Vertex(source)); | |
333 | 333 | Subcircuit source_site; | ||
334 |
1/2✓ Branch 1 taken 333 times.
✗ Branch 2 not taken.
|
333 | source_site.q_in_hole = circ.get_in_edges(source); | |
335 | ✗ | source_site.q_out_hole = { | ||
336 |
3/6✓ Branch 1 taken 333 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 333 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 333 times.
✗ Branch 8 not taken.
|
333 | circ.get_nth_out_edge(source, 0), circ.get_nth_out_edge(source, 1)}; | |
337 |
1/2✓ Branch 1 taken 333 times.
✗ Branch 2 not taken.
|
333 | source_site.verts.insert(source); | |
338 |
1/2✓ Branch 1 taken 333 times.
✗ Branch 2 not taken.
|
333 | substitute(source_locals, source_site); | |
339 |
1/2✓ Branch 3 taken 333 times.
✗ Branch 4 not taken.
|
333 | Circuit v_locals = local_cliffords(op->get_type()); | |
340 | 333 | Subcircuit v_site; | ||
341 |
1/2✓ Branch 1 taken 333 times.
✗ Branch 2 not taken.
|
333 | v_site.q_in_hole = circ.get_in_edges(v); | |
342 | ✗ | v_site.q_out_hole = { | ||
343 |
3/6✓ Branch 1 taken 333 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 333 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 333 times.
✗ Branch 8 not taken.
|
333 | circ.get_nth_out_edge(v, 0), circ.get_nth_out_edge(v, 1)}; | |
344 |
1/2✓ Branch 1 taken 333 times.
✗ Branch 2 not taken.
|
333 | v_site.verts.insert(v); | |
345 |
1/2✓ Branch 1 taken 333 times.
✗ Branch 2 not taken.
|
333 | substitute(v_locals, v_site); | |
346 |
2/2✓ Branch 5 taken 1514 times.
✓ Branch 6 taken 136 times.
|
2/2✓ Decision 'true' taken 1514 times.
✓ Decision 'false' taken 136 times.
|
1650 | for (const Vertex &new_v : inserted.verts) { |
347 |
5/6✓ Branch 1 taken 1514 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 211 times.
✓ Branch 4 taken 1303 times.
✓ Branch 5 taken 197 times.
✓ Branch 6 taken 1317 times.
|
2/2✓ Decision 'true' taken 197 times.
✓ Decision 'false' taken 1528 times.
|
1725 | if (circ.n_in_edges(new_v) == 2 && |
348 |
3/4✓ Branch 1 taken 211 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 197 times.
✓ Branch 4 taken 14 times.
|
211 | circ.get_OpType_from_Vertex(new_v) != OpType::SWAP) { | |
349 |
1/2✓ Branch 1 taken 197 times.
✗ Branch 2 not taken.
|
197 | to_process.push_back(new_v); | |
350 | 197 | break; | ||
351 | } | |||
352 | } | |||
353 | 333 | success = true; | ||
354 | 333 | } else { | ||
355 |
1/2✓ Branch 1 taken 1840 times.
✗ Branch 2 not taken.
|
1840 | std::vector<std::optional<Edge>> outs = circ.get_linear_out_edges(v); | |
356 |
1/2✓ Branch 1 taken 1840 times.
✗ Branch 2 not taken.
|
1840 | InteractionPoint ip0 = {*outs.at(0), v, basis0, false}; | |
357 |
1/2✓ Branch 1 taken 1840 times.
✗ Branch 2 not taken.
|
1840 | insert_interaction_point(ip0); | |
358 |
1/2✓ Branch 1 taken 1840 times.
✗ Branch 2 not taken.
|
1840 | InteractionPoint ip1 = {*outs.at(1), v, basis1, false}; | |
359 |
1/2✓ Branch 1 taken 1840 times.
✗ Branch 2 not taken.
|
1840 | insert_interaction_point(ip1); | |
360 | 1840 | } | ||
361 | 2173 | } | ||
362 | 1976 | } | ||
363 | ||||
364 | 999 | Subcircuit CliffordReductionPass::substitute( | ||
365 | const Circuit &to_insert, const Subcircuit &to_replace) { | |||
366 | 999 | unsigned q_width = to_replace.q_in_hole.size(); | ||
367 | TKET_ASSERT(q_width == 2); | |||
368 | ||||
369 | // Construct tables of predecessors, successors, units, in-edges, out-edges. | |||
370 | // Only quantum circuit replacments here so don't care about classical stuff | |||
371 |
1/2✓ Branch 2 taken 999 times.
✗ Branch 3 not taken.
|
999 | std::vector<VertPort> preds(q_width); | |
372 |
1/2✓ Branch 2 taken 999 times.
✗ Branch 3 not taken.
|
999 | std::vector<VertPort> succs(q_width); | |
373 |
1/2✓ Branch 2 taken 999 times.
✗ Branch 3 not taken.
|
999 | std::vector<UnitID> units(q_width); | |
374 |
1/2✓ Branch 2 taken 999 times.
✗ Branch 3 not taken.
|
999 | std::vector<Edge> in_edges(q_width); | |
375 |
1/2✓ Branch 2 taken 999 times.
✗ Branch 3 not taken.
|
999 | std::vector<Edge> out_edges(q_width); | |
376 |
2/2✓ Branch 0 taken 1998 times.
✓ Branch 1 taken 999 times.
|
2/2✓ Decision 'true' taken 1998 times.
✓ Decision 'false' taken 999 times.
|
2997 | for (unsigned qi = 0; qi < q_width; ++qi) { |
377 |
1/2✓ Branch 1 taken 1998 times.
✗ Branch 2 not taken.
|
1998 | const Edge &in = to_replace.q_in_hole.at(qi); | |
378 |
1/2✓ Branch 1 taken 1998 times.
✗ Branch 2 not taken.
|
1998 | const Edge &out = to_replace.q_out_hole.at(qi); | |
379 |
2/4✓ Branch 1 taken 1998 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1998 times.
✗ Branch 5 not taken.
|
1998 | preds[qi] = {circ.source(in), circ.get_source_port(in)}; | |
380 |
2/4✓ Branch 1 taken 1998 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1998 times.
✗ Branch 5 not taken.
|
1998 | succs[qi] = {circ.target(out), circ.get_target_port(out)}; | |
381 |
1/2✓ Branch 1 taken 1998 times.
✗ Branch 2 not taken.
|
1998 | units[qi] = e_to_unit.at(in); | |
382 | 1998 | in_edges[qi] = in; | ||
383 | 1998 | out_edges[qi] = out; | ||
384 | TKET_ASSERT(in == out || circ.target(in) == circ.source(out)); | |||
385 | } | |||
386 | ||||
387 | // List of points that will be invalidated by the substitution. | |||
388 | 999 | std::list<InteractionPoint> invalidated_points; | ||
389 | ||||
390 | // Lists of points having the same "in"/"out" edge as the replacement: | |||
391 | // These are all invalidated (though the "in" ones can be replaced later | |||
392 | // with the new edge). | |||
393 |
1/2✓ Branch 2 taken 999 times.
✗ Branch 3 not taken.
|
999 | std::vector<std::list<InteractionPoint>> points_with_in(q_width); | |
394 |
1/2✓ Branch 2 taken 999 times.
✗ Branch 3 not taken.
|
999 | std::vector<std::list<InteractionPoint>> points_with_out(q_width); | |
395 |
2/2✓ Branch 0 taken 1998 times.
✓ Branch 1 taken 999 times.
|
2/2✓ Decision 'true' taken 1998 times.
✓ Decision 'false' taken 999 times.
|
2997 | for (unsigned qi = 0; qi < q_width; ++qi) { |
396 |
1/2✓ Branch 3 taken 1998 times.
✗ Branch 4 not taken.
|
1998 | auto r = itable.get<TagEdge>().equal_range(in_edges[qi]); | |
397 |
4/6✓ Branch 1 taken 1731 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3729 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1731 times.
✓ Branch 7 taken 1998 times.
|
0/1? Decision couldn't be analyzed.
|
3729 | for (auto it = r.first; it != r.second; it++) { |
398 |
2/4✓ Branch 2 taken 1731 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1731 times.
✗ Branch 6 not taken.
|
1731 | points_with_in[qi].push_back(*it); | |
399 |
2/4✓ Branch 1 taken 1731 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1731 times.
✗ Branch 5 not taken.
|
1731 | invalidated_points.push_back(*it); | |
400 | } | |||
401 |
1/2✓ Branch 3 taken 1998 times.
✗ Branch 4 not taken.
|
1998 | r = itable.get<TagEdge>().equal_range(out_edges[qi]); | |
402 |
4/6✓ Branch 1 taken 2015 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4013 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 2015 times.
✓ Branch 7 taken 1998 times.
|
0/1? Decision couldn't be analyzed.
|
4013 | for (auto it = r.first; it != r.second; it++) { |
403 |
2/4✓ Branch 2 taken 2015 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 2015 times.
✗ Branch 6 not taken.
|
2015 | points_with_out[qi].push_back(*it); | |
404 |
2/4✓ Branch 1 taken 2015 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2015 times.
✗ Branch 5 not taken.
|
2015 | invalidated_points.push_back(*it); | |
405 | } | |||
406 | } | |||
407 | ||||
408 | // For any (e0, v0) in points_with_out, any point (e1, v0) where e1 is in | |||
409 | // the causal future of e0 is also invalidated. Calculate all the future | |||
410 | // edges. | |||
411 | 999 | EdgeList future_edges; | ||
412 | 999 | VertexSet v_frontier; | ||
413 |
2/2✓ Branch 0 taken 1998 times.
✓ Branch 1 taken 999 times.
|
2/2✓ Decision 'true' taken 1998 times.
✓ Decision 'false' taken 999 times.
|
2997 | for (unsigned qi = 0; qi < q_width; ++qi) { |
414 |
2/4✓ Branch 2 taken 1998 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1998 times.
✗ Branch 6 not taken.
|
1998 | v_frontier.insert(circ.target(out_edges[qi])); | |
415 | } | |||
416 |
2/2✓ Branch 1 taken 5616 times.
✓ Branch 2 taken 999 times.
|
2/2✓ Decision 'true' taken 5616 times.
✓ Decision 'false' taken 999 times.
|
6615 | while (!v_frontier.empty()) { |
417 | 5616 | EdgeSet out_edges; | ||
418 |
2/2✓ Branch 5 taken 15529 times.
✓ Branch 6 taken 5616 times.
|
2/2✓ Decision 'true' taken 15529 times.
✓ Decision 'false' taken 5616 times.
|
21145 | for (auto v : v_frontier) { |
419 |
3/4✓ Branch 2 taken 15529 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 11282 times.
✓ Branch 6 taken 4247 times.
|
2/2✓ Decision 'true' taken 11282 times.
✓ Decision 'false' taken 4247 times.
|
15529 | if (v_to_depth.find(v) != v_to_depth.end()) { |
420 |
1/2✓ Branch 1 taken 11282 times.
✗ Branch 2 not taken.
|
11282 | EdgeVec v_out_edges = circ.get_out_edges_of_type(v, EdgeType::Quantum); | |
421 |
1/2✓ Branch 3 taken 11282 times.
✗ Branch 4 not taken.
|
11282 | out_edges.insert(v_out_edges.begin(), v_out_edges.end()); | |
422 | 11282 | } | ||
423 | } | |||
424 |
1/2✓ Branch 5 taken 5616 times.
✗ Branch 6 not taken.
|
5616 | future_edges.insert(future_edges.end(), out_edges.begin(), out_edges.end()); | |
425 | 5616 | VertexSet new_v_frontier; | ||
426 |
2/2✓ Branch 4 taken 14245 times.
✓ Branch 5 taken 5616 times.
|
2/2✓ Decision 'true' taken 14245 times.
✓ Decision 'false' taken 5616 times.
|
19861 | for (auto e : out_edges) { |
427 |
2/4✓ Branch 1 taken 14245 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 14245 times.
✗ Branch 5 not taken.
|
14245 | new_v_frontier.insert(circ.target(e)); | |
428 | } | |||
429 | 5616 | v_frontier = std::move(new_v_frontier); | ||
430 | 5616 | } | ||
431 | ||||
432 | // Invalidate the (e1, v0) as above. | |||
433 | 999 | VertexSet invalid_sources; | ||
434 |
2/2✓ Branch 0 taken 1998 times.
✓ Branch 1 taken 999 times.
|
2/2✓ Decision 'true' taken 1998 times.
✓ Decision 'false' taken 999 times.
|
2997 | for (unsigned qi = 0; qi < q_width; ++qi) { |
435 |
2/2✓ Branch 6 taken 2015 times.
✓ Branch 7 taken 1998 times.
|
2/2✓ Decision 'true' taken 2015 times.
✓ Decision 'false' taken 1998 times.
|
4013 | for (auto ip : points_with_out[qi]) { |
436 | TKET_ASSERT(ip.e == out_edges[qi]); | |||
437 |
1/2✓ Branch 1 taken 2015 times.
✗ Branch 2 not taken.
|
2015 | invalid_sources.insert(ip.source); | |
438 | } | |||
439 | } | |||
440 |
2/2✓ Branch 5 taken 14245 times.
✓ Branch 6 taken 999 times.
|
2/2✓ Decision 'true' taken 14245 times.
✓ Decision 'false' taken 999 times.
|
15244 | for (auto e1 : future_edges) { |
441 |
1/2✓ Branch 2 taken 14245 times.
✗ Branch 3 not taken.
|
14245 | auto r = itable.get<TagEdge>().equal_range(e1); | |
442 |
4/6✓ Branch 1 taken 15629 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 29874 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 15629 times.
✓ Branch 7 taken 14245 times.
|
0/1? Decision couldn't be analyzed.
|
29874 | for (auto it = r.first; it != r.second; ++it) { |
443 |
4/6✓ Branch 2 taken 15629 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 15629 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 7464 times.
✓ Branch 9 taken 8165 times.
|
2/2✓ Decision 'true' taken 7464 times.
✓ Decision 'false' taken 8165 times.
|
15629 | if (invalid_sources.find(it->source) != invalid_sources.end()) { |
444 |
2/4✓ Branch 1 taken 7464 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 7464 times.
✗ Branch 5 not taken.
|
7464 | invalidated_points.push_back(*it); | |
445 | } | |||
446 | } | |||
447 | } | |||
448 | ||||
449 | // Erase the invalidated points from the table. | |||
450 |
2/2✓ Branch 4 taken 11210 times.
✓ Branch 5 taken 999 times.
|
2/2✓ Decision 'true' taken 11210 times.
✓ Decision 'false' taken 999 times.
|
12209 | for (auto ip : invalidated_points) { |
451 |
2/4✓ Branch 1 taken 11210 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 11210 times.
✗ Branch 5 not taken.
|
11210 | itable.erase(ip.key()); | |
452 | } | |||
453 | ||||
454 | // Erase edges from e_to_unit | |||
455 |
2/2✓ Branch 0 taken 1998 times.
✓ Branch 1 taken 999 times.
|
2/2✓ Decision 'true' taken 1998 times.
✓ Decision 'false' taken 999 times.
|
2997 | for (unsigned qi = 0; qi < q_width; ++qi) { |
456 |
1/2✓ Branch 2 taken 1998 times.
✗ Branch 3 not taken.
|
1998 | e_to_unit.erase(in_edges[qi]); | |
457 |
1/2✓ Branch 2 taken 1998 times.
✗ Branch 3 not taken.
|
1998 | e_to_unit.erase(out_edges[qi]); | |
458 | // Depth of to_replace is at most 1, so there are no other edges | |||
459 | } | |||
460 | ||||
461 | // Remove replaced vertices from depth and units maps and erase all points | |||
462 | // from the itable that have a replaced vertex as source. | |||
463 |
2/2✓ Branch 5 taken 666 times.
✓ Branch 6 taken 999 times.
|
2/2✓ Decision 'true' taken 666 times.
✓ Decision 'false' taken 999 times.
|
1665 | for (const Vertex &v : to_replace.verts) { |
464 |
1/2✓ Branch 1 taken 666 times.
✗ Branch 2 not taken.
|
666 | v_to_depth.erase(v); | |
465 |
1/2✓ Branch 1 taken 666 times.
✗ Branch 2 not taken.
|
666 | v_to_units.erase(v); | |
466 |
1/2✓ Branch 2 taken 666 times.
✗ Branch 3 not taken.
|
666 | auto r = itable.get<TagSource>().equal_range(v); | |
467 |
2/4✓ Branch 1 taken 666 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 666 times.
|
0/1? Decision couldn't be analyzed.
|
666 | for (auto next = r.first; next != r.second; r.first = next) { |
468 | ✗ | ++next; | ||
469 | ✗ | itable.erase(itable.project<TagKey>(r.first)); | ||
470 | } | |||
471 | } | |||
472 | ||||
473 |
1/2✓ Branch 1 taken 999 times.
✗ Branch 2 not taken.
|
999 | circ.substitute(to_insert, to_replace); | |
474 | ||||
475 | // Update the tables of in and out edges, and amend the stored points | |||
476 |
2/2✓ Branch 0 taken 1998 times.
✓ Branch 1 taken 999 times.
|
2/2✓ Decision 'true' taken 1998 times.
✓ Decision 'false' taken 999 times.
|
2997 | for (unsigned qi = 0; qi < q_width; ++qi) { |
477 |
1/2✓ Branch 3 taken 1998 times.
✗ Branch 4 not taken.
|
1998 | in_edges[qi] = circ.get_nth_out_edge(preds[qi].first, preds[qi].second); | |
478 |
1/2✓ Branch 3 taken 1998 times.
✗ Branch 4 not taken.
|
1998 | out_edges[qi] = circ.get_nth_in_edge(succs[qi].first, succs[qi].second); | |
479 |
2/2✓ Branch 5 taken 1731 times.
✓ Branch 6 taken 1998 times.
|
2/2✓ Decision 'true' taken 1731 times.
✓ Decision 'false' taken 1998 times.
|
3729 | for (InteractionPoint &ip : points_with_in[qi]) { |
480 | 1731 | ip.e = in_edges[qi]; | ||
481 | } | |||
482 | } | |||
483 | ||||
484 | // Construct inserted Subcircuit, update e_to_unit and v_to_units, and | |||
485 | // add new vertices to v_to_depth, with (temporary) value 0. | |||
486 | 999 | Subcircuit inserted; | ||
487 |
2/2✓ Branch 0 taken 1998 times.
✓ Branch 1 taken 999 times.
|
2/2✓ Decision 'true' taken 1998 times.
✓ Decision 'false' taken 999 times.
|
2997 | for (unsigned qi = 0; qi < q_width; ++qi) { |
488 | 1998 | Edge in = in_edges[qi]; | ||
489 |
1/2✓ Branch 1 taken 1998 times.
✗ Branch 2 not taken.
|
1998 | inserted.q_in_hole.push_back(in); | |
490 | 1998 | Edge out = out_edges[qi]; | ||
491 |
1/2✓ Branch 1 taken 1998 times.
✗ Branch 2 not taken.
|
1998 | inserted.q_out_hole.push_back(out); | |
492 |
3/4✓ Branch 1 taken 5490 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3492 times.
✓ Branch 4 taken 1998 times.
|
0/1? Decision couldn't be analyzed.
|
5490 | while (in != out) { |
493 |
1/2✓ Branch 1 taken 3492 times.
✗ Branch 2 not taken.
|
3492 | Vertex next = circ.target(in); | |
494 |
1/2✓ Branch 1 taken 3492 times.
✗ Branch 2 not taken.
|
3492 | inserted.verts.insert(next); | |
495 |
1/2✓ Branch 3 taken 3492 times.
✗ Branch 4 not taken.
|
3492 | e_to_unit.insert({in, units[qi]}); | |
496 |
1/2✓ Branch 2 taken 3492 times.
✗ Branch 3 not taken.
|
3492 | v_to_depth.insert({next, 0}); | |
497 |
2/4✓ Branch 1 taken 3492 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 3492 times.
✗ Branch 6 not taken.
|
3492 | v_to_units[next].insert(units[qi]); | |
498 |
2/4✓ Branch 1 taken 3492 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3492 times.
✗ Branch 5 not taken.
|
3492 | in = circ.get_nth_out_edge(next, circ.get_target_port(in)); | |
499 | } | |||
500 |
1/2✓ Branch 3 taken 1998 times.
✗ Branch 4 not taken.
|
1998 | e_to_unit.insert({in, units[qi]}); | |
501 | } | |||
502 | ||||
503 | // Now `v_to_depth` is 0 at all `inserted.verts`. Fix this and propagate | |||
504 | // updates to the depth map into the future cone, ensuring that the depths | |||
505 | // are strictly increasing along wires. Stop when we reach a vertex that | |||
506 | // isn't already in v_to_depth (because we haven't processed it yet). | |||
507 |
2/2✓ Branch 0 taken 1998 times.
✓ Branch 1 taken 999 times.
|
2/2✓ Decision 'true' taken 1998 times.
✓ Decision 'false' taken 999 times.
|
2997 | for (unsigned qi = 0; qi < q_width; ++qi) { |
508 | 1998 | Edge in = in_edges[qi]; | ||
509 |
1/2✓ Branch 1 taken 1998 times.
✗ Branch 2 not taken.
|
1998 | Vertex next = circ.target(in); | |
510 |
2/2✓ Branch 1 taken 1914 times.
✓ Branch 2 taken 84 times.
|
2/2✓ Decision 'true' taken 1914 times.
✓ Decision 'false' taken 84 times.
|
1998 | if (next != succs[qi].first) { |
511 |
4/6✓ Branch 2 taken 1914 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1914 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 1913 times.
✓ Branch 8 taken 1 times.
|
2/2✓ Decision 'true' taken 1913 times.
✓ Decision 'false' taken 1 times.
|
1914 | if (v_to_depth.at(preds[qi].first) >= v_to_depth[next]) { |
512 | // We may have already set v_to_depth[next] to a higher value | |||
513 | // when tracing another qubit, so the check above is necessary. | |||
514 |
2/4✓ Branch 2 taken 1913 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1913 times.
✗ Branch 6 not taken.
|
1913 | v_to_depth[next] = v_to_depth.at(preds[qi].first) + 1; | |
515 | } | |||
516 | std::function<bool(const Vertex &, const Vertex &)> c = | |||
517 | 2152 | [&](const Vertex &a, const Vertex &b) { | ||
518 | 2152 | unsigned deptha = v_to_depth.at(a); | ||
519 | 2152 | unsigned depthb = v_to_depth.at(b); | ||
520 |
2/2✓ Branch 0 taken 1233 times.
✓ Branch 1 taken 919 times.
|
2/2✓ Decision 'true' taken 1233 times.
✓ Decision 'false' taken 919 times.
|
2152 | if (deptha == depthb) { |
521 |
2/4✓ Branch 1 taken 1233 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1233 times.
✗ Branch 5 not taken.
|
1233 | unit_set_t unitsa = v_to_units.at(a); | |
522 |
2/4✓ Branch 1 taken 1233 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1233 times.
✗ Branch 5 not taken.
|
1233 | unit_set_t unitsb = v_to_units.at(b); | |
523 |
1/2✓ Branch 2 taken 1233 times.
✗ Branch 3 not taken.
|
1233 | return unitsa < unitsb; | |
524 | 1233 | } | ||
525 | 919 | return deptha < depthb; | ||
526 | 1914 | }; | ||
527 | 1914 | std::set<Vertex> to_search; | ||
528 |
1/2✓ Branch 1 taken 1914 times.
✗ Branch 2 not taken.
|
1914 | to_search.insert(next); | |
529 |
2/2✓ Branch 1 taken 5502 times.
✓ Branch 2 taken 1914 times.
|
2/2✓ Decision 'true' taken 5502 times.
✓ Decision 'false' taken 1914 times.
|
7416 | while (!to_search.empty()) { |
530 |
2/4✓ Branch 1 taken 5502 times.
✗ Branch 2 not taken.
✓ Branch 6 taken 5502 times.
✗ Branch 7 not taken.
|
5502 | Vertex v = *std::min_element(to_search.begin(), to_search.end(), c); | |
531 |
1/2✓ Branch 1 taken 5502 times.
✗ Branch 2 not taken.
|
5502 | to_search.erase(v); | |
532 |
1/2✓ Branch 1 taken 5502 times.
✗ Branch 2 not taken.
|
5502 | unsigned v_depth = v_to_depth.at(v); | |
533 |
1/2✓ Branch 1 taken 5502 times.
✗ Branch 2 not taken.
|
5502 | EdgeVec outs = circ.get_all_out_edges(v); | |
534 |
2/2✓ Branch 5 taken 6651 times.
✓ Branch 6 taken 5502 times.
|
2/2✓ Decision 'true' taken 6651 times.
✓ Decision 'false' taken 5502 times.
|
12153 | for (const Edge &e : outs) { |
535 |
1/2✓ Branch 1 taken 6651 times.
✗ Branch 2 not taken.
|
6651 | Vertex succ = circ.target(e); | |
536 |
1/2✓ Branch 1 taken 6651 times.
✗ Branch 2 not taken.
|
6651 | std::map<Vertex, unsigned>::iterator succ_it = v_to_depth.find(succ); | |
537 |
2/2✓ Branch 2 taken 4729 times.
✓ Branch 3 taken 1922 times.
|
2/2✓ Decision 'true' taken 4729 times.
✓ Decision 'false' taken 1922 times.
|
6651 | if (succ_it != v_to_depth.end()) { |
538 |
2/2✓ Branch 1 taken 3628 times.
✓ Branch 2 taken 1101 times.
|
2/2✓ Decision 'true' taken 3628 times.
✓ Decision 'false' taken 1101 times.
|
4729 | if (succ_it->second <= v_depth) { |
539 | 3628 | succ_it->second = v_depth + 1; | ||
540 |
2/2✓ Branch 0 taken 1506 times.
✓ Branch 1 taken 2122 times.
|
2/2✓ Decision 'true' taken 1506 times.
✓ Decision 'false' taken 2122 times.
|
3628 | if (v_depth >= current_depth) { |
541 | 1506 | current_depth = v_depth + 1; | ||
542 | } | |||
543 |
1/2✓ Branch 1 taken 3628 times.
✗ Branch 2 not taken.
|
3628 | to_search.insert(succ); | |
544 | } | |||
545 | } | |||
546 | } | |||
547 | 5502 | } | ||
548 | 1914 | } | ||
549 | } | |||
550 | ||||
551 | // Re-insert all the interaction points we erased above. | |||
552 |
2/2✓ Branch 0 taken 1998 times.
✓ Branch 1 taken 999 times.
|
2/2✓ Decision 'true' taken 1998 times.
✓ Decision 'false' taken 999 times.
|
2997 | for (unsigned qi = 0; qi < q_width; ++qi) { |
553 |
2/2✓ Branch 6 taken 1731 times.
✓ Branch 7 taken 1998 times.
|
2/2✓ Decision 'true' taken 1731 times.
✓ Decision 'false' taken 1998 times.
|
3729 | for (const InteractionPoint &ip : points_with_in[qi]) { |
554 |
1/2✓ Branch 1 taken 1731 times.
✗ Branch 2 not taken.
|
1731 | insert_interaction_point(ip); | |
555 | } | |||
556 | } | |||
557 | ||||
558 | 1998 | return inserted; | ||
559 | 999 | } | ||
560 | ||||
561 | 14 | std::optional<Edge> CliffordReductionPass::find_earliest_successor( | ||
562 | const Edge &source, const EdgeSet &candidates) const { | |||
563 | typedef std::function<bool(Vertex, Vertex)> Comp; | |||
564 | 214 | Comp c = [&](Vertex a, Vertex b) { | ||
565 | 214 | unsigned deptha = v_to_depth.at(a); | ||
566 | 214 | unsigned depthb = v_to_depth.at(b); | ||
567 |
2/2✓ Branch 0 taken 78 times.
✓ Branch 1 taken 136 times.
|
2/2✓ Decision 'true' taken 78 times.
✓ Decision 'false' taken 136 times.
|
214 | if (deptha == depthb) { |
568 |
2/4✓ Branch 1 taken 78 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 78 times.
✗ Branch 5 not taken.
|
78 | unit_set_t unitsa = v_to_units.at(a); | |
569 |
2/4✓ Branch 1 taken 78 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 78 times.
✗ Branch 5 not taken.
|
78 | unit_set_t unitsb = v_to_units.at(b); | |
570 |
1/2✓ Branch 2 taken 78 times.
✗ Branch 3 not taken.
|
78 | return unitsa < unitsb; | |
571 | 78 | } | ||
572 | 136 | return deptha < depthb; | ||
573 | 14 | }; | ||
574 |
1/2✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
|
14 | std::set<Vertex, Comp> to_search(c); | |
575 |
2/4✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 14 times.
✗ Branch 5 not taken.
|
14 | to_search.insert(circ.target(source)); | |
576 |
1/2✓ Branch 1 taken 93 times.
✗ Branch 2 not taken.
|
1/2✓ Decision 'true' taken 93 times.
✗ Decision 'false' not taken.
|
93 | while (!to_search.empty()) { |
577 | 93 | Vertex v = *to_search.begin(); | ||
578 |
1/2✓ Branch 2 taken 93 times.
✗ Branch 3 not taken.
|
93 | to_search.erase(to_search.begin()); | |
579 |
1/2✓ Branch 1 taken 93 times.
✗ Branch 2 not taken.
|
93 | EdgeVec outs = circ.get_all_out_edges(v); | |
580 |
2/2✓ Branch 5 taken 113 times.
✓ Branch 6 taken 79 times.
|
2/2✓ Decision 'true' taken 113 times.
✓ Decision 'false' taken 79 times.
|
192 | for (const Edge &e : outs) { |
581 |
3/4✓ Branch 2 taken 113 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 14 times.
✓ Branch 6 taken 99 times.
|
2/2✓ Decision 'true' taken 99 times.
✓ Decision 'false' taken 14 times.
|
113 | if (candidates.find(e) != candidates.end()) return e; |
582 |
1/2✓ Branch 1 taken 99 times.
✗ Branch 2 not taken.
|
99 | Vertex succ = circ.target(e); | |
583 |
4/6✓ Branch 2 taken 99 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 91 times.
✓ Branch 6 taken 8 times.
✓ Branch 8 taken 91 times.
✗ Branch 9 not taken.
|
2/2✓ Decision 'true' taken 93 times.
✓ Decision 'false' taken 6 times.
|
99 | if (v_to_depth.find(succ) != v_to_depth.end()) to_search.insert(succ); |
584 | } | |||
585 |
2/2✓ Branch 1 taken 79 times.
✓ Branch 2 taken 14 times.
|
93 | } | |
586 | ✗ | return std::nullopt; | ||
587 | 14 | } | ||
588 | ||||
589 | std::optional<std::pair<InteractionPoint, InteractionPoint>> | |||
590 | 402 | CliffordReductionPass::valid_insertion_point( | ||
591 | const std::list<InteractionPoint> &seq0, | |||
592 | const std::list<InteractionPoint> &seq1) const { | |||
593 | // seq0 is chain of edges (in temporal order) from the first qubit | |||
594 | // likewise seq1 for the other qubit | |||
595 | 402 | InteractionPoint seq0max = seq0.back(); | ||
596 | 402 | InteractionPoint seq1max = seq1.back(); | ||
597 | 1206 | if (circ.in_causal_order( | ||
598 |
4/6✓ Branch 1 taken 402 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 402 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 3 times.
✓ Branch 7 taken 399 times.
|
402 | circ.source(seq1max.e), circ.target(seq0max.e), true, v_to_depth, | |
599 |
1/2✓ Branch 1 taken 402 times.
✗ Branch 2 not taken.
|
402 | v_to_units, false)) { | |
600 | // Search for any points in seq1 from future of seq0max | |||
601 | 3 | EdgeSet candidates; | ||
602 | 3 | std::map<Edge, InteractionPoint> lookup; | ||
603 |
2/2✓ Branch 4 taken 76 times.
✓ Branch 5 taken 3 times.
|
2/2✓ Decision 'true' taken 76 times.
✓ Decision 'false' taken 3 times.
|
79 | for (const InteractionPoint &ip : seq1) { |
604 |
1/2✓ Branch 1 taken 76 times.
✗ Branch 2 not taken.
|
76 | candidates.insert(ip.e); | |
605 |
1/2✓ Branch 2 taken 76 times.
✗ Branch 3 not taken.
|
76 | lookup.insert({ip.e, ip}); | |
606 | } | |||
607 | std::optional<Edge> successor = | |||
608 |
1/2✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
|
3 | find_earliest_successor(seq0max.e, candidates); | |
609 |
4/8✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 3 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 3 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 3 times.
|
1/2✓ Decision 'true' taken 3 times.
✗ Decision 'false' not taken.
|
3 | if (!successor || successor == seq1.front().e) return std::nullopt; |
610 |
1/2✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
|
3 | Vertex v = circ.source(*successor); | |
611 |
1/2✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
|
3 | port_t p = circ.get_source_port(*successor); | |
612 |
2/4✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
|
1/2✓ Decision 'true' taken 3 times.
✗ Decision 'false' not taken.
|
3 | if (circ.get_OpType_from_Vertex(v) == OpType::SWAP) p = 1 - p; |
613 |
2/4✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
|
3 | return {{seq0max, lookup.at(circ.get_nth_in_edge(v, p))}}; | |
614 | 3 | } else if (circ.in_causal_order( | ||
615 |
4/6✓ Branch 1 taken 399 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 399 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 11 times.
✓ Branch 7 taken 388 times.
|
399 | circ.source(seq0max.e), circ.target(seq1max.e), true, | |
616 |
1/2✓ Branch 1 taken 399 times.
✗ Branch 2 not taken.
|
399 | v_to_depth, v_to_units, false)) { | |
617 | // Search for any points in seq0 from future of seq1max | |||
618 | 11 | EdgeSet candidates; | ||
619 | 11 | std::map<Edge, InteractionPoint> lookup; | ||
620 |
2/2✓ Branch 4 taken 34 times.
✓ Branch 5 taken 11 times.
|
2/2✓ Decision 'true' taken 34 times.
✓ Decision 'false' taken 11 times.
|
45 | for (const InteractionPoint &ip : seq0) { |
621 |
1/2✓ Branch 1 taken 34 times.
✗ Branch 2 not taken.
|
34 | candidates.insert(ip.e); | |
622 |
1/2✓ Branch 2 taken 34 times.
✗ Branch 3 not taken.
|
34 | lookup.insert({ip.e, ip}); | |
623 | } | |||
624 | std::optional<Edge> successor = | |||
625 |
1/2✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
|
11 | find_earliest_successor(seq1max.e, candidates); | |
626 |
6/8✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 11 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 7 times.
✓ Branch 8 taken 4 times.
✓ Branch 9 taken 7 times.
✓ Branch 10 taken 4 times.
|
2/2✓ Decision 'true' taken 4 times.
✓ Decision 'false' taken 7 times.
|
11 | if (!successor || successor == seq0.front().e) return std::nullopt; |
627 |
1/2✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
|
4 | Vertex v = circ.source(*successor); | |
628 |
1/2✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
|
4 | port_t p = circ.get_source_port(*successor); | |
629 |
2/4✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
|
1/2✓ Decision 'true' taken 4 times.
✗ Decision 'false' not taken.
|
4 | if (circ.get_OpType_from_Vertex(v) == OpType::SWAP) p = 1 - p; |
630 |
2/4✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
|
4 | return {{lookup.at(circ.get_nth_in_edge(v, p)), seq1max}}; | |
631 | 11 | } else { | ||
632 | // seq0max and seq1max are space-like separated | |||
633 | 388 | return {{seq0max, seq1max}}; | ||
634 | } | |||
635 | } | |||
636 | ||||
637 | 181 | CliffordReductionPass::CliffordReductionPass(Circuit &c, bool swaps) | ||
638 | 181 | : circ(c), | ||
639 | 181 | itable(), | ||
640 | 181 | v_to_depth(), | ||
641 | 181 | success(false), | ||
642 | 181 | current_depth(1), | ||
643 | 181 | allow_swaps(swaps) { | ||
644 |
1/2✓ Branch 1 taken 181 times.
✗ Branch 2 not taken.
|
181 | v_to_units = circ.vertex_unit_map(); | |
645 |
1/2✓ Branch 1 taken 181 times.
✗ Branch 2 not taken.
|
181 | e_to_unit = circ.edge_unit_map(); | |
646 | 181 | } | ||
647 | ||||
648 | 180 | bool CliffordReductionPass::reduce_circuit(Circuit &circ, bool allow_swaps) { | ||
649 |
1/2✓ Branch 1 taken 180 times.
✗ Branch 2 not taken.
|
180 | CliffordReductionPass context(circ, allow_swaps); | |
650 | ||||
651 |
1/2✓ Branch 1 taken 180 times.
✗ Branch 2 not taken.
|
180 | SliceVec slices = circ.get_slices(); | |
652 | ||||
653 |
3/4✓ Branch 1 taken 180 times.
✗ Branch 2 not taken.
✓ Branch 7 taken 519 times.
✓ Branch 8 taken 180 times.
|
0/1? Decision couldn't be analyzed.
|
699 | for (const Vertex &in : circ.all_inputs()) { |
654 |
1/2✓ Branch 2 taken 519 times.
✗ Branch 3 not taken.
|
519 | context.v_to_depth.insert({in, 0}); | |
655 | 180 | } | ||
656 | ||||
657 | // Process all 2qb Clifford vertices. | |||
658 |
2/2✓ Branch 4 taken 6992 times.
✓ Branch 5 taken 180 times.
|
2/2✓ Decision 'true' taken 6992 times.
✓ Decision 'false' taken 180 times.
|
7172 | for (const Slice &sl : slices) { |
659 |
2/2✓ Branch 5 taken 11985 times.
✓ Branch 6 taken 6992 times.
|
2/2✓ Decision 'true' taken 11985 times.
✓ Decision 'false' taken 6992 times.
|
18977 | for (const Vertex &v : sl) { |
660 |
1/2✓ Branch 2 taken 11985 times.
✗ Branch 3 not taken.
|
11985 | context.v_to_depth.insert({v, context.current_depth}); | |
661 |
1/2✓ Branch 1 taken 11985 times.
✗ Branch 2 not taken.
|
11985 | Op_ptr op = circ.get_Op_ptr_from_Vertex(v); | |
662 |
4/6✓ Branch 2 taken 11985 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 11985 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 20 times.
✓ Branch 9 taken 11965 times.
|
2/2✓ Decision 'true' taken 11965 times.
✓ Decision 'false' taken 20 times.
|
11985 | if (!op->get_desc().is_gate()) continue; |
663 |
1/2✓ Branch 1 taken 11965 times.
✗ Branch 2 not taken.
|
11965 | EdgeVec ins = circ.get_in_edges(v); | |
664 |
1/2✓ Branch 1 taken 11965 times.
✗ Branch 2 not taken.
|
11965 | std::vector<std::optional<Edge>> outs = circ.get_linear_out_edges(v); | |
665 | 11965 | OpType type = op->get_type(); | ||
666 | 11965 | std::list<InteractionPoint> new_points; | ||
667 |
3/3✓ Branch 0 taken 8437 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 3527 times.
|
11965 | switch (type) { | |
668 | 8437 | case OpType::H: | ||
669 | case OpType::S: | |||
670 | case OpType::Sdg: | |||
671 | case OpType::V: | |||
672 | case OpType::Vdg: | |||
673 | case OpType::X: | |||
674 | case OpType::Y: | |||
675 | case OpType::Z: { | |||
676 |
1/2✓ Branch 3 taken 8437 times.
✗ Branch 4 not taken.
|
8437 | auto r = context.itable.get<TagEdge>().equal_range(ins[0]); | |
677 |
4/6✓ Branch 1 taken 7589 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 16026 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 7589 times.
✓ Branch 7 taken 8437 times.
|
0/1? Decision couldn't be analyzed.
|
16026 | for (auto it = r.first; it != r.second; ++it) { |
678 |
1/2✓ Branch 1 taken 7589 times.
✗ Branch 2 not taken.
|
7589 | InteractionPoint ip = *it; | |
679 | std::pair<Pauli, bool> new_basis = | |||
680 |
1/2✓ Branch 1 taken 7589 times.
✗ Branch 2 not taken.
|
7589 | conjugate_Pauli(type, ip.p, true); | |
681 | 7589 | ip.p = new_basis.first; | ||
682 | 7589 | ip.phase ^= new_basis.second; | ||
683 | 7589 | ip.e = *outs[0]; | ||
684 |
1/2✓ Branch 1 taken 7589 times.
✗ Branch 2 not taken.
|
7589 | new_points.push_back(ip); | |
685 | } | |||
686 | 8437 | break; | ||
687 | } | |||
688 |
1/1✓ Decision 'true' taken 1 times.
|
1 | case OpType::SWAP: { | |
689 |
1/2✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
|
1 | auto r0 = context.itable.get<TagEdge>().equal_range(ins[0]); | |
690 |
2/6✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 1 times.
|
0/1? Decision couldn't be analyzed.
|
1 | for (auto it = r0.first; it != r0.second; ++it) { |
691 | ✗ | InteractionPoint ip = *it; | ||
692 | ✗ | ip.e = *outs[1]; | ||
693 | ✗ | new_points.push_back(ip); | ||
694 | } | |||
695 |
1/2✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
|
1 | auto r1 = context.itable.get<TagEdge>().equal_range(ins[1]); | |
696 |
4/6✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1 times.
✓ Branch 7 taken 1 times.
|
0/1? Decision couldn't be analyzed.
|
2 | for (auto it = r1.first; it != r1.second; ++it) { |
697 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | InteractionPoint ip = *it; | |
698 | 1 | ip.e = *outs[0]; | ||
699 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | new_points.push_back(ip); | |
700 | } | |||
701 | 1 | break; | ||
702 | } | |||
703 |
1/1✓ Decision 'true' taken 9047 times.
|
3527 | default: { | |
704 |
2/2✓ Branch 1 taken 5520 times.
✓ Branch 2 taken 3527 times.
|
2/2✓ Decision 'true' taken 5520 times.
✓ Decision 'false' taken 3527 times.
|
9047 | for (unsigned i = 0; i < ins.size(); ++i) { |
705 |
1/2✓ Branch 3 taken 5520 times.
✗ Branch 4 not taken.
|
5520 | auto r = context.itable.get<TagEdge>().equal_range(ins[i]); | |
706 |
4/6✓ Branch 1 taken 4506 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 10026 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 4506 times.
✓ Branch 7 taken 5520 times.
|
0/1? Decision couldn't be analyzed.
|
10026 | for (auto it = r.first; it != r.second; ++it) { |
707 |
1/2✓ Branch 1 taken 4506 times.
✗ Branch 2 not taken.
|
4506 | InteractionPoint ip = *it; | |
708 |
3/4✓ Branch 2 taken 4506 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1715 times.
✓ Branch 5 taken 2791 times.
|
2/2✓ Decision 'true' taken 1715 times.
✓ Decision 'false' taken 2791 times.
|
4506 | if (circ.commutes_with_basis(v, ip.p, PortType::Target, i)) { |
709 | 1715 | ip.e = *outs[i]; | ||
710 |
1/2✓ Branch 1 taken 1715 times.
✗ Branch 2 not taken.
|
1715 | new_points.push_back(ip); | |
711 | } | |||
712 | } | |||
713 | } | |||
714 | 3527 | break; | ||
715 | } | |||
716 | } | |||
717 |
2/2✓ Branch 5 taken 9305 times.
✓ Branch 6 taken 11965 times.
|
2/2✓ Decision 'true' taken 9305 times.
✓ Decision 'false' taken 11965 times.
|
21270 | for (const InteractionPoint &ip : new_points) { |
718 |
1/2✓ Branch 1 taken 9305 times.
✗ Branch 2 not taken.
|
9305 | context.itable.insert(ip); | |
719 | } | |||
720 |
2/2✓ Branch 0 taken 1976 times.
✓ Branch 1 taken 9989 times.
|
11965 | switch (type) { | |
721 | 1976 | case OpType::CX: | ||
722 | case OpType::CY: | |||
723 | case OpType::CZ: | |||
724 | case OpType::ZZMax: { | |||
725 |
1/2✓ Branch 1 taken 1976 times.
✗ Branch 2 not taken.
|
1976 | context.process_new_interaction(v); | |
726 | 1976 | break; | ||
727 | } | |||
728 |
1/1✓ Decision 'true' taken 9989 times.
|
9989 | default: { | |
729 | 9989 | break; | ||
730 | } | |||
731 | } | |||
732 |
2/2✓ Branch 4 taken 11965 times.
✓ Branch 5 taken 20 times.
|
11985 | } | |
733 | 6992 | ++context.current_depth; | ||
734 | } | |||
735 | ||||
736 |
2/2✓ Branch 0 taken 71 times.
✓ Branch 1 taken 109 times.
|
2/2✓ Decision 'true' taken 71 times.
✓ Decision 'false' taken 109 times.
|
180 | if (allow_swaps) { |
737 |
1/2✓ Branch 1 taken 71 times.
✗ Branch 2 not taken.
|
71 | circ.replace_SWAPs(); | |
738 | } | |||
739 | ||||
740 | 180 | return context.success; | ||
741 | 180 | } | ||
742 | ||||
743 | namespace Transforms { | |||
744 | ||||
745 | 177 | Transform clifford_reduction(bool allow_swaps) { | ||
746 | 180 | return Transform([=](Circuit &circ) { | ||
747 | 180 | return CliffordReductionPass::reduce_circuit(circ, allow_swaps); | ||
748 |
1/2✓ Branch 2 taken 177 times.
✗ Branch 3 not taken.
|
177 | }); | |
749 | } | |||
750 | ||||
751 | } // namespace Transforms | |||
752 | ||||
753 | 1 | CliffordReductionPassTester::CliffordReductionPassTester(Circuit &circ) | ||
754 | 1 | : context(circ, true) { | ||
755 | // populate v_to_depth | |||
756 |
3/4✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 7 taken 4 times.
✓ Branch 8 taken 1 times.
|
0/1? Decision couldn't be analyzed.
|
5 | for (const Vertex &in : circ.all_inputs()) { |
757 |
1/2✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
|
4 | context.v_to_depth.insert({in, 0}); | |
758 | 1 | } | ||
759 | ||||
760 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | SliceVec slices = circ.get_slices(); | |
761 |
2/2✓ Branch 5 taken 3 times.
✓ Branch 6 taken 1 times.
|
2/2✓ Decision 'true' taken 3 times.
✓ Decision 'false' taken 1 times.
|
4 | for (const Slice &sl : slices) { |
762 |
2/2✓ Branch 4 taken 4 times.
✓ Branch 5 taken 3 times.
|
2/2✓ Decision 'true' taken 4 times.
✓ Decision 'false' taken 3 times.
|
7 | for (const Vertex &v : sl) { |
763 |
1/2✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
|
4 | context.v_to_depth.insert({v, context.current_depth}); | |
764 | } | |||
765 | } | |||
766 | 1 | } | ||
767 | ||||
768 | std::optional<std::pair<InteractionPoint, InteractionPoint>> | |||
769 | 1 | CliffordReductionPassTester::valid_insertion_point( | ||
770 | const std::list<InteractionPoint> &seq0, | |||
771 | const std::list<InteractionPoint> &seq1) const { | |||
772 | 1 | return context.valid_insertion_point(seq0, seq1); | ||
773 | } | |||
774 | ||||
775 | } // namespace tket | |||
776 |