1 /*
2 * This file is part of Quantum++.
3 *
4 * Copyright (c) 2013 - 2021 softwareQ Inc. All rights reserved.
5 *
6 * MIT License
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a copy
9 * of this software and associated documentation files (the "Software"), to deal
10 * in the Software without restriction, including without limitation the rights
11 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 * copies of the Software, and to permit persons to whom the Software is
13 * furnished to do so, subject to the following conditions:
14 *
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 * SOFTWARE.
25 */
26
27 /**
28 * \file MATLAB/matlab.hpp
29 * \brief Input/output interfacing with MATLAB
30 */
31
32 #ifndef MATLAB_MATLAB_HPP_
33 #define MATLAB_MATLAB_HPP_
34
35 #include "mat.h"
36 #include "mex.h"
37
38 namespace qpp {
39 /**
40 * \brief Loads a complex Eigen dynamic matrix from a MATLAB .mat file
41 * \see qpp::save_MATLAB()
42 *
43 * The template parameter cannot be automatically deduced and must be explicitly
44 * provided
45 *
46 * Example:
47 * \code
48 * // loads a previously saved Eigen ket
49 * // from the MATLAB file "input.mat"
50 * ket psi = load_MATLAB<ket>("input.mat");
51 * \endcode
52 *
53 * \tparam Derived Complex Eigen type
54 * \param mat_file MATLAB .mat file
55 * \param var_name Variable name in the .mat file representing the matrix to be
56 * loaded
57 * \return Eigen dynamic matrix
58 */
59 template <typename Derived> // complex
60 typename std::enable_if<std::is_same<typename Derived::Scalar, cplx>::value,
61 dyn_mat<cplx>>::type
load_MATLAB(const std::string & mat_file,const std::string & var_name)62 load_MATLAB(const std::string& mat_file, const std::string& var_name) {
63 MATFile* pmat = matOpen(mat_file.c_str(), "r");
64
65 // EXCEPTION CHECKS
66
67 if (!pmat) {
68 throw std::runtime_error(
69 "qpp::load_MATLAB(): Can not open MATLAB file " + mat_file + "!");
70 }
71
72 mxArray* pa = matGetVariable(pmat, var_name.c_str());
73 if (!pa)
74 throw std::runtime_error(
75 "qpp::load_MATLAB(): Can not load the variable " + var_name +
76 " from MATLAB file " + mat_file + "!");
77
78 if (mxGetNumberOfDimensions(pa) != 2) // not a matrix
79 throw std::runtime_error("qpp::load_MATLAB(): Loaded variable " +
80 var_name + " is not 2-dimensional!");
81
82 if (!mxIsDouble(pa))
83 throw std::runtime_error("qpp::load_MATLAB(): Loaded variable " +
84 var_name +
85 " is not in double-precision format!");
86 // END EXCEPTION CHECKS
87
88 // populate the result
89 #if MX_HAS_INTERLEAVED_COMPLEX
90 dyn_mat<cplx> result(mxGetM(pa), mxGetN(pa));
91 std::memcpy((void*) result.data(), (void*) mxGetComplexDoubles(pa),
92 sizeof(cplx) * result.size());
93 mxDestroyArray(pa);
94 matClose(pmat);
95
96 return result;
97 #else
98 dyn_mat<double> result_re(mxGetM(pa), mxGetN(pa));
99 dyn_mat<double> result_im(mxGetM(pa), mxGetN(pa));
100
101 // populate the real part of the created array
102 std::memcpy((void*) result_re.data(), (void*) mxGetPr(pa),
103 sizeof(double) * result_re.size());
104
105 if (mxIsComplex(pa)) // populate the imaginary part if exists
106 std::memcpy((void*) result_im.data(), (void*) mxGetPi(pa),
107 sizeof(double) * result_im.size());
108 else // set to zero the imaginary part
109 std::memset(result_im.data(), 0, sizeof(double) * result_im.size());
110
111 mxDestroyArray(pa);
112 matClose(pmat);
113
114 return (result_re.cast<cplx>()) + 1_i * (result_im.cast<cplx>());
115 #endif // MX_HAS_INTERLEAVED_COMPLEX
116 }
117
118 /**
119 * \brief Loads a non-complex (real, integer etc.) Eigen dynamic matrix from a
120 * MATLAB .mat file
121 * \see qpp::save_MATLAB()
122 *
123 * The template parameter cannot be automatically deduced and must be explicitly
124 * provided
125 *
126 * Example:
127 * \code
128 * // loads a previously saved Eigen dynamic double matrix
129 * // from the MATLAB file "input.mat"
130 * dmat mat = load_MATLAB<dmat>("input.mat");
131 * \endcode
132 *
133 * \tparam Derived Non-complex Eigen type
134 * \param mat_file MATLAB .mat file
135 * \param var_name Variable name in the .mat file representing the matrix to be
136 * loaded
137 * \return Eigen dynamic matrix
138 */
139 template <typename Derived> // real
140 typename std::enable_if<!std::is_same<typename Derived::Scalar, cplx>::value,
141 dyn_mat<typename Derived::Scalar>>::type
load_MATLAB(const std::string & mat_file,const std::string & var_name)142 load_MATLAB(const std::string& mat_file, const std::string& var_name) {
143 MATFile* pmat = matOpen(mat_file.c_str(), "r");
144
145 // EXCEPTION CHECKS
146
147 if (!pmat) {
148 throw std::runtime_error(
149 "qpp::load_MATLAB(): Can not open MATLAB file " + mat_file + "!");
150 }
151
152 mxArray* pa = matGetVariable(pmat, var_name.c_str());
153 if (!pa)
154 throw std::runtime_error(
155 "qpp::load_MATLAB(): Can not load the variable " + var_name +
156 " from MATLAB file " + mat_file + "!");
157
158 if (mxGetNumberOfDimensions(pa) != 2) // not a matrix
159 throw std::runtime_error("qpp::load_MATLAB(): Loaded variable " +
160 var_name + " is not 2-dimensional!");
161
162 if (!mxIsDouble(pa))
163 throw std::runtime_error("qpp::load_MATLAB(): Loaded variable " +
164 var_name +
165 " is not in double-precision format!");
166 // END EXCEPTION CHECKS
167
168 dyn_mat<double> result(mxGetM(pa), mxGetN(pa));
169
170 // populate the result
171 #if MX_HAS_INTERLEAVED_COMPLEX
172 std::memcpy((void*) result.data(), (void*) mxGetDoubles(pa),
173 sizeof(double) * result.size());
174 #else
175 std::memcpy((void*) result.data(), (void*) mxGetPr(pa),
176 sizeof(double) * result.size());
177 #endif // MX_HAS_INTERLEAVED_COMPLEX
178
179 mxDestroyArray(pa);
180 matClose(pmat);
181
182 // cast back to the original type
183 return result.cast<typename Derived::Scalar>();
184 }
185
186 /**
187 * \brief Saves a complex Eigen dynamic matrix to a MATLAB .mat file
188 * \see qpp::load_MATLAB()
189 *
190 * \tparam Complex Eigen type
191 * \param A Eigen expression over the complex field
192 * \param mat_file MATLAB .mat file
193 * \param var_name Variable name in the .mat file representing the matrix to be
194 * saved
195 * \param mode Saving mode (append, overwrite etc.), see MATLAB \a matOpen()
196 * documentation for details
197 */
198 template <typename Derived> // complex
199 typename std::enable_if<
200 std::is_same<typename Derived::Scalar, cplx>::value>::type
save_MATLAB(const Eigen::MatrixBase<Derived> & A,const std::string & mat_file,const std::string & var_name,const std::string & mode)201 save_MATLAB(const Eigen::MatrixBase<Derived>& A, const std::string& mat_file,
202 const std::string& var_name, const std::string& mode) {
203 const dyn_mat<cplx>& rA = A.derived();
204
205 // EXCEPTION CHECKS
206
207 // check zero-size
208 if (!internal::check_nonzero_size(rA))
209 throw exception::ZeroSize("qpp::save_MATLAB()");
210
211 MATFile* pmat = matOpen(mat_file.c_str(), mode.c_str());
212 if (!pmat)
213 throw std::runtime_error(
214 "qpp::save_MATLAB(): Can not open/create MATLAB file " + mat_file +
215 "!");
216 mxArray* pa = mxCreateDoubleMatrix(rA.rows(), rA.cols(), mxCOMPLEX);
217 if (!pa) {
218 throw std::runtime_error(
219 "qpp::save_MATLAB(): mxCreateDoubleMatrix failed!");
220 }
221 // END EXCEPTION CHECKS
222
223 // populate the MATLAB structure
224 #if MX_HAS_INTERLEAVED_COMPLEX
225 std::memcpy((void*) mxGetComplexDoubles(pa), (void*) rA.data(),
226 sizeof(cplx) * rA.size());
227 #else
228 // cast the input to a double (internal MATLAB format)
229 dyn_mat<double> tmp_re = rA.real();
230 dyn_mat<double> tmp_im = rA.imag();
231
232 // populate the real part of the created array
233 std::memcpy((void*) mxGetPr(pa), (void*) tmp_re.data(),
234 sizeof(double) * tmp_re.size());
235
236 // populate the imaginary part of the created array
237 std::memcpy((void*) mxGetPi(pa), (void*) tmp_im.data(),
238 sizeof(double) * tmp_im.size());
239 #endif // MX_HAS_INTERLEAVED_COMPLEX
240
241 // write it as a MATLAB variable
242 if (matPutVariable(pmat, var_name.c_str(), pa))
243 throw std::runtime_error(
244 "qpp::save_MATLAB(): Can not write the variable " + var_name +
245 " to MATLAB file " + mat_file + "!");
246
247 mxDestroyArray(pa);
248 matClose(pmat);
249 }
250
251 /**
252 * \brief Saves a non-complex (real, integer etc.) Eigen dynamic matrix to a
253 * MATLAB .mat file
254 * \see qpp::load_MATLAB()
255 *
256 * \tparam Non-complex Eigen type
257 * \param A Non-complex Eigen expression
258 * \param mat_file MATLAB .mat file
259 * \param var_name Variable name in the .mat file representing the matrix to be
260 * saved
261 * \param mode Saving mode (append, overwrite etc.), see MATLAB \a matOpen()
262 * documentation for details
263 */
264 template <typename Derived> // real
265 typename std::enable_if<
266 !std::is_same<typename Derived::Scalar, cplx>::value>::type
save_MATLAB(const Eigen::MatrixBase<Derived> & A,const std::string & mat_file,const std::string & var_name,const std::string & mode)267 save_MATLAB(const Eigen::MatrixBase<Derived>& A, const std::string& mat_file,
268 const std::string& var_name, const std::string& mode) {
269 // cast to double, since MATLAB does not work with other types
270 const dyn_mat<double>& rA = A.template cast<double>();
271
272 // EXCEPTION CHECKS
273
274 // check zero-size
275 if (!internal::check_nonzero_size(rA))
276 throw exception::ZeroSize("qpp::save_MATLAB()");
277
278 MATFile* pmat = matOpen(mat_file.c_str(), mode.c_str());
279 if (!pmat)
280 throw std::runtime_error(
281 "qpp::save_MATLAB(): Can not open/create MATLAB file " + mat_file +
282 "!");
283 mxArray* pa = mxCreateDoubleMatrix(rA.rows(), rA.cols(), mxREAL);
284 if (!pa) {
285 throw std::runtime_error(
286 "qpp::save_MATLAB(): mxCreateDoubleMatrix failed!");
287 }
288 // END EXCEPTION CHECKS
289
290 // populate the MATLAB structure
291 #if MX_HAS_INTERLEAVED_COMPLEX
292 std::memcpy((void*) mxGetDoubles(pa), (void*) rA.data(),
293 sizeof(double) * rA.size());
294 #else
295 std::memcpy((void*) mxGetPr(pa), (void*) rA.data(),
296 sizeof(double) * rA.size());
297 #endif // MX_HAS_INTERLEAVED_COMPLEX
298
299 // write it as a MATLAB variable
300 if (matPutVariable(pmat, var_name.c_str(), pa))
301 throw std::runtime_error(
302 "qpp::save_MATLAB(): Can not write the variable " + var_name +
303 " to MATLAB file " + mat_file + "!");
304
305 mxDestroyArray(pa);
306 matClose(pmat);
307 }
308
309 } /* namespace qpp */
310 #endif /* MATLAB_MATLAB_HPP_ */
311