diff --git a/matlab/disclyap_fast.m b/matlab/missing/mex/disclyap_fast/disclyap_fast.m
similarity index 100%
rename from matlab/disclyap_fast.m
rename to matlab/missing/mex/disclyap_fast/disclyap_fast.m
diff --git a/matlab/pruned_state_space_system.m b/matlab/pruned_state_space_system.m
index d82bfd6777dd8295b46ad8ffd2d76e5ed82d05b2..1f180b9ae575e1f1bcd18cb17d0f9112828b6983 100644
--- a/matlab/pruned_state_space_system.m
+++ b/matlab/pruned_state_space_system.m
@@ -86,7 +86,7 @@ function pruned_state_space = pruned_state_space_system(M, options, dr, indy, nl
 % This function calls
 %   * allVL1.m
 %   * commutation.m
-%   * disclyap_fast.m
+%   * disclyap_fast (MEX)
 %   * duplication.m
 %   * lyapunov_symm.m
 %   * prodmom
diff --git a/mex/build/disclyap_fast.am b/mex/build/disclyap_fast.am
new file mode 100644
index 0000000000000000000000000000000000000000..011d4e0eebdf386c8b8f531492425737d9eeff56
--- /dev/null
+++ b/mex/build/disclyap_fast.am
@@ -0,0 +1,11 @@
+mex_PROGRAMS = disclyap_fast
+
+nodist_disclyap_fast_SOURCES = disclyap_fast.f08 matlab_mex.F08 blas_lapack.F08
+
+BUILT_SOURCES = $(nodist_disclyap_fast_SOURCES)
+CLEANFILES = $(nodist_disclyap_fast_SOURCES)
+
+disclyap_fast.o : matlab_mex.mod lapack.mod
+
+%.f08: $(top_srcdir)/../../sources/disclyap_fast/%.f08
+	$(LN_S) -f $< $@
diff --git a/mex/build/matlab/Makefile.am b/mex/build/matlab/Makefile.am
index 9faaef6c39f68776bda175cc1f11ba9e0d74fb2d..8bad19f68ed65d81ddedc38a3b930dc4a8dca9e7 100644
--- a/mex/build/matlab/Makefile.am
+++ b/mex/build/matlab/Makefile.am
@@ -1,6 +1,6 @@
 ACLOCAL_AMFLAGS = -I ../../../m4
 
-SUBDIRS = mjdgges kronecker bytecode block_kalman_filter sobol perfect_foresight_problem num_procs
+SUBDIRS = mjdgges kronecker bytecode block_kalman_filter sobol perfect_foresight_problem num_procs disclyap_fast
 
 # libdynare++ must come before gensylv, k_order_perturbation, dynare_simul_
 if ENABLE_MEX_DYNAREPLUSPLUS
diff --git a/mex/build/matlab/configure.ac b/mex/build/matlab/configure.ac
index 79115300b7bf61113c2ae43d3ae8130edaeb328b..edde01978281c20398f3b5ef2d1c73ba5f78331b 100644
--- a/mex/build/matlab/configure.ac
+++ b/mex/build/matlab/configure.ac
@@ -172,6 +172,7 @@ AC_CONFIG_FILES([Makefile
 	         sobol/Makefile
 		 local_state_space_iterations/Makefile
                  perfect_foresight_problem/Makefile
-                 num_procs/Makefile])
+                 num_procs/Makefile
+                 disclyap_fast/Makefile])
 
 AC_OUTPUT
diff --git a/mex/build/matlab/disclyap_fast/Makefile.am b/mex/build/matlab/disclyap_fast/Makefile.am
new file mode 100644
index 0000000000000000000000000000000000000000..ddf0a49b5f9a8f467aa3fb361f20037ee3bdc2ab
--- /dev/null
+++ b/mex/build/matlab/disclyap_fast/Makefile.am
@@ -0,0 +1,2 @@
+include ../mex.am
+include ../../disclyap_fast.am
diff --git a/mex/build/octave/Makefile.am b/mex/build/octave/Makefile.am
index 4226cbff3f2f45cb2d681ff767904307b9977daa..c9e1f91569fde8f9dfed65af85326b2cd8005aa0 100644
--- a/mex/build/octave/Makefile.am
+++ b/mex/build/octave/Makefile.am
@@ -1,6 +1,6 @@
 ACLOCAL_AMFLAGS = -I ../../../m4
 
-SUBDIRS = mjdgges kronecker bytecode block_kalman_filter sobol perfect_foresight_problem num_procs
+SUBDIRS = mjdgges kronecker bytecode block_kalman_filter sobol perfect_foresight_problem num_procs disclyap_fast
 
 # libdynare++ must come before gensylv, k_order_perturbation, dynare_simul_
 if ENABLE_MEX_DYNAREPLUSPLUS
diff --git a/mex/build/octave/configure.ac b/mex/build/octave/configure.ac
index 825272c2245e159a8f2da594d914fc8dfdb869be..000857eda685b930a457004d428170d4cb6436a2 100644
--- a/mex/build/octave/configure.ac
+++ b/mex/build/octave/configure.ac
@@ -137,6 +137,7 @@ AC_CONFIG_FILES([Makefile
 		 sobol/Makefile
 		 local_state_space_iterations/Makefile
                  perfect_foresight_problem/Makefile
-                 num_procs/Makefile])
+                 num_procs/Makefile
+                 disclyap_fast/Makefile])
 
 AC_OUTPUT
diff --git a/mex/build/octave/disclyap_fast/Makefile.am b/mex/build/octave/disclyap_fast/Makefile.am
new file mode 100644
index 0000000000000000000000000000000000000000..929f34ace63ebf01030942b1a333801c3ec98ea3
--- /dev/null
+++ b/mex/build/octave/disclyap_fast/Makefile.am
@@ -0,0 +1,3 @@
+EXEEXT = .mex
+include ../mex.am
+include ../../disclyap_fast.am
diff --git a/mex/sources/Makefile.am b/mex/sources/Makefile.am
index 9df8ab4f21a312e15083d913313057193170bdd1..e04b7983fd418b6f0f6e2d5f2dfffc03688e1d2c 100644
--- a/mex/sources/Makefile.am
+++ b/mex/sources/Makefile.am
@@ -18,7 +18,8 @@ EXTRA_DIST = \
 	gensylv \
 	dynare_simul_ \
 	perfect_foresight_problem \
-	num_procs
+	num_procs \
+	disclyap_fast
 
 clean-local:
 	rm -rf `find mex/sources -name *.o`
diff --git a/mex/sources/disclyap_fast/disclyap_fast.f08 b/mex/sources/disclyap_fast/disclyap_fast.f08
new file mode 100644
index 0000000000000000000000000000000000000000..7ed5ae4b35887f300094db84159cf2b41293d613
--- /dev/null
+++ b/mex/sources/disclyap_fast/disclyap_fast.f08
@@ -0,0 +1,161 @@
+! Solve the discrete Lyapunov Equation (X = G·X·Gᵀ + V) using the Doubling Algorithm
+!
+! Syntax:
+!   [X, error_flag] = disclyap_fast(G, V, tol, check_flag)
+!
+! Inputs:
+!   G             [double]    (n×n) first input matrix
+!   V             [double]    (n×n) second input matrix
+!   tol           [double]    scalar, tolerance criterion
+!   check_flag    [boolean]   if true: check positive-definiteness (optional)
+!   max_iter      [integer]   scalar, maximum number of iterations (optional)
+!
+! Outputs:
+!   X             [double]    solution matrix
+!   error_flag    [boolean]   true if solution is found, false otherwise (optional)
+!
+! If check_flag is true, then the code will check if the resulting X
+! is positive definite and generate an error message if it is not.
+!
+! This is a Fortran translation of a code originally written by Joe Pearlman
+! and Alejandro Justiniano.
+
+! Copyright © 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 <http://www.gnu.org/licenses/>.
+
+subroutine mexFunction(nlhs, plhs, nrhs, prhs) bind(c, name='mexFunction')
+  use iso_fortran_env
+  use ieee_arithmetic
+  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) :: n
+  real(real64) :: tol, max_iter
+  logical :: check_flag
+
+  real(real64), dimension(:, :), allocatable :: P0, P1, A0, A1, Ptmp
+  real(real64) :: matd
+  integer :: iter
+  integer (blint) :: n_bl
+
+  real(real64), dimension(:, :), pointer :: X
+
+  if (nlhs < 1 .or. nlhs > 2 .or. nrhs < 3 .or. nrhs > 5) then
+     call mexErrMsgTxt("disclyap_fast: requires between 3 and 5 input arguments, and 1 or 2 output arguments")
+     return
+  end if
+
+  n = mxGetM(prhs(1))
+  if (.not. mxIsDouble(prhs(1)) .or. mxIsComplex(prhs(1)) &
+      .or. .not. mxIsDouble(prhs(2)) .or. mxIsComplex(prhs(2)) &
+      .or. mxGetN(prhs(1)) /= n .or. mxGetM(prhs(2)) /= n .or. mxGetN(prhs(2)) /= n) then
+     call mexErrMsgTxt("disclyap_fast: first two arguments should be real matrices of the same dimension")
+     return
+  end if
+
+  if (.not. (mxIsScalar(prhs(3)) .and. mxIsNumeric(prhs(3)))) then
+     call mexErrMsgTxt("disclyap_fast: third argument (tol) should be a numeric scalar")
+     return
+  end if
+  tol = mxGetScalar(prhs(3))
+
+  if (nrhs >= 4) then
+     if (.not. (mxIsLogicalScalar(prhs(4)))) then
+        call mexErrMsgTxt("disclyap_fast: fourth argument (check_flag) should be a logical scalar")
+        return
+     end if
+     check_flag = mxGetScalar(prhs(4)) == 1_c_double
+  else
+     check_flag = .false.
+  end if
+
+  if (nrhs >= 5) then
+     if (.not. (mxIsScalar(prhs(5)) .and. mxIsNumeric(prhs(5)))) then
+        call mexErrMsgTxt("disclyap_fast: fifth argument (max_iter) should be a numeric scalar")
+        return
+     end if
+     max_iter = int(mxGetScalar(prhs(5)))
+  else
+     max_iter = 2000
+  end if
+
+  ! Allocate and initialize temporary variables
+  allocate(P0(n,n), P1(n,n), A0(n,n), A1(n,n), Ptmp(n,n))
+  associate (G => mxGetPr(prhs(1)), V => mxGetPr(prhs(2)))
+    P0 = reshape(V, [n, n])
+    A0 = reshape(G, [n, n])
+  end associate
+  iter = 1
+
+  n_bl = int(n, blint)
+  do
+     ! We don't use matmul() for the time being because -fuse-external-blas does
+     ! not work as expected under gfortran 8
+
+     ! Ptmp = A0·P0
+     call dgemm("N", "N", n_bl, n_bl, n_bl, 1._real64, A0, n_bl, P0, n_bl, 0._real64, Ptmp, n_bl)
+     ! P1 = P0+Ptmp·A0ᵀ
+     P1 = P0
+     call dgemm("N", "T", n_bl, n_bl, n_bl, 1._real64, Ptmp, n_bl, A0, n_bl, 1._real64, P1, n_bl)
+     ! A1 = A0·A0
+     call dgemm("N", "N", n_bl, n_bl, n_bl, 1._real64, A0, n_bl, A0, n_bl, 0._real64, A1, n_bl)
+
+     matd = maxval(abs(P1-P0))
+
+     P0 = P1
+     A0 = A1
+     iter = iter + 1
+
+     if (matd <= tol .or. iter == max_iter) exit
+  end do
+
+  ! Allocate and set outputs
+  plhs(1) = mxCreateDoubleMatrix(n, n, mxREAL)
+  X(1:n, 1:n) => mxGetPr(plhs(1))
+  if (nlhs > 1) plhs(2) = mxCreateLogicalScalar(.false._mxLogical)
+
+  if (iter == max_iter) then
+     X = ieee_value(X, ieee_quiet_nan)
+     if (nlhs > 1) then
+        call mxDestroyArray(plhs(2))
+        plhs(2) = mxCreateLogicalScalar(.true._mxLogical)
+     end if
+     return
+  end if
+
+  X = (P0+transpose(P0))/2._real64
+
+  ! Check that X is positive definite
+  if (check_flag .and. nlhs > 1) then
+     block
+       real(real64), dimension(n, n) :: X2
+       integer(blint) :: info
+       ! X2=chol(X)
+       X2 = X
+       call dpotrf("L", n_bl, X2, n_bl, info)
+       if (info /= 0) then
+          call mxDestroyArray(plhs(2))
+          plhs(2) = mxCreateLogicalScalar(.true._mxLogical)
+       end if
+   end block
+  end if
+end subroutine mexFunction