Maestro 0.3.11
Unified interface for quantum circuit simulation
Loading...
Searching...
No Matches
Json.h
Go to the documentation of this file.
1
12
13#pragma once
14
15#ifndef _JSON_H_
16#define _JSON_H_
17
18#include <boost/json.hpp>
19#include <exception>
20#include <fstream>
21#include <iostream>
22
23#include "../Circuit/Factory.h"
24#include "../qasm/QasmCirc.h"
25
26namespace Json
27{
28
29template <typename Time = Types::time_type> class JsonParserMaestro
30{
31 public:
40 static boost::json::value ParseString(const char *str)
41 {
42 boost::system::error_code ec;
43 std::string strs(str);
44 std::stringstream ss(strs);
45 return Read(ss, ec);
46 }
47
48 std::shared_ptr<Circuits::Circuit<Time>> ParseCircuit(const char *str) const
49 {
50 const boost::json::value circuitJson = ParseString(str);
51
52 std::shared_ptr<Circuits::Circuit<Time>> circuit;
53
54 if (circuitJson.is_array())
55 {
56 const auto circuitArray = circuitJson.as_array();
57 circuit = ParseCircuitArray(circuitArray);
58 }
59 else if (circuitJson.is_object())
60 {
61 std::string circuitStr;
62 const auto jsonObject = circuitJson.as_object();
63 if (jsonObject.contains("qasm"))
64 {
65 const auto qasmValue = jsonObject.at("qasm");
66 if (qasmValue.is_string())
67 circuitStr = qasmValue.as_string().c_str();
68 }
69 else if (jsonObject.contains("QASM"))
70 {
71 const auto qasmValue = jsonObject.at("QASM");
72 if (qasmValue.is_string())
73 circuitStr = qasmValue.as_string().c_str();
74 }
75 if (circuitStr.empty())
76 return nullptr;
77
78 // QASM 2.0 format
79 qasm::QasmToCirc<> parser;
80 std::string qasmInput(circuitStr);
81 circuit = parser.ParseAndTranslate(qasmInput);
82 }
83 else
84 return nullptr;
85
86 return circuit;
87 }
88
89 static std::string GetConfigString(const std::string &config, const boost::json::value &jsonConfig)
90 {
91 if (jsonConfig.is_object())
92 {
93 const auto jsonObject = jsonConfig.as_object();
94
95 if (jsonObject.contains(config))
96 {
97 const auto configValue = jsonObject.at(config);
98 if (configValue.is_string())
99 return configValue.as_string().c_str();
100 else if (configValue.is_number())
101 {
102 if (configValue.is_int64())
103 return std::to_string(configValue.as_int64());
104 else if (configValue.is_uint64())
105 return std::to_string(configValue.as_uint64());
106 else if (configValue.is_double())
107 return boost::json::serialize(configValue);
108 }
109 else if (configValue.is_bool())
110 {
111 return configValue.as_bool() ? "true" : "false";
112 }
113 }
114 }
115
116 return "";
117 }
118
119 private:
129 std::shared_ptr<Circuits::Circuit<Time>> ParseCircuitArray(const boost::json::array &circuitArray) const
130 {
131 const auto circuit = std::make_shared<Circuits::Circuit<Time>>();
132
133 for (auto operationJson : circuitArray)
134 {
135 if (!operationJson.is_object())
136 throw std::runtime_error("Circuit operation must be an object.");
137
138 auto operationObject = operationJson.as_object();
139 if (!operationObject.contains(nameString))
140 throw std::runtime_error("Circuit operation does not have a type.");
141 else if (!operationObject.at(nameString).is_string())
142 throw std::runtime_error("Circuit operation type must be a string.");
143
144 const boost::json::string type = operationObject.at(nameString).as_string();
145
146 if (type != "id")
147 circuit->AddOperation(ParseOperation(type, operationObject));
148 }
149
150 return circuit;
151 }
152
163 std::shared_ptr<Circuits::IOperation<Time>> ParseOperation(const boost::json::string &type, boost::json::object &obj) const
164 {
165 std::shared_ptr<Circuits::IOperation<Time>> operation;
166
167 if (type == measurementString)
168 operation = ParseMeasurement(obj);
169 else
170 operation = ParseGate(type, obj);
171
172 return operation;
173 }
174
184 std::shared_ptr<Circuits::MeasurementOperation<Time>> ParseMeasurement(boost::json::object &obj) const
185 {
186 const auto qubits = ParseQubits(obj);
187 auto cbits = ParseCbits(obj);
188 if (cbits.empty())
189 cbits = std::vector<size_t>(qubits.begin(), qubits.end());
190
191 if (qubits.size() != cbits.size())
192 throw std::runtime_error("Number of qubits and cbits must be the same.");
193
194 std::vector<std::pair<Types::qubit_t, size_t>> qs;
195
196 for (size_t i = 0; i < qubits.size(); i++)
197 qs.push_back(std::make_pair(qubits[i], cbits[i]));
198
199 const auto operation = std::static_pointer_cast<Circuits::MeasurementOperation<Time>>(Circuits::CircuitFactory<Time>::CreateMeasurement(qs));
200
201 return operation;
202 }
203
213 std::shared_ptr<Circuits::IOperation<Time>> ParseGate(const boost::json::string &type, boost::json::object &obj) const
214 {
215 const std::string gateName = type.c_str();
216
217 const auto qubits = ParseQubits(obj);
218 if (qubits.empty())
219 throw std::runtime_error("No qubits specified.");
220 else if (qubits.size() > 3)
221 throw std::runtime_error("Number of qubits must be 1, 2, or 3.");
222
224
225 if (gatesMap.find(gateName) == gatesMap.end())
226 throw std::runtime_error("Gate type not supported.");
227
228 gateType = gatesMap.at(gateName);
229
230 // parse gate parameters
231 const auto parameters = ParseParameters(obj);
232
233 const auto operation = Circuits::CircuitFactory<Time>::CreateGate(
234 gateType, qubits[0], qubits.size() > 1 ? qubits[1] : 0, qubits.size() > 2 ? qubits[2] : 0, parameters.size() > 0 ? parameters[0] : 0.,
235 parameters.size() > 1 ? parameters[1] : 0., parameters.size() > 2 ? parameters[2] : 0., parameters.size() > 3 ? parameters[3] : 0.);
236
237 // check if the number of qubits is correct for the gate
238
239 if (operation->GetNumQubits() != qubits.size())
240 throw std::runtime_error("The specified number of qubits does not match the number of qubits "
241 "required by the gate type.");
242
243 // TODO: check parameters?
244
245 // check if it's a classically controlled gate
246 if (obj.contains("conditional_reg"))
247 {
248 auto cond = obj.at("conditional_reg");
249 unsigned long long int cbit = 0;
250 if (cond.is_uint64())
251 cbit = cond.as_uint64();
252 else if (cond.is_int64())
253 cbit = static_cast<unsigned long long int>(cond.as_int64());
254 else if (cond.is_array())
255 {
256 auto arr = cond.as_array();
257 if (arr.size() != 1 || !arr[0].is_number())
258 throw std::runtime_error("Conditional register must be a single integer.");
259
260 if (arr[0].is_uint64())
261 cbit = arr[0].as_uint64();
262 else if (arr[0].is_int64())
263 cbit = static_cast<unsigned long long int>(arr[0].as_int64());
264 else
265 throw std::runtime_error("Conditional register must be an integer or "
266 "an array of one integer.");
267 }
268 else
269 throw std::runtime_error("Conditional register must be an integer or "
270 "an array of one integer.");
271
273 }
274
275 return std::static_pointer_cast<Circuits::IOperation<Time>>(operation);
276 }
277
286 std::vector<double> ParseParameters(boost::json::object &obj) const
287 {
288 std::vector<double> parameters;
289
290 if (obj.contains(paramsString) && !obj.at(paramsString).is_array())
291 throw std::runtime_error("Parameters must be an array.");
292
293 auto paramsJson = (obj.contains(paramsString) ? obj.at(paramsString).as_array() : boost::json::array());
294 for (auto param : paramsJson)
295 {
296 if (!param.is_number())
297 throw std::runtime_error("Parameter must be a number.");
298 else if (param.is_double())
299 parameters.push_back(param.as_double());
300 else if (param.is_int64())
301 parameters.push_back(static_cast<double>(param.as_int64()));
302 else
303 parameters.push_back(static_cast<double>(param.as_uint64()));
304 }
305
306 return parameters;
307 }
308
317 std::vector<Types::qubit_t> ParseQubits(const boost::json::object &obj) const
318 {
319 std::vector<Types::qubit_t> qubits;
320
321 if (!obj.contains(qubitsString))
322 throw std::runtime_error("No Qubits specified.");
323 else if (!obj.at(qubitsString).is_array())
324 throw std::runtime_error("Qubits must be an array.");
325 else if (obj.at(qubitsString).as_array().size() == 0)
326 throw std::runtime_error("Qubits array must not be empty.");
327
328 auto qubitsJson = obj.at(qubitsString).as_array();
329 for (auto qubit : qubitsJson)
330 {
331 if (!qubit.is_int64() && !qubit.is_uint64())
332 throw std::runtime_error("Number of qubits must be an integer.");
333 else if (qubit.is_int64())
334 qubits.push_back(qubit.as_int64());
335 else
336 qubits.push_back(qubit.as_uint64());
337 }
338
339 return qubits;
340 }
341
352 std::vector<size_t> ParseCbits(const boost::json::object &obj, bool mustBeSpecified = false) const
353 {
354 std::vector<size_t> cbits;
355
356 if (mustBeSpecified)
357 {
358 if (!obj.contains(memoryString))
359 throw std::runtime_error("No Cbits specified.");
360 else if (!obj.at(memoryString).is_array())
361 throw std::runtime_error("Cbits must be an array.");
362 else if (obj.at(memoryString).as_array().size() == 0)
363 throw std::runtime_error("Cbits array must not be empty.");
364 }
365 else if (obj.contains(memoryString) && !obj.at(memoryString).is_array())
366 throw std::runtime_error("Cbits must be an array.");
367
368 auto cbitsJson = (obj.contains(memoryString) ? obj.at(memoryString).as_array() : boost::json::array());
369 for (auto cbit : cbitsJson)
370 {
371 if (!cbit.is_int64() && !cbit.is_uint64())
372 throw std::runtime_error("Number of cbits must be an integer.");
373 else if (cbit.is_int64())
374 cbits.push_back(cbit.as_int64());
375 else
376 cbits.push_back(cbit.as_uint64());
377 }
378
379 return cbits;
380 }
381
391 static boost::json::value ParseString(const std::string &str, boost::system::error_code &ec)
392 {
393 std::stringstream ss(str);
394 return Read(ss, ec);
395 }
396
406 static boost::json::value Parse(std::istream &is, boost::system::error_code &ec)
407 {
408 return Read(is, ec);
409 }
410
420 static boost::json::value Read(std::istream &is, boost::system::error_code &ec)
421 {
422 boost::json::stream_parser p;
423 std::string line;
424 while (std::getline(is, line))
425 {
426 p.write(line, ec);
427 if (ec)
428 return nullptr;
429 }
430 p.finish(ec);
431 if (ec)
432 return nullptr;
433
434 return p.release();
435 }
436
437 const std::string circuitString = "instructions";
438 const std::string nameString = "name";
439 const std::string measurementString = "measure";
440
441 const std::string qubitsString = "qubits";
442 const std::string paramsString = "params";
443 const std::string memoryString = "clbits";
444
445 const std::unordered_map<std::string, Circuits::QuantumGateType> gatesMap = {
464};
465
466} // namespace Json
467
468#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:40
std::shared_ptr< Circuits::Circuit< Time > > ParseCircuit(const char *str) const
Definition Json.h:48
static std::string GetConfigString(const std::string &config, const boost::json::value &jsonConfig)
Definition Json.h:89
std::shared_ptr< Circuits::Circuit< Time > > ParseAndTranslate(const std::string &qasmInputStr)
Definition QasmCirc.h:46
QuantumGateType
The type of quantum gates.
Definition Json.h:27