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