@@ -16,6 +16,56 @@ import LinearAlgebra as ℒ
1616using MacroModelling
1717import MacroModelling: clear_solution_caches!, get_NSSS_and_parameters, calculate_jacobian, merge_calculation_options, solve_lyapunov_equation, ℳ
1818
19+ # Check if new workspace API is available (not present in old package versions)
20+ const HAS_WORKSPACE_API = isdefined (MacroModelling, :Lyapunov_workspace )
21+
22+ # Conditionally import workspace types only if they exist
23+ if HAS_WORKSPACE_API
24+ import MacroModelling: Lyapunov_workspace, lyapunov_workspace, ensure_lyapunov_workspace!, ensure_qme_workspace!, ensure_sylvester_1st_order_workspace!
25+ end
26+
27+ # Version-aware wrapper for solve_lyapunov_equation benchmarking
28+ # For new API: uses pre-allocated workspace for true benchmark of workspace reuse
29+ # For old API: calls without workspace argument
30+ function solve_lyapunov_for_bench (A, C, lyap_ws; lyapunov_algorithm:: Symbol = :doubling )
31+ if HAS_WORKSPACE_API
32+ # New API - reuse pre-allocated workspace (shows benefit of workspace caching)
33+ return solve_lyapunov_equation (A, C, lyap_ws; lyapunov_algorithm = lyapunov_algorithm)
34+ else
35+ # Old API - no workspace argument
36+ return solve_lyapunov_equation (A, C; lyapunov_algorithm = lyapunov_algorithm)
37+ end
38+ end
39+
40+ function timings_for_bench (𝓂:: ℳ)
41+ if hasproperty (𝓂, :timings )
42+ out = 𝓂. timings
43+ else
44+ out = 𝓂. constants. post_model_macro
45+ end
46+ return out
47+ end
48+
49+ function first_order_solution_for_bench (∇₁:: AbstractMatrix , 𝓂:: ℳ; opts = merge_calculation_options ())
50+ if HAS_WORKSPACE_API
51+ qme_ws = ensure_qme_workspace! (𝓂)
52+ sylv_ws = ensure_sylvester_1st_order_workspace! (𝓂)
53+ out = calculate_first_order_solution (∇₁, 𝓂. constants, qme_ws, sylv_ws; opts = opts)
54+ else
55+ out = calculate_first_order_solution (∇₁; T = timings_for_bench (𝓂), opts = opts)
56+ end
57+ return out
58+ end
59+
60+ function calculate_jacobian_for_bench (parameters, SS_and_pars, 𝓂:: ℳ)
61+ if hasmethod (calculate_jacobian, Tuple{typeof (parameters), typeof (SS_and_pars), ℳ})
62+ out = calculate_jacobian (parameters, SS_and_pars, 𝓂)
63+ else
64+ out = calculate_jacobian (parameters, SS_and_pars, 𝓂. caches, 𝓂. functions. jacobian)
65+ end
66+ return out
67+ end
68+
1969
2070function run_benchmarks! (𝓂:: ℳ, SUITE:: BenchmarkGroup )
2171 SUITE[𝓂. model_name] = BenchmarkGroup ()
@@ -34,37 +84,46 @@ function run_benchmarks!(𝓂::ℳ, SUITE::BenchmarkGroup)
3484 SUITE[𝓂. model_name][" NSSS" ] = @benchmarkable get_NSSS_and_parameters ($ 𝓂, $ 𝓂. parameter_values) setup = clear_solution_caches! ($ 𝓂, :first_order )
3585
3686
37- ∇₁ = calculate_jacobian (𝓂. parameter_values, reference_steady_state, 𝓂)
87+ ∇₁ = calculate_jacobian_for_bench (𝓂. parameter_values, reference_steady_state, 𝓂)
3888
3989 clear_solution_caches! (𝓂, :first_order )
4090
41- SUITE[𝓂. model_name][" jacobian" ] = @benchmarkable calculate_jacobian ($ 𝓂. parameter_values, $ reference_steady_state, $ 𝓂) setup = clear_solution_caches! ($ 𝓂, :first_order )
91+ SUITE[𝓂. model_name][" jacobian" ] = @benchmarkable calculate_jacobian_for_bench ($ 𝓂. parameter_values, $ reference_steady_state, $ 𝓂) setup = clear_solution_caches! ($ 𝓂, :first_order )
4292
4393
4494 SUITE[𝓂. model_name][" qme" ] = BenchmarkGroup ()
45-
46- sol, qme_sol, solved = calculate_first_order_solution (∇₁; T = 𝓂. timings, opts = merge_calculation_options (quadratic_matrix_equation_algorithm = :schur ))
47-
95+
96+ qme_schur_opts = merge_calculation_options (quadratic_matrix_equation_algorithm = :schur )
97+ qme_doubling_opts = merge_calculation_options (quadratic_matrix_equation_algorithm = :doubling )
98+
99+ sol, qme_sol, solved = first_order_solution_for_bench (∇₁, 𝓂; opts = qme_schur_opts)
100+
48101 clear_solution_caches! (𝓂, :first_order )
49-
50- SUITE[𝓂. model_name][" qme" ][" schur" ] = @benchmarkable calculate_first_order_solution ($ ∇₁; T = $ 𝓂 . timings, opts = merge_calculation_options (quadratic_matrix_equation_algorithm = :schur ) ) setup = clear_solution_caches! ($ 𝓂, :first_order )
51-
52- SUITE[𝓂 . model_name][ " qme " ][ " doubling " ] = @benchmarkable calculate_first_order_solution ( $ ∇₁; T = $ 𝓂 . timings, opts = merge_calculation_options (quadratic_matrix_equation_algorithm = :doubling )) setup = clear_solution_caches! ( $ 𝓂, :first_order )
53-
54-
55- A = @views sol[:, 1 : 𝓂 . timings . nPast_not_future_and_mixed] * ℒ. diagm (ones (𝓂 . timings . nVars))[𝓂 . timings . past_not_future_and_mixed_idx,:]
56-
57- C = @views sol[:, 𝓂 . timings . nPast_not_future_and_mixed+ 1 : end ]
58-
102+
103+ SUITE[𝓂. model_name][" qme" ][" schur" ] = @benchmarkable first_order_solution_for_bench ($ ∇₁, $ 𝓂; opts = $ qme_schur_opts ) setup = clear_solution_caches! ($ 𝓂, :first_order )
104+ SUITE[𝓂 . model_name][ " qme " ][ " doubling " ] = @benchmarkable first_order_solution_for_bench ( $ ∇₁, $ 𝓂; opts = $ qme_doubling_opts) setup = clear_solution_caches! ( $ 𝓂, :first_order )
105+
106+ T = timings_for_bench (𝓂)
107+
108+ A = @views sol[:, 1 : T . nPast_not_future_and_mixed] * ℒ. diagm (ones (T . nVars))[T . past_not_future_and_mixed_idx,:]
109+
110+ C = @views sol[:, T . nPast_not_future_and_mixed+ 1 : end ]
111+
59112 CC = C * C'
60113
61- solve_lyapunov_equation (A, CC)
114+ # Create workspace once before benchmarks (new API) or use nothing (old API)
115+ # For new API: pre-allocated workspace shows benefit of workspace caching
116+ # For old API: workspace is not used
117+ lyap_ws = HAS_WORKSPACE_API ? Lyapunov_workspace (size (A, 1 )) : nothing
118+
119+ # Warm up call
120+ solve_lyapunov_for_bench (A, CC, lyap_ws)
62121
63122 SUITE[𝓂. model_name][" lyapunov" ] = BenchmarkGroup ()
64- SUITE[𝓂. model_name][" lyapunov" ][" doubling" ] = @benchmarkable solve_lyapunov_equation ($ A, $ CC, lyapunov_algorithm = :doubling ) # setup = clear_solution_caches!($𝓂, :first_order )
65- SUITE[𝓂. model_name][" lyapunov" ][" bartels_stewart" ] = @benchmarkable solve_lyapunov_equation ($ A, $ CC, lyapunov_algorithm = :bartels_stewart ) # setup = clear_solution_caches!($𝓂, :first_order )
66- SUITE[𝓂. model_name][" lyapunov" ][" bicgstab" ] = @benchmarkable solve_lyapunov_equation ($ A, $ CC, lyapunov_algorithm = :bicgstab ) # setup = clear_solution_caches!($𝓂, :first_order )
67- SUITE[𝓂. model_name][" lyapunov" ][" gmres" ] = @benchmarkable solve_lyapunov_equation ($ A, $ CC, lyapunov_algorithm = :gmres ) # setup = clear_solution_caches!($𝓂, :first_order )
123+ SUITE[𝓂. model_name][" lyapunov" ][" doubling" ] = @benchmarkable solve_lyapunov_for_bench ($ A, $ CC, $ lyap_ws, lyapunov_algorithm = :doubling )
124+ SUITE[𝓂. model_name][" lyapunov" ][" bartels_stewart" ] = @benchmarkable solve_lyapunov_for_bench ($ A, $ CC, $ lyap_ws, lyapunov_algorithm = :bartels_stewart )
125+ SUITE[𝓂. model_name][" lyapunov" ][" bicgstab" ] = @benchmarkable solve_lyapunov_for_bench ($ A, $ CC, $ lyap_ws, lyapunov_algorithm = :bicgstab )
126+ SUITE[𝓂. model_name][" lyapunov" ][" gmres" ] = @benchmarkable solve_lyapunov_for_bench ($ A, $ CC, $ lyap_ws, lyapunov_algorithm = :gmres )
68127
69128
70129 clear_solution_caches! (𝓂, :first_order )
@@ -103,7 +162,7 @@ run_benchmarks!(Smets_Wouters_2007, SUITE)
103162# end
104163# end
105164
106- # If a cache of tuned parameters already exists, use it, otherwise, tune and cache
165+ # If a caches of tuned parameters already exists, use it, otherwise, tune and caches
107166# the benchmark parameters. Reusing cached parameters is faster and more reliable
108167# than re-tuning `SUITE` every time the file is included.
109168# paramspath = joinpath(dirname(@__FILE__), "params.json")
@@ -113,4 +172,4 @@ run_benchmarks!(Smets_Wouters_2007, SUITE)
113172# else
114173# tune!(SUITE)
115174# BenchmarkTools.save(paramspath, params(SUITE))
116- # end
175+ # end
0 commit comments