Maestro 0.1.0
Unified interface for quantum circuit simulation
Loading...
Searching...
No Matches
Json.h
Go to the documentation of this file.
1
12#pragma once
13
14#ifndef _JSON_H_
15#define _JSON_H_
16
17#include <boost/json.hpp>
18#include <exception>
19#include <fstream>
20#include <iostream>
21
22#include "../Circuit/Factory.h"
23#include "../qasm/QasmCirc.h"
24
25namespace Json {
26
27template <typename Time = Types::time_type>
29 public:
38 static boost::json::value ParseString(const char *str) {
39 boost::system::error_code ec;
40 std::string strs(str);
41 std::stringstream ss(strs);
42 return Read(ss, ec);
43 }
44
45 std::shared_ptr<Circuits::Circuit<Time>> ParseCircuit(const char *str) const {
46 const boost::json::value circuitJson = ParseString(str);
47
48 std::shared_ptr<Circuits::Circuit<Time>> circuit;
49
50 if (circuitJson.is_array()) {
51 const auto circuitArray = circuitJson.as_array();
52 circuit = ParseCircuitArray(circuitArray);
53 } else if (circuitJson.is_object()) {
54 std::string circuitStr;
55 const auto jsonObject = circuitJson.as_object();
56 if (jsonObject.contains("qasm")) {
57 const auto qasmValue = jsonObject.at("qasm");
58 if (qasmValue.is_string()) circuitStr = qasmValue.as_string().c_str();
59 } else if (jsonObject.contains("QASM")) {
60 const auto qasmValue = jsonObject.at("QASM");
61 if (qasmValue.is_string()) circuitStr = qasmValue.as_string().c_str();
62 }
63 if (circuitStr.empty()) return nullptr;
64
65 // QASM 2.0 format
66 qasm::QasmToCirc<> parser;
67 std::string qasmInput(circuitStr);
68 circuit = parser.ParseAndTranslate(qasmInput);
69 } else
70 return nullptr;
71
72 return circuit;
73 }
74
75 static std::string GetConfigString(const std::string &config,
76 const boost::json::value &jsonConfig) {
77 if (jsonConfig.is_object()) {
78 const auto jsonObject = jsonConfig.as_object();
79
80 if (jsonObject.contains(config)) {
81 const auto configValue = jsonObject.at(config);
82 if (configValue.is_string())
83 return configValue.as_string().c_str();
84 else if (configValue.is_number()) {
85 if (configValue.is_int64())
86 return std::to_string(configValue.as_int64());
87 else if (configValue.is_uint64())
88 return std::to_string(configValue.as_uint64());
89 else if (configValue.is_double())
90 return std::to_string(configValue.as_double());
91 } else if (configValue.is_bool()) {
92 return configValue.as_bool() ? "true" : "false";
93 }
94 }
95 }
96
97 return "";
98 }
99
100 private:
110 std::shared_ptr<Circuits::Circuit<Time>> ParseCircuitArray(
111 const boost::json::array &circuitArray) const {
112 const auto circuit = std::make_shared<Circuits::Circuit<Time>>();
113
114 for (auto operationJson : circuitArray) {
115 if (!operationJson.is_object())
116 throw std::runtime_error("Circuit operation must be an object.");
117
118 auto operationObject = operationJson.as_object();
119 if (!operationObject.contains(nameString))
120 throw std::runtime_error("Circuit operation does not have a type.");
121 else if (!operationObject.at(nameString).is_string())
122 throw std::runtime_error("Circuit operation type must be a string.");
123
124 const boost::json::string type =
125 operationObject.at(nameString).as_string();
126
127 if (type != "id")
128 circuit->AddOperation(ParseOperation(type, operationObject));
129 }
130
131 return circuit;
132 }
133
144 std::shared_ptr<Circuits::IOperation<Time>> ParseOperation(
145 const boost::json::string &type, boost::json::object &obj) const {
146 std::shared_ptr<Circuits::IOperation<Time>> operation;
147
148 if (type == measurementString)
149 operation = ParseMeasurement(obj);
150 else
151 operation = ParseGate(type, obj);
152
153 return operation;
154 }
155
165 std::shared_ptr<Circuits::MeasurementOperation<Time>> ParseMeasurement(
166 boost::json::object &obj) const {
167 const auto qubits = ParseQubits(obj);
168 auto cbits = ParseCbits(obj);
169 if (cbits.empty())
170 cbits = std::vector<size_t>(qubits.begin(), qubits.end());
171
172 if (qubits.size() != cbits.size())
173 throw std::runtime_error("Number of qubits and cbits must be the same.");
174
175 std::vector<std::pair<Types::qubit_t, size_t>> qs;
176
177 for (size_t i = 0; i < qubits.size(); i++)
178 qs.push_back(std::make_pair(qubits[i], cbits[i]));
179
180 const auto operation =
181 std::static_pointer_cast<Circuits::MeasurementOperation<Time>>(
183
184 return operation;
185 }
186
196 std::shared_ptr<Circuits::IOperation<Time>> ParseGate(
197 const boost::json::string &type, boost::json::object &obj) const {
198 const std::string gateName = type.c_str();
199
200 const auto qubits = ParseQubits(obj);
201 if (qubits.empty())
202 throw std::runtime_error("No qubits specified.");
203 else if (qubits.size() > 3)
204 throw std::runtime_error("Number of qubits must be 1, 2, or 3.");
205
207
208 if (gatesMap.find(gateName) == gatesMap.end())
209 throw std::runtime_error("Gate type not supported.");
210
211 gateType = gatesMap.at(gateName);
212
213 // parse gate parameters
214 const auto parameters = ParseParameters(obj);
215
216 const auto operation = Circuits::CircuitFactory<Time>::CreateGate(
217 gateType, qubits[0], qubits.size() > 1 ? qubits[1] : 0,
218 qubits.size() > 2 ? qubits[2] : 0,
219 parameters.size() > 0 ? parameters[0] : 0.,
220 parameters.size() > 1 ? parameters[1] : 0.,
221 parameters.size() > 2 ? parameters[2] : 0.,
222 parameters.size() > 3 ? parameters[3] : 0.);
223
224 // check if the number of qubits is correct for the gate
225
226 if (operation->GetNumQubits() != qubits.size())
227 throw std::runtime_error(
228 "The specified number of qubits does not match the number of qubits "
229 "required by the gate type.");
230
231 // TODO: check parameters?
232
233 // check if it's a classically controlled gate
234 if (obj.contains("conditional_reg")) {
235 auto cond = obj.at("conditional_reg");
236 unsigned long long int cbit = 0;
237 if (cond.is_uint64())
238 cbit = cond.as_uint64();
239 else if (cond.is_int64())
240 cbit = static_cast<unsigned long long int>(cond.as_int64());
241 else if (cond.is_array()) {
242 auto arr = cond.as_array();
243 if (arr.size() != 1 || !arr[0].is_number())
244 throw std::runtime_error(
245 "Conditional register must be a single integer.");
246
247 if (arr[0].is_uint64())
248 cbit = arr[0].as_uint64();
249 else if (arr[0].is_int64())
250 cbit = static_cast<unsigned long long int>(arr[0].as_int64());
251 else
252 throw std::runtime_error(
253 "Conditional register must be an integer or "
254 "an array of one integer.");
255 } else
256 throw std::runtime_error(
257 "Conditional register must be an integer or "
258 "an array of one integer.");
259
261 operation, cbit);
262 }
263
264 return std::static_pointer_cast<Circuits::IOperation<Time>>(operation);
265 }
266
275 std::vector<double> ParseParameters(boost::json::object &obj) const {
276 std::vector<double> parameters;
277
278 if (obj.contains(paramsString) && !obj.at(paramsString).is_array())
279 throw std::runtime_error("Parameters must be an array.");
280
281 auto paramsJson =
282 (obj.contains(paramsString) ? obj.at(paramsString).as_array()
283 : boost::json::array());
284 for (auto param : paramsJson) {
285 if (!param.is_number())
286 throw std::runtime_error("Parameter must be a number.");
287 else if (param.is_double())
288 parameters.push_back(param.as_double());
289 else if (param.is_int64())
290 parameters.push_back(static_cast<double>(param.as_int64()));
291 else
292 parameters.push_back(static_cast<double>(param.as_uint64()));
293 }
294
295 return parameters;
296 }
297
306 std::vector<Types::qubit_t> ParseQubits(
307 const boost::json::object &obj) const {
308 std::vector<Types::qubit_t> qubits;
309
310 if (!obj.contains(qubitsString))
311 throw std::runtime_error("No Qubits specified.");
312 else if (!obj.at(qubitsString).is_array())
313 throw std::runtime_error("Qubits must be an array.");
314 else if (obj.at(qubitsString).as_array().size() == 0)
315 throw std::runtime_error("Qubits array must not be empty.");
316
317 auto qubitsJson = obj.at(qubitsString).as_array();
318 for (auto qubit : qubitsJson) {
319 if (!qubit.is_int64() && !qubit.is_uint64())
320 throw std::runtime_error("Number of qubits must be an integer.");
321 else if (qubit.is_int64())
322 qubits.push_back(qubit.as_int64());
323 else
324 qubits.push_back(qubit.as_uint64());
325 }
326
327 return qubits;
328 }
329
340 std::vector<size_t> ParseCbits(const boost::json::object &obj,
341 bool mustBeSpecified = false) const {
342 std::vector<size_t> cbits;
343
344 if (mustBeSpecified) {
345 if (!obj.contains(memoryString))
346 throw std::runtime_error("No Cbits specified.");
347 else if (!obj.at(memoryString).is_array())
348 throw std::runtime_error("Cbits must be an array.");
349 else if (obj.at(memoryString).as_array().size() == 0)
350 throw std::runtime_error("Cbits array must not be empty.");
351 } else if (obj.contains(memoryString) && !obj.at(memoryString).is_array())
352 throw std::runtime_error("Cbits must be an array.");
353
354 auto cbitsJson =
355 (obj.contains(memoryString) ? obj.at(memoryString).as_array()
356 : boost::json::array());
357 for (auto cbit : cbitsJson) {
358 if (!cbit.is_int64() && !cbit.is_uint64())
359 throw std::runtime_error("Number of cbits must be an integer.");
360 else if (cbit.is_int64())
361 cbits.push_back(cbit.as_int64());
362 else
363 cbits.push_back(cbit.as_uint64());
364 }
365
366 return cbits;
367 }
368
378 static boost::json::value ParseString(const std::string &str,
379 boost::system::error_code &ec) {
380 std::stringstream ss(str);
381 return Read(ss, ec);
382 }
383
393 static boost::json::value Parse(std::istream &is,
394 boost::system::error_code &ec) {
395 return Read(is, ec);
396 }
397
407 static boost::json::value Read(std::istream &is,
408 boost::system::error_code &ec) {
409 boost::json::stream_parser p;
410 std::string line;
411 while (std::getline(is, line)) {
412 p.write(line, ec);
413 if (ec) return nullptr;
414 }
415 p.finish(ec);
416 if (ec) return nullptr;
417
418 return p.release();
419 }
420
421 const std::string circuitString = "instructions";
422 const std::string nameString = "name";
423 const std::string measurementString = "measure";
424
425 const std::string qubitsString = "qubits";
426 const std::string paramsString = "params";
427 const std::string memoryString = "clbits";
428
429 const std::unordered_map<std::string, Circuits::QuantumGateType> gatesMap = {
462 {"ccx",
465};
466
467} // namespace Json
468
469#endif // !_JSON_H_
static std::shared_ptr< IOperation< Time > > CreateSimpleConditionalGate(const std::shared_ptr< IGateOperation< Time > > &operation, const size_t cbit)
Construct a simple conditional gate.
static const std::shared_ptr< IQuantumGate< Time > > CreateGate(QuantumGateType type, size_t q1, size_t q2=0, size_t q3=0, double param1=0, double param2=0, double param3=0, double param4=0)
Construct a quantum gate.
static const std::shared_ptr< IOperation< Time > > CreateMeasurement(const std::vector< std::pair< Types::qubit_t, size_t > > &qs={})
Construct a measurement operation.
static boost::json::value ParseString(const char *str)
Parses a string containing json.
Definition Json.h:38
std::shared_ptr< Circuits::Circuit< Time > > ParseCircuit(const char *str) const
Definition Json.h:45
static std::string GetConfigString(const std::string &config, const boost::json::value &jsonConfig)
Definition Json.h:75
QuantumGateType
The type of quantum gates.
Definition Json.h:25