diff --git a/matlab/default_option_values.m b/matlab/default_option_values.m index 929079add12b208366a19ca4451c5d887a9cb333..10747727c73411cecd61dad7400b136ca05abfca 100644 --- a/matlab/default_option_values.m +++ b/matlab/default_option_values.m @@ -328,6 +328,17 @@ options_.simul.endval_steady = false; options_.simul.first_simulation_period = dates(); options_.simul.last_simulation_period = dates(); +% For stack_solve_algo={2,3} +options_.simul.preconditioner = 'umfiter'; +options_.simul.itertol = 1e-12; +options_.simul.itermaxit = 500; +options_.simul.gmres_restart = 100; +options_.simul.iterstack.maxlu = 20000; % Similar to TROLL’s ITERSTACK +options_.simul.iterstack.relu = 0.5; % Similar to TROLL’s ITERSTACK +options_.simul.ilu.type = 'ilutp'; +options_.simul.ilu.droptol = 1e-12; +options_.simul.ilu.udiag = true; + options_.simul.homotopy_max_completion_share = 1; options_.simul.homotopy_min_step_size = 1e-3; options_.simul.homotopy_step_size_increase_success_count = 3; diff --git a/matlab/optimization/newton_solve.m b/matlab/optimization/newton_solve.m index de1d2e48286ebd95973d632d1d7d3873e7f9a3bb..120bb475db3258258c0f69850e0c01de34f265a2 100644 --- a/matlab/optimization/newton_solve.m +++ b/matlab/optimization/newton_solve.m @@ -23,7 +23,7 @@ function [x, errorflag, errorcode] = newton_solve(func, x, jacobian_flag, gstep, % x: results % errorflag=true: the model can not be solved -% Copyright © 2024 Dynare Team +% Copyright © 2024-2025 Dynare Team % % This file is part of Dynare. % @@ -103,6 +103,7 @@ for it = 1:maxit if solve_algo == 6 p = fjac\fvec; else + % Should be the same options as in solve_one_boundary.m and bytecode/Interpreter.cc (static case) ilu_setup.type = 'ilutp'; ilu_setup.droptol = 1e-10; [L1, U1] = ilu(fjac, ilu_setup); diff --git a/matlab/optimization/solve_one_boundary.m b/matlab/optimization/solve_one_boundary.m index e9177db7d02acb3d508cc5eb9a7ef8d566b17c3d..0bd84cff39d5dc59bb0fc9e6622c8d5d4cfecdfd 100644 --- a/matlab/optimization/solve_one_boundary.m +++ b/matlab/optimization/solve_one_boundary.m @@ -40,7 +40,7 @@ function [y, T, success, max_res, iter] = solve_one_boundary(fh, y, x, params, s % ALGORITHM % Newton with LU or GMRES or BicGstab for dynamic block -% Copyright © 1996-2023 Dynare Team +% Copyright © 1996-2025 Dynare Team % % This file is part of Dynare. % @@ -59,11 +59,8 @@ function [y, T, success, max_res, iter] = solve_one_boundary(fh, y, x, params, s Blck_size=size(y_index_eq,2); correcting_factor=0.01; -ilu_setup.type='crout'; -ilu_setup.droptol=1e-10; max_resa=1e100; lambda = 1; % Length of Newton step -reduced = 0; if is_forward incr = 1; start = y_kmin+1; @@ -141,7 +138,6 @@ for it_=start:incr:finish end elseif lambda>1e-8 lambda=lambda/2; - reduced = 1; if verbose disp(['reducing the path length: lambda=' num2str(lambda,'%f')]) end @@ -192,7 +188,7 @@ for it_=start:incr:finish M_.block_structure.block(Block_Num).g1_sparse_rowval, ... M_.block_structure.block(Block_Num).g1_sparse_colval, ... M_.block_structure.block(Block_Num).g1_sparse_colptr, T(:, it_)); - elseif (is_dynamic && (stack_solve_algo==1 || stack_solve_algo==0 || stack_solve_algo==6)) || (~is_dynamic && options_.solve_algo==6) + elseif (is_dynamic && (stack_solve_algo==1 || stack_solve_algo==0 || stack_solve_algo==6 || (ismember(stack_solve_algo, [2, 3]) && strcmp(options_.simul.preconditioner, 'iterstack') && size(g1, 1) < options_.simul.iterstack.maxlu))) || (~is_dynamic && options_.solve_algo==6) % Fallback to LU if block too small for iterstack if verbose && ~is_dynamic disp('steady: Sparse LU ') end @@ -203,64 +199,45 @@ for it_=start:incr:finish else y(y_index_eq) = ya; end - elseif (stack_solve_algo==2 && is_dynamic) || (options_.solve_algo==7 && ~is_dynamic) - flag1=1; - if verbose && ~is_dynamic - disp('steady: GMRES ') + elseif is_dynamic && ismember(stack_solve_algo, [2 3]) + if strcmp(options_.simul.preconditioner, 'umfiter') && iter == 0 + [L, U, P, Q] = lu(g1); + zc = L\(P*r); + zb = U\zc; + elseif strcmp(options_.simul.preconditioner, 'iterstack') + [L, U, P, Q] = iterstack_preconditioner(g1, options_); + elseif strcmp(options_.simul.preconditioner, 'ilu') + [L, U, P] = ilu(g1, options_.simul.ilu); + Q = speye(size(g1)); end - while flag1>0 - [L1, U1]=ilu(g1,ilu_setup); - [dx,flag1] = gmres(g1,-r,Blck_size,1e-6,Blck_size,L1,U1); - if flag1>0 || reduced - if verbose - if flag1==1 - disp(['Error in simul: No convergence inside GMRES after ' num2str(iter,'%6d') ' iterations, in block' num2str(Block_Num,'%3d')]) - elseif(flag1==2) - disp(['Error in simul: Preconditioner is ill-conditioned, in block' num2str(Block_Num,'%3d')]) - elseif(flag1==3) - disp(['Error in simul: GMRES stagnated (Two consecutive iterates were the same.), in block' num2str(Block_Num,'%3d')]) - end - end - ilu_setup.droptol = ilu_setup.droptol/10; - reduced = 0; + if ~(strcmp(options_.simul.preconditioner, 'umfiter') && iter == 0) + if (stack_solve_algo==2 && is_dynamic) || (options_.solve_algo==7 && ~is_dynamic) + zb = gmres(P*g1*Q, P*r, options_.simul.gmres_restart, options_.simul.itertol, options_.simul.itermaxit, L, U); else - ya = ya + lambda*dx; - if is_dynamic - y(y_index_eq, it_) = ya; - else - y(y_index_eq) = ya'; - end + zb = bicgstab(P*g1*Q, P*r, options_.simul.itertol, options_.simul.itermaxit, L, U); end end - elseif (stack_solve_algo==3 && is_dynamic) || (options_.solve_algo==8 && ~is_dynamic) - flag1=1; - if verbose && ~is_dynamic - disp('steady: BiCGStab') - end - while flag1>0 - [L1, U1]=ilu(g1,ilu_setup); - [dx,flag1] = bicgstab(g1,-r,1e-6,Blck_size,L1,U1); - if flag1>0 || reduced - if verbose - if(flag1==1) - disp(['Error in simul: No convergence inside BiCGStab after ' num2str(iter,'%6d') ' iterations, in block' num2str(Block_Num,'%3d')]) - elseif(flag1==2) - disp(['Error in simul: Preconditioner is ill-conditioned, in block' num2str(Block_Num,'%3d')]) - elseif(flag1==3) - disp(['Error in simul: BiCGStab stagnated (Two consecutive iterates were the same.), in block' num2str(Block_Num,'%3d')]) - end - end - ilu_setup.droptol = ilu_setup.droptol/10; - reduced = 0; + ya = ya - lambda*Q*zb; + y(y_index_eq, it_) = ya; + elseif ~is_dynamic && ismember(options_.solve_algo, [7 8]) + if verbose + if options_.solve_algo == 7 + disp('steady: GMRES ') else - ya = ya + lambda*dx; - if is_dynamic - y(y_index_eq, it_) = ya; - else - y(y_index_eq) = ya'; - end + disp('steady: BiCGStab') end end + %% Should be the same options as in newton_solve.m and bytecode/Interpreter.cc (static case) + ilu_setup.type = 'ilutp'; + ilu_setup.droptol = 1e-10; + [L, U] = ilu(g1, ilu_setup); + if options_.solve_algo == 7 + zb = gmres(g1, r, [], [], [], L, U); + else + zb = bicgstab(g1, r, [], [], L, U); + end + ya = ya - lambda*zb; + y(y_index_eq) = ya; else if is_dynamic error(['options_.stack_solve_algo = ' num2str(stack_solve_algo) ' not implemented']) diff --git a/matlab/perfect-foresight-models/iterstack_preconditioner.m b/matlab/perfect-foresight-models/iterstack_preconditioner.m new file mode 100644 index 0000000000000000000000000000000000000000..dafa60622fd26919c0c231dc37b0e730ab1db2ac --- /dev/null +++ b/matlab/perfect-foresight-models/iterstack_preconditioner.m @@ -0,0 +1,41 @@ +function [L, U, P, Q] = iterstack_preconditioner(A, options_) +% Copyright © 2025 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 <https://www.gnu.org/licenses/>. + +if size(A, 1) < options_.simul.iterstack.maxlu + error('iterstack preconditionner: too small problem') +end +ny = size(A, 1) / options_.periods; +if ny > options_.simul.iterstack.maxlu + error('iterstack preconditionner: too large problem; either increase maxlu or switch to another preconditionner/algorithm') +end +lusize = floor(options_.simul.iterstack.maxlu / ny) * ny; +luidx = floor((floor(size(A, 1) / lusize)-1) * options_.simul.iterstack.relu) * lusize + (1:lusize); +[L1,U1,P1,Q1] = lu(A(luidx,luidx)); +eyek = speye(floor(size(A, 1) / lusize)); +L = kron(eyek, L1); +U = kron(eyek, U1); +P = kron(eyek, P1); +Q = kron(eyek, Q1); +r = rem(size(A, 1), lusize); +if r > 0 % Compute additional smaller LU for remainder, if any + [L2,U2,P2,Q2] = lu(A(end-r+1:end,end-r+1:end)); + L = blkdiag(L, L2); + U = blkdiag(U, U2); + P = blkdiag(P, P2); + Q = blkdiag(Q, Q2); +end diff --git a/matlab/perfect-foresight-models/sim1.m b/matlab/perfect-foresight-models/sim1.m index 974cda2d5ccdabf650ba0a9384e0b187b6383db6..df86221031d926f368d566c83d02e97f3fb9c1ad 100644 --- a/matlab/perfect-foresight-models/sim1.m +++ b/matlab/perfect-foresight-models/sim1.m @@ -82,6 +82,7 @@ end h1 = clock; iter = 1; converged = false; +umfiter_precond = []; while ~(converged || iter > options_.simul.maxit) h2 = clock; @@ -148,7 +149,8 @@ while ~(converged || iter > options_.simul.maxit) if options_.simul.robust_lin_solve dy = -lin_solve_robust(A, res, verbose, options_); else - dy = -lin_solve(A, res, verbose, options_); + [mdy, umfiter_precond] = lin_solve(A, res, verbose, options_, umfiter_precond); + dy = -mdy; end if any(isnan(dy)) || any(isinf(dy)) if verbose @@ -217,7 +219,8 @@ if verbose skipline(); end -function x = lin_solve(A, b, verbose, options_) +function [x, umfiter_precond] = lin_solve(A, b, verbose, options_, umfiter_precond) + if norm(b) < sqrt(eps) % then x = 0 is a solution x = 0; return @@ -226,16 +229,37 @@ end if options_.stack_solve_algo == 0 x = A\b; else - ilu_setup.type = 'ilutp'; - ilu_setup.droptol = 1e-10; - [L1, U1] = ilu(A, ilu_setup); - if options_.stack_solve_algo == 2 - x = gmres(A, b, [], [], [], L1, U1); - elseif options_.stack_solve_algo == 3 - x = bicgstab(A, b, [], [], L1, U1); + if strcmp(options_.simul.preconditioner, 'umfiter') + if isempty(umfiter_precond) + [L, U, P, Q] = lu(A); + else + L = umfiter_precond.L; + U = umfiter_precond.U; + P = umfiter_precond.P; + Q = umfiter_precond.Q; + end + elseif strcmp(options_.simul.preconditioner, 'iterstack') + [L, U, P, Q] = iterstack_preconditioner(A, options_); + elseif strcmp(options_.simul.preconditioner, 'ilu') + [L, U, P] = ilu(A, options_.simul.ilu); + Q = speye(size(A)); + end + + if strcmp(options_.simul.preconditioner, 'umfiter') && isempty(umfiter_precond) + z = L\(P*b); + y = U\z; + umfiter_precond = struct('L', L, 'U', U, 'P', P, 'Q', Q); else - error('sim1: invalid value for options_.stack_solve_algo') + if options_.stack_solve_algo == 2 + y = gmres(P*A*Q, P*b, options_.simul.gmres_restart, options_.simul.itertol, options_.simul.itermaxit, L, U); + elseif options_.stack_solve_algo == 3 + y = bicgstab(P*A*Q, P*b, options_.simul.itertol, options_.simul.itermaxit, L, U); + else + error('sim1: invalid value for options_.stack_solve_algo') + end end + + x = Q*y; end x(~isfinite(x)) = 0; relres = norm(b - A*x) / norm(b); diff --git a/matlab/perfect-foresight-models/solve_block_decomposed_problem.m b/matlab/perfect-foresight-models/solve_block_decomposed_problem.m index 14db6dee693ee24108b535248d5c5b5147395361..16840c89f426b893d1640c8b65e5c7e85f87785f 100644 --- a/matlab/perfect-foresight-models/solve_block_decomposed_problem.m +++ b/matlab/perfect-foresight-models/solve_block_decomposed_problem.m @@ -40,9 +40,9 @@ switch options_.stack_solve_algo case {1,6} mthd='LBJ with LU solver'; case 2 - mthd='GMRES on stacked system'; + mthd=sprintf('GMRES on stacked system with ''%s'' preconditioner', options_.simul.preconditioner); case 3 - mthd='BiCGStab on stacked system'; + mthd=sprintf('BiCGStab on stacked system with ''%s'' preconditioner', options_.simul.preconditioner); case 4 mthd='Sparse LU solver with optimal path length on stacked system'; case 7 diff --git a/matlab/perfect-foresight-models/solve_two_boundaries_stacked.m b/matlab/perfect-foresight-models/solve_two_boundaries_stacked.m index e3934e2579cefe5092d4b7f6f0bf59559cf56302..67a36b559ad0e67f55874348199c8d94b7016fa2 100644 --- a/matlab/perfect-foresight-models/solve_two_boundaries_stacked.m +++ b/matlab/perfect-foresight-models/solve_two_boundaries_stacked.m @@ -25,7 +25,7 @@ function [y, T, success, max_res, iter] = solve_two_boundaries_stacked(fh, y, x, % ALGORITHM % Newton with LU or GMRES or BiCGStab -% Copyright © 1996-2024 Dynare Team +% Copyright © 1996-2025 Dynare Team % % This file is part of Dynare. % @@ -57,11 +57,8 @@ verbose = options_.verbosity; cvg=false; iter=0; correcting_factor=0.01; -ilu_setup.droptol=1e-10; -ilu_setup.type = 'ilutp'; max_resa=1e100; lambda = 1; % Length of Newton step (unused for stack_solve_algo=4) -reduced = 0; while ~(cvg || iter > options_.simul.maxit) r = NaN(Blck_size, periods); g1a = spalloc(Blck_size*periods, Blck_size*periods, M_.block_structure.block(Block_Num).NNZDerivatives*periods); @@ -81,7 +78,6 @@ while ~(cvg || iter > options_.simul.maxit) g1a((it_-y_kmin-1)*Blck_size+(1:Blck_size), (it_-y_kmin-2)*Blck_size+(1:3*Blck_size)) = g1; end end - preconditioner = 2; ya = reshape(y(y_index, y_kmin+(1:periods)), 1, periods*Blck_size)'; ra = reshape(r, periods*Blck_size, 1); b=-ra+g1a*ya; @@ -98,7 +94,7 @@ while ~(cvg || iter > options_.simul.maxit) end if ~cvg if iter>0 - if ~isreal(max_res) || isnan(max_res) || (max_resa<max_res && iter>1) + if ~isreal(max_res) || isnan(max_res) %|| (max_resa<max_res && iter>1) if verbose && ~isreal(max_res) disp(['Variable ' M_.endo_names{max_indx} ' (' int2str(max_indx) ') returns an undefined value']); end @@ -128,7 +124,6 @@ while ~(cvg || iter > options_.simul.maxit) end elseif lambda>1e-8 && stack_solve_algo ~= 4 lambda=lambda/2; - reduced = 1; if verbose disp(['reducing the path length: lambda=' num2str(lambda,'%f')]); end @@ -155,102 +150,31 @@ while ~(cvg || iter > options_.simul.maxit) g1aa=g1a; ba=b; max_resa=max_res; - if stack_solve_algo==0 + if stack_solve_algo==0 || (ismember(stack_solve_algo, [2, 3]) && strcmp(options_.simul.preconditioner, 'iterstack') && size(g1a, 1) < options_.simul.iterstack.maxlu) % Fallback to LU if block too small for iterstack dx = g1a\b- ya; ya = ya + lambda*dx; y(y_index, y_kmin+(1:periods))=reshape(ya',length(y_index),periods); - elseif stack_solve_algo==2 - flag1=1; - while flag1>0 - if preconditioner==2 - [L1, U1]=ilu(g1a,ilu_setup); - elseif preconditioner==3 - Size = Blck_size; - gss1 = g1a(Size + 1: 2*Size,Size + 1: 2*Size) + g1a(Size + 1: 2*Size,2*Size+1: 3*Size); - [L1, U1]=lu(gss1); - L(1:Size,1:Size) = L1; - U(1:Size,1:Size) = U1; - gss2 = g1a(Size + 1: 2*Size,1: Size) + g1a(Size + 1: 2*Size,Size+1: 2*Size) + g1a(Size + 1: 2*Size,2*Size+1: 3*Size); - [L2, U2]=lu(gss2); - L(Size+1:(periods-1)*Size,Size+1:(periods-1)*Size) = kron(eye(periods-2), L2); - U(Size+1:(periods-1)*Size,Size+1:(periods-1)*Size) = kron(eye(periods-2), U2); - gss2 = g1a(Size + 1: 2*Size,1: Size) + g1a(Size + 1: 2*Size,Size+1: 2*Size); - [L3, U3]=lu(gss2); - L((periods-1)*Size+1:periods*Size,(periods-1)*Size+1:periods*Size) = L3; - U((periods-1)*Size+1:periods*Size,(periods-1)*Size+1:periods*Size) = U3; - L1 = L; - U1 = U; - elseif preconditioner==4 - Size = Blck_size; - gss1 = g1a(1: 3*Size, 1: 3*Size); - [L, U] = lu(gss1); - L1 = kron(eye(ceil(periods/3)),L); - U1 = kron(eye(ceil(periods/3)),U); - L1 = L1(1:periods * Size, 1:periods * Size); - U1 = U1(1:periods * Size, 1:periods * Size); - end - [za,flag1] = gmres(g1a,b,Blck_size,1e-6,Blck_size*periods,L1,U1); - if (flag1>0 || reduced) - if verbose - if flag1==1 - disp(['Error in simul: No convergence inside GMRES after ' num2str(periods*10,'%6d') ' iterations, in block ' num2str(Blck_size,'%3d')]); - elseif flag1==2 - disp(['Error in simul: Preconditioner is ill-conditioned, in block ' num2str(Blck_size,'%3d')]); - elseif flag1==3 - disp(['Error in simul: GMRES stagnated (Two consecutive iterates were the same.), in block ' num2str(Blck_size,'%3d')]); - end - end - ilu_setup.droptol = ilu_setup.droptol/10; - reduced = 0; - else - dx = za - ya; - ya = ya + lambda*dx; - y(y_index, y_kmin+(1:periods))=reshape(ya',length(y_index),periods); - end + elseif ismember(stack_solve_algo, [2, 3]) + if strcmp(options_.simul.preconditioner, 'umfiter') && iter == 0 + [L, U, P, Q] = lu(g1a); + zc = L\(P*b); + zb = U\zc; + elseif strcmp(options_.simul.preconditioner, 'iterstack') + [L, U, P, Q] = iterstack_preconditioner(g1a, options_); + elseif strcmp(options_.simul.preconditioner, 'ilu') + [L, U, P] = ilu(g1a, options_.simul.ilu); + Q = speye(size(g1a)); end - elseif stack_solve_algo==3 - flag1=1; - while flag1>0 - if preconditioner==2 - [L1, U1]=ilu(g1a,ilu_setup); - [za,flag1] = bicgstab(g1a,b,1e-7,Blck_size*periods,L1,U1); - elseif preconditioner==3 - Size = Blck_size; - gss0 = g1a(Size + 1: 2*Size,1: Size) + g1a(Size + 1: 2*Size,Size+1: 2*Size) + g1a(Size + 1: 2*Size,2*Size+1: 3*Size); - [L1, U1]=lu(gss0); - P1 = eye(size(gss0)); - Q1 = eye(size(gss0)); - L = kron(eye(periods),L1); - U = kron(eye(periods),U1); - P = kron(eye(periods),P1); - Q = kron(eye(periods),Q1); - [za,flag1] = bicgstab1(g1a,b,1e-7,Blck_size*periods,L,U, P, Q); + if ~(strcmp(options_.simul.preconditioner, 'umfiter') && iter == 0) + if stack_solve_algo == 2 + zb = gmres(P*g1a*Q, P*b, options_.simul.gmres_restart, options_.simul.itertol, options_.simul.itermaxit, L, U); else - Size = Blck_size; - gss0 = g1a(Size + 1: 2*Size,1: Size) + g1a(Size + 1: 2*Size,Size+1: 2*Size) + g1a(Size + 1: 2*Size,2*Size+1: 3*Size); - [L1, U1]=lu(gss0); - L1 = kron(eye(periods),L1); - U1 = kron(eye(periods),U1); - [za,flag1] = bicgstab(g1a,b,1e-7,Blck_size*periods,L1,U1); - end - if flag1>0 || reduced - if verbose - if flag1==1 - disp(['Error in simul: No convergence inside BICGSTAB after ' num2str(periods*10,'%6d') ' iterations, in block ' num2str(Blck_size,'%3d')]); - elseif flag1==2 - disp(['Error in simul: Preconditioner is ill-conditioned, in block ' num2str(Blck_size,'%3d')]); - elseif flag1==3 - disp(['Error in simul: GMRES stagnated (Two consecutive iterates were the same.), in block ' num2str(Blck_size,'%3d')]); - end - end - ilu_setup.droptol = ilu_setup.droptol/10; - reduced = 0; - else - dx = za - ya; - ya = ya + lambda*dx; - y(y_index, y_kmin+(1:periods))=reshape(ya',length(y_index),periods); + zb = bicgstab(P*g1a*Q, P*b, options_.simul.itertol, options_.simul.itermaxit, L, U); end end + dx = Q*zb - ya; + ya = ya + lambda*dx; + y(y_index, y_kmin+(1:periods))=reshape(ya',length(y_index),periods); elseif stack_solve_algo==4 stpmx = 100 ; stpmax = stpmx*max([sqrt(ya'*ya);size(y_index,2)]); diff --git a/mex/sources/bytecode/Interpreter.cc b/mex/sources/bytecode/Interpreter.cc index c7d318d94c3fdb7ef911a75c3ae1feff9f1a68ed..cf7b609dafeb4198f5eb22992b78408cc99525d2 100644 --- a/mex/sources/bytecode/Interpreter.cc +++ b/mex/sources/bytecode/Interpreter.cc @@ -30,6 +30,50 @@ #include "Interpreter.hh" +void +iter_solver_opts_t::set_static_defaults() +{ + // Should be the same options as in newton_solve.m and solve_one_boundary.m (static case) + itertol = mxCreateDoubleMatrix(0, 0, mxREAL); + itermaxit = mxCreateDoubleMatrix(0, 0, mxREAL); + gmres_restart = mxCreateDoubleMatrix(0, 0, mxREAL); + + std::array field_names {"droptol", "type"}; + std::array dims {static_cast<mwSize>(1)}; + ilu = mxCreateStructArray(dims.size(), dims.data(), field_names.size(), field_names.data()); + mxSetFieldByNumber(ilu, 0, 0, mxCreateDoubleScalar(1e-10)); + mxSetFieldByNumber(ilu, 0, 1, mxCreateString("ilutp")); +} + +void +iter_solver_opts_t::set_dynamic_values(const mxArray* options_) +{ + int field {mxGetFieldNumber(options_, "simul")}; + if (field < 0) + mexErrMsgTxt("simul is not a field of options_"); + mxArray* simul {mxGetFieldByNumber(options_, 0, field)}; + + field = mxGetFieldNumber(simul, "itertol"); + if (field < 0) + mexErrMsgTxt("itertol is not a field of options_.simul"); + itertol = mxGetFieldByNumber(simul, 0, field); + + field = mxGetFieldNumber(simul, "itermaxit"); + if (field < 0) + mexErrMsgTxt("itermaxit is not a field of options_.simul"); + itermaxit = mxGetFieldByNumber(simul, 0, field); + + field = mxGetFieldNumber(simul, "gmres_restart"); + if (field < 0) + mexErrMsgTxt("gmres_restart is not a field of options_.simul"); + gmres_restart = mxGetFieldByNumber(simul, 0, field); + + field = mxGetFieldNumber(simul, "ilu"); + if (field < 0) + mexErrMsgTxt("ilu is not a field of options_.simul"); + ilu = mxGetFieldByNumber(simul, 0, field); +} + Interpreter::Interpreter(Evaluate& evaluator_arg, double* params_arg, double* y_arg, double* ya_arg, double* x_arg, double* steady_y_arg, double* direction_arg, int y_size_arg, int nb_row_x_arg, int periods_arg, int y_kmin_arg, int y_kmax_arg, @@ -38,11 +82,13 @@ Interpreter::Interpreter(Evaluate& evaluator_arg, double* params_arg, double* y_ int solve_algo_arg, bool print_arg, const mxArray* GlobalTemporaryTerms_arg, bool steady_state_arg, bool block_decomposed_arg, int col_x_arg, int col_y_arg, - const BasicSymbolTable& symbol_table_arg, int verbosity_arg) : + const BasicSymbolTable& symbol_table_arg, int verbosity_arg, + iter_solver_opts_t iter_solver_opts_arg) : symbol_table {symbol_table_arg}, steady_state {steady_state_arg}, block_decomposed {block_decomposed_arg}, evaluator {evaluator_arg}, + iter_solver_opts {iter_solver_opts_arg}, minimal_solving_periods {minimal_solving_periods_arg}, y_size {y_size_arg}, y_kmin {y_kmin_arg}, @@ -60,7 +106,6 @@ Interpreter::Interpreter(Evaluate& evaluator_arg, double* params_arg, double* y_ start_compare = 0; restart = 0; IM_i.clear(); - lu_inc_tol = 1e-10; Symbolic = nullptr; Numeric = nullptr; params = params_arg; @@ -2260,65 +2305,6 @@ Interpreter::simple_bksub() } } -mxArray* -Interpreter::mult_SAT_B(const mxArray* A_m, const mxArray* B_m) -{ - size_t n_A = mxGetN(A_m); - mwIndex* A_i = mxGetIr(A_m); - mwIndex* A_j = mxGetJc(A_m); - double* A_d = mxGetPr(A_m); - size_t n_B = mxGetN(B_m); - double* B_d = mxGetPr(B_m); - mxArray* C_m = mxCreateDoubleMatrix(n_A, n_B, mxREAL); - double* C_d = mxGetPr(C_m); - for (int j = 0; j < static_cast<int>(n_B); j++) - for (unsigned int i = 0; i < n_A; i++) - { - double sum = 0; - size_t nze_A = A_j[i]; - while (nze_A < static_cast<unsigned int>(A_j[i + 1])) - { - size_t i_A = A_i[nze_A]; - sum += A_d[nze_A++] * B_d[i_A]; - } - C_d[j * n_A + i] = sum; - } - return C_m; -} - -mxArray* -Interpreter::Sparse_transpose(const mxArray* A_m) -{ - size_t n_A = mxGetN(A_m); - size_t m_A = mxGetM(A_m); - mwIndex* A_i = mxGetIr(A_m); - mwIndex* A_j = mxGetJc(A_m); - size_t total_nze_A = A_j[n_A]; - double* A_d = mxGetPr(A_m); - mxArray* C_m = mxCreateSparse(n_A, m_A, total_nze_A, mxREAL); - mwIndex* C_i = mxGetIr(C_m); - mwIndex* C_j = mxGetJc(C_m); - double* C_d = mxGetPr(C_m); - unsigned int nze_C = 0, nze_A = 0; - ranges::fill_n(C_j, m_A + 1, 0); - map<pair<mwIndex, unsigned int>, double> B2; - for (unsigned int i = 0; i < n_A; i++) - while (nze_A < static_cast<unsigned int>(A_j[i + 1])) - { - C_j[A_i[nze_A] + 1]++; - B2[{A_i[nze_A], i}] = A_d[nze_A]; - nze_A++; - } - for (unsigned int i = 0; i < m_A; i++) - C_j[i + 1] += C_j[i]; - for (auto& [key, val] : B2) - { - C_d[nze_C] = val; - C_i[nze_C++] = key.second; - } - return C_m; -} - void Interpreter::compute_block_time(int my_Per_u_, bool evaluate, bool no_derivatives) { @@ -2750,24 +2736,19 @@ void Interpreter::Solve_Matlab_GMRES(mxArray* A_m, mxArray* b_m, bool is_two_boundaries, mxArray* x0_m) { size_t n = mxGetM(A_m); - std::array field_names {"droptol", "type"}; - std::array dims {static_cast<mwSize>(1)}; - mxArray* Setup - = mxCreateStructArray(dims.size(), dims.data(), field_names.size(), field_names.data()); - mxSetFieldByNumber(Setup, 0, 0, mxCreateDoubleScalar(lu_inc_tol)); - mxSetFieldByNumber(Setup, 0, 1, mxCreateString("ilutp")); + std::array<mxArray*, 2> lhs0; - std::array rhs0 {A_m, Setup}; + std::array rhs0 {A_m, iter_solver_opts.ilu}; if (mexCallMATLAB(lhs0.size(), lhs0.data(), rhs0.size(), rhs0.data(), "ilu")) throw FatalException("In GMRES, the incomplete LU decomposition (ilu) has failed"); mxArray* L1 = lhs0[0]; mxArray* U1 = lhs0[1]; - /*[za,flag1] = gmres(g1a,b,Blck_size,1e-6,Blck_size*periods,L1,U1);*/ + std::array rhs {A_m, b_m, - mxCreateDoubleScalar(size), - mxCreateDoubleScalar(1e-6), - mxCreateDoubleScalar(static_cast<double>(n)), + iter_solver_opts.gmres_restart, + iter_solver_opts.itertol, + iter_solver_opts.itermaxit, L1, U1, x0_m}; @@ -2776,28 +2757,24 @@ Interpreter::Solve_Matlab_GMRES(mxArray* A_m, mxArray* b_m, bool is_two_boundari mxArray* z = lhs[0]; mxArray* flag = lhs[1]; double flag1 {mxGetScalar(flag)}; - mxDestroyArray(rhs0[1]); - mxDestroyArray(rhs[2]); - mxDestroyArray(rhs[3]); - mxDestroyArray(rhs[4]); mxDestroyArray(rhs[5]); mxDestroyArray(rhs[6]); + if (flag1 > 0) { if (flag1 == 1) - mexWarnMsgTxt( + mexErrMsgTxt( ("Error in bytecode: No convergence inside GMRES, in block " + to_string(block_num + 1)) .c_str()); else if (flag1 == 2) - mexWarnMsgTxt(("Error in bytecode: Preconditioner is ill-conditioned, in block " - + to_string(block_num + 1)) - .c_str()); + mexErrMsgTxt(("Error in bytecode: Preconditioner is ill-conditioned, in block " + + to_string(block_num + 1)) + .c_str()); else if (flag1 == 3) - mexWarnMsgTxt(("Error in bytecode: GMRES stagnated (Two consecutive iterates were the " - "same.), in block " - + to_string(block_num + 1)) - .c_str()); - lu_inc_tol /= 10; + mexErrMsgTxt(("Error in bytecode: GMRES stagnated (Two consecutive iterates were the " + "same.), in block " + + to_string(block_num + 1)) + .c_str()); } else { @@ -2819,6 +2796,7 @@ Interpreter::Solve_Matlab_GMRES(mxArray* A_m, mxArray* b_m, bool is_two_boundari y[eq + it_ * y_size] += slowc * yy; } } + mxDestroyArray(A_m); mxDestroyArray(b_m); mxDestroyArray(x0_m); @@ -2828,143 +2806,42 @@ Interpreter::Solve_Matlab_GMRES(mxArray* A_m, mxArray* b_m, bool is_two_boundari void Interpreter::Solve_Matlab_BiCGStab(mxArray* A_m, mxArray* b_m, bool is_two_boundaries, - mxArray* x0_m, int preconditioner) + mxArray* x0_m) { - /* precond = 0 => Jacobi - precond = 1 => Incomplet LU decomposition*/ size_t n = mxGetM(A_m); - mxArray *L1 = nullptr, *U1 = nullptr, *Diag = nullptr; - if (preconditioner == 0) - { - std::array<mxArray*, 1> lhs0; - std::array rhs0 {A_m, mxCreateDoubleScalar(0)}; - mexCallMATLAB(lhs0.size(), lhs0.data(), rhs0.size(), rhs0.data(), "spdiags"); - mxArray* tmp = lhs0[0]; - double* tmp_val = mxGetPr(tmp); - Diag = mxCreateSparse(n, n, n, mxREAL); - mwIndex* Diag_i = mxGetIr(Diag); - mwIndex* Diag_j = mxGetJc(Diag); - double* Diag_val = mxGetPr(Diag); - for (size_t i = 0; i < n; i++) - { - Diag_val[i] = tmp_val[i]; - Diag_j[i] = i; - Diag_i[i] = i; - } - Diag_j[n] = n; - } - else if (preconditioner == 1) - { - /*[L1, U1] = ilu(g1a=;*/ - std::array field_names {"type", "droptol"}; - const int type {0}, droptol {1}; - std::array dims {static_cast<mwSize>(1)}; - mxArray* Setup - = mxCreateStructArray(dims.size(), dims.data(), field_names.size(), field_names.data()); - mxSetFieldByNumber(Setup, 0, type, mxCreateString("ilutp")); - mxSetFieldByNumber(Setup, 0, droptol, mxCreateDoubleScalar(lu_inc_tol)); - std::array<mxArray*, 2> lhs0; - std::array rhs0 {A_m, Setup}; - if (mexCallMATLAB(lhs0.size(), lhs0.data(), rhs0.size(), rhs0.data(), "ilu")) - throw FatalException {"In BiCGStab, the incomplete LU decomposition (ilu) has failed"}; - L1 = lhs0[0]; - U1 = lhs0[1]; - mxDestroyArray(Setup); - } - double flags = 2; - mxArray* z = nullptr; - if (steady_state) /*Octave BicStab algorihtm involves a 0 division in case of a preconditionner - equal to the LU decomposition of A matrix*/ - { - mxArray* res = mult_SAT_B(Sparse_transpose(A_m), x0_m); - double* resid = mxGetPr(res); - double* b = mxGetPr(b_m); - for (int i = 0; i < static_cast<int>(n); i++) - resid[i] = b[i] - resid[i]; - std::array<mxArray*, 1> lhs; - std::array rhs {L1, res}; - mexCallMATLAB(lhs.size(), lhs.data(), rhs.size(), rhs.data(), "mldivide"); - std::array rhs2 {U1, lhs[0]}; - mexCallMATLAB(lhs.size(), lhs.data(), rhs2.size(), rhs2.data(), "mldivide"); - z = lhs[0]; - double* phat = mxGetPr(z); - double* x0 = mxGetPr(x0_m); - for (int i = 0; i < static_cast<int>(n); i++) - phat[i] = x0[i] + phat[i]; - - /*Check the solution*/ - res = mult_SAT_B(Sparse_transpose(A_m), z); - resid = mxGetPr(res); - double cum_abs = 0; - for (int i = 0; i < static_cast<int>(n); i++) - { - resid[i] = b[i] - resid[i]; - cum_abs += fabs(resid[i]); - } - if (cum_abs > 1e-7) - flags = 2; - else - flags = 0; - mxDestroyArray(res); - } + std::array<mxArray*, 2> lhs0; + std::array rhs0 {A_m, iter_solver_opts.ilu}; + if (mexCallMATLAB(lhs0.size(), lhs0.data(), rhs0.size(), rhs0.data(), "ilu")) + throw FatalException {"In BiCGStab, the incomplete LU decomposition (ilu) has failed"}; + mxArray* L1 = lhs0[0]; + mxArray* U1 = lhs0[1]; - if (flags == 2) - { - if (preconditioner == 0) - { - /*[za,flag1] = bicgstab(g1a,b,1e-6,Blck_size*periods,L1,U1);*/ - std::array rhs {A_m, b_m, mxCreateDoubleScalar(1e-6), - mxCreateDoubleScalar(static_cast<double>(n)), Diag}; - std::array<mxArray*, 2> lhs; - mexCallMATLAB(lhs.size(), lhs.data(), rhs.size(), rhs.data(), "bicgstab"); - z = lhs[0]; - mxArray* flag = lhs[1]; - flags = mxGetScalar(flag); - mxDestroyArray(flag); - mxDestroyArray(rhs[2]); - mxDestroyArray(rhs[3]); - mxDestroyArray(rhs[4]); - } - else if (preconditioner == 1) - { - /*[za,flag1] = bicgstab(g1a,b,1e-6,Blck_size*periods,L1,U1);*/ - std::array rhs {A_m, - b_m, - mxCreateDoubleScalar(1e-6), - mxCreateDoubleScalar(static_cast<double>(n)), - L1, - U1, - x0_m}; - std::array<mxArray*, 2> lhs; - mexCallMATLAB(lhs.size(), lhs.data(), rhs.size(), rhs.data(), "bicgstab"); - z = lhs[0]; - mxArray* flag = lhs[1]; - flags = mxGetScalar(flag); - mxDestroyArray(flag); - mxDestroyArray(rhs[2]); - mxDestroyArray(rhs[3]); - mxDestroyArray(rhs[4]); - mxDestroyArray(rhs[5]); - } - } + std::array rhs {A_m, b_m, iter_solver_opts.itertol, iter_solver_opts.itermaxit, L1, U1, x0_m}; + std::array<mxArray*, 2> lhs; + mexCallMATLAB(lhs.size(), lhs.data(), rhs.size(), rhs.data(), "bicgstab"); + mxArray* z = lhs[0]; + mxArray* flag = lhs[1]; + double flags {mxGetScalar(flag)}; + mxDestroyArray(flag); + mxDestroyArray(rhs[4]); + mxDestroyArray(rhs[5]); if (flags > 0) { if (flags == 1) - mexWarnMsgTxt(("Error in bytecode: No convergence inside BiCGStab, in block " - + to_string(block_num + 1)) - .c_str()); + mexErrMsgTxt(("Error in bytecode: No convergence inside BiCGStab, in block " + + to_string(block_num + 1)) + .c_str()); else if (flags == 2) - mexWarnMsgTxt(("Error in bytecode: Preconditioner is ill-conditioned, in block " - + to_string(block_num + 1)) - .c_str()); + mexErrMsgTxt(("Error in bytecode: Preconditioner is ill-conditioned, in block " + + to_string(block_num + 1)) + .c_str()); else if (flags == 3) - mexWarnMsgTxt(("Error in bytecode: BiCGStab stagnated (Two consecutive iterates were the " - "same.), in block " - + to_string(block_num + 1)) - .c_str()); - lu_inc_tol /= 10; + mexErrMsgTxt(("Error in bytecode: BiCGStab stagnated (Two consecutive iterates were the " + "same.), in block " + + to_string(block_num + 1)) + .c_str()); } else { @@ -2986,6 +2863,7 @@ Interpreter::Solve_Matlab_BiCGStab(mxArray* A_m, mxArray* b_m, bool is_two_bound y[eq + it_ * y_size] += slowc * yy; } } + mxDestroyArray(A_m); mxDestroyArray(b_m); mxDestroyArray(x0_m); @@ -3941,7 +3819,6 @@ Interpreter::Simulate_One_Boundary() mxArray *b_m = nullptr, *A_m = nullptr, *x0_m = nullptr; SuiteSparse_long *Ap = nullptr, *Ai = nullptr; double *Ax = nullptr, *b = nullptr; - int preconditioner = 1; try_at_iteration = 0; Clear_u(); @@ -4010,14 +3887,10 @@ Interpreter::Simulate_One_Boundary() mexPrintf("MODEL STEADY STATE: (method=Sparse LU)\n"); break; case 7: - mexPrintf(preconditioner_print_out("MODEL STEADY STATE: (method=GMRES)\n", - preconditioner, true) - .c_str()); + mexPrintf("MODEL STEADY STATE: (method=GMRES with 'ilu' preconditioner)\n"); break; case 8: - mexPrintf(preconditioner_print_out("MODEL STEADY STATE: (method=BiCGStab)\n", - preconditioner, true) - .c_str()); + mexPrintf("MODEL STEADY STATE: (method=BiCGStab with 'ilu' preconditioner)\n"); break; } } @@ -4087,7 +3960,7 @@ Interpreter::Simulate_One_Boundary() else if ((solve_algo == 7 && steady_state) || (stack_solve_algo == 2 && !steady_state)) Solve_Matlab_GMRES(A_m, b_m, false, x0_m); else if ((solve_algo == 8 && steady_state) || (stack_solve_algo == 3 && !steady_state)) - Solve_Matlab_BiCGStab(A_m, b_m, false, x0_m, preconditioner); + Solve_Matlab_BiCGStab(A_m, b_m, false, x0_m); else if ((solve_algo == 6 && steady_state) || ((stack_solve_algo == 0 || stack_solve_algo == 1 || stack_solve_algo == 4 || stack_solve_algo == 6) @@ -4204,43 +4077,12 @@ Interpreter::Simulate_Newton_One_Boundary(bool forward) mxFree(r); } -string -Interpreter::preconditioner_print_out(string s, int preconditioner, bool ss) -{ - int n = s.length(); - string tmp = ", preconditioner="; - switch (preconditioner) - { - case 0: - if (ss) - tmp.append("Jacobi on static jacobian"); - else - tmp.append("Jacobi on dynamic jacobian"); - break; - case 1: - if (ss) - tmp.append("incomplete lutp on static jacobian"); - else - tmp.append("incomplete lu0 on dynamic jacobian"); - break; - case 2: - tmp.append("incomplete lutp on dynamic jacobian"); - break; - case 3: - tmp.append("lu on static jacobian"); - break; - } - s.insert(n - 2, tmp); - return s; -} - void Interpreter::Simulate_Newton_Two_Boundaries( bool cvg, const vector_table_conditional_local_type& vector_table_conditional_local) { double top = 0.5; double bottom = 0.1; - int preconditioner = 2; if (start_compare == 0) start_compare = y_kmin; u_count_alloc_save = u_count_alloc; @@ -4394,15 +4236,11 @@ Interpreter::Simulate_Newton_Two_Boundaries( break; case 2: mexPrintf( - preconditioner_print_out("MODEL SIMULATION: (method=GMRES on stacked system)\n", - preconditioner, false) - .c_str()); + "MODEL SIMULATION: (method=GMRES on stacked system with 'ilu' preconditioner)\n"); break; case 3: - mexPrintf(preconditioner_print_out( - "MODEL SIMULATION: (method=BiCGStab on stacked system)\n", - preconditioner, false) - .c_str()); + mexPrintf("MODEL SIMULATION: (method=BiCGStab on stacked system with 'ilu' " + "preconditioner)\n"); break; case 4: mexPrintf("MODEL SIMULATION: (method=Sparse LU solver with optimal path length on " @@ -4460,7 +4298,7 @@ Interpreter::Simulate_Newton_Two_Boundaries( else if (stack_solve_algo == 2) Solve_Matlab_GMRES(A_m, b_m, true, x0_m); else if (stack_solve_algo == 3) - Solve_Matlab_BiCGStab(A_m, b_m, true, x0_m, 1); + Solve_Matlab_BiCGStab(A_m, b_m, true, x0_m); else if (stack_solve_algo == 5) Solve_ByteCode_Symbolic_Sparse_GaussianElimination(symbolic); } diff --git a/mex/sources/bytecode/Interpreter.hh b/mex/sources/bytecode/Interpreter.hh index 0259d5e8e0b3e95a8ce427a8ef615b8e95431948..e89461a46b58ec4b1e2c220b89f0232b62bf5cec 100644 --- a/mex/sources/bytecode/Interpreter.hh +++ b/mex/sources/bytecode/Interpreter.hh @@ -69,6 +69,19 @@ constexpr double mem_increasing_factor = 1.1; constexpr int NO_ERROR_ON_EXIT {0}, ERROR_ON_EXIT {1}; +// Stores various options for iterative solvers (GMRES, BiCGStab), from options_.simul +struct iter_solver_opts_t +{ + const char* preconditioner; + mxArray* itertol; + mxArray* itermaxit; + mxArray* gmres_restart; + mxArray* ilu; + + void set_static_defaults(); + void set_dynamic_values(const mxArray* options_); +}; + class Interpreter { private: @@ -110,7 +123,7 @@ private: int iter; int start_compare; int restart; - double lu_inc_tol; + iter_solver_opts_t iter_solver_opts; SuiteSparse_long *Ap_save, *Ai_save; double *Ax_save, *b_save; @@ -199,13 +212,11 @@ private: double* b); void Solve_Matlab_GMRES(mxArray* A_m, mxArray* b_m, bool is_two_boundaries, mxArray* x0_m); - void Solve_Matlab_BiCGStab(mxArray* A_m, mxArray* b_m, bool is_two_boundaries, mxArray* x0_m, - int precond); + void Solve_Matlab_BiCGStab(mxArray* A_m, mxArray* b_m, bool is_two_boundaries, mxArray* x0_m); void Check_and_Correct_Previous_Iteration(); bool Simulate_One_Boundary(); bool solve_linear(bool do_check_and_correct); void solve_non_linear(); - string preconditioner_print_out(string s, int preconditioner, bool ss); bool compare(int* save_op, int* save_opa, int* save_opaa, int beg_t, long nop4); void Insert(int r, int c, int u_index, int lag_index); void Delete(int r, int c); @@ -222,10 +233,6 @@ private: int complete(int beg_t); void bksub(int tbreak, int last_period); void simple_bksub(); - // Computes Aᵀ where A is are sparse. The result is sparse. - static mxArray* Sparse_transpose(const mxArray* A_m); - // Computes Aᵀ·B where A is sparse and B is dense. The result is dense. - static mxArray* mult_SAT_B(const mxArray* A_m, const mxArray* B_m); void compute_block_time(int my_Per_u_, bool evaluate, bool no_derivatives); bool compute_complete(bool no_derivatives); @@ -240,7 +247,8 @@ public: int stack_solve_algo_arg, int solve_algo_arg, bool print_arg, const mxArray* GlobalTemporaryTerms_arg, bool steady_state_arg, bool block_decomposed_arg, int col_x_arg, int col_y_arg, - const BasicSymbolTable& symbol_table_arg, int verbosity_arg); + const BasicSymbolTable& symbol_table_arg, int verbosity_arg, + iter_solver_opts_t iter_solver_opts_arg); pair<bool, vector<int>> extended_path(const string& file_name, bool evaluate, int block, int nb_periods, const vector<s_plan>& sextended_path, diff --git a/mex/sources/bytecode/bytecode.cc b/mex/sources/bytecode/bytecode.cc index 86bbda4d57c59b6231b295f178c2f98e2e0950a1..d5e531dcc50db76fbc4b0e20bcd5a613609f711d 100644 --- a/mex/sources/bytecode/bytecode.cc +++ b/mex/sources/bytecode/bytecode.cc @@ -1,5 +1,5 @@ /* - * Copyright © 2007-2024 Dynare Team + * Copyright © 2007-2025 Dynare Team * * This file is part of Dynare. * @@ -19,6 +19,7 @@ #include <algorithm> #include <cmath> +#include <cstring> #include <type_traits> #include "ErrorHandling.hh" @@ -480,6 +481,34 @@ mexFunction(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) } }()}; + iter_solver_opts_t iter_solver_opts; + if (steady_state) + iter_solver_opts.set_static_defaults(); + else + { + if (stack_solve_algo == 2 || stack_solve_algo == 3) + { + // Check that a preconditioner other than 'ilu' has not been requested + int field {mxGetFieldNumber(options_, "simul")}; + if (field < 0) + mexErrMsgTxt("simul is not a field of options_"); + mxArray* simul {mxGetFieldByNumber(options_, 0, field)}; + field = mxGetFieldNumber(simul, "preconditioner"); + if (field < 0) + mexErrMsgTxt("preconditioner is not a field of options_.simul"); + mxArray* preconditioner {mxGetFieldByNumber(simul, 0, field)}; + if (!mxIsChar(preconditioner)) + mexErrMsgTxt("options_.simul.preconditioner should be a character array"); + char* preconditioner_str {mxArrayToString(preconditioner)}; + if (std::strcmp(preconditioner_str, "ilu")) + mexErrMsgTxt( + "The 'bytecode' option is not compatible with a preconditioner other than 'ilu'"); + mxFree(preconditioner_str); + } + + iter_solver_opts.set_dynamic_values(options_); + } + field = mxGetFieldNumber(M_, "fname"); if (field < 0) mexErrMsgTxt("fname is not a field of M_"); @@ -549,7 +578,8 @@ mexFunction(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) static_cast<int>(col_x), static_cast<int>(col_y), symbol_table, - verbosity}; + verbosity, + iter_solver_opts}; bool r; vector<int> blocks;