Maestro 0.2.5
Unified interface for quantum circuit simulation
Loading...
Searching...
No Matches
Json.h
Go to the documentation of this file.
1
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
28template <typename Time = Types::time_type>
30 public:
39 static boost::json::value ParseString(const char *str) {
40 boost::system::error_code ec;
41 std::string strs(str);
42 std::stringstream ss(strs);
43 return Read(ss, ec);
44 }
45
46 std::shared_ptr<Circuits::Circuit<Time>> ParseCircuit(const char *str) const {
47 const boost::json::value circuitJson = ParseString(str);
48
49 std::shared_ptr<Circuits::Circuit<Time>> circuit;
50
51 if (circuitJson.is_array()) {
52 const auto circuitArray = circuitJson.as_array();
53 circuit = ParseCircuitArray(circuitArray);
54 } else if (circuitJson.is_object()) {
55 std::string circuitStr;
56 const auto jsonObject = circuitJson.as_object();
57 if (jsonObject.contains("qasm")) {
58 const auto qasmValue = jsonObject.at("qasm");
59 if (qasmValue.is_string()) circuitStr = qasmValue.as_string().c_str();
60 } else if (jsonObject.contains("QASM")) {
61 const auto qasmValue = jsonObject.at("QASM");
62 if (qasmValue.is_string()) circuitStr = qasmValue.as_string().c_str();
63 }
64 if (circuitStr.empty()) return nullptr;
65
66 // QASM 2.0 format
67 qasm::QasmToCirc<> parser;
68 std::string qasmInput(circuitStr);
69 circuit = parser.ParseAndTranslate(qasmInput);
70 } else
71 return nullptr;
72
73 return circuit;
74 }
75
76 static std::string GetConfigString(const std::string &config,
77 const boost::json::value &jsonConfig) {
78 if (jsonConfig.is_object()) {
79 const auto jsonObject = jsonConfig.as_object();
80
81 if (jsonObject.contains(config)) {
82 const auto configValue = jsonObject.at(config);
83 if (configValue.is_string())
84 return configValue.as_string().c_str();
85 else if (configValue.is_number()) {
86 if (configValue.is_int64())
87 return std::to_string(configValue.as_int64());
88 else if (configValue.is_uint64())
89 return std::to_string(configValue.as_uint64());
90 else if (configValue.is_double())
91 return std::to_string(configValue.as_double());
92 } else if (configValue.is_bool()) {
93 return configValue.as_bool() ? "true" : "false";
94 }
95 }
96 }
97
98 return "";
99 }
100
101 private:
111 std::shared_ptr<Circuits::Circuit<Time>> ParseCircuitArray(
112 const boost::json::array &circuitArray) const {
113 const auto circuit = std::make_shared<Circuits::Circuit<Time>>();
114
115 for (auto operationJson : circuitArray) {
116 if (!operationJson.is_object())
117 throw std::runtime_error("Circuit operation must be an object.");
118
119 auto operationObject = operationJson.as_object();
120 if (!operationObject.contains(nameString))
121 throw std::runtime_error("Circuit operation does not have a type.");
122 else if (!operationObject.at(nameString).is_string())
123 throw std::runtime_error("Circuit operation type must be a string.");
124
125 const boost::json::string type =
126 operationObject.at(nameString).as_string();
127
128 if (type != "id")
129 circuit->AddOperation(ParseOperation(type, operationObject));
130 }
131
132 return circuit;
133 }
134
145 std::shared_ptr<Circuits::IOperation<Time>> ParseOperation(
146 const boost::json::string &type, boost::json::object &obj) const {
147 std::shared_ptr<Circuits::IOperation<Time>> operation;
148
149 if (type == measurementString)
150 operation = ParseMeasurement(obj);
151 else
152 operation = ParseGate(type, obj);
153
154 return operation;
155 }
156
166 std::shared_ptr<Circuits::MeasurementOperation<Time>> ParseMeasurement(
167 boost::json::object &obj) const {
168 const auto qubits = ParseQubits(obj);
169 auto cbits = ParseCbits(obj);
170 if (cbits.empty())
171 cbits = std::vector<size_t>(qubits.begin(), qubits.end());
172
173 if (qubits.size() != cbits.size())
174 throw std::runtime_error("Number of qubits and cbits must be the same.");
175
176 std::vector<std::pair<Types::qubit_t, size_t>> qs;
177
178 for (size_t i = 0; i < qubits.size(); i++)
179 qs.push_back(std::make_pair(qubits[i], cbits[i]));
180
181 const auto operation =
182 std::static_pointer_cast<Circuits::MeasurementOperation<Time>>(
184
185 return operation;
186 }
187
197 std::shared_ptr<Circuits::IOperation<Time>> ParseGate(
198 const boost::json::string &type, boost::json::object &obj) const {
199 const std::string gateName = type.c_str();
200
201 const auto qubits = ParseQubits(obj);
202 if (qubits.empty())
203 throw std::runtime_error("No qubits specified.");
204 else if (qubits.size() > 3)
205 throw std::runtime_error("Number of qubits must be 1, 2, or 3.");
206
208
209 if (gatesMap.find(gateName) == gatesMap.end())
210 throw std::runtime_error("Gate type not supported.");
211
212 gateType = gatesMap.at(gateName);
213
214 // parse gate parameters
215 const auto parameters = ParseParameters(obj);
216
217 const auto operation = Circuits::CircuitFactory<Time>::CreateGate(
218 gateType, qubits[0], qubits.size() > 1 ? qubits[1] : 0,
219 qubits.size() > 2 ? qubits[2] : 0,
220 parameters.size() > 0 ? parameters[0] : 0.,
221 parameters.size() > 1 ? parameters[1] : 0.,
222 parameters.size() > 2 ? parameters[2] : 0.,
223 parameters.size() > 3 ? parameters[3] : 0.);
224
225 // check if the number of qubits is correct for the gate
226
227 if (operation->GetNumQubits() != qubits.size())
228 throw std::runtime_error(
229 "The specified number of qubits does not match the number of qubits "
230 "required by the gate type.");
231
232 // TODO: check parameters?
233
234 // check if it's a classically controlled gate
235 if (obj.contains("conditional_reg")) {
236 auto cond = obj.at("conditional_reg");
237 unsigned long long int cbit = 0;
238 if (cond.is_uint64())
239 cbit = cond.as_uint64();
240 else if (cond.is_int64())
241 cbit = static_cast<unsigned long long int>(cond.as_int64());
242 else if (cond.is_array()) {
243 auto arr = cond.as_array();
244 if (arr.size() != 1 || !arr[0].is_number())
245 throw std::runtime_error(
246 "Conditional register must be a single integer.");
247
248 if (arr[0].is_uint64())
249 cbit = arr[0].as_uint64();
250 else if (arr[0].is_int64())
251 cbit = static_cast<unsigned long long int>(arr[0].as_int64());
252 else
253 throw std::runtime_error(
254 "Conditional register must be an integer or "
255 "an array of one integer.");
256 } else
257 throw std::runtime_error(
258 "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> ParseQubits(
308 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) return nullptr;
415 }
416 p.finish(ec);
417 if (ec) return nullptr;
418
419 return p.release();
420 }
421
422 const std::string circuitString = "instructions";
423 const std::string nameString = "name";
424 const std::string measurementString = "measure";
425
426 const std::string qubitsString = "qubits";
427 const std::string paramsString = "params";
428 const std::string memoryString = "clbits";
429
430 const std::unordered_map<std::string, Circuits::QuantumGateType> gatesMap = {
463 {"ccx",
466};
467
468} // namespace Json
469
470#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:39
std::shared_ptr< Circuits::Circuit< Time > > ParseCircuit(const char *str) const
Definition Json.h:46
static std::string GetConfigString(const std::string &config, const boost::json::value &jsonConfig)
Definition Json.h:76
std::shared_ptr< Circuits::Circuit< Time > > ParseAndTranslate(const std::string &qasmInputStr)
Definition QasmCirc.h:30
QuantumGateType
The type of quantum gates.
Definition Json.h:26