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