1 /**
2  * This code is part of Qiskit.
3  *
4  * (C) Copyright IBM 2018, 2019.
5  *
6  * This code is licensed under the Apache License, Version 2.0. You may
7  * obtain a copy of this license in the LICENSE.txt file in the root directory
8  * of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
9  *
10  * Any modifications or derivative works of this code must retain this
11  * copyright notice, and modified files need to carry a notice indicating
12  * that they have been altered from the originals.
13  */
14 
15 #ifndef _aer_framework_json_hpp_
16 #define _aer_framework_json_hpp_
17 
18 #include <complex>
19 #include <cstdint>
20 #include <fstream>
21 #include <iostream>
22 #include <map>
23 #include <vector>
24 
25 #include <iostream>
26 #include <type_traits>
27 
28 #include <nlohmann_json.hpp>
29 #include "framework/matrix.hpp"
30 
31 namespace nl = nlohmann;
32 using json_t = nlohmann::json;
33 
34 //============================================================================
35 // JSON Library helper functions
36 //============================================================================
37 
38 namespace JSON {
39 
40 /**
41  * Load a json_t from a file. If the file name is 'stdin' or '-' the json_t will
42  * be
43  * loaded from the standard input stream.
44  * @param name: file name to load.
45  * @returns: the loaded json.
46  */
47 json_t load(std::string name);
48 
49 /**
50  * Check if a key exists in a json_t object.
51  * @param key: key name.
52  * @param js: the json_t to search for key.
53  * @returns: true if the key exists, false otherwise.
54  */
55 bool check_key(std::string key, const json_t &js);
56 
57 /**
58  * Check if all keys exists in a json_t object.
59  * @param keys: vector of key names.
60  * @param js: the json_t to search for keys.
61  * @returns: true if all keys exists, false otherwise.
62  */
63 bool check_keys(std::vector<std::string> keys, const json_t &js);
64 
65 /**
66  * Load a json_t object value into a variable if the key name exists.
67  * @param var: variable to store key value.
68  * @param key: key name.
69  * @param js: the json_t to search for key.
70  * @returns: true if the keys exists and val was set, false otherwise.
71  */
72 template <typename T> bool get_value(T &var, std::string key, const json_t &js);
73 
74 } // end namespace JSON
75 
76 //============================================================================
77 // JSON Conversion for complex STL types
78 //============================================================================
79 
80 namespace std {
81 
82 /**
83  * Convert a complex number to a json list z -> [real(z), imag(z)].
84  * @param js a json_t object to contain converted type.
85  * @param z a complex number to convert.
86  */
87 template <typename T> void to_json(json_t &js, const std::complex<T> &z);
88 
89 /**
90  * Convert a JSON value to a complex number z. If the json value is a float
91  * it will be converted to a complex z = (val, 0.). If the json value is a
92  * length two list it will be converted to a complex z = (val[0], val[1]).
93  * @param js a json_t object to convert.
94  * @param z a complex number to contain result.
95  */
96 template <typename T> void from_json(const json_t &js, std::complex<T> &z);
97 
98 /**
99  * Convert a complex vector to a json list
100  * v -> [ [real(v[0]), imag(v[0])], ...]
101  * @param js a json_t object to contain converted type.
102  * @param vec a complex vector to convert.
103  */
104 template <typename RealType>
105 void to_json(json_t &js, const std::vector<std::complex<RealType>> &vec);
106 
107 /**
108  * Convert a JSON list to a complex vector. The input JSON value may be:
109  * - an object with complex pair values: {'00': [re, im], ... }
110  * - an object with real pair values: {'00': n, ... }
111  * - an list with complex values: [ [a0re, a0im], ...]
112  * - an list with real values: [a0, a1, ....]
113  * @param js a json_t object to convert.
114  * @param vec a complex vector to contain result.
115  */
116 template <typename RealType>
117 void from_json(const json_t &js, std::vector<std::complex<RealType>> &vec);
118 
119 /**
120  * Convert a map with integer keys to a json. This converts the integer keys
121  * to strings in the resulting json object.
122  * @param js a json_t object to contain converted type.
123  * @param map a map to convert.
124  */
125 template <typename T1, typename T2>
126 void to_json(json_t &js, const std::map<int64_t, T1, T2> &map);
127 
128 template <typename T1, typename T2>
129 void to_json(json_t &js, const std::map<uint64_t, T1, T2> &map);
130 
131 } // end namespace std.
132 
133 /**
134  * Convert a matrix to a json.
135  * @param js a json_t object to contain converted type.
136  * @param mat a matrix to convert.
137  */
138 template<class T>
139 void from_json(const json_t &js, matrix<T> &mat);
140 template<class T>
141 void to_json(json_t &js, const matrix<T> &mat);
142 
143 /*******************************************************************************
144  *
145  * Implementations
146  *
147  ******************************************************************************/
148 
149 //------------------------------------------------------------------------------
150 // JSON Helper Functions
151 //------------------------------------------------------------------------------
152 
load(std::string name)153 json_t JSON::load(std::string name) {
154   if (name == "") {
155     json_t js;
156     return js; // Return empty node if no config file
157   }
158   json_t js;
159   if (name == "stdin" || name == "-") // Load from stdin
160     std::cin >> js;
161   else { // Load from file
162     std::ifstream ifile;
163     ifile.exceptions(std::ifstream::failbit | std::ifstream::badbit);
164     try {
165       ifile.open(name);
166     } catch (std::exception &e) {
167       throw std::runtime_error(std::string("no such file or directory"));
168     }
169     ifile >> js;
170   }
171   return js;
172 }
173 
check_key(std::string key,const json_t & js)174 bool JSON::check_key(std::string key, const json_t &js) {
175   // returns false if the value is 'null'
176   if (js.find(key) != js.end() && !js[key].is_null())
177     return true;
178   else
179     return false;
180 }
181 
check_keys(std::vector<std::string> keys,const json_t & js)182 bool JSON::check_keys(std::vector<std::string> keys, const json_t &js) {
183   bool pass = true;
184   for (auto s : keys)
185     pass &= check_key(s, js);
186   return pass;
187 }
188 
189 template <typename T>
get_value(T & var,std::string key,const json_t & js)190 bool JSON::get_value(T &var, std::string key, const json_t &js) {
191   if (check_key(key, js)) {
192     var = js[key].get<T>();
193     return true;
194   } else {
195     return false;
196   }
197 }
198 
199 //------------------------------------------------------------------------------
200 // JSON Conversion
201 //------------------------------------------------------------------------------
202 
203 template <typename RealType>
to_json(json_t & js,const std::complex<RealType> & z)204 void std::to_json(json_t &js, const std::complex<RealType> &z) {
205   js = std::pair<RealType, RealType>{z.real(), z.imag()};
206 }
207 
208 template <typename RealType>
from_json(const json_t & js,std::complex<RealType> & z)209 void std::from_json(const json_t &js, std::complex<RealType> &z) {
210   if (js.is_number())
211     z = std::complex<RealType>{js.get<RealType>()};
212   else if (js.is_array() && js.size() == 2) {
213     z = std::complex<RealType>{js[0].get<RealType>(), js[1].get<RealType>()};
214   } else {
215     throw std::invalid_argument(
216         std::string("JSON: invalid complex number"));
217   }
218 }
219 
220 template <typename RealType>
to_json(json_t & js,const std::vector<std::complex<RealType>> & vec)221 void std::to_json(json_t &js, const std::vector<std::complex<RealType>> &vec) {
222   std::vector<std::vector<RealType>> out;
223   for (auto &z : vec) {
224     out.push_back(std::vector<RealType>{real(z), imag(z)});
225   }
226   js = out;
227 }
228 
229 template <typename RealType>
from_json(const json_t & js,std::vector<std::complex<RealType>> & vec)230 void std::from_json(const json_t &js, std::vector<std::complex<RealType>> &vec) {
231   std::vector<std::complex<RealType>> ret;
232   if (js.is_array()) {
233     for (auto &elt : js)
234       ret.push_back(elt);
235     vec = ret;
236   }
237   else {
238     throw std::invalid_argument(
239         std::string("JSON: invalid complex vector."));
240   }
241 }
242 
243 // Int-key maps
244 template <typename T1, typename T2>
to_json(json_t & js,const std::map<int64_t,T1,T2> & map)245 void std::to_json(json_t &js, const std::map<int64_t, T1, T2> &map) {
246   js = json_t();
247   for (const auto &p : map) {
248     std::string key = std::to_string(p.first);
249     js[key] = p.second;
250   }
251 }
252 
253 // Int-key maps
254 template <typename T1, typename T2>
to_json(json_t & js,const std::map<uint64_t,T1,T2> & map)255 void std::to_json(json_t &js, const std::map<uint64_t, T1, T2> &map) {
256   js = json_t();
257   for (const auto &p : map) {
258     std::string key = std::to_string(p.first);
259     js[key] = p.second;
260   }
261 }
262 
263 // Matrices
264 //------------------------------------------------------------------------------
265 // Implementation: JSON Conversion
266 //------------------------------------------------------------------------------
267 
to_json(json_t & js,const matrix<T> & mat)268 template <typename T> void to_json(json_t &js, const matrix<T> &mat) {
269   js = json_t();
270   size_t rows = mat.GetRows();
271   size_t cols = mat.GetColumns();
272   for (size_t r = 0; r < rows; r++) {
273     std::vector<T> mrow;
274     for (size_t c = 0; c < cols; c++)
275       mrow.push_back(mat(r, c));
276     js.push_back(mrow);
277   }
278 }
279 
280 
from_json(const json_t & js,matrix<T> & mat)281 template <typename T> void from_json(const json_t &js, matrix<T> &mat) {
282   // Check JSON is an array
283   if(!js.is_array()) {
284     throw std::invalid_argument(
285         std::string("JSON: invalid matrix (not array)."));
286   }
287   // Check JSON isn't empty
288   if(js.empty()) {
289     throw std::invalid_argument(
290         std::string("JSON: invalid matrix (empty array)."));
291   }
292   // check rows are all same length
293   bool rows_valid = js.is_array() && !js.empty();
294   // Check all entries of array are same size
295   size_t ncols = js[0].size();
296   size_t nrows = js.size();
297   for (auto &row : js)
298     rows_valid &= (row.is_array() && row.size() == ncols);
299   if(!rows_valid) {
300     throw std::invalid_argument(
301         std::string("JSON: invalid matrix (rows different sizes)."));
302   }
303   // Matrix looks ok, now we parse it
304   mat = matrix<T>(nrows, ncols);
305   for (size_t r = 0; r < nrows; r++)
306     for (size_t c = 0; c < ncols; c++)
307       mat(r, c) = js[r][c].get<T>();
308 }
309 
310 //------------------------------------------------------------------------------
311 #endif
312