GCC Code Coverage Report


Directory: ./
File: Utils/include/Utils/UnitID.hpp
Date: 2022-10-15 05:10:18
Warnings: 1 unchecked decisions!
Exec Total Coverage
Lines: 40 91 44.0%
Functions: 17 44 38.6%
Branches: 28 154 18.2%
Decisions: 7 20 35.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 /**
18 * @file
19 * @brief Named registers of arrays of (quantum or classical) nodes
20 */
21
22 #include <boost/functional/hash.hpp>
23 #include <map>
24 #include <memory>
25 #include <optional>
26 #include <regex>
27 #include <set>
28 #include <sstream>
29 #include <string>
30 #include <tklog/TketLog.hpp>
31
32 #include "BiMapHeaders.hpp"
33 #include "Json.hpp"
34
35 namespace tket {
36
37 /** Type of information held */
38 enum class UnitType { Qubit, Bit };
39
40 /** The type and dimension of a register */
41 typedef std::pair<UnitType, unsigned> register_info_t;
42
43 typedef std::optional<register_info_t> opt_reg_info_t;
44
45 const std::string &q_default_reg();
46 const std::string &c_default_reg();
47 const std::string &node_default_reg();
48 const std::string &c_debug_zero_prefix();
49 const std::string &c_debug_one_prefix();
50 const std::string &c_debug_default_name();
51
52 /** Conversion invalid */
53 class InvalidUnitConversion : public std::logic_error {
54 public:
55 InvalidUnitConversion(const std::string &name, const std::string &new_type)
56 : std::logic_error("Cannot convert " + name + " to " + new_type) {}
57 };
58
59 /**
60 * Location holding a bit or qubit of information
61 *
62 * Each location has a name (signifying the 'register' to which it belongs) and
63 * an index within that register (which may be multi-dimensional).
64 */
65 class UnitID {
66 public:
67 UnitID() : data_(std::make_shared<UnitData>()) {}
68
69 /** String representation including name and index */
70 std::string repr() const;
71
72 /** Register name */
73 std::string reg_name() const { return data_->name_; }
74
75 /** Index dimension */
76 unsigned reg_dim() const { return data_->index_.size(); }
77
78 /** Index */
79 std::vector<unsigned> index() const { return data_->index_; }
80
81 /** Unit type */
82 UnitType type() const { return data_->type_; }
83
84 /** Register dimension and type */
85 808337 register_info_t reg_info() const { return {type(), reg_dim()}; }
86
87 bool operator<(const UnitID &other) const {
88 int n = data_->name_.compare(other.data_->name_);
89
0/2
✗ Decision 'true' not taken.
✗ Decision 'false' not taken.
if (n > 0) return false;
90
0/2
✗ Decision 'true' not taken.
✗ Decision 'false' not taken.
if (n < 0) return true;
91 return data_->index_ < other.data_->index_;
92 }
93 203 bool operator>(const UnitID &other) const { return other < *this; }
94 bool operator==(const UnitID &other) const {
95 return (this->data_->name_ == other.data_->name_) &&
96 (this->data_->index_ == other.data_->index_);
97 }
98 bool operator!=(const UnitID &other) const { return !(*this == other); }
99
100 494 friend std::size_t hash_value(UnitID const &unitid) {
101 494 size_t seed = 0;
102
1/2
✓ Branch 2 taken 494 times.
✗ Branch 3 not taken.
494 boost::hash_combine(seed, unitid.data_->name_);
103
1/2
✓ Branch 2 taken 494 times.
✗ Branch 3 not taken.
494 boost::hash_combine(seed, unitid.data_->index_);
104
1/2
✓ Branch 2 taken 494 times.
✗ Branch 3 not taken.
494 boost::hash_combine(seed, unitid.data_->type_);
105 494 return seed;
106 }
107
108 protected:
109 UnitID(
110 const std::string &name, const std::vector<unsigned> &index,
111 UnitType type)
112 : data_(std::make_shared<UnitData>(name, index, type)) {}
113
114 private:
115 struct UnitData {
116 std::string name_;
117 std::vector<unsigned> index_;
118 UnitType type_;
119
120 UnitData() : name_(), index_(), type_(UnitType::Qubit) {}
121 UnitData(
122 const std::string &name, const std::vector<unsigned> &index,
123 UnitType type)
124 : name_(name), index_(index), type_(type) {
125 static const std::string id_regex_str = "[a-z][A-Za-z0-9_]*";
126 static const std::regex id_regex(id_regex_str);
127
0/2
✗ Decision 'true' not taken.
✗ Decision 'false' not taken.
if (!name.empty() && !std::regex_match(name, id_regex)) {
128 std::stringstream msg;
129 msg << "UnitID name '" << name << "' does not match '" << id_regex_str
130
0/1
? Decision couldn't be analyzed.
<< "', as required for QASM conversion.";
131 tket_log()->warn(msg.str());
132 }
133 }
134 };
135 std::shared_ptr<UnitData> data_;
136 };
137
138 template <class Unit_T>
139 14298 void unitid_to_json(nlohmann::json &j, const Unit_T &unit) {
140 static_assert(std::is_base_of<UnitID, Unit_T>::value);
141
2/4
✓ Branch 2 taken 7149 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 7149 times.
✗ Branch 6 not taken.
14298 j.push_back(unit.reg_name());
142
2/4
✓ Branch 2 taken 7149 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 7149 times.
✗ Branch 6 not taken.
14298 j.push_back(unit.index());
143 14298 }
144
145 template <class T>
146 8520 void json_to_unitid(const nlohmann::json &j, T &unit) {
147
3/6
✓ Branch 3 taken 4260 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 4260 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 4260 times.
✗ Branch 10 not taken.
8520 unit = T(j.at(0).get<std::string>(), j.at(1).get<std::vector<unsigned>>());
148 8520 }
149
150 /** Location holding a qubit */
151 class Qubit : public UnitID {
152 public:
153 Qubit() : UnitID("", {}, UnitType::Qubit) {}
154
155 /** Qubit in default register */
156 explicit Qubit(unsigned index)
157 : UnitID(q_default_reg(), {index}, UnitType::Qubit) {}
158
159 /** Named register with no index */
160 explicit Qubit(const std::string &name) : UnitID(name, {}, UnitType::Qubit) {}
161
162 /** Named register with a one-dimensional index */
163 Qubit(const std::string &name, unsigned index)
164 : UnitID(name, {index}, UnitType::Qubit) {}
165
166 /** Named register with a two-dimensional index */
167 Qubit(const std::string &name, unsigned row, unsigned col)
168 : UnitID(name, {row, col}, UnitType::Qubit) {}
169
170 /** Named register with a three-dimensional index */
171 4822 Qubit(const std::string &name, unsigned row, unsigned col, unsigned layer)
172
2/4
✓ Branch 2 taken 4822 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 4822 times.
✗ Branch 6 not taken.
4822 : UnitID(name, {row, col, layer}, UnitType::Qubit) {}
173
174 /** Named register with a multi-dimensional index */
175 Qubit(const std::string &name, std::vector<unsigned> index)
176 : UnitID(name, index, UnitType::Qubit) {}
177
178 /** Copy constructor */
179 explicit Qubit(const UnitID &other) : UnitID(other) {
180
0/2
✗ Decision 'true' not taken.
✗ Decision 'false' not taken.
if (other.type() != UnitType::Qubit) {
181 throw InvalidUnitConversion(other.repr(), "Qubit");
182 }
183 }
184 };
185
186 JSON_DECL(Qubit)
187
188 /** Location holding a bit */
189 class Bit : public UnitID {
190 public:
191 Bit() : UnitID("", {}, UnitType::Bit) {}
192
193 /** Bit in default register */
194 explicit Bit(unsigned index)
195 : UnitID(c_default_reg(), {index}, UnitType::Bit) {}
196
197 /** Named register with no index */
198 explicit Bit(const std::string &name) : UnitID(name, {}, UnitType::Bit) {}
199
200 /** Named register with a one-dimensional index */
201 Bit(const std::string &name, unsigned index)
202 : UnitID(name, {index}, UnitType::Bit) {}
203
204 /** Named register with a two-dimensional index */
205 Bit(const std::string &name, unsigned row, unsigned col)
206 : UnitID(name, {row, col}, UnitType::Bit) {}
207
208 /** Named register with a three-dimensional index */
209 Bit(const std::string &name, unsigned row, unsigned col, unsigned layer)
210 : UnitID(name, {row, col, layer}, UnitType::Bit) {}
211
212 /** Named register with a multi-dimensional index */
213 Bit(const std::string &name, std::vector<unsigned> index)
214 : UnitID(name, index, UnitType::Bit) {}
215
216 explicit Bit(const UnitID &other) : UnitID(other) {
217
0/2
✗ Decision 'true' not taken.
✗ Decision 'false' not taken.
if (other.type() != UnitType::Bit) {
218 throw InvalidUnitConversion(other.repr(), "Bit");
219 }
220 }
221 };
222
223 JSON_DECL(Bit)
224
225 /** Architectural qubit location */
226 class Node : public Qubit {
227 public:
228 Node() : Qubit() {}
229
230 /** Qubit in default register */
231 explicit Node(unsigned index) : Qubit(node_default_reg(), index) {}
232
233 /** Named register with a one-dimensional index */
234 Node(const std::string &name, unsigned index) : Qubit(name, index) {}
235
236 /** Named register with a two-dimensional index */
237 Node(const std::string &name, unsigned row, unsigned col)
238 : Qubit(name, row, col) {}
239
240 /** Named register with a three-dimensional index */
241 4822 Node(const std::string &name, unsigned row, unsigned col, unsigned layer)
242 4822 : Qubit(name, row, col, layer) {}
243
244 /** Named register with a multi-dimensional index */
245 866 Node(const std::string &name, std::vector<unsigned> index)
246
1/2
✓ Branch 2 taken 866 times.
✗ Branch 3 not taken.
866 : Qubit(name, index) {}
247
248 explicit Node(const UnitID &other) : Qubit(other) {}
249 };
250
251 JSON_DECL(Node)
252
253 /** A correspondence between two sets of unit IDs */
254 typedef boost::bimap<UnitID, UnitID> unit_bimap_t;
255
256 /** A pair of ("initial" and "final") correspondences between unit IDs */
257 typedef struct {
258 unit_bimap_t initial;
259 unit_bimap_t final;
260 } unit_bimaps_t;
261
262 typedef std::vector<UnitID> unit_vector_t;
263 typedef std::map<UnitID, UnitID> unit_map_t;
264 typedef std::set<UnitID> unit_set_t;
265
266 typedef std::vector<Qubit> qubit_vector_t;
267 typedef std::map<Qubit, Qubit> qubit_map_t;
268 JSON_DECL(qubit_map_t)
269
270 typedef std::vector<Bit> bit_vector_t;
271 typedef std::map<Bit, Bit> bit_map_t;
272
273 typedef std::set<Node> node_set_t;
274 typedef std::vector<Node> node_vector_t;
275
276 /** A register of locations sharing the same name */
277 typedef std::map<unsigned, UnitID> register_t;
278
279 template <typename UnitA, typename UnitB>
280 10234 static bool update_map(unit_bimap_t &m, const std::map<UnitA, UnitB> &um) {
281 10234 unit_map_t new_m;
282 10234 bool changed = false;
283
2/2
✓ Branch 5 taken 7086 times.
✓ Branch 6 taken 5140 times.
2/2
✓ Decision 'true' taken 7086 times.
✓ Decision 'false' taken 5140 times.
24350 for (const std::pair<const UnitA, UnitB> &pair : um) {
284
1/2
✓ Branch 1 taken 7086 times.
✗ Branch 2 not taken.
14116 const auto &it = m.right.find(pair.first);
285
4/6
✓ Branch 1 taken 7086 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 7086 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 54 times.
✓ Branch 7 taken 7032 times.
2/2
✓ Decision 'true' taken 108 times.
✓ Decision 'false' taken 14008 times.
14116 if (it == m.right.end()) {
286 108 continue;
287 }
288
2/4
✓ Branch 1 taken 7032 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 7032 times.
✗ Branch 6 not taken.
14008 new_m.insert({it->second, pair.second});
289
1/2
✓ Branch 1 taken 7032 times.
✗ Branch 2 not taken.
14008 changed |= (m.right.erase(pair.first) > 0);
290 }
291
2/2
✓ Branch 4 taken 7032 times.
✓ Branch 5 taken 5140 times.
2/2
✓ Decision 'true' taken 7032 times.
✓ Decision 'false' taken 5140 times.
24242 for (const std::pair<const UnitID, UnitID> &pair : new_m) {
292
2/4
✓ Branch 1 taken 7032 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 7032 times.
✗ Branch 5 not taken.
14008 changed |= m.left.insert(pair).second;
293 }
294 10234 return changed;
295 10234 }
296
297 /**
298 * Update a pair of "initial" and "final" correspondences.
299 *
300 * If \p maps is null then the function does nothing and returns false.
301 *
302 * @param[in,out] maps maps to be updated
303 * @param[in] um_initial new correspondences added to initial map
304 * @param[in] um_final new correspondences added to final map
305 *
306 * @tparam UnitA first unit type
307 * @tparam UnitB second unit type
308 *
309 * @return whether any changes were made to the maps
310 */
311 template <typename UnitA, typename UnitB>
312 2580 bool update_maps(
313 std::shared_ptr<unit_bimaps_t> maps,
314 const std::map<UnitA, UnitB> &um_initial,
315 const std::map<UnitA, UnitB> &um_final) {
316
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
1/2
✓ Decision 'true' taken 23 times.
✗ Decision 'false' not taken.
2580 if (!maps) return false;
317 // Can only work for Unit classes
318 static_assert(std::is_base_of<UnitID, UnitA>::value);
319 static_assert(std::is_base_of<UnitID, UnitB>::value);
320 // Unit types must be related, so cannot rename e.g. Bits to Qubits
321 static_assert(
322 std::is_base_of<UnitA, UnitB>::value ||
323 std::is_base_of<UnitB, UnitA>::value);
324
325 2570 bool changed = false;
326 2570 changed |= update_map(maps->initial, um_initial);
327 2570 changed |= update_map(maps->final, um_final);
328 2570 return changed;
329 }
330
331 } // namespace tket
332