GCC Code Coverage Report


Directory: ./
File: Transformations/include/Transformations/CliffordReductionPass.hpp
Date: 2022-10-15 05:10:18
Exec Total Coverage
Lines: 1 1 100.0%
Functions: 1 1 100.0%
Branches: 0 0 -%
Decisions: 0 0 -%

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 #pragma once
16
17 #include "Transform.hpp"
18
19 namespace tket {
20
21 /**
22 * Represents a position that one port of a 2qb Clifford gadget could be
23 * commuted to (towards the end of the circuit).
24 */
25 struct InteractionPoint {
26 /**
27 * Edge to which one point of the interaction could potentially be moved.
28 * This is always in the causal future of the source vertex.
29 */
30 Edge e;
31
32 /** Vertex representing the current interaction */
33 Vertex source;
34
35 /** What the Pauli term would be transformed to if moved to new location */
36 Pauli p;
37
38 /** Phase of source (true for -pi/2, false for +pi/2) */
39 bool phase; // For each source of an interaction, True for -pi/2, false for
40 // +pi/2
41
42 81419 std::pair<Edge, Vertex> key() const { return {e, source}; }
43 };
44
45 /**
46 * Represents a position that one port of a 2qb Clifford gadget could be
47 * commuted to (towards the start of the circuit). We don't need to store the
48 * source since we only consider commuting one gadget backwards at a time to
49 * search for matches.
50 */
51 struct RevInteractionPoint {
52 /**
53 * Edge to which one point of the interaction could potentially be moved.
54 * This is always in the causal past of the source vertex.
55 */
56 Edge e;
57
58 /** What the Pauli term would be transformed to if moved to new location */
59 Pauli p;
60
61 /** Phase of source (true for -pi/2, false for +pi/2) */
62 bool phase;
63 };
64
65 /**
66 * Represents a position to which two 2q Clifford gadgets could be commuted to
67 * Assumes:
68 * - point0.source == point1.source
69 * - point0.e == rev0.e
70 * - point1.e == rev1.e
71 */
72 struct InteractionMatch {
73 InteractionPoint point0;
74 InteractionPoint point1;
75 RevInteractionPoint rev0;
76 RevInteractionPoint rev1;
77 };
78
79 struct TagEdge {};
80 struct TagSource {};
81
82 /**
83 * Stores all current InteractionPoints encountered.
84 * Each gadget can only commute to a given edge in a unique way.
85 * Searching by edge is needed to search for possible matches.
86 * Searching by vertex is needed to remove InteractionPoints when a pair merge.
87 */
88 typedef boost::multi_index::multi_index_container<
89 InteractionPoint,
90 boost::multi_index::indexed_by<
91 boost::multi_index::hashed_unique<
92 boost::multi_index::tag<TagKey>,
93 boost::multi_index::const_mem_fun<
94 InteractionPoint, std::pair<Edge, Vertex>,
95 &InteractionPoint::key>>,
96 boost::multi_index::hashed_non_unique<
97 boost::multi_index::tag<TagEdge>,
98 boost::multi_index::member<
99 InteractionPoint, Edge, &InteractionPoint::e>>,
100 boost::multi_index::hashed_non_unique<
101 boost::multi_index::tag<TagSource>,
102 boost::multi_index::member<
103 InteractionPoint, Vertex, &InteractionPoint::source>>>>
104 interaction_table_t;
105
106 class CliffordReductionPass {
107 public:
108 /** Apply the transformation to a circuit */
109 static bool reduce_circuit(Circuit &circ, bool allow_swaps = false);
110
111 private:
112 /** The circuit under transformation */
113 Circuit &circ;
114
115 /** Table of potential transports of 2qb interactions through the circuit */
116 interaction_table_t itable;
117
118 /** Map from vertex to depth in circuit */
119 std::map<Vertex, unsigned> v_to_depth;
120
121 /** Map from vertex to sets of units that it acts on */
122 std::map<Vertex, unit_set_t> v_to_units;
123
124 /** Map from edge to corresponding unit */
125 std::map<Edge, UnitID> e_to_unit;
126
127 /** Whether any changes have been made to the circuit */
128 bool success;
129
130 /** Current depth in forward pass through circuit. */
131 unsigned current_depth;
132
133 /** Whether to allow SWAP gates in the final circuit */
134 bool allow_swaps;
135
136 /**
137 * Inserts a new interaction point into the table and tries to commute it as
138 * far forwards as possible, taking into account commutations and
139 * change-of-basis through 1q Cliffords. Does not change the circuit, but
140 * extends the table of interaction points as it goes. Stops propagating
141 * when it reaches an interaction point that is already in the table.
142 */
143 void insert_interaction_point(InteractionPoint ip);
144
145 /**
146 * Tries to commute an interaction backwards to search for a match,
147 * consulting the current table of interaction points to find canditates.
148 */
149 std::optional<InteractionMatch> search_back_for_match(
150 const RevInteractionPoint &rip0, const RevInteractionPoint &rip1) const;
151
152 /**
153 * Called when a new 2q Clifford gate is observed (either encountered by
154 * advancing through the circuit or by substitution mid-circuit). Handles
155 * scanning for pattern matches and substituting, and adding new
156 * InteractionPoints to look for matches further into the circuit.
157 */
158 void process_new_interaction(const Vertex &v);
159
160 /**
161 * Inserts a circuit at the given point, updating itable, v_to_depth,
162 * v_to_units, and e_to_unit appropriately.
163 *
164 * @pre Depth of `to_replace` is at most 1.
165 */
166 Subcircuit substitute(const Circuit &to_insert, const Subcircuit &to_replace);
167
168 /**
169 * Given a set of candidate edges, finds the earliest one that is in the
170 * strict causal future of source. Returns nullopt if none are in the future
171 * of source. Search threshold is set by only having v_to_depth defined for
172 * the vertices to search.
173 */
174 std::optional<Edge> find_earliest_successor(
175 const Edge &source, const EdgeSet &candidates) const;
176
177 /**
178 * Given two lists of edges, identifies a cut where a two-qubit operation
179 * could feasibly be introduced without breaking the causal structure (i.e.
180 * introducing loops in the DAG). Returns nullopt if there is no such cut,
181 * otherwise will yield the future-most valid point with respect to each of
182 * the sequences.
183 */
184 std::optional<std::pair<InteractionPoint, InteractionPoint>>
185 valid_insertion_point(
186 const std::list<InteractionPoint> &seq0,
187 const std::list<InteractionPoint> &seq1) const;
188
189 CliffordReductionPass(Circuit &c, bool swaps);
190
191 friend class CliffordReductionPassTester;
192 };
193
194 /** a simple friend class of `CliffordReductionPass` used for testing
195 * private member functions */
196 class CliffordReductionPassTester {
197 public:
198 explicit CliffordReductionPassTester(Circuit &circ);
199
200 std::optional<std::pair<InteractionPoint, InteractionPoint>>
201 valid_insertion_point(
202 const std::list<InteractionPoint> &seq0,
203 const std::list<InteractionPoint> &seq1) const;
204
205 private:
206 CliffordReductionPass context;
207 };
208
209 namespace Transforms {
210
211 Transform clifford_reduction(bool allow_swaps = false);
212
213 }
214
215 } // namespace tket
216