diff --git a/mex/build/mjdgges.am b/mex/build/mjdgges.am
index 6a238c9c6..d9e05a2d2 100644
--- a/mex/build/mjdgges.am
+++ b/mex/build/mjdgges.am
@@ -1,9 +1,11 @@
mex_PROGRAMS = mjdgges
-nodist_mjdgges_SOURCES = mjdgges.cc
+nodist_mjdgges_SOURCES = mjdgges.F08 matlab_mex.F08 blas_lapack.F08
BUILT_SOURCES = $(nodist_mjdgges_SOURCES)
CLEANFILES = $(nodist_mjdgges_SOURCES)
-%.cc: $(top_srcdir)/../../sources/mjdgges/%.cc
+mjdgges.o : matlab_mex.mod lapack.mod
+
+%.F08: $(top_srcdir)/../../sources/mjdgges/%.F08
$(LN_S) -f $< $@
diff --git a/mex/sources/blas_lapack.F08 b/mex/sources/blas_lapack.F08
index 440e10513..a93aff118 100644
--- a/mex/sources/blas_lapack.F08
+++ b/mex/sources/blas_lapack.F08
@@ -1,4 +1,4 @@
-! Copyright © 2019 Dynare Team
+! Copyright © 2019-2020 Dynare Team
!
! This file is part of Dynare.
!
@@ -20,8 +20,10 @@ module blas
#if defined(MATLAB_MEX_FILE) && __SIZEOF_POINTER__ == 8
integer, parameter :: blint = int64
+ integer, parameter :: bllog = 8 ! Logical kind, gfortran-specific
#else
integer, parameter :: blint = int32
+ integer, parameter :: bllog = 4 ! Logical kind, gfortran-specific
#endif
interface
@@ -43,10 +45,29 @@ module lapack
subroutine dgesv(n, nrhs, a, lda, ipiv, b, ldb, info)
import :: blint, real64
integer(blint), intent(in) :: n, nrhs, lda, ldb
- real(real64), dimension(*), intent(inout) :: a
- real(real64), dimension(*), intent(inout) :: b
+ real(real64), dimension(*), intent(inout) :: a, b
integer(blint), dimension(*), intent(out) :: ipiv
integer(blint), intent(out) :: info
end subroutine dgesv
end interface
+
+ interface
+ subroutine dgges(jobvsl, jobvsr, sort, selctg, n, a, lda, b, ldb, sdim, &
+ alphar, alphai, beta, vsl, ldvsl, vsr, ldvsr, work, lwork, bwork, &
+ info)
+ import :: blint, bllog, real64
+ character :: jobvsl, jobvsr, sort
+ interface
+ logical(bllog) function selctg(alphar, alphai, beta)
+ import :: bllog, real64
+ real(real64), intent(in) :: alphar, alphai, beta
+ end function selctg
+ end interface
+ integer(blint), intent(in) :: n, lda, ldb, ldvsl, ldvsr, lwork
+ real(real64), dimension(*), intent(inout) :: a, b
+ real(real64), dimension(*), intent(out) :: alphar, alphai, beta, vsl, vsr, work
+ logical(bllog), dimension(*), intent(out) :: bwork
+ integer(blint), intent(out) :: sdim, info
+ end subroutine dgges
+ end interface
end module lapack
diff --git a/mex/sources/defines.F08 b/mex/sources/defines.F08
new file mode 100644
index 000000000..80d607f63
--- /dev/null
+++ b/mex/sources/defines.F08
@@ -0,0 +1 @@
+#define MX_HAS_INTERLEAVED_COMPLEX (defined(MATLAB_MEX_FILE) && MATLAB_VERSION >= 0x0904)
diff --git a/mex/sources/matlab_mex.F08 b/mex/sources/matlab_mex.F08
index 1fbbf8ba9..7a853f710 100644
--- a/mex/sources/matlab_mex.F08
+++ b/mex/sources/matlab_mex.F08
@@ -52,7 +52,7 @@
# define API_VER2 ""
#endif
-#define MX_HAS_INTERLEAVED_COMPLEX (defined(MATLAB_MEX_FILE) && MATLAB_VERSION >= 0x0904)
+#include "defines.F08"
!!! C Matrix API
!!! Listed in same order as https://fr.mathworks.com/help/matlab/cc-mx-matrix-library.html
diff --git a/mex/sources/mjdgges/mjdgges.F08 b/mex/sources/mjdgges/mjdgges.F08
new file mode 100644
index 000000000..dbc5da9d1
--- /dev/null
+++ b/mex/sources/mjdgges/mjdgges.F08
@@ -0,0 +1,167 @@
+! Copyright © 2006-2020 Dynare Team
+!
+! This file is part of Dynare.
+!
+! Dynare is free software: you can redistribute it and/or modify
+! it under the terms of the GNU General Public License as published by
+! the Free Software Foundation, either version 3 of the License, or
+! (at your option) any later version.
+!
+! Dynare is distributed in the hope that it will be useful,
+! but WITHOUT ANY WARRANTY; without even the implied warranty of
+! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+! GNU General Public License for more details.
+!
+! You should have received a copy of the GNU General Public License
+! along with Dynare. If not, see .
+
+#include "defines.F08"
+
+module select_fct_mod
+ use iso_fortran_env
+ implicit none
+
+ real(real64) :: criterium
+contains
+ logical(bllog) function select_fct(alpha_r, alpha_i, beta)
+ use blas
+
+ real(real64), intent(in) :: alpha_r, alpha_i, beta
+
+ select_fct = alpha_r**2 + alpha_i**2 < criterium**2 * beta**2
+ end function select_fct
+end module select_fct_mod
+
+subroutine mexFunction(nlhs, plhs, nrhs, prhs) bind(c, name='mexFunction')
+ use iso_fortran_env
+ use iso_c_binding
+ use select_fct_mod
+ use matlab_mex
+ use lapack
+ implicit none
+
+ type(c_ptr), dimension(*), intent(in), target :: prhs
+ type(c_ptr), dimension(*), intent(out) :: plhs
+ integer(c_int), intent(in), value :: nlhs, nrhs
+
+ integer(c_size_t) :: m1, n1, m2, n2
+ real(real64) :: zhreshold
+ integer(blint) :: n_bl, lwork, info_bl, sdim_bl
+ real(real64), dimension(:), allocatable :: alpha_r, alpha_i, beta, work
+ logical(bllog), dimension(:), allocatable :: bwork
+ real(real64), dimension(:), pointer :: s, t, z, info, sdim, vsl
+#if MX_HAS_INTERLEAVED_COMPLEX
+ complex(real64), dimension(:), pointer :: gev
+#else
+ real(real64), dimension(:), pointer :: gev_r, gev_i
+#endif
+#if defined(MATLAB_MEX_FILE) && MATLAB_VERSION < 0x0904
+ ! Workaround for MKL bug, see below
+ real(real64), dimension(:), allocatable, target :: vsl_target
+#endif
+
+ if (nrhs < 2 .or. nrhs > 4 .or. nlhs /= 7) then
+ call mexErrMsgTxt("MJDGGES: takes 2, 3 or 4 input arguments and exactly 7 output arguments.")
+ return
+ end if
+
+ m1 = mxGetM(prhs(1))
+ n1 = mxGetN(prhs(1))
+ m2 = mxGetM(prhs(2))
+ n2 = mxGetN(prhs(2))
+
+ if (.not. mxIsDouble(prhs(1)) .or. mxIsComplex(prhs(1)) &
+ .or. .not. mxIsDouble(prhs(2)) .or. mxIsComplex(prhs(2)) &
+ .or. m1 /= n1 .or. m2 /= n1 .or. m2 /= n2) then
+ call mexErrMsgTxt("MJDGGES requires two square real matrices of the same dimension.")
+ return
+ end if
+
+ ! Set criterium for stable eigenvalues
+ if (nrhs >= 3 .and. mxGetM(prhs(3)) > 0) then
+ associate (crit_arg => mxGetPr(prhs(3)))
+ criterium = crit_arg(1)
+ end associate
+ else
+ criterium = 1_real64 + 1e-6_real64
+ end if
+
+ ! set criterium for 0/0 generalized eigenvalues */
+ if (nrhs == 4 .and. mxGetM(prhs(4)) > 0) then
+ associate (zhresh_arg => mxGetPr(prhs(4)))
+ zhreshold = zhresh_arg(1)
+ end associate
+ else
+ zhreshold = 1e-6_real64
+ end if
+
+ plhs(2) = mxCreateDoubleMatrix(n1, n1, mxREAL)
+ plhs(3) = mxCreateDoubleMatrix(n1, n1, mxREAL)
+ plhs(4) = mxCreateDoubleMatrix(n1, n1, mxREAL)
+ plhs(5) = mxCreateDoubleMatrix(1_mwSize, 1_mwSize, mxREAL)
+ plhs(6) = mxCreateDoubleMatrix(n1, 1_mwSize, mxCOMPLEX)
+ plhs(7) = mxCreateDoubleMatrix(1_mwSize, 1_mwSize, mxREAL)
+
+ s => mxGetPr(plhs(2))
+ t => mxGetPr(plhs(3))
+ sdim => mxGetPr(plhs(5))
+#if MX_HAS_INTERLEAVED_COMPLEX
+ gev => mxGetComplexDoubles(plhs(6))
+#else
+ gev_r => mxGetPr(plhs(6))
+ gev_i => mxGetPi(plhs(6))
+#endif
+ info => mxGetPr(plhs(7))
+ z => mxGetPr(plhs(4))
+#if defined(MATLAB_MEX_FILE) && MATLAB_VERSION < 0x0904
+ ! The left Schur vectors (VSL) are normally not computed, since JOBVSL="N".
+ ! But old MKL versions (at least the one shipped with MATLAB R2009b/7.9,
+ ! which is MKL 10.1) are buggy, and passing nullptr for VSL leads to a crash.
+ ! Hence we need to allocate space for it.
+ ! The bug seems to be fixed in MATLAB R2010a/7.10 (MKL 10.2), but we use the
+ ! workaround for all versions < R2018a/9.4, since those share the same
+ ! ABI and hence the same executables.
+ allocate(vsl_target(n1*n1))
+ vsl => vsl_target
+#else
+ vsl => null()
+#endif
+
+ ! Copy input matrices, since we can’t modify them
+ associate (a => mxGetPr(prhs(1)), b => mxGetPr(prhs(2)))
+ s = a
+ t = b
+ end associate
+
+ n_bl = int(n1, blint)
+ lwork = 16*n_bl + 16
+ allocate(alpha_r(n_bl), alpha_i(n_bl), beta(n_bl), bwork(n_bl), work(lwork))
+
+ call dgges("N", "V", "S", select_fct, n_bl, s, n_bl, t, n_bl, sdim_bl, &
+ alpha_r, alpha_i, beta, vsl, n_bl, z, n_bl, work, lwork, bwork, info_bl)
+
+ info = info_bl
+ sdim = sdim_bl
+
+#if MX_HAS_INTERLEAVED_COMPLEX
+ where (alpha_i == 0_real64 .and. beta == 0_real64)
+ gev = alpha_r / beta
+ elsewhere
+ gev = cmplx(alpha_r, alpha_i, real64) / beta
+ end where
+#else
+ gev_r = alpha_r / beta
+ where (alpha_i == 0_real64 .and. beta == 0_real64)
+ gev_i = 0_real64
+ elsewhere
+ gev_i = alpha_i / beta
+ end where
+#endif
+
+ ! If the ratio of some eigenvalue is too close to 0/0, return specific
+ ! error number (only if no other error)
+ if (any(abs(alpha_r) <= zhreshold .and. abs(beta) <= zhreshold) .and. info_bl == 0) &
+ info = 30
+
+ plhs(1) = mxCreateDoubleScalar(0._real64)
+end subroutine mexFunction
diff --git a/mex/sources/mjdgges/mjdgges.cc b/mex/sources/mjdgges/mjdgges.cc
deleted file mode 100644
index b3873a90a..000000000
--- a/mex/sources/mjdgges/mjdgges.cc
+++ /dev/null
@@ -1,157 +0,0 @@
-/*
- * Copyright © 2006-2019 Dynare Team
- *
- * This file is part of Dynare.
- *
- * Dynare is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * Dynare is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Dynare. If not, see .
- */
-
-#include
-#include
-#include
-
-#include
-#include
-
-double criterium;
-
-lapack_int
-my_criteria(const double *alpha_r, const double *alpha_i, const double *beta)
-{
- return *alpha_r * *alpha_r + *alpha_i * *alpha_i < criterium * criterium * *beta * *beta;
-}
-
-/* MATLAB interface */
-void
-mexFunction(int nlhs, mxArray *plhs[],
- int nrhs, const mxArray *prhs[])
-{
- /* Check for proper number of arguments */
- if (nrhs < 2 || nrhs > 4 || nlhs == 0 || nlhs > 7)
- DYN_MEX_FUNC_ERR_MSG_TXT("MJDGGES: takes 2, 3 or 4 input arguments and between 1 and 7 output arguments.");
-
- /* Check that A and B are real matrices of the same dimension.*/
- size_t m1 = mxGetM(prhs[0]);
- size_t n1 = mxGetN(prhs[0]);
- size_t m2 = mxGetM(prhs[1]);
- size_t n2 = mxGetN(prhs[1]);
- if (!mxIsDouble(prhs[0]) || mxIsComplex(prhs[0])
- || !mxIsDouble(prhs[1]) || mxIsComplex(prhs[1])
- || m1 != n1 || m2 != n1 || m2 != n2)
- DYN_MEX_FUNC_ERR_MSG_TXT("MJDGGES requires two square real matrices of the same dimension.");
-
- /* Create a matrix for the return argument */
- plhs[1] = mxCreateDoubleMatrix(n1, n1, mxREAL);
- plhs[2] = mxCreateDoubleMatrix(n1, n1, mxREAL);
- plhs[3] = mxCreateDoubleMatrix(n1, n1, mxREAL);
- plhs[4] = mxCreateDoubleMatrix(1, 1, mxREAL);
- plhs[5] = mxCreateDoubleMatrix(n1, 1, mxCOMPLEX);
- plhs[6] = mxCreateDoubleMatrix(1, 1, mxREAL);
-
- /* Assign pointers to the various parameters */
- double *s = mxGetPr(plhs[1]);
- double *t = mxGetPr(plhs[2]);
- double *z = mxGetPr(plhs[3]);
- double *sdim = mxGetPr(plhs[4]);
-#if MX_HAS_INTERLEAVED_COMPLEX
- mxComplexDouble *gev = mxGetComplexDoubles(plhs[5]);
-#else
- double *gev_r = mxGetPr(plhs[5]);
- double *gev_i = mxGetPi(plhs[5]);
-#endif
- double *info = mxGetPr(plhs[6]);
-
- const double *a = mxGetPr(prhs[0]);
- const double *b = mxGetPr(prhs[1]);
-
- /* set criterium for stable eigenvalues */
- if (nrhs >= 3 && mxGetM(prhs[2]) > 0)
- criterium = *mxGetPr(prhs[2]);
- else
- criterium = 1+1e-6;
-
- /* set criterium for 0/0 generalized eigenvalues */
- double zhreshold;
- if (nrhs == 4 && mxGetM(prhs[3]) > 0)
- zhreshold = *mxGetPr(prhs[3]);
- else
- zhreshold = 1e-6;
-
- /* keep a and b intact */
- std::copy_n(a, n1*n1, s);
- std::copy_n(b, n1*n1, t);
-
- lapack_int i_n = static_cast(n1);
- auto alpha_r = std::make_unique(n1);
- auto alpha_i = std::make_unique(n1);
- auto beta = std::make_unique(n1);
- lapack_int lwork = 16*i_n+16;
- auto work = std::make_unique(lwork);
- auto bwork = std::make_unique(i_n);
- lapack_int i_info, i_sdim;
-#if defined(MATLAB_MEX_FILE) && MATLAB_VERSION < 0x0904
- /* The left Schur vectors (VSL) are normally not computed, since JOBVSL="N".
- But old MKL versions (at least the one shipped with MATLAB R2009b/7.9, which
- is MKL 10.1) are
- buggy, and passing nullptr for VSL leads to a crash. Hence we need to
- allocate space for it.
- The bug seems to be fixed in MATLAB R2010a/7.10 (MKL 10.2), but we use the
- workaround for all versions < R2018a/9.4, since those share the same
- ABI and hence the same executables. */
- auto vsl = std::make_unique(n1*n1);
-#endif
-
- dgges("N", "V", "S", my_criteria, &i_n, s, &i_n, t, &i_n, &i_sdim, alpha_r.get(), alpha_i.get(),
- beta.get(),
-#if defined(MATLAB_MEX_FILE) && MATLAB_VERSION < 0x0904
- vsl.get(),
-#else
- nullptr,
-#endif
- &i_n, z, &i_n, work.get(), &lwork, bwork.get(), &i_info);
-
- *sdim = static_cast(i_sdim);
- *info = static_cast(i_info);
-
- for (size_t i = 0; i < n1; i++)
- {
- if (std::abs(alpha_r[i]) > zhreshold || std::abs(beta[i]) > zhreshold)
-#if MX_HAS_INTERLEAVED_COMPLEX
- gev[i].real = alpha_r[i] / beta[i];
-#else
- gev_r[i] = alpha_r[i] / beta[i];
-#endif
- else
- {
- /* the ratio is too close to 0/0;
- returns specific error number only if no other error */
- if (*info == 0)
- *info = -30;
- }
- if (alpha_i[i] == 0.0 && beta[i] == 0.0)
-#if MX_HAS_INTERLEAVED_COMPLEX
- gev[i].imag = 0.0;
-#else
- gev_i[i] = 0.0;
-#endif
- else
-#if MX_HAS_INTERLEAVED_COMPLEX
- gev[i].imag = alpha_i[i] / beta[i];
-#else
- gev_i[i] = alpha_i[i] / beta[i];
-#endif
- }
-
- plhs[0] = mxCreateDoubleScalar(0);
-}