From 223363e795d7777557ae218a2899046337db6863 Mon Sep 17 00:00:00 2001 From: "armin.nurkanovic" Date: Sun, 31 Aug 2025 20:12:09 +0200 Subject: [PATCH 01/31] add minlp infrastructure, modify settings, polish examples --- .../macmpec/run_macmpec_experiments_general.m | 60 +- .../macmpec/solve_single_macmpec_problem.m | 63 +- .../generate_nonlinear_mpec_problem_set.m | 13 + .../run_nonlinear_mpec_benchmark_large.m | 217 ++++++ .../solve_single_problem_nonlinear_mpec.m | 129 ++-- .../global_vs_local_min_in_lpec.m | 139 ++-- ...example.m => lpec_acq_violation_example.m} | 51 +- .../examples_in_mpecopt_paper/scale1_mpcc.m | 48 +- examples/getting_started.m | 15 +- examples/kirches2022_ex1.m | 53 +- examples/knitro_example.m | 46 +- examples/leyffer2007_ex2.m | 2 +- examples/nonlinear_mpec.m | 89 +++ examples/nonlinear_mpec2.m | 92 +++ examples/nonlinear_mpec3.m | 72 ++ examples/ralph2_mpcc.m | 82 +- src/+mpecopt/Options.m | 3 +- src/+mpecopt/Solver.m | 2 + src/lpec_solver.m | 21 +- src/mpec_homotopy_solver.m | 5 +- src/mpec_minlp_solver.m | 705 ++++++++++++++++++ src/options/HomotopySolverOptions.m | 2 +- src/options/LPECSolverOptions.m | 2 +- src/options/MINLPSolverOptions.m | 95 +++ src/verbose/print_iter_summary_minlp.m | 17 + src/verbose/print_iter_summary_scholtes.m | 2 +- 26 files changed, 1638 insertions(+), 387 deletions(-) create mode 100644 benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_large.m rename examples/examples_in_mpecopt_paper/{lpec_projection_example.m => lpec_acq_violation_example.m} (71%) create mode 100644 examples/nonlinear_mpec.m create mode 100644 examples/nonlinear_mpec2.m create mode 100644 examples/nonlinear_mpec3.m create mode 100644 src/mpec_minlp_solver.m create mode 100644 src/options/MINLPSolverOptions.m create mode 100644 src/verbose/print_iter_summary_minlp.m diff --git a/benchmarks/macmpec/run_macmpec_experiments_general.m b/benchmarks/macmpec/run_macmpec_experiments_general.m index 40a675c..5cfbe87 100644 --- a/benchmarks/macmpec/run_macmpec_experiments_general.m +++ b/benchmarks/macmpec/run_macmpec_experiments_general.m @@ -21,8 +21,8 @@ N_infeasible = [60 66]; N_almost_feasible = [114 68]; N_easy = [34 36 65 87 49 67 69 33 51 53 50 55 57 174 25 40 101 16 102 1 2 3 4]; % bunch of easy problems for more diversity -N_interesting = [N_failed_by_scohltes, N_biactive, N_non_S, N_qpecs, N_not_presolve, N_easy, N_infeasible, N_almost_feasible]; -% N_interesting = [N_easy]; +% N_interesting = [N_failed_by_scohltes, N_biactive, N_non_S, N_qpecs, N_not_presolve, N_easy, N_infeasible, N_almost_feasible]; +N_interesting = N_easy; % N_interesting = [N_easy, N_failed_by_scohltes, N_biactive]; % for ii = N_interesting @@ -38,49 +38,67 @@ mpec.g_fun = Function.deserialize(mpec.g_fun); mpec.G_fun = Function.deserialize(mpec.G_fun); mpec.H_fun = Function.deserialize(mpec.H_fun); + mpec.n_w = length(mpec.w); + mpec.n_comp = length(length(mpec.H_fun(mpec.w))); mpecs = [mpecs,mpec]; end -%% Define list of solvers to use - -solver_names = ["MPECopt-Gurobi", "MPECopt-HiGHS",... - "MPECopt-Simple", "Reg" , "Pen-$\ell_{\infty}$", "Pen-$\ell_{1}$"]; +% N_interesting = []; +% for ii=1:length(macmpec_json) +% if mpecs(ii).n_w <= 50 +% N_interesting = [N_interesting; ii]; +% end +% end +% mpecs = mpecs(N_interesting); -solver_names = ["MPECopt-Reg-Gurobi", "MPECopt-Reg-HiGHS",... - "MPECopt-$\ell_1$-Gurobi", "Reg" , "Pen-$\ell_{\infty}$", "Pen-$\ell_{1}$"]; +%% Define list of solvers to use +solver_names = ["MPECopt-Reg-Gurobi", "MPECopt-Reg-HiGHS", "MPECopt-$\ell_1$-Gurobi", ... + "Reg" , "NLP", ... + "MINLP"]; solver_functions = {@mpec_optimizer,@mpec_optimizer,@mpec_optimizer,... - @mpec_homotopy_solver,@mpec_homotopy_solver,@mpec_homotopy_solver}; + @mpec_homotopy_solver,@mpec_homotopy_solver,... + @mpec_minlp_solver}; default_opts1 = mpecopt.Options(); default_opts1.solver_name = solver_names{1}; +default_opts1.settings_lpec.lpec_solver = "Gurobi"; +default_opts1.relax_and_project_homotopy_parameter_steering = "Direct"; default_opts2 = mpecopt.Options(); default_opts2.solver_name = solver_names{2}; -default_opts2.settings_lpec.lpec_solver = "Highs"; +default_opts2.settings_lpec.lpec_solver = "Highs_casadi"; +default_opts2.relax_and_project_homotopy_parameter_steering = "Direct"; default_opts2.rho_TR_phase_i_init = 1e-3; default_opts3 = mpecopt.Options(); default_opts3.solver_name = solver_names{3}; +default_opts3.settings_lpec.lpec_solver = "Gurobi"; default_opts3.relax_and_project_homotopy_parameter_steering = "Ell_1"; -% default_opts3.bnlp_projection_strategy = "Simple"; + scholtes_opts1 = HomotopySolverOptions(); scholtes_opts1.homotopy_parameter_steering = 'Direct'; scholtes_opts2 = HomotopySolverOptions(); -scholtes_opts2.homotopy_parameter_steering = 'Ell_inf'; +scholtes_opts2.homotopy_parameter_steering = 'Direct'; +scholtes_opts2.max_iter = 1; +scholtes_opts2.sigma0 = 0; + +minlp_opts = MINLPSolverOptions(); +minlp_opts.settings_casadi_nlp.bonmin.time_limit = 900; -scholtes_opts3 = HomotopySolverOptions(); -scholtes_opts3.homotopy_parameter_steering = 'Ell_1'; +opts = {default_opts1, default_opts2, default_opts3, ... + scholtes_opts1, scholtes_opts2,... + minlp_opts}; % list of options to pass to mpecsol (option structs) -opts = {default_opts1, default_opts2, default_opts3, scholtes_opts1, scholtes_opts2, scholtes_opts3}; % list of options to pass to mpecsol (option structs) %% Create data struct -N_experiments = [1,2,3,4,6]; +N_experiments = [1, 3:6]; mpec_benchmark_dtable_loop; % this script runs the experimetns, creates a dtable + %% Pick which results to plot dtable = dtable1; % Modift possibly which solvers are plotted @@ -118,8 +136,8 @@ plot_settings.bar_timing_plots = 0; plot_settings.nlps_solved = 1; plot_settings.max_nlp_cpu_time = 1; -plot_settings.max_nlp_cpu_time_phase_i = 1; -plot_settings.max_nlp_cpu_time_phase_ii = 1; +plot_settings.max_nlp_cpu_time_phase_i = 0; +plot_settings.max_nlp_cpu_time_phase_ii = 0; plot_settings.nlp_cpu_time = 1; % aggegated nlp times phase I and II plot_settings.nlp_lpec_cpu_comparisson = 0; plot_settings.objective = 0; @@ -127,7 +145,7 @@ plot_settings.max_lpec_cpu_time = 0; plot_settings.relative = 1; -plot_settings.relative_phase_i = 1; +plot_settings.relative_phase_i = 0; plot_settings.relative_phase_ii = 0; plot_settings.relative_lpec = 0; @@ -145,8 +163,8 @@ plot_settings.max_lpec_cpu_time = 0; plot_settings.stationary_points = 1; -plot_settings.b_stationarity = 0; -plot_settings.b_stationarty_as_success_criterion = 0; +plot_settings.b_stationarity = 1; +plot_settings.b_stationarty_as_success_criterion = 1; plot_settings.plot_only_sucessful = 1; plot_settings.bar_comparisson_plots = 0; diff --git a/benchmarks/macmpec/solve_single_macmpec_problem.m b/benchmarks/macmpec/solve_single_macmpec_problem.m index 1abaf90..723c8ab 100644 --- a/benchmarks/macmpec/solve_single_macmpec_problem.m +++ b/benchmarks/macmpec/solve_single_macmpec_problem.m @@ -10,6 +10,8 @@ % problame_name = 'ralph1'; % problame_name = 'pack-rig-32'; % problame_name = 'pack-rig-8'; +problame_name = 'gnash15m.nl'; +problame_name = 'pack-rig2p-16.nl'; % ii_prob = find(contains({macmpec_json.name},problame_name)); @@ -33,6 +35,7 @@ g = mpec.g_fun(mpec.w); G = mpec.G_fun(mpec.w); H = mpec.H_fun(mpec.w); + for ii=1:length(H) if mpec.lbH(ii) ~= -inf && mpec.ubH(ii) ~= inf Hi = H(ii); @@ -65,37 +68,43 @@ solver_initalization = struct('x0', w0, 'lbx',lbw, 'ubx',ubw,'lbg',lbg, 'ubg',ubg); clc fprintf('Problem info, n_w = %d, n_g = %d, n_comp = %d, name = %s\n', length(w),length(g),length(G),name) -%% homotopy -settings_homotopy = HomotopySolverOptions(); -settings_homotopy.initial_comp_all_zero = 0; -settings_homotopy.homotopy_parameter_steering = "Direct"; -% settings_homotopy.comp_tol = 1e-8; -% settings_homotopy.max_iter = 30; +%% Homotopy solver +settings_homotopy = HomotopySolverOptions(); [result_homotopy,stats_homotopy] = mpec_homotopy_solver(mpec,solver_initalization,settings_homotopy); -w_opt = full(result_homotopy.x); f_opt_homotopy = full(result_homotopy.f); -% solver_initalization.w0 = w_opt; -%% mpecopt -fprintf('Problem info, n_w = %d, n_g = %d, n_comp = %d, name = %s\n', length(w),length(g),length(G),name) -solver_settings = mpecopt.Options(); -solver_settings.settings_lpec.lpec_solver = 'Highs_casadi'; +w_opt_homotopy = full(result_homotopy.x); -[results,stats] = mpec_optimizer(mpec, solver_initalization, solver_settings); -w_opt_mpecopt = full(results.x); -f_opt_mpecopt = full(results.f); +%% MINLP solver +settings_minlp = MINLPSolverOptions(); +settings_minlp.settings_casadi_nlp.bonmin.time_limit = 10; +% settings_minlp.settings_casadi_nlp.bonmin.node_limit = 5; +% settings_minlp.settings_casadi_nlp.bonmin.solution_limit = 5; +% settings_minlp.settings_casadi_nlp.bonmin.max_consecutive_failures = 5; +[result_minlp,stats_minlp] = mpec_minlp_solver(mpec,solver_initalization,settings_minlp); +f_opt_minlp = full(result_minlp.f); +w_opt_minlp = full(result_minlp.x); -%% Print result details -if 1 - fprintf('\n'); - fprintf('Problem info,name = %s, n_w = %d, n_g = %d, n_comp = %d\n',name,length(w),length(g),length(G)) - fprintf('\n-------------------------------------------------------------------------------\n'); - fprintf('Method \t\t Objective \t comp_res \t n_biactive \t CPU time (s)\t Sucess\t Stat. type\n') - fprintf('-------------------------------------------------------------------------------\n'); - fprintf('homotopy \t %2.2e \t %2.2e \t\t %d \t\t\t %2.2f \t\t\t\t %d\t %s\n',f_opt_homotopy,stats_homotopy.comp_res,stats_homotopy.n_biactive,stats_homotopy.cpu_time_total,stats_homotopy.success,stats_homotopy.multiplier_based_stationarity) - fprintf('mpecopt \t %2.2e \t %2.2e \t\t %d \t\t\t %2.2f \t\t\t\t %d\t %s\n',f_opt_mpecopt,stats.comp_res,stats.n_biactive,stats.cpu_time_total,stats.success,stats.multiplier_based_stationarity) - fprintf('\n'); - fprintf(' || w_homotopy - w_mpecopt || = %2.2e \n',norm(w_opt-w_opt_mpecopt)); -end +%% MPECopt solver +solver_settings = mpecopt.Options(); +solver_settings.relax_and_project_homotopy_parameter_steering = "Direct"; +solver_settings.settings_lpec.lpec_solver = 'Gurobi'; +% solver_settings.initialization_strategy = "FeasibilityEll1General"; +solver_settings.rho_TR_phase_i_init = 10; +solver_settings.tol_active = 1e-6; +solver = mpecopt.Solver(mpec, solver_settings); +[result_mpecopt,stats_mpecopt] = solver.solve(solver_initalization); +w_opt_mpecopt = full(result_mpecopt.x); +f_opt_mpecopt = full(result_mpecopt.f); +%% Results comparison +fprintf('\n-------------------------------------------------------------------------------\n'); +fprintf('Method \t\t Objective \t comp_res \t n_biactive \t CPU time (s)\t Success\t Stat. type\n') +fprintf('-------------------------------------------------------------------------------\n'); +fprintf('Reg \t %2.2e \t %2.2e \t\t %d \t\t\t %2.2f \t\t\t\t %d\t %s\n',f_opt_homotopy,stats_homotopy.comp_res,stats_homotopy.n_biactive,stats_homotopy.cpu_time_total,stats_homotopy.success,stats_homotopy.multiplier_based_stationarity) +fprintf('MINLP \t\t %2.2e \t %2.2e \t\t %d \t\t\t %2.2f \t\t\t\t %d\t %s\n',f_opt_minlp,stats_minlp.comp_res,stats_minlp.n_biactive,stats_minlp.cpu_time_total,stats_minlp.success,stats_minlp.multiplier_based_stationarity) +fprintf('MPECopt \t %2.2e \t %2.2e \t\t %d \t\t\t %2.2f \t\t\t\t %d\t %s\n',f_opt_mpecopt,stats_mpecopt.comp_res,stats_mpecopt.n_biactive,stats_mpecopt.cpu_time_total,stats_mpecopt.success,stats_mpecopt.multiplier_based_stationarity) +fprintf('-------------------------------------------------------------------------------\n'); +fprintf('||w_reg - w_mpec|| = %2.2e \n',norm(w_opt_homotopy-w_opt_mpecopt)); +fprintf('||w_minlp - w_mpec|| = %2.2e \n',norm(w_opt_minlp-w_opt_mpecopt)); diff --git a/benchmarks/nonlinear_mpec_benchmark/generate_nonlinear_mpec_problem_set.m b/benchmarks/nonlinear_mpec_benchmark/generate_nonlinear_mpec_problem_set.m index 2d50775..505686d 100644 --- a/benchmarks/nonlinear_mpec_benchmark/generate_nonlinear_mpec_problem_set.m +++ b/benchmarks/nonlinear_mpec_benchmark/generate_nonlinear_mpec_problem_set.m @@ -15,6 +15,10 @@ % x>=0 % 0 <=w<= w_ub +% n_x number of noncomplementarity variablex (x0) +% n_y number of complementarity variablex (x1,x3) +% n = n_x+2n_y total number of variables + % LPEC generator is inspired by: % % [1] Hu, Jing, John E. Mitchell, Jong-Shi Pang, Kristin P. Bennett, and Gautam Kunapuli. @@ -60,6 +64,7 @@ N_per_problem = N_rand_prob; figure n_var = n_x_vec+2*n_y_vec; + subplot(121) scatter(n_var,n_ineq_vec*(1*settings.s_ineq_copy)); hold on scatter(n_var,n_x_vec); @@ -68,6 +73,14 @@ ylabel('Number of constraints') legend({'Inequality constraints','Equality constraints'},'Location','northwest') axis equal + subplot(122) + scatter(n_var,n_y_vec); + hold on + scatter(n_var,n_ineq_vec*(1+settings.s_ineq_copy)+n_x_vec); + hold on + xlabel('$n+2m$ - number of variables') + legend({'$m$ number of complementairt variables','Total num. of constraints'},'Location','northwest') + axis equal % elseif settings.random_problem_sizes_individual % n_x_vec = round(n_x_min+n_x_max*rand(N_rand_prob,1)); % n_x_vec = sort(n_x_vec); diff --git a/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_large.m b/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_large.m new file mode 100644 index 0000000..92c8ccf --- /dev/null +++ b/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_large.m @@ -0,0 +1,217 @@ +clear; clc; close all; +problem_set_name = 'nonlinear_mpec2'; % (add large, degenerate, nonlinear, include lifted) + +%% file names +filename = 'nonlinear_mpec_general2'; % name for figures and excel table +results_name = ['results/' filename '_' datestr(datetime("today"))]; % name for matlab .dat with results +%% Generate test set +settings.nonlinear_eq_constraints = 1; +settings.nonlinear_ineq_constraints = 1; +settings.s_nonlinear_eq = 0.05; +settings.s_nonlinear_ineq = 0.1; +settings.include_lifted_variables_in_objective = 0; +settings.copy_ineq = 1; %have two copies of the inequality constrants -> violate licq if active +settings.s_ineq_copy = 0.25; +settings.copy_eq = 0; %have two copies of the inequality constrants -> violate licq if active +settings.s_eq_copy = 0.0; + +settings.inequality_constraints = 1; +settings.inequality_constraints_coupling_terms = 1; % if 0, then in Ax+By >=f, B = 0; + +settings.objective_functions = {'Quadratic_psd','Quadratic_ind',... + 'Fletcher','Himmelblau','McCormick',... + 'Powell','Trigonometric','Rosenbrock',... + 'Raydan1','Raydan2',... + 'Diagonal3','Diagonal4','Diagonal5',... + 'Extended_Trigiaonal','Three_Exponential_Terms',... + 'Generalized_PSC1','Extended_PSC1',... + 'Fletcvb3','Bdqrtic','Tridia',... + 'EG2','Edensch','Indef',... + 'Cube','Bdexp','Genhumps',... + 'Arwhead', 'Quartc', 'Cosine', 'Sine'}; + +settings.rescale_factor = 1; +settings.round_all_data = 1; +settings.n_digits = 4; +settings.bounded_w = 1; +settings.use_normal_distributions_for_feasible_point = 1; +settings.alpha_lin = 10; % f + alpha_lin*[c;d]'*[x;y]' +settings.n_non_zero_E = 2000; +settings.symmetric_psd_M_matrix = 0; + +settings.s_density_A_B = 0.1; % all same or change +settings.s_density_M = 0.5; + +settings.s_density_A_B = 0.05; % all same or change +settings.s_density_M = 0.1; + +settings.variable_density = 1; +settings.range_s_density = [0.1 0.3]; +settings.random_problem_sizes = 1; + +settings.n_ineq_ub = 2; % n_ineq = n_ineq_ub*n_x % max relative number of ineq w.r.t x +settings.n_ineq_lb = 0.1; + +% Problem size +dimensions.N_rand_prob = 3; % number of problems per objective +dimensions.n_x_max = 100; +dimensions.n_x_min = 10; + +% dimensions.N_rand_prob = 4; % number of problems per objective +% dimensions.n_x_max = 50; +% dimensions.n_x_min = 10; +% +% +dimensions.N_rand_prob = 2; % number of problems per objective +dimensions.n_x_max = 30; +dimensions.n_x_min = 10; + +dimensions.n_fraction_of_x = 0.5; % n_y = round(n_x/n_fraction_of_x) + +% example for nonradnom problem sizes +dimensions.n_x_vec = [10,20,50,75,100,100]; +dimensions.n_y_vec = [10,10,25,40,50,70]; +dimensions.n_ineq_vec = [10,10,20,50,40,250]; + +mpecs = generate_nonlinear_mpec_problem_set(problem_set_name,settings,dimensions); +length(mpecs) + + +%% Solver settings +%% Define list of solvers to use +solver_names = ["MPECopt-Reg-Gurobi", "MPECopt-Reg-HiGHS", "MPECopt-$\ell_1$-Gurobi", ... + "Reg" , "NLP", ... + "MINLP"]; + +solver_functions = {@mpec_optimizer,@mpec_optimizer,@mpec_optimizer,... + @mpec_homotopy_solver,@mpec_homotopy_solver,... + @mpec_minlp_solver}; + +default_opts1 = mpecopt.Options(); +default_opts1.solver_name = solver_names{1}; +default_opts1.settings_lpec.lpec_solver = "Gurobi"; +default_opts1.relax_and_project_homotopy_parameter_steering = "Direct"; + +default_opts2 = mpecopt.Options(); +default_opts2.solver_name = solver_names{2}; +default_opts2.settings_lpec.lpec_solver = "Highs_casadi"; +default_opts2.relax_and_project_homotopy_parameter_steering = "Direct"; +default_opts2.rho_TR_phase_i_init = 1e-3; + +default_opts3 = mpecopt.Options(); +default_opts3.solver_name = solver_names{3}; +default_opts3.settings_lpec.lpec_solver = "Gurobi"; +default_opts3.relax_and_project_homotopy_parameter_steering = "Ell_1"; + + +scholtes_opts1 = HomotopySolverOptions(); +scholtes_opts1.homotopy_parameter_steering = 'Direct'; + +scholtes_opts2 = HomotopySolverOptions(); +scholtes_opts2.homotopy_parameter_steering = 'Direct'; +scholtes_opts2.max_iter = 1; +scholtes_opts2.settings_casadi_nlp.ipopt.bound_relax_factor = 1e-12; +scholtes_opts2.sigma0 = 0; + +minlp_opts = MINLPSolverOptions(); +scholtes_opts3.homotopy_parameter_steering = 'Ell_1'; + +opts = {default_opts1, default_opts2, default_opts3, ... + scholtes_opts1, scholtes_opts2,... + minlp_opts}; % list of options to pass to mpecsol (option structs) + +%% Create data struct +N_experiments = [1 5]; + +nonlinear_mpec_benchmark_dtable_loop; % this script runs the experimetns, creates a dtable +%% Pick which results to plot +dtable = dtable1; +% Modift possibly which solvers are plotted +[~,temp_b] = unique(dtable.solver_name); +solver_name = dtable.solver_name(sort(temp_b)); +% solver_tab = table(solver_name); +N_plot = N_experiments; +% N_plot = [1,2]; + +dtable_plot = []; +for ii = N_plot + dtable_plot = [dtable_plot; dtable(dtable.solver_name == solver_names(ii),:)]; +end + +%% Export to excel +write_in_separate_sheets = true; +filename_excel = [filename '_details.xlsx']; +if write_in_separate_sheets + solver_tab = pivot(dtable_plot, Rows= "solver_name"); + for ii = 1:length(solver_tab.solver_name) + solver_name =solver_tab.solver_name{ii}; + dtable_ii = dtable_plot(dtable_plot.solver_name == solver_name,:); + writetable(dtable_ii,filename_excel,'Sheet',ii) + end +else + writetable(dtable_plot,filename_excel,'Sheet',1) +end + +%% Plot results +plot_settings.success_fail_statistics = 1; +plot_settings.n_biactive = 1; +plot_settings.lpecs_solved = 0; +plot_settings.active_set_changes = 0; +plot_settings.lpecs_cpu_time = 0; +plot_settings.bar_timing_plots = 0; +plot_settings.nlps_solved = 1; +plot_settings.max_nlp_cpu_time = 0; +plot_settings.max_nlp_cpu_time_phase_i = 0; +plot_settings.max_nlp_cpu_time_phase_ii = 0; +plot_settings.nlp_cpu_time = 0; % aggegated nlp times phase I and II +plot_settings.nlp_lpec_cpu_comparisson = 0; +plot_settings.objective = 0; +plot_settings.objective_rescaled = 1; +plot_settings.max_lpec_cpu_time = 0; + +plot_settings.relative = 1; +plot_settings.relative_phase_i = 0; +plot_settings.relative_phase_ii = 0; +plot_settings.relative_lpec = 0; + +plot_settings.absolute = 1; +plot_settings.absolute_phase_i = 0; +plot_settings.absolute_phase_ii = 0; +plot_settings.absolute_lpec = 0; + +plot_settings.solved_in_phase_i = 0; + +% split cpu time among phases and show in bar plot (just report for mpecopt +% guroi) +plot_settings.nlp_phases_cpu_time = 0; +plot_settings.lpec_phases_cpu_time = 0; + +plot_settings.max_lpec_cpu_time = 0; +plot_settings.stationary_points = 1; +plot_settings.b_stationarity = 1; +plot_settings.b_stationarty_as_success_criterion = 0; +plot_settings.plot_only_sucessful = 1; +plot_settings.bar_comparisson_plots = 0; + +plot_settings.save_plot = 1; + +close all; +plot_mpec_benchmark_result(dtable_plot,filename,plot_settings) +%% +dtable_ii = dtable_plot(dtable_plot.solver_name == solver_names{1},:); +dtable_jj = dtable_plot(dtable_plot.solver_name == solver_names{4},:); +hh = find(dtable_jj.b_stationarity == 0 & dtable_jj.success == 1); +% dtable_ii.f_lpec((hh)) +% dtable_jj.f((hh)) +% dtable_jj.f_lpec((hh)) +% abs((dtable_ii.f((hh))-dtable_jj.f((hh))))./abs(dtable_ii.f((hh)))>1e-3 + +%% +% figure +% semilogy(abs(dtable_ii.f_lpec)+1e-16,'LineWidth',1.5); +% hold on +% semilogy(abs(dtable_jj.f_lpec)+1e-16,'LineWidth',1.5); +% ylabel('$|\nabla f(x^*)^\top d|$') +% xlabel('Problem instance') +% grid on +% yline(1e-8,'LineWidth',2) \ No newline at end of file diff --git a/benchmarks/nonlinear_mpec_benchmark/solve_single_problem_nonlinear_mpec.m b/benchmarks/nonlinear_mpec_benchmark/solve_single_problem_nonlinear_mpec.m index 16369fe..187a0ad 100644 --- a/benchmarks/nonlinear_mpec_benchmark/solve_single_problem_nonlinear_mpec.m +++ b/benchmarks/nonlinear_mpec_benchmark/solve_single_problem_nonlinear_mpec.m @@ -1,15 +1,14 @@ clear; clc; close all; problem_set_name = 'nonlinear_mpec'; -%% Generate test set settings.nonlinear_eq_constraints = 1; settings.nonlinear_ineq_constraints = 1; -settings.s_nonlinear_eq = 0.5; -settings.s_nonlinear_ineq = 0.5; -settings.include_lifted_variables_in_objective = 1; +settings.s_nonlinear_eq = 0.05; +settings.s_nonlinear_ineq = 0.05; +settings.include_lifted_variables_in_objective = 0; settings.copy_ineq = 1; %have two copies of the inequality constrants -> violate licq if active -settings.s_ineq_copy = 0.5; +settings.s_ineq_copy = 0.2; settings.copy_eq = 0; %have two copies of the inequality constrants -> violate licq if active -settings.s_eq_copy = 0.25; +settings.s_eq_copy = 0.0; settings.inequality_constraints = 1; settings.inequality_constraints_coupling_terms = 1; % if 0, then in Ax+By >=f, B = 0; @@ -26,9 +25,11 @@ 'Cube','Bdexp','Genhumps',... 'Arwhead', 'Quartc', 'Cosine', 'Sine'}; +settings.objective_functions = {'Himmelblau'}; + settings.rescale_factor = 1; -settings.round_all_data = 0; -settings.n_digits = 6; +settings.round_all_data = 1; +settings.n_digits = 4; settings.bounded_w = 1; settings.use_normal_distributions_for_feasible_point = 1; settings.alpha_lin = 10; % f + alpha_lin*[c;d]'*[x;y]' @@ -37,32 +38,22 @@ settings.s_density_A_B = 0.1; % all same or change settings.s_density_M = 0.5; -settings.variable_density = 1; -settings.range_s_density = [0.1 0.3]; -% settings.range_s_density = [0.1 0.2]; -% settings.s_density_M = 0.1; -% settings.range_s_density = [0.1 0.8]; +settings.s_density_A_B = 0.05; % all same or change +settings.s_density_M = 0.1; +settings.variable_density = 1; +settings.range_s_density = [0.1 0.3]; settings.random_problem_sizes = 1; -% settings.n_ineq_ub = 1.5; % n_ineq = n_ineq_ub*n_x % max relative number of ineq w.r.t x -% settings.n_ineq_lb = 0.5; -settings.n_ineq_ub = 1; % n_ineq = n_ineq_ub*n_x % max relative number of ineq w.r.t x +settings.n_ineq_ub = 2; % n_ineq = n_ineq_ub*n_x % max relative number of ineq w.r.t x settings.n_ineq_lb = 0.1; -dimensions.N_rand_prob = 5; % number of problems per objective -dimensions.n_x_max = 100; -dimensions.n_x_min = 10; -settings.n_ineq_ub = 2; % n_ineq = n_ineq_ub*n_x % max relative number of ineq w.r.t x -dimensions.N_rand_prob = 4; % number of problems per objective -dimensions.n_x_max = 50; -dimensions.n_x_min = 10; +% Problem size -% settings.n_ineq_ub = 2; % n_ineq = n_ineq_ub*n_x % max relative number of ineq w.r.t x -% dimensions.N_rand_prob = 1; % number of problems per objective -% dimensions.n_x_max = 30; -% dimensions.n_x_min = 10; +dimensions.N_rand_prob = 1; % number of problems per objective +dimensions.n_x_max = 400; +dimensions.n_x_min = 400; dimensions.n_fraction_of_x = 1; % n_y = round(n_x/n_fraction_of_x) @@ -73,13 +64,9 @@ mpecs = generate_nonlinear_mpec_problem_set(problem_set_name,settings,dimensions); length(mpecs) -% pack-rig-32.nl % scholtes %% mpecopt solvers - % 2 - - -ii_prob = 85; % 10 41 48 85 87 +ii_prob = 1; % 10 41 48 85 87 close all; mpec = mpecs(ii_prob); @@ -95,55 +82,45 @@ ubg = mpec.ubg; mpec_name = mpec.name; +%% Set up problem fprintf('Problem info:name = %s, n_w = %d, n_g = %d, n_comp = %d\n',mpec_name, length(w),length(g),length(G)) mpec_struct = struct('x',w,'f',f,'g',g,'G',G,'H',H); solver_initalization = struct('x0', w0, 'lbx',lbw, 'ubx',ubw,'lbg',lbg,'ubg',ubg); -% homotopy +%% Homotopy solver settings_homotopy = HomotopySolverOptions(); -settings_homotopy.homotopy_parameter_steering ="Direct"; -% settings_homotopy.comp_tol = 1e-12; -% settings_homotopy.max_iter = 25; -% settings_homotopy.lift_complementarities = 1; -% settings_homotopy.lift_complementarities_full = 0; -% settings_homotopy.check_B_stationarity = 1; -% settings_homotopy.comp_res_bilinear = 1; -% settings_homotopy.homotopy_parameter_steering = "Ell_1"; -settings_homotopy.plot_mpec_multipliers = true; -% settings_homotopy.comp_tol = 1e-12; -% settings_homotopy.tol_active = 1e-12; +settings_homotopy.homotopy_parameter_steering = "Direct"; [result_homotopy,stats_homotopy] = mpec_homotopy_solver(mpec,solver_initalization,settings_homotopy); -w_opt_homotopy = full(result_homotopy.x); f_opt_homotopy = full(result_homotopy.f); -% solver_initalization.w0 = w_opt_homotopy; -% mpecopt -fprintf('Problem info, n_w = %d, n_g = %d, n_comp = %d, name = %s\n', length(w),length(g),length(G),mpec_name) +w_opt_homotopy = full(result_homotopy.x); + +%% MINLP solver +% settings_minlp = MINLPSolverOptions(); +% [result_minlp,stats_minlp] = mpec_minlp_solver(mpec,solver_initalization,settings_minlp); +% f_opt_minlp = full(result_minlp.f); +% w_opt_minlp = full(result_minlp.x); + +%% MPECopt solver solver_settings = mpecopt.Options(); -solver_settings.plot_mpec_multipliers = true; -% solver_settings.rho_TR_phase_i_init = 1e1; -solver_settings.stop_if_S_stationary = 0; -% solver_settings.tol_active = 1e-12; -solver_settings.initialization_strategy = "FeasibilityEll1General"; -[results,stats] = mpec_optimizer(mpec, solver_initalization, solver_settings); -w_opt_active_set = full(results.x); -f_opt_active_set = full(results.f); -% stats.success_phase_i -% stats.success -% -if 1 - fprintf('\n'); - fprintf('Problem info,name = %s, n_w = %d, n_g = %d, n_comp = %d\n',mpec_name,length(w),length(g),length(G)) - fprintf('\n-------------------------------------------------------------------------------\n'); - fprintf('Method \t\t Objective \t comp_res \t n_biactive \t CPU time (s)\t Sucess\t Stat. type\n') - fprintf('-------------------------------------------------------------------------------\n'); - fprintf('homotopy \t %2.2e \t %2.2e \t\t %d \t\t\t %2.2f \t\t\t\t %d\t %s\n',f_opt_homotopy,stats_homotopy.comp_res,stats_homotopy.n_biactive,stats_homotopy.cpu_time_total,stats_homotopy.success,stats_homotopy.multiplier_based_stationarity) - fprintf('Active Set \t %2.2e \t %2.2e \t\t %d \t\t\t %2.2f \t\t\t\t %d\t %s\n',f_opt_active_set,stats.comp_res,stats.n_biactive,stats.cpu_time_total,stats.success,stats.multiplier_based_stationarity) - fprintf('\n'); - fprintf(' || w_homotopy - w_active_set || = %2.2e \n',norm(w_opt_homotopy-w_opt_active_set)); - fprintf(' || f_homotopy - f_active_set || = %2.2e \n',norm(f_opt_homotopy-f_opt_active_set)); -end - - -%% -figure -plot(w_opt_active_set-w_opt_homotopy); +solver_settings.relax_and_project_homotopy_parameter_steering = "Direct"; +solver_settings.settings_lpec.lpec_solver = 'Gurobi'; +% solver_settings.rho_TR_phase_i_init = 10; + +solver = mpecopt.Solver(mpec, solver_settings); +[result_mpecopt,stats_mpecopt] = solver.solve(solver_initalization); +w_opt_mpecopt = full(result_mpecopt.x); +f_opt_mpecopt = full(result_mpecopt.f); + +%% Results comparison +fprintf('\n-------------------------------------------------------------------------------\n'); +fprintf('Method \t\t Objective \t comp_res \t n_biactive \t CPU time (s)\t Success\t Stat. type\n') +fprintf('-------------------------------------------------------------------------------\n'); +fprintf('Reg \t %2.2e \t %2.2e \t\t %d \t\t\t %2.2f \t\t\t\t %d\t %s\n',f_opt_homotopy,stats_homotopy.comp_res,stats_homotopy.n_biactive,stats_homotopy.cpu_time_total,stats_homotopy.success,stats_homotopy.multiplier_based_stationarity) +% fprintf('MINLP \t\t %2.2e \t %2.2e \t\t %d \t\t\t %2.2f \t\t\t\t %d\t %s\n',f_opt_minlp,stats_minlp.comp_res,stats_minlp.n_biactive,stats_minlp.cpu_time_total,stats_minlp.success,stats_minlp.multiplier_based_stationarity) +fprintf('MPECopt \t %2.2e \t %2.2e \t\t %d \t\t\t %2.2f \t\t\t\t %d\t %s\n',f_opt_mpecopt,stats_mpecopt.comp_res,stats_mpecopt.n_biactive,stats_mpecopt.cpu_time_total,stats_mpecopt.success,stats_mpecopt.multiplier_based_stationarity) +fprintf('-------------------------------------------------------------------------------\n'); +fprintf('||w_reg - w_mpec|| = %2.2e \n',norm(w_opt_homotopy-w_opt_mpecopt)); +fprintf('||f_reg - f_mpec|| = %2.2e \n',norm(f_opt_homotopy-f_opt_mpecopt)); +% fprintf('||w_minlp - w_mpec|| = %2.2e \n',norm(w_opt_minlp-w_opt_mpecopt)); +fprintf('Solution: (%2.2f,%2.2f) \n',w_opt_mpecopt(1),w_opt_mpecopt(2)); + diff --git a/examples/examples_in_mpecopt_paper/global_vs_local_min_in_lpec.m b/examples/examples_in_mpecopt_paper/global_vs_local_min_in_lpec.m index 28a08f1..27484ef 100644 --- a/examples/examples_in_mpecopt_paper/global_vs_local_min_in_lpec.m +++ b/examples/examples_in_mpecopt_paper/global_vs_local_min_in_lpec.m @@ -1,4 +1,6 @@ clear; clc; close all +nice_plot_colors +latexify_plot() %% Very good example where global optimum leads to more iterations, and reduced lpec is useful! % Example MPCC * requires a large penalty parametres @@ -30,7 +32,7 @@ solver_settings.rho_TR_phase_ii_init = 1.5; solver_settings.TR_reducing_factor = 0.5; solver_settings.compute_tnlp_stationary_point = 0; -solver_settings.plot_lpec_iterate = 1; +solver_settings.plot_lpec_iterate = 0; solver_settings.stop_if_S_stationary = 0; solver_initalization = struct('x0', x0, 'lbx',lbx, 'ubx',ubx,'lbg',lbg, 'ubg',ubg); @@ -41,55 +43,63 @@ fprintf('objective is %2.4f \n',f_opt ); fprintf('\n'); %% -nice_plot_colors -tt = 0:1:5; -figure -plot(tt,tt*0,'k','LineWidth',1.5); -hold on -grid on -plot(tt*0,tt,'k','LineWidth',1.5); -axis equal -plot(stats_active_set.iter.X_outer(1,:),stats_active_set.iter.X_outer(2,:),'rs') -plot(stats_active_set.iter.X_lpec(1,:),stats_active_set.iter.X_lpec(2,:),'bd') - - -if 1 - for i = 1:size(stats_active_set.iter.X_outer,2)-1 - quiver(stats_active_set.iter.X_outer(1,i), stats_active_set.iter.X_outer(2,i), stats_active_set.iter.X_outer(1,i+1)-stats_active_set.iter.X_outer(1,i), stats_active_set.iter.X_outer(2,i+1)-stats_active_set.iter.X_outer(2,i), 1, 'Color',matlab_red,'LineWidth',2); - text(stats_active_set.iter.X_outer(1,i), stats_active_set.iter.X_outer(2,i),num2str(i)); - end -else - for i = 1:size(stats_active_set.iter.X_outer,2)-1 - quiver(stats_active_set.iter.X_outer(1,i), stats_active_set.iter.X_outer(2,i), -2*(a*stats_active_set.iter.X_outer(1,i)-1),-2*(stats_active_set.iter.X_outer(2,i)-1), 0.5, 'Color',matlab_red,'LineWidth',2); - text(stats_active_set.iter.X_outer(1,i), stats_active_set.iter.X_outer(2,i),num2str(i)); - end -end -xlim([-1 5]) -ylim([-1 5]) - -% Generate grid points for x and y -x = linspace(-1, 5, 50); -y = linspace(-1, 5, 50); -% Create a grid of points -[X, Y] = meshgrid(x, y); -% Evaluate the function at each point in the grid -Z = f_sym(X, Y); -% Plot the contour lines -contour(X, Y, Z, 30); -xlabel('x'); -ylabel('y'); +% tt = 0:1:5; +% figure +% plot(tt,tt*0,'k','LineWidth',1.5); +% hold on +% grid on +% plot(tt*0,tt,'k','LineWidth',1.5); +% axis equal +% plot(stats_active_set.iter.X_outer(1,:),stats_active_set.iter.X_outer(2,:),'rs') +% plot(stats_active_set.iter.X_lpec(1,:),stats_active_set.iter.X_lpec(2,:),'bd') +% +% +% if 1 +% for i = 1:size(stats_active_set.iter.X_outer,2)-1 +% quiver(stats_active_set.iter.X_outer(1,i), stats_active_set.iter.X_outer(2,i), stats_active_set.iter.X_outer(1,i+1)-stats_active_set.iter.X_outer(1,i), stats_active_set.iter.X_outer(2,i+1)-stats_active_set.iter.X_outer(2,i), 1, 'Color',matlab_red,'LineWidth',2); +% text(stats_active_set.iter.X_outer(1,i), stats_active_set.iter.X_outer(2,i),num2str(i)); +% end +% else +% for i = 1:size(stats_active_set.iter.X_outer,2)-1 +% quiver(stats_active_set.iter.X_outer(1,i), stats_active_set.iter.X_outer(2,i), -2*(a*stats_active_set.iter.X_outer(1,i)-1),-2*(stats_active_set.iter.X_outer(2,i)-1), 0.5, 'Color',matlab_red,'LineWidth',2); +% text(stats_active_set.iter.X_outer(1,i), stats_active_set.iter.X_outer(2,i),num2str(i)); +% end +% end +% xlim([-1 5]) +% ylim([-1 5]) +% +% % Generate grid points for x and y +% x = linspace(-1, 5, 50); +% y = linspace(-1, 5, 50); +% % Create a grid of points +% [X, Y] = meshgrid(x, y); +% % Evaluate the function at each point in the grid +% Z = f_sym(X, Y); +% % Plot the contour lines +% contour(X, Y, Z, 30); +% xlabel('x'); +% ylabel('y'); %% % x_opt = [1;0]; -x_opt = [0;1]; + +plot_case = 3; figure -latexify_plot() -filename = 'global_local_mpec1.pdf'; +filename = ['global_local_mpec' num2str(plot_case) '.pdf']; linewidht = 2; fontsize = 16; markersize = 10; -rho = 1.2; -rho = 0.5; +switch plot_case + case 1 + rho = 0.6; + x_opt = [0;1]; + case 2 + rho = 1.2; + x_opt = [0;1]; + otherwise + rho = 1.2; + x_opt = [1;0]; +end % Given function % Define grid [x1, x2] = meshgrid(-1.2:0.01:3, -1.2:0.01:3); @@ -102,14 +112,17 @@ dx2 = 2*(x2-1); tt = 0:0.5:3; f_grad = -f_grad_sym(x_opt(1),x_opt(2)); -quiver(x_opt(1),x_opt(2),f_grad(1),f_grad(2),'LineWidth',linewidht+0.5,'AutoScaleFactor',0.2,'Color',matlab_red) - -% case 1 -quiver(x_opt(1),x_opt(2),0,0,'LineWidth',linewidht+0.5,'AutoScaleFactor',1,'Color',matlab_magenta) -% case 2 -% quiver(x_opt(1),x_opt(2),1.2,-1,'LineWidth',linewidht+0.5,'AutoScaleFactor',1,'Color',matlab_magenta) -% case 3 -% quiver(x_opt(1),x_opt(2),-1,1.2,'LineWidth',linewidht+0.5,'AutoScaleFactor',1,'Color',matlab_magenta) +switch plot_case + case 1 + quiver(x_opt(1),x_opt(2),f_grad(1),f_grad(2),'LineWidth',linewidht+0.5,'AutoScaleFactor',0.2,'Color',matlab_red) + quiver(x_opt(1),x_opt(2),0,0,'LineWidth',linewidht+0.5,'AutoScaleFactor',1,'Color',matlab_magenta) + case 2 + quiver(x_opt(1),x_opt(2),f_grad(1),f_grad(2),'LineWidth',linewidht+0.5,'AutoScaleFactor',0.2,'Color',matlab_red) + quiver(x_opt(1),x_opt(2),1.2,-1,'LineWidth',linewidht+0.5,'AutoScaleFactor',1,'Color',matlab_magenta) + case 3 + quiver(x_opt(1),x_opt(2),f_grad(1),f_grad(2),'LineWidth',linewidht+0.6,'AutoScaleFactor',0.5,'Color',matlab_red) + quiver(x_opt(1),x_opt(2),-1,1.2,'LineWidth',linewidht+0.5,'AutoScaleFactor',1,'Color',matlab_magenta) +end plot(tt*0,tt,'k','LineWidth',linewidht) @@ -129,27 +142,29 @@ plot(tt*0+x(1)-rho,tt,'color',matlab_blood_red,'LineWidth',linewidht+0.5,'HandleVisibility','off'); x_bar = [1,0]; -f_bar = ['$f(\bar{x}) = ' sprintf('%2.2f$',f_sym(x_bar(1),x_bar(2)))]; -text(0.9,+0.3, '$\bar{x}$','FontSize',fontsize) -% text(1,-0.5, f_bar,'FontSize',fontsize) - x_hat = [0,1]; -f_hat = ['$f(\hat{x}) = ' sprintf('%2.2f$',f_sym(x_hat(1),x_hat(2)))]; +text(1.03,+0.25, '$\bar{x}$','FontSize',fontsize) text(-0.25,1, '$\hat{x}$','FontSize',fontsize) -% text(-0.5,1.2, f_hat,'FontSize',fontsize) -% Latexify labels +f_bar = ['$f(\bar{x}) = ' sprintf('%2.2f$',f_sym(x_bar(1),x_bar(2)))]; +f_hat = ['$f(\hat{x}) = ' sprintf('%2.2f$',f_sym(x_hat(1),x_hat(2)))]; + + xlabel('$x_1$', 'Interpreter', 'latex'); ylabel('$x_2$', 'Interpreter', 'latex'); axis equal xlim([-1.2 3]) ylim([-1.2 3]) -% legend({'$f(x)$', '$\nabla f(x^*)$', '$d$','$0\leq x_1 \perp x_2\geq 0$' },'BackgroundAlpha',0.9) -legend({'$f(x)$', '$-\nabla f(\bar{x})$', '$d$' },'BackgroundAlpha',0.9) + +switch plot_case + case {1,2} + legend({'$f(x)$', '$-\nabla f(\hat{x})$', '$d$' },'BackgroundAlpha',0.9) + case 3 + legend({'$f(x)$', '$-\nabla f(\bar{x})$', '$d$' },'BackgroundAlpha',0.9) +end set(gca,'FontSize',fontsize) exportgraphics(gcf, filename, 'ContentType', 'vector') - - +% legend({'$f(x)$', '$\nabla f(x^*)$', '$d$','$0\leq x_1 \perp x_2\geq 0$' },'BackgroundAlpha',0.9) diff --git a/examples/examples_in_mpecopt_paper/lpec_projection_example.m b/examples/examples_in_mpecopt_paper/lpec_acq_violation_example.m similarity index 71% rename from examples/examples_in_mpecopt_paper/lpec_projection_example.m rename to examples/examples_in_mpecopt_paper/lpec_acq_violation_example.m index 062336f..d1290d8 100644 --- a/examples/examples_in_mpecopt_paper/lpec_projection_example.m +++ b/examples/examples_in_mpecopt_paper/lpec_acq_violation_example.m @@ -1,14 +1,15 @@ clear; clc; close all - +nice_plot_colors; +latexify_plot(); %% An attemp to make the projection lpee fail import casadi.* -a = 100; x1 = SX.sym('x1'); x2 = SX.sym('x2'); x = [x1;x2]; f_sym = @(x,y) -2*x+y; -a = 0.1; +% a = 0.1; a = 1e-2; +% a = 0.0; g_sym = @(x,y) y-(x)^2-a; % good lpec inconsistent bnlp g_nabla_sym = @(x,y) [-2*x;1]; % g_sym = @(x,y) y-(x)^2; % hard to verify B @@ -36,7 +37,7 @@ % x0 = [1/a;0]; % x0 = [2;0]; solver_settings = mpecopt.Options(); -solver_settings.settings_lpec.lpec_solver ="Gurobi"; +solver_settings.settings_lpec.lpec_solver ="Highs"; solver_settings.initialization_strategy = "RelaxAndProject"; x0 = [0;0.5]; solver_initalization = struct('x0', x0, 'lbx',lbx, 'ubx',ubx,'lbg',lbg, 'ubg',ubg); @@ -47,49 +48,11 @@ fprintf('x_homotopy = (%2.4f,%2.4f), f_opt = %2.4f. \n',w_opt_homotopy(1),w_opt_homotopy(2),f_opt_homotopy); fprintf('x_active_set = (%2.4f,%2.4f), f_opt = %2.4f. \n',w_opt_active_set(1),w_opt_active_set(2),f_opt_active_set); -%% -nice_plot_colors -tt = 0:1:5; -figure -plot(tt,tt*0,'k','LineWidth',1.5); -hold on -grid on -plot(tt*0,tt,'k','LineWidth',1.5); -axis equal -plot(stats_active_set.iter.X_outer(1,:),stats_active_set.iter.X_outer(2,:),'rs') -plot(stats_active_set.iter.X_lpec(1,:),stats_active_set.iter.X_lpec(2,:),'bd') -if 1 - for i = 1:size(stats_active_set.iter.X_outer,2)-1 - quiver(stats_active_set.iter.X_outer(1,i), stats_active_set.iter.X_outer(2,i), stats_active_set.iter.X_outer(1,i+1)-stats_active_set.iter.X_outer(1,i), stats_active_set.iter.X_outer(2,i+1)-stats_active_set.iter.X_outer(2,i), 1, 'Color',matlab_red,'LineWidth',2); - text(stats_active_set.iter.X_outer(1,i), stats_active_set.iter.X_outer(2,i),num2str(i)); - end -else - for i = 1:size(stats_active_set.iter.X_outer,2)-1 - quiver(stats_active_set.iter.X_outer(1,i), stats_active_set.iter.X_outer(2,i), -2*(a*stats_active_set.iter.X_outer(1,i)-1),-2*(stats_active_set.iter.X_outer(2,i)-1), 0.5, 'Color',matlab_red,'LineWidth',2); - text(stats_active_set.iter.X_outer(1,i), stats_active_set.iter.X_outer(2,i),num2str(i)); - end -end -xlim([-1 5]) -ylim([-1 5]) - -f = @(x,y) (a*x-1).^2+(y-1).^2; -% Generate grid points for x and y -x = linspace(-1, 5, 50); -y = linspace(-1, 5, 50); -% Create a grid of points -[X, Y] = meshgrid(x, y); -% Evaluate the function at each point in the grid -Z = f_sym(X, Y); -% Plot the contour lines -contour(X, Y, Z, 30); -fimplicit(g_sym, [-2 2]) -xlabel('x'); -ylabel('y'); + %% filename1 = 'cq_mpec_1.pdf'; filename2 = 'cq_lpec_2.pdf'; rho = 0.5; -nice_plot_colors; linewidht = 2; fontsize = 16; markersize = 10; @@ -162,7 +125,7 @@ axis equal xlim([-1.2 3]) ylim([-1.2 3]) -legend({'$f(x)$', '$-\nabla f(x^*(\tau))$', '$c(x)=0$', '$c(x^*(\tau))\!+\! \nabla c(x^*(\tau))^\top d=0$', '$d$' },'BackgroundAlpha',0.8,'NumColumns',1,'FontSize',fontsize-2) +legend({'$f(x)$', '$-\nabla f(x^*)$', '$c(x)=0$', '$c(x^*)\!+\! \nabla c(x^*)^\top d=0$', '$d$' },'BackgroundAlpha',0.8,'NumColumns',1,'FontSize',fontsize-2) xlabel('$x_1$'); ylabel('$x_2$'); set(gca,'FontSize',fontsize); diff --git a/examples/examples_in_mpecopt_paper/scale1_mpcc.m b/examples/examples_in_mpecopt_paper/scale1_mpcc.m index 1c4a296..47402eb 100644 --- a/examples/examples_in_mpecopt_paper/scale1_mpcc.m +++ b/examples/examples_in_mpecopt_paper/scale1_mpcc.m @@ -1,6 +1,7 @@ clear; clc; close all - -%% Very good example where global optimum leads to more iterations, and reduced lpec is useful! +nice_plot_colors +latexify_plot(); +%% Example where global optimum leads to more iterations, and reduced lpec is useful! % Example MPCC * requires a large penalty parametres import casadi.* a = 10; @@ -44,7 +45,7 @@ solver_initalization = struct('x0', x0, 'lbx',lbx, 'ubx',ubx,'lbg',lbg, 'ubg',ubg); -[result_active_set,stats_active_set] = mpec_optimizer(mpec, solver_initalization, solver_settings); +[result_active_set, stats_active_set] = mpec_optimizer(mpec, solver_initalization, solver_settings); w_opt_active_set = full(result_active_set.x); f_opt_active_set = full(result_active_set.f); fprintf('solution is (%2.4f,%2.4f) \n',w_opt_active_set(1),w_opt_active_set(2)); @@ -61,48 +62,7 @@ fprintf(' || w_homotopy - w_active_set || = %2.2e \n',norm(w_opt_homotopy-w_opt_active_set)); %% -nice_plot_colors -tt = 0:1:5; -figure -plot(tt,tt*0,'k','LineWidth',1.5); -hold on -grid on -plot(tt*0,tt,'k','LineWidth',1.5); -axis equal -plot(stats_active_set.iter.X_outer(1,:),stats_active_set.iter.X_outer(2,:),'rs') -plot(stats_active_set.iter.X_lpec(1,:),stats_active_set.iter.X_lpec(2,:),'bd') - - -if 1 - for i = 1:size(stats_active_set.iter.X_outer,2)-1 - quiver(stats_active_set.iter.X_outer(1,i), stats_active_set.iter.X_outer(2,i), stats_active_set.iter.X_outer(1,i+1)-stats_active_set.iter.X_outer(1,i), stats_active_set.iter.X_outer(2,i+1)-stats_active_set.iter.X_outer(2,i), 1, 'Color',matlab_red,'LineWidth',2); - text(stats_active_set.iter.X_outer(1,i), stats_active_set.iter.X_outer(2,i),num2str(i)); - end -else - for i = 1:size(stats_active_set.iter.X_outer,2)-1 - quiver(stats_active_set.iter.X_outer(1,i), stats_active_set.iter.X_outer(2,i), -2*(a*stats_active_set.iter.X_outer(1,i)-1),-2*(stats_active_set.iter.X_outer(2,i)-1), 0.5, 'Color',matlab_red,'LineWidth',2); - text(stats_active_set.iter.X_outer(1,i), stats_active_set.iter.X_outer(2,i),num2str(i)); - end -end -xlim([-1 5]) -ylim([-1 5]) - -f = @(x,y) (a*x-1).^2+(y-1).^2; -% Generate grid points for x and y -x = linspace(-1, 5, 50); -y = linspace(-1, 5, 50); -% Create a grid of points -[X, Y] = meshgrid(x, y); -% Evaluate the function at each point in the grid -Z = f(X, Y); -% Plot the contour lines -contour(X, Y, Z, 30); -xlabel('x'); -ylabel('y'); -%% - figure -latexify_plot() a = 4; filename = 'lpec_small_rho1.pdf'; linewidht = 2; diff --git a/examples/getting_started.m b/examples/getting_started.m index 213bdca..e0d06fb 100644 --- a/examples/getting_started.m +++ b/examples/getting_started.m @@ -47,10 +47,11 @@ % Solver settings solver_settings = mpecopt.Options(); % change some settings -solver_settings.settings_lpec.lpec_solver = 'Highs_casadi' ; % 'Gurobi'; for best perfomance; 'Highs_casadi' - via CasADi conic -solver_settings.settings_casadi_nlp.ipopt.linear_solver = 'mumps'; % 'ma27' for better perfomance +solver_settings.settings_lpec.lpec_solver = 'Gurobi' ; % 'Gurobi'; for best perfomance; 'Highs_casadi' - via CasADi conic +solver_settings.settings_casadi_nlp.ipopt.linear_solver = 'ma27'; % 'ma27' for better perfomance solver_settings.rho_TR_phase_i_init = 1e1; -solver_settings.rho_TR_phase_ii_init = 1e-4; +solver_settings.rho_TR_phase_ii_init = 1e-2; + % Call solver % [result_active_set,stats_active_set] = mpec_optimizer(mpec, solver_initalization, solver_settings); @@ -59,14 +60,14 @@ % solve problem [result_active_set,stats_active_set] = solver.solve(solver_initalization); -w_opt_active_set = full(result_active_set.x); -f_opt_active_set = full(result_active_set.f); +w_opt_mpecopt = full(result_active_set.x); +f_opt_mpecot = full(result_active_set.f); fprintf('\n'); fprintf('\n-------------------------------------------------------------------------------\n'); fprintf('Method \t\t Objective \t comp_res \t n_biactive \t CPU time (s)\t Sucess\t Stat. type\n') fprintf('-------------------------------------------------------------------------------\n'); fprintf('Reg \t %2.2e \t %2.2e \t\t %d \t\t\t %2.2f \t\t\t\t %d\t %s\n',f_opt_homotopy,stats_homotopy.comp_res,stats_homotopy.n_biactive,stats_homotopy.cpu_time_total,stats_homotopy.success,stats_homotopy.multiplier_based_stationarity) -fprintf('Active Set \t %2.2e \t %2.2e \t\t %d \t\t\t %2.2f \t\t\t\t %d\t %s\n',f_opt_active_set,stats_active_set.comp_res,stats_active_set.n_biactive,stats_active_set.cpu_time_total,stats_active_set.success,stats_active_set.multiplier_based_stationarity) +fprintf('MPECopt \t %2.2e \t %2.2e \t\t %d \t\t\t %2.2f \t\t\t\t %d\t %s\n',f_opt_mpecot,stats_active_set.comp_res,stats_active_set.n_biactive,stats_active_set.cpu_time_total,stats_active_set.success,stats_active_set.multiplier_based_stationarity) fprintf('\n'); -fprintf(' || w_homotopy - w_active_set || = %2.2e \n',norm(w_opt_homotopy-w_opt_active_set)); +fprintf(' || w_homotopy - w_mpecopt || = %2.2e \n',norm(w_opt_homotopy-w_opt_mpecopt)); diff --git a/examples/kirches2022_ex1.m b/examples/kirches2022_ex1.m index eeb67a5..3909b2c 100644 --- a/examples/kirches2022_ex1.m +++ b/examples/kirches2022_ex1.m @@ -30,34 +30,39 @@ ubg = [ ]; mpec = struct('x', x, 'f', f, 'g', g,'G',G,'H',H,'p',p); -solver_initalization = struct('x0', x0,'lbx',lbx,'ubx',ubx,'lbg',lbg,'ubg',ubg,'p0',p0); -% Scholtes -settings = HomotopySolverOptions(); -[result_scholtes,stats_scholtes] = mpec_homotopy_solver(mpec,solver_initalization,settings); -f_opt_scholtes = full(result_scholtes.f); -x_opt_scholtes = full(result_scholtes.x); - -% x0 = w_opt_scholtes; -solver_settings = mpecopt.Options(); -% solver_settings.initialization_strategy = "TakeInitialGuessDirectly"; -solver_settings.consider_all_complementarities_in_lpec = true; -solver_settings.tol_B_stationarity = 1e-8; -solver_settings.rho_TR_phase_ii_init = 1e-2; -% solver_initalization.x0 = x0; +solver_initialization = struct('x0', x0,'lbx',lbx,'ubx',ubx,'lbg',lbg,'ubg',ubg,'p0',p0); -solver = mpecopt.Solver(mpec, solver_settings); -[result_active_set,stats_active_set] = solver.solve(solver_initalization); +%% Homotopy solver +settings_homotopy = HomotopySolverOptions(); +settings_homotopy.homotopy_parameter_steering = "Direct"; +[result_homotopy,stats_homotopy] = mpec_homotopy_solver(mpec,solver_initialization,settings_homotopy); +f_opt_homotopy = full(result_homotopy.f); +w_opt_homotopy = full(result_homotopy.x); +%% MINLP solver +settings_minlp = MINLPSolverOptions(); +[result_minlp,stats_minlp] = mpec_minlp_solver(mpec,solver_initialization,settings_minlp); +f_opt_minlp = full(result_minlp.f); +w_opt_minlp = full(result_minlp.x); -x_opt_active_set = full(result_active_set.x); -f_opt_active_set = full(result_active_set.f); +%% MPECopt solver +solver_settings = mpecopt.Options(); +solver_settings.consider_all_complementarities_in_lpec = false; +solver_settings.settings_lpec.lpec_solver = 'Gurobi'; +solver = mpecopt.Solver(mpec, solver_settings); +[result_mpecopt,stats_mpecopt] = solver.solve(solver_initialization); +w_opt_mpecopt = full(result_mpecopt.x); +f_opt_mpecopt = full(result_mpecopt.f); +%% Results comparison fprintf('\n-------------------------------------------------------------------------------\n'); -fprintf('Method \t\t Objective \t comp_res \t n_biactive \t CPU time (s)\t Sucess\t Stat. type\n') +fprintf('Method \t\t Objective \t comp_res \t n_biactive \t CPU time (s)\t Success\t Stat. type\n') fprintf('-------------------------------------------------------------------------------\n'); -fprintf('Scholtes \t %2.2e \t %2.2e \t\t %d \t\t\t %2.2f \t\t\t\t %d\t %s\n',f_opt_scholtes,stats_scholtes.comp_res,stats_scholtes.n_biactive,stats_scholtes.cpu_time_total,stats_scholtes.success,stats_scholtes.multiplier_based_stationarity) -fprintf('Active Set \t %2.2e \t %2.2e \t\t %d \t\t\t %2.2f \t\t\t\t %d\t %s\n',f_opt_active_set,stats_active_set.comp_res,stats_active_set.n_biactive,stats_active_set.cpu_time_total,stats_active_set.success,stats_active_set.multiplier_based_stationarity) -fprintf('\n'); -fprintf(' || x_scholtes - x_active_set || = %2.2e \n',norm(x_opt_scholtes-x_opt_active_set)); - +fprintf('Reg \t %2.2e \t %2.2e \t\t %d \t\t\t %2.2f \t\t\t\t %d\t %s\n',f_opt_homotopy,stats_homotopy.comp_res,stats_homotopy.n_biactive,stats_homotopy.cpu_time_total,stats_homotopy.success,stats_homotopy.multiplier_based_stationarity) +fprintf('MINLP \t\t %2.2e \t %2.2e \t\t %d \t\t\t %2.2f \t\t\t\t %d\t %s\n',f_opt_minlp,stats_minlp.comp_res,stats_minlp.n_biactive,stats_minlp.cpu_time_total,stats_minlp.success,stats_minlp.multiplier_based_stationarity) +fprintf('MPECopt \t %2.2e \t %2.2e \t\t %d \t\t\t %2.2f \t\t\t\t %d\t %s\n',f_opt_mpecopt,stats_mpecopt.comp_res,stats_mpecopt.n_biactive,stats_mpecopt.cpu_time_total,stats_mpecopt.success,stats_mpecopt.multiplier_based_stationarity) +fprintf('-------------------------------------------------------------------------------\n'); +fprintf('||w_reg - w_mpec|| = %2.2e \n',norm(w_opt_homotopy-w_opt_mpecopt)); +fprintf('||w_minlp - w_mpec|| = %2.2e \n',norm(w_opt_minlp-w_opt_mpecopt)); +fprintf('Solution: (%2.2f,%2.2f) \n',w_opt_mpecopt(1),w_opt_mpecopt(2)); \ No newline at end of file diff --git a/examples/knitro_example.m b/examples/knitro_example.m index c5ac8c9..4f8a408 100644 --- a/examples/knitro_example.m +++ b/examples/knitro_example.m @@ -45,27 +45,39 @@ lbg = zeros(4,1); ubg = zeros(4,1); -solver_settings = mpecopt.Options(); -solver_settings.relax_and_project_homotopy_parameter_steering = "Direct"; -solver_settings.initialization_strategy = "FeasibilityEll1General"; +%% Homotopy solver +settings_homotopy = HomotopySolverOptions(); +[result_homotopy,stats_homotopy] = mpec_homotopy_solver(mpec,solver_initalization,settings_homotopy); +f_opt_homotopy = full(result_homotopy.f); +w_opt_homotopy = full(result_homotopy.x); +%% MINLP solver +settings_minlp = MINLPSolverOptions(); +[result_minlp,stats_minlp] = mpec_minlp_solver(mpec,solver_initalization,settings_minlp); +f_opt_minlp = full(result_minlp.f); +w_opt_minlp = full(result_minlp.x); - % [sol_active_set,stats_active_set] = mpec_optimizer(mpec, solver_initalization, solver_settings); +%% MPECopt solver +solver_settings = mpecopt.Options(); +solver_settings.relax_and_project_homotopy_parameter_steering = "Direct"; +solver_settings.settings_lpec.lpec_solver = 'Highs_casadi'; +% solver_settings.initialization_strategy = "FeasibilityEll1General"; +solver_settings.rho_TR_phase_i_init = 10; +solver_settings.tol_active = 1e-6; solver = mpecopt.Solver(mpec, solver_settings); -[sol_active_set,stats_active_set] = solver.solve(solver_initalization); - - -x_opt_active_set = full(sol_active_set.x); -f_opt_homotopy = full(sol_homotopy.f); -f_opt_mpec_opt = sol_active_set.f; - +[result_mpecopt,stats_mpecopt] = solver.solve(solver_initalization); +w_opt_mpecopt = full(result_mpecopt.x); +f_opt_mpecopt = full(result_mpecopt.f); +%% Results comparison fprintf('\n-------------------------------------------------------------------------------\n'); -fprintf('Method \t\t Objective \t comp_res \t n_biactive \t CPU time (s)\t Sucess\t Stat. type\n') +fprintf('Method \t\t Objective \t comp_res \t n_biactive \t CPU time (s)\t Success\t Stat. type\n') fprintf('-------------------------------------------------------------------------------\n'); -fprintf('Scholtes \t %2.2e \t %2.2e \t\t %d \t\t\t %2.2f \t\t\t\t %d\t %s\n',f_opt_homotopy,stats_homotopy.comp_res,stats_homotopy.n_biactive,stats_homotopy.cpu_time_total,stats_homotopy.success,stats_homotopy.multiplier_based_stationarity) -fprintf('Active Set \t %2.2e \t %2.2e \t\t %d \t\t\t %2.2f \t\t\t\t %d\t %s\n',f_opt_mpec_opt,stats_active_set.comp_res,stats_active_set.n_biactive,stats_active_set.cpu_time_total,stats_active_set.success,stats_active_set.multiplier_based_stationarity) -fprintf('\n'); -fprintf(' || x_reg- x_active_set || = %2.2e \n',norm(x_opt_homotopy-x_opt_active_set)); - +fprintf('Reg \t %2.2e \t %2.2e \t\t %d \t\t\t %2.2f \t\t\t\t %d\t %s\n',f_opt_homotopy,stats_homotopy.comp_res,stats_homotopy.n_biactive,stats_homotopy.cpu_time_total,stats_homotopy.success,stats_homotopy.multiplier_based_stationarity) +fprintf('MINLP \t\t %2.2e \t %2.2e \t\t %d \t\t\t %2.2f \t\t\t\t %d\t %s\n',f_opt_minlp,stats_minlp.comp_res,stats_minlp.n_biactive,stats_minlp.cpu_time_total,stats_minlp.success,stats_minlp.multiplier_based_stationarity) +fprintf('MPECopt \t %2.2e \t %2.2e \t\t %d \t\t\t %2.2f \t\t\t\t %d\t %s\n',f_opt_mpecopt,stats_mpecopt.comp_res,stats_mpecopt.n_biactive,stats_mpecopt.cpu_time_total,stats_mpecopt.success,stats_mpecopt.multiplier_based_stationarity) +fprintf('-------------------------------------------------------------------------------\n'); +fprintf('||w_reg - w_mpec|| = %2.2e \n',norm(w_opt_homotopy-w_opt_mpecopt)); +fprintf('||w_minlp - w_mpec|| = %2.2e \n',norm(w_opt_minlp-w_opt_mpecopt)); +fprintf('Solution: (%2.2f,%2.2f) \n',w_opt_mpecopt(1),w_opt_mpecopt(2)); diff --git a/examples/leyffer2007_ex2.m b/examples/leyffer2007_ex2.m index b0aa164..f123595 100644 --- a/examples/leyffer2007_ex2.m +++ b/examples/leyffer2007_ex2.m @@ -31,7 +31,7 @@ %% % Pivoting solver_settings = mpecopt.Options(); -solver_settings.settings_lpec.lpec_solver ="Highs"; +solver_settings.settings_lpec.lpec_solver ="Highs_casadi"; solver_settings.initialization_strategy = "TakeInitialGuessDirectly"; solver_settings.consider_all_complementarities_in_lpec = 1; % indentifying the biactive set fails and wrong sol? % solver_settings.tol_active = 1e-5; diff --git a/examples/nonlinear_mpec.m b/examples/nonlinear_mpec.m new file mode 100644 index 0000000..5ff7740 --- /dev/null +++ b/examples/nonlinear_mpec.m @@ -0,0 +1,89 @@ +clear all +clc +close all +import casadi.* + +%% settings +% violate LICQ +violate_licq = true; +%% Example Nonlinear MPCC +% Variables + +% Variables +x1 = SX.sym('x1'); +x2 = SX.sym('x2'); +x3 = SX.sym('x3'); +x4 = SX.sym('x4'); +x = [x1; x2; x3; x4]; + +% Nonlinear objective +f = (x1-1)^2 + (x2-1)^2 + (x3-1)^2 + (x4-1)^2 ... + + 0.5*sin(x1+x2); + +% Nonlinear equality (forces pair (x1,x2) biactive possible) +g1 = x1 + x2; % = 0 → only satisfied if both x1=0 and x2=0 + % (since x1,x2 ≥ 0 from bounds) + +% Nonlinear inequality +g2 = x3^2 + x4^2 - 1; % ≥ 0 (keeps (x3,x4) on or outside unit circle) + + +% Complementarity pairs +G = [x1; x3]; +H = [x2; x4]; + +% Bounds +lbx = [0; 0; 0; 0]; +ubx = [inf; inf; inf; inf]; + +% Constraints +g = [g1; g2]; +lbg = [0; 0]; +ubg = [0; inf]; + +if violate_licq + g = [g; g2]; + lbg = [lbg; 0]; + ubg = [ubg; inf]; +end + +% Initial guess +x0 = [0.1; 0.1; 1; 0]; + +mpec = struct('x', x,'f',f,'g',g,'G',G,'H',H); +solver_initalization = struct('x0', x0,'lbx',lbx,'ubx',ubx,'lbg',lbg,'ubg',ubg); + +%% Homotopy solver +settings_homotopy = HomotopySolverOptions(); +[result_homotopy,stats_homotopy] = mpec_homotopy_solver(mpec,solver_initalization,settings_homotopy); +f_opt_homotopy = full(result_homotopy.f); +w_opt_homotopy = full(result_homotopy.x); + +%% MINLP solver +settings_minlp = MINLPSolverOptions(); +[result_minlp,stats_minlp] = mpec_minlp_solver(mpec,solver_initalization,settings_minlp); +f_opt_minlp = full(result_minlp.f); +w_opt_minlp = full(result_minlp.x); + +%% MPECopt solver +solver_settings = mpecopt.Options(); +solver_settings.consider_all_complementarities_in_lpec = false; +solver_settings.settings_lpec.lpec_solver = 'Gurobi'; +solver_settings.rho_TR_phase_i_init = 10; +solver = mpecopt.Solver(mpec, solver_settings); +[result_mpecopt,stats_mpecopt] = solver.solve(solver_initalization); +w_opt_mpecopt = full(result_mpecopt.x); +f_opt_mpecopt = full(result_mpecopt.f); + + +%% Results comparison +fprintf('\n-------------------------------------------------------------------------------\n'); +fprintf('Method \t\t Objective \t comp_res \t n_biactive \t CPU time (s)\t Success\t Stat. type\n') +fprintf('-------------------------------------------------------------------------------\n'); +fprintf('Reg \t %2.2e \t %2.2e \t\t %d \t\t\t %2.2f \t\t\t\t %d\t %s\n',f_opt_homotopy,stats_homotopy.comp_res,stats_homotopy.n_biactive,stats_homotopy.cpu_time_total,stats_homotopy.success,stats_homotopy.multiplier_based_stationarity) +fprintf('MINLP \t\t %2.2e \t %2.2e \t\t %d \t\t\t %2.2f \t\t\t\t %d\t %s\n',f_opt_minlp,stats_minlp.comp_res,stats_minlp.n_biactive,stats_minlp.cpu_time_total,stats_minlp.success,stats_minlp.multiplier_based_stationarity) +fprintf('MPECopt \t %2.2e \t %2.2e \t\t %d \t\t\t %2.2f \t\t\t\t %d\t %s\n',f_opt_mpecopt,stats_mpecopt.comp_res,stats_mpecopt.n_biactive,stats_mpecopt.cpu_time_total,stats_mpecopt.success,stats_mpecopt.multiplier_based_stationarity) +fprintf('-------------------------------------------------------------------------------\n'); +fprintf('||w_reg - w_mpec|| = %2.2e \n',norm(w_opt_homotopy-w_opt_mpecopt)); +fprintf('||w_minlp - w_mpec|| = %2.2e \n',norm(w_opt_minlp-w_opt_mpecopt)); +fprintf('Solution: (%2.2f,%2.2f) \n',w_opt_mpecopt(1),w_opt_mpecopt(2)); \ No newline at end of file diff --git a/examples/nonlinear_mpec2.m b/examples/nonlinear_mpec2.m new file mode 100644 index 0000000..6c048cc --- /dev/null +++ b/examples/nonlinear_mpec2.m @@ -0,0 +1,92 @@ +clear all +clc +close all +import casadi.* + +%% settings +% violate LICQ +violate_licq = true; +import casadi.* + +% Variables +x1 = SX.sym('x1'); +x2 = SX.sym('x2'); +x3 = SX.sym('x3'); +x4 = SX.sym('x4'); +x = [x1; x2; x3; x4]; + +% Nonlinear objective +f = 5*(x1-1)^2 + (x2-1)^2 + (x3-1)^4 + (x4-1)^4 +10*(x3+x4); + +% Nonlinear equality constraint +g1 = x1+x2+x3+x4; % = 0 + +% Nonlinear inequality constraint +g2 = (x1-0.5)^2 + (x3-0.5)^2 - 0.25; % >= 0, keeps vars near origin + +% Complementarity pairs +G = [x1; x3]; +H = [x2; x4]; + +% Bounds +lbx = [0; 0; 0; 0]; +ubx = [inf; inf; inf; inf]; + +% Collect constraints +g = [g1; g2]; +lbg = [2; 0]; % eq:0, ineq: >=0 +ubg = [2; inf]; + +% Initial guess +x0 = [0.1; 0.1; 0.1; 0.1]; + +if violate_licq + g = [g; g2]; + lbg = [lbg; 0]; + ubg = [ubg; inf]; +end + +mpec = struct('x', x,'f',f,'g',g,'G',G,'H',H); +solver_initalization = struct('x0', x0,'lbx',lbx,'ubx',ubx,'lbg',lbg,'ubg',ubg); + +%% Reg homotopy +settings_homotopy = HomotopySolverOptions(); +settings_homotopy.homotopy_parameter_steering = 'Direct'; +[result_homotopy,stats_homotopy] = mpec_homotopy_solver(mpec,solver_initalization,settings_homotopy); +f_opt_homotopy = full(result_homotopy.f); +w_opt_homotopy = full(result_homotopy.x); + +%% MINLP reformulation +settings_minlp = MINLPSolverOptions(); +[result_minlp,stats_minlp] = mpec_minlp_solver(mpec,solver_initalization,settings_minlp); +f_opt_minlp = full(result_minlp.f); +w_opt_minlp = full(result_minlp.x); + +%% MpecOpt +solver_settings = mpecopt.Options(); +solver_settings.consider_all_complementarities_in_lpec = false; +solver_settings.settings_lpec.lpec_solver = 'Gurobi'; +solver_settings.rho_TR_phase_i_init = 10; +solver_settings.relax_and_project_homotopy_parameter_steering = "Direct"; +% solver_settings.initialization_strategy = "FeasibilityEllInfGeneral"; + +solver_initalization = struct('x0', x0, 'lbx',lbx, 'ubx',ubx,'lbg',lbg, 'ubg',ubg); +% Create solver object +solver = mpecopt.Solver(mpec, solver_settings); +% solve problem +[result_mpecopt,stats_mpecopt] = solver.solve(solver_initalization); + +w_opt_mpecopt = full(result_mpecopt.x); +f_opt_mpecopt = full(result_mpecopt.f); + +%% print and compare results +fprintf('\n-------------------------------------------------------------------------------\n'); +fprintf('Method \t\t Objective \t comp_res \t n_biactive \t CPU time (s)\t Success\t Stat. type\n') +fprintf('-------------------------------------------------------------------------------\n'); +fprintf('Reg \t %2.2e \t %2.2e \t\t %d \t\t\t %2.2f \t\t\t\t %d\t %s\n',f_opt_homotopy,stats_homotopy.comp_res,stats_homotopy.n_biactive,stats_homotopy.cpu_time_total,stats_homotopy.success,stats_homotopy.multiplier_based_stationarity) +fprintf('MINLP \t\t %2.2e \t %2.2e \t\t %d \t\t\t %2.2f \t\t\t\t %d\t %s\n',f_opt_minlp,stats_minlp.comp_res,stats_minlp.n_biactive,stats_minlp.cpu_time_total,stats_minlp.success,stats_minlp.multiplier_based_stationarity) +fprintf('MPECopt \t %2.2e \t %2.2e \t\t %d \t\t\t %2.2f \t\t\t\t %d\t %s\n',f_opt_mpecopt,stats_mpecopt.comp_res,stats_mpecopt.n_biactive,stats_mpecopt.cpu_time_solvers,stats_mpecopt.success,stats_mpecopt.multiplier_based_stationarity) +fprintf('-------------------------------------------------------------------------------\n'); +fprintf('||w_reg - w_mpec|| = %2.2e \n',norm(w_opt_homotopy-w_opt_mpecopt)); +fprintf('||w_minlp - w_mpec|| = %2.2e \n',norm(w_opt_minlp-w_opt_mpecopt)); +fprintf('solution is (%2.2f,%2.2f,%2.2f,%2.2f) \n',w_opt_mpecopt(1),w_opt_mpecopt(2),w_opt_mpecopt(3),w_opt_mpecopt(4)); diff --git a/examples/nonlinear_mpec3.m b/examples/nonlinear_mpec3.m new file mode 100644 index 0000000..8db3544 --- /dev/null +++ b/examples/nonlinear_mpec3.m @@ -0,0 +1,72 @@ +clear all +clc +close all +import casadi.* + +%% settings +% Simple nonlinear MPEC test problem +import casadi.* + +% Variables +x1 = SX.sym('x1'); +x2 = SX.sym('x2'); +x = [x1; x2]; + +% Simple quadratic objective +f = (x1-1)^4 + (x2-2)^4; + +% Simple equality constraint +g1 = x1 + x2 - 1; % = 0 + +% Complementarity pairs +G = x1; +H = x2; + +% Bounds +lbx = [0; 0]; +ubx = [inf; inf]; + +% Constraints +g = g1; +lbg = 0; +ubg = 0; + +% Initial guess +x0 = [0.5; 0.5]; + +mpec = struct('x', x, 'f', f, 'g', g, 'G', G, 'H', H); +solver_initialization = struct('x0', x0, 'lbx', lbx, 'ubx', ubx, 'lbg', lbg, 'ubg', ubg); + +%% Homotopy solver +settings_homotopy = HomotopySolverOptions(); +[result_homotopy,stats_homotopy] = mpec_homotopy_solver(mpec,solver_initialization,settings_homotopy); +f_opt_homotopy = full(result_homotopy.f); +w_opt_homotopy = full(result_homotopy.x); + +%% MINLP solver +settings_minlp = MINLPSolverOptions(); +[result_minlp,stats_minlp] = mpec_minlp_solver(mpec,solver_initialization,settings_minlp); +f_opt_minlp = full(result_minlp.f); +w_opt_minlp = full(result_minlp.x); + +%% MPECopt solver +solver_settings = mpecopt.Options(); +solver_settings.consider_all_complementarities_in_lpec = false; +solver_settings.settings_lpec.lpec_solver = 'Gurobi'; +solver = mpecopt.Solver(mpec, solver_settings); +[result_mpecopt,stats_mpecopt] = solver.solve(solver_initialization); +w_opt_mpecopt = full(result_mpecopt.x); +f_opt_mpecopt = full(result_mpecopt.f); + + +%% Results comparison +fprintf('\n-------------------------------------------------------------------------------\n'); +fprintf('Method \t\t Objective \t comp_res \t n_biactive \t CPU time (s)\t Success\t Stat. type\n') +fprintf('-------------------------------------------------------------------------------\n'); +fprintf('Reg \t %2.2e \t %2.2e \t\t %d \t\t\t %2.2f \t\t\t\t %d\t %s\n',f_opt_homotopy,stats_homotopy.comp_res,stats_homotopy.n_biactive,stats_homotopy.cpu_time_total,stats_homotopy.success,stats_homotopy.multiplier_based_stationarity) +fprintf('MINLP \t\t %2.2e \t %2.2e \t\t %d \t\t\t %2.2f \t\t\t\t %d\t %s\n',f_opt_minlp,stats_minlp.comp_res,stats_minlp.n_biactive,stats_minlp.cpu_time_total,stats_minlp.success,stats_minlp.multiplier_based_stationarity) +fprintf('MPECopt \t %2.2e \t %2.2e \t\t %d \t\t\t %2.2f \t\t\t\t %d\t %s\n',f_opt_mpecopt,stats_mpecopt.comp_res,stats_mpecopt.n_biactive,stats_mpecopt.cpu_time_solvers,stats_mpecopt.success,stats_mpecopt.multiplier_based_stationarity) +fprintf('-------------------------------------------------------------------------------\n'); +fprintf('||w_reg - w_mpec|| = %2.2e \n',norm(w_opt_homotopy-w_opt_mpecopt)); +fprintf('||w_minlp - w_mpec|| = %2.2e \n',norm(w_opt_minlp-w_opt_mpecopt)); +fprintf('Solution: (%2.2f,%2.2f) \n',w_opt_mpecopt(1),w_opt_mpecopt(2)); \ No newline at end of file diff --git a/examples/ralph2_mpcc.m b/examples/ralph2_mpcc.m index ffd3d56..2b6ec2f 100644 --- a/examples/ralph2_mpcc.m +++ b/examples/ralph2_mpcc.m @@ -23,65 +23,39 @@ ubg = []; mpec = struct('x', x, 'f', f, 'g', g,'G',G,'H',H); solver_initalization = struct('x0', x0, 'lbx',lbx, 'ubx',ubx,'lbg',lbg, 'ubg',ubg); -% Scholtes -settings = HomotopySolverOptions(); -[result_homotopy,stats_homotopy] = mpec_homotopy_solver(mpec,solver_initalization,settings); + +%% Homotopy solver +settings_homotopy = HomotopySolverOptions(); +[result_homotopy,stats_homotopy] = mpec_homotopy_solver(mpec,solver_initalization,settings_homotopy); f_opt_homotopy = full(result_homotopy.f); -x_opt_homotopy = full(result_homotopy.x); -fprintf('x_opt = (%2.4f,%2.4f), f_opt = %2.4f. \n',x_opt_homotopy(1),x_opt_homotopy(2),f_opt_homotopy); -%% Settings -solver_settings = mpecopt.Options(); -solver_settings.settings_lpec.lpec_solver ="Gurobi"; -% solver_settings.initialization_strategy = "TakeInitialGuessDirectly"; -% solver_settings.initialization_strategy = "RelaxAndProject"; -solver_settings.consider_all_complementarities_in_lpec = true; -solver_settings.tol_B_stationarity = 1e-8; -solver_settings.relax_and_project_iters = 2; -solver_settings.relax_and_project_kappa = 0.5; -solver_settings.relax_and_project_sigma0 = 0.01; -solver_settings.plot_lpec_iterate = 1; -solver_initalization = struct('x0', x0, 'lbx',lbx, 'ubx',ubx,'lbg',lbg, 'ubg',ubg); -% [result_active_set,stats_active_set] = mpec_optimizer(mpec, solver_initalization, solver_settings); +w_opt_homotopy = full(result_homotopy.x); +%% MINLP solver +settings_minlp = MINLPSolverOptions(); +[result_minlp,stats_minlp] = mpec_minlp_solver(mpec,solver_initalization,settings_minlp); +f_opt_minlp = full(result_minlp.f); +w_opt_minlp = full(result_minlp.x); +%% MPECopt solver +solver_settings = mpecopt.Options(); +solver_settings.consider_all_complementarities_in_lpec = false; +solver_settings.settings_lpec.lpec_solver = 'Gurobi'; +solver_settings.rho_TR_phase_i_init = 10; +solver_settings.tol_active = 1e-6; solver = mpecopt.Solver(mpec, solver_settings); -[result_active_set,stats_active_set] = solver.solve(solver_initalization); - -x_opt_active_set = full(result_active_set.x); -f_opt_active_set = full(result_active_set.f); - +[result_mpecopt,stats_mpecopt] = solver.solve(solver_initalization); +w_opt_mpecopt = full(result_mpecopt.x); +f_opt_mpecopt = full(result_mpecopt.f); +%% Results comparison fprintf('\n-------------------------------------------------------------------------------\n'); -fprintf('Method \t\t Objective \t comp_res \t n_biactive \t CPU time (s)\t Sucess\t Stat. type\n') +fprintf('Method \t\t Objective \t comp_res \t n_biactive \t CPU time (s)\t Success\t Stat. type\n') fprintf('-------------------------------------------------------------------------------\n'); -fprintf('Scholtes \t %2.2e \t %2.2e \t\t %d \t\t\t %2.2f \t\t\t\t %d\t %s\n',f_opt_homotopy,stats_homotopy.comp_res,stats_homotopy.n_biactive,stats_homotopy.cpu_time_total,stats_homotopy.success,stats_homotopy.multiplier_based_stationarity) -fprintf('Active Set \t %2.2e \t %2.2e \t\t %d \t\t\t %2.2f \t\t\t\t %d\t %s\n',f_opt_active_set,stats_active_set.comp_res,stats_active_set.n_biactive,stats_active_set.cpu_time_total,stats_active_set.success,stats_active_set.multiplier_based_stationarity) -fprintf('\n'); -fprintf(' || x_reg - x_active_set || = %2.2e \n',norm(x_opt_homotopy-x_opt_active_set)); - -fprintf('solution is (%2.4f,%2.4f) \n',x_opt_active_set(1),x_opt_active_set(2)); +fprintf('Reg \t %2.2e \t %2.2e \t\t %d \t\t\t %2.2f \t\t\t\t %d\t %s\n',f_opt_homotopy,stats_homotopy.comp_res,stats_homotopy.n_biactive,stats_homotopy.cpu_time_total,stats_homotopy.success,stats_homotopy.multiplier_based_stationarity) +fprintf('MINLP \t\t %2.2e \t %2.2e \t\t %d \t\t\t %2.2f \t\t\t\t %d\t %s\n',f_opt_minlp,stats_minlp.comp_res,stats_minlp.n_biactive,stats_minlp.cpu_time_total,stats_minlp.success,stats_minlp.multiplier_based_stationarity) +fprintf('MPECopt \t %2.2e \t %2.2e \t\t %d \t\t\t %2.2f \t\t\t\t %d\t %s\n',f_opt_mpecopt,stats_mpecopt.comp_res,stats_mpecopt.n_biactive,stats_mpecopt.cpu_time_solvers,stats_mpecopt.success,stats_mpecopt.multiplier_based_stationarity) +fprintf('-------------------------------------------------------------------------------\n'); +fprintf('||w_reg - w_mpec|| = %2.2e \n',norm(w_opt_homotopy-w_opt_mpecopt)); +fprintf('||w_minlp - w_mpec|| = %2.2e \n',norm(w_opt_minlp-w_opt_mpecopt)); +fprintf('Solution: (%2.2f,%2.2f) \n',w_opt_mpecopt(1),w_opt_mpecopt(2)); -%% -nice_plot_colors -figure -xline(0,'k'); -hold on -grid on -yline(0,'k'); -axis equal -plot(stats_active_set.iter.X_outer(1,:),stats_active_set.iter.X_outer(2,:),'rs') -for i = 1:size(stats_active_set.iter.X_outer,2)-1 - quiver(stats_active_set.iter.X_outer(1,i), stats_active_set.iter.X_outer(2,i), stats_active_set.iter.X_outer(1,i+1)-stats_active_set.iter.X_outer(1,i), stats_active_set.iter.X_outer(2,i+1)-stats_active_set.iter.X_outer(2,i), 1, 'Color',matlab_red,'LineWidth',2); -end -f = @(x,y) x.^2+y.^2-4*x*y; -% Generate grid points for x and y -x = linspace(-1, 5, 50); -y = linspace(-1, 5, 50); -% Create a grid of points -[X, Y] = meshgrid(x, y); -% Evaluate the function at each point in the grid -Z = f(X, Y); -% Plot the contour lines -contour(X, Y, Z, 30); -xlabel('x'); -ylabel('y'); diff --git a/src/+mpecopt/Options.m b/src/+mpecopt/Options.m index f2dcc8d..1353f29 100644 --- a/src/+mpecopt/Options.m +++ b/src/+mpecopt/Options.m @@ -125,6 +125,7 @@ % obj.settings_lpec.abs_tol= 1e-8; default_tol = 1e-12; + obj.settings_casadi_nlp.detect_simple_bounds = true; obj.settings_casadi_nlp.ipopt.print_level = 0; obj.settings_casadi_nlp.print_time = 0; obj.settings_casadi_nlp.ipopt.sb = 'yes'; @@ -146,7 +147,7 @@ % obj.settings_casadi_nlp.ipopt.warm_start_bound_push = 1e-6; % obj.settings_casadi_nlp.ipopt.warm_start_bound_frac = 1e-6; obj.settings_casadi_nlp.ipopt.warm_start_entire_iterate = 'yes'; - obj.settings_casadi_nlp.ipopt.linear_solver = 'mumps'; % mumps, ma27, ma57 + obj.settings_casadi_nlp.ipopt.linear_solver = 'ma27'; % mumps, ma27, ma57 obj.settings_casadi_nlp.ipopt.fixed_variable_treatment = 'make_parameter'; % make_parameter make_constraint relax_bounds % obj.opts_casadi_nlp.snopt = struct(); diff --git a/src/+mpecopt/Solver.m b/src/+mpecopt/Solver.m index e2dd49e..9cb246c 100644 --- a/src/+mpecopt/Solver.m +++ b/src/+mpecopt/Solver.m @@ -653,6 +653,8 @@ stats.cpu_time_nlp_phase_ii = sum(stats.iter.cpu_time_nlp_phase_ii_iter); stats.cpu_time_nlp = stats.cpu_time_nlp_phase_i+stats.cpu_time_nlp_phase_ii; stats.cpu_time_globalization = sum(stats.iter.cpu_time_globalization_iter); + stats.cpu_time_solvers = stats.cpu_time_nlp+stats.cpu_time_lpec; + if isempty(stats.iter.cpu_time_nlp_iter) stats.iter.cpu_time_nlp_iter = nan; end diff --git a/src/lpec_solver.m b/src/lpec_solver.m index 0ebaba5..5ecb2d4 100644 --- a/src/lpec_solver.m +++ b/src/lpec_solver.m @@ -160,7 +160,7 @@ model.Algorithm = 'legacy'; end case "Highs_casadi" - + % See for options: https://ergo-code.github.io/HiGHS/dev/options/definitions/ A_highs = sparse([[lpec.A_eq, zeros(lpec.dims.n_eq, lpec.dims.n_auxiliary)];... [lpec.A_ineq,zeros(lpec.dims.n_ineq, lpec.dims.n_auxiliary)];... lpec.A_lpec]); @@ -173,8 +173,21 @@ highs_opts = struct; highs_opts.discrete = lpec.vtype_num; highs_opts.highs.log_to_console = false; - %highs_opts.highs.simplex_strategy = 4; - %highs_opts.error_on_fail = false; + % highs_opts.highs.simplex_strategy = 4; + % highs_opts.highs.error_on_fail = false; + + % highs_opts.highs.mip_max_nodes = settings.max_nodes; + % highs_opts.highs.time_limit = settings.max_time; + % highs_opts.highs.optimality_tolerance = 1e-9; + % highs_opts.highs.mip_feasibility_tolerance = 1e-9; + % highs_opts.highs.kkt_tolerance = 1e-9; + % highs_opts.highs.primal_feasibility_tolerance = 1e-9; + % highs_opts.highs.dual_feasibility_tolerance = 1e-9; + highs_opts.highs.mip_heuristic_effort = 0.1; + + + % highs_opts.highs.simplex_strategy = 4; + % highs_opts.error_on_fail = false; lpsol = conic('lp', 'highs', lp, highs_opts); case "Projected_Gradient" @@ -279,7 +292,7 @@ cpu_time = toc; catch model; - keyboard; + % keyboard; result_gurobi = []; end switch status diff --git a/src/mpec_homotopy_solver.m b/src/mpec_homotopy_solver.m index 50005af..ef1b18a 100644 --- a/src/mpec_homotopy_solver.m +++ b/src/mpec_homotopy_solver.m @@ -513,10 +513,11 @@ x_k_before_tnlp = x_k; %% % ----------------------------------- MULTIPLIER BASED STAT ------------------------------------- -N_TNLP = 10; % max attemps to solve a TNLP; +N_TNLP = 3; % max attemps to solve a TNLP; ii = 1; tol_ell_inf = full(h_comp_constraints_tol_fun(x_k,p0)); -tol_active = max(2*settings.tol_active*1e3,2*tol_ell_inf); +% tol_active = max(2*settings.tol_active*1e3,2*tol_ell_inf); +tol_active = settings.tol_active; if settings.compute_tnlp_stationary_point && success && settings.lift_complementarities && ~settings.problem_is_lpec fprintf('----------------------------------- determining stationarity -----------------------------------------------\n') diff --git a/src/mpec_minlp_solver.m b/src/mpec_minlp_solver.m new file mode 100644 index 0000000..a1521bb --- /dev/null +++ b/src/mpec_minlp_solver.m @@ -0,0 +1,705 @@ +function [solution,stats] = mpec_minlp_solver(mpec,solver_initalization,settings) +import casadi.* +try + x = mpec.x; +catch + x = mpec.w; +end +f = mpec.f; +g = mpec.g; +G = mpec.G; +H = mpec.H; +x_k = solver_initalization.x0; +lbx = solver_initalization.lbx; +ubx = solver_initalization.ubx; +lbg = solver_initalization.lbg; +ubg = solver_initalization.ubg; + +if isfield(mpec,'p') + p = mpec.p; + p0 = solver_initalization.p0; +else + p = []; + p0 = []; +end + +x_class = class(x); % get SX or MX class + +%% Bring MPEC into vertical form +n_primal_non_lifted = length(x); +n_comp = size(G,1); +G_fun = Function('G_fun',{x,p},{G}); +H_fun = Function('G_fun',{x,p},{H}); + +G_copy = G_fun(x,p); +H_copy = H_fun(x,p); + +% G_eval = full(G_fun(x_k,p0))*0; +% H_eval = full(H_fun(x_k,p0))*0; +if settings.initial_comp_all_zero + G_eval = zeros(n_comp,1); + H_eval = G_eval; +else + G_eval = full(G_fun(x_k,p0)); + H_eval = full(H_fun(x_k,p0)); +end + +% 0 \leq G(x) \perp H(x) \geq 0 +ind_x1 = []; +ind_x2 = []; + +% Lift complemetraties G +if settings.lift_complementarities_full + % full lifting with duplicatse; + if strcmp(x_class,'casadi.SX') + x1 = SX.sym('x1', n_comp); + else + x1 = MX.sym('x1', n_comp); + end + + lbx = [lbx;0*ones(n_comp,1)]; + ubx = [ubx;inf*ones(n_comp,1)]; + x = [x;x1]; + x_k = [x_k;G_eval]; + + g = [g;x1-G]; + lbg = [lbg;0*ones(n_comp,1)]; + ubg = [ubg;0*ones(n_comp,1)]; + + ind_x1_fun = Function('ind_1',{x},{x.jacobian(x1)}); + [ind_x1,~] = find(sparse(ind_x1_fun(x_k)==1)); +elseif settings.lift_complementarities + % lifting with only those that are not scaler + [ind_scalar,ind_nonscalar, ind_map] = find_nonscalar(G,x); + n_lift_x1 = length(ind_nonscalar); + if n_lift_x1 == 0 + try x.jacobian(G_copy); + catch + n_lift_x1 = length(G_copy); + ind_nonscalar = 1:n_lift_x1; + ind_scalar = []; + end + end + if n_lift_x1 > 0 + if strcmp(x_class,'casadi.SX') + x1_lift = SX.sym('x1_lift',n_lift_x1); + else + x1_lift = MX.sym('x1_lift',n_lift_x1); + end + + lbx = [lbx;0*ones(n_lift_x1,1)]; + ubx = [ubx;inf*ones(n_lift_x1 ,1)]; + x = [x;x1_lift]; + x_k = [x_k;G_eval(ind_nonscalar)]; + % x1 = [x(ind_scalar);x1_lift]; + % ind_x1 = [ind_map;n_primal_non_lifted+1:n_primal_non_lifted+n_lift_x1]; + % lift + g = [g;x1_lift-G(ind_nonscalar)]; + lbg = [lbg;0*ones(n_lift_x1 ,1)]; + ubg = [ubg;0*ones(n_lift_x1 ,1)]; + + x1 = G_copy; + x1(ind_nonscalar) = x1_lift; + else + x1 = G; + % ind_x1 = ind_map; + end +else + x1 = G; % (an expression, a nontivial function of x) + if settings.update_comp_lower_bounds + g = [g;G]; + lbg = [lbg;0*ones(n_comp,1)]; + ubg = [ubg; inf*ones(n_comp,1)]; + end +end + +% Lift complemetraties H +if settings.lift_complementarities_full + % full lifting with duplicatse; + if strcmp(x_class,'casadi.SX') + x2 = SX.sym('x2', n_comp); + else + x2 = MX.sym('x2', n_comp); + end + lbx = [lbx;0*ones(n_comp,1)]; + ubx = [ubx;inf*ones(n_comp,1)]; + x = [x;x2]; + x_k = [x_k;H_eval ]; + + g = [g;x2-H]; + lbg = [lbg;0*ones(n_comp,1)]; + ubg = [ubg;0*ones(n_comp,1)]; + + ind_x2_fun = Function('ind_2',{x},{x.jacobian(x2)}); + [ind_x2,~] = find(sparse(ind_x2_fun(x_k))==1); +elseif settings.lift_complementarities + % lifting with only those that are not scaler + [ind_scalar,ind_nonscalar, ind_map] = find_nonscalar(H,x); + n_lift_x2 = length(ind_nonscalar); + + if n_lift_x2 == 0 + try x.jacobian(H_copy); + catch + n_lift_x2 = length(H_copy); + ind_nonscalar = 1:n_lift_x2; + ind_scalar = []; + end + end + if n_lift_x2 > 0 + if strcmp(x_class,'casadi.SX') + x2_lift = SX.sym('x2_lift',n_lift_x2); + else + x2_lift = MX.sym('x2_lift',n_lift_x2); + end + lbx = [lbx;0*ones(n_lift_x2,1)]; + ubx = [ubx;inf*ones(n_lift_x2 ,1)]; + x = [x;x2_lift]; + x_k = [x_k;H_eval(ind_nonscalar)]; + % ind_x2 = [ind_map;n_primal_non_lifted+n_lift_x1+1:n_primal_non_lifted+n_lift_x1+n_lift_x2]; + % lift + g = [g;x2_lift-H(ind_nonscalar)]; + lbg = [lbg;0*ones(n_lift_x2 ,1)]; + ubg = [ubg;0*ones(n_lift_x2 ,1)]; + + x2 = H_copy; + x2(ind_nonscalar) = x2_lift; + else + x2 = H; + % ind_x2 = ind_map; + end +else + x2 = H; % (an expression, a nontivial function of x) + if settings.update_comp_lower_bounds + g = [g;H]; + lbg = [lbg;0*ones(n_comp,1)]; + ubg = [ubg; inf*ones(n_comp,1)]; + end +end + +if settings.lift_complementarities_full || settings.lift_complementarities + ind_x1_fun = Function('ind_1',{x},{x.jacobian(x1)}); + [ind_x1,~] = find(sparse(ind_x1_fun(x_k)==1)); + ind_x2_fun = Function('ind_2',{x},{x.jacobian(x2)}); + [ind_x2,~] = find(sparse(ind_x2_fun(x_k))==1); +end +% update lb on x1 and x2 +if settings.update_comp_lower_bounds + lbx(ind_x1) = 0; + lbx(ind_x2) = 0; +end +n_primal = length(x); +ind_x0 = [1:n_primal]'; +ind_x0([ind_x1,ind_x2]) = []; + + +%% primal infeasiblity (% todo; there is repetition of code in creating the lpec) +% Split into equalites and inequalities +ind_g_eq = find(lbg == ubg); +ind_g_ineq = find(lbg < ubg); + +ind_g_ineq_lb = find(lbg >- inf & lbg < ubg); +ind_g_ineq_ub = find(ubg < inf & lbg < ubg); + +ind_x_lb = find(lbx > -inf); +ind_x_ub = find(ubx < inf); + +n_eq = length(ind_g_eq); +n_g_ineq_ub = length(ind_g_ineq_ub); +n_g_ineq_lb = length(ind_g_ineq_lb); + +n_ubx = length(ind_x_ub); +n_lbx = length(ind_x_lb); + +lbx_reduced = lbx(ind_x_lb); +ubx_reduced = ubx(ind_x_ub); +g_sym = g; +% Generate casadi functions for objective and constraint function evaluations +nabla_f = f.jacobian(x)'; +% Zero order +g_eq = g(ind_g_eq)-lbg(ind_g_eq); % g_eq = g - g_lb = 0 +g_ineq_ub = ubg(ind_g_ineq_ub)-g(ind_g_ineq_ub); % g_ineq_ub = g_ub - g >= 0 +g_ineq_lb = g(ind_g_ineq_lb)-lbg(ind_g_ineq_lb); % g_ineq_lb = g - g_lb >= 0 +g_ineq = [ubg(ind_g_ineq_ub)-g(ind_g_ineq_ub);... + g(ind_g_ineq_lb)-lbg(ind_g_ineq_lb)]; % g_ineq = [g_ub; g_lb] +n_ineq = size(g_ineq,1); + +h_eq = max(abs(g_eq)); +h_ineq_ub = max(min(g_ineq_ub,0)); +h_ineq_lb = max(min(g_ineq_lb,0)); +h_ubx = max(min(ubx-x,0)); +h_lbx = max(min(x-lbx,0)); +% Summary +h_std = max([h_eq;h_ineq_ub;h_ineq_lb;h_ubx;h_lbx]); + + +%% Prepare LPEC for checking B-stationarity +if settings.check_B_stationarity + % Split into equalites and inequalities + ind_g_eq = find(lbg == ubg); + ind_g_ineq = find(lbg < ubg); + + ind_g_ineq_lb = find(lbg >- inf & lbg < ubg); + ind_g_ineq_ub = find(ubg < inf & lbg < ubg); + + ind_x_lb = find(lbx > -inf); + ind_x_ub = find(ubx < inf); + + n_eq = length(ind_g_eq); + n_g_ineq_ub = length(ind_g_ineq_ub); + n_g_ineq_lb = length(ind_g_ineq_lb); + + n_ubx = length(ind_x_ub); + n_lbx = length(ind_x_lb); + + lbx_reduced = lbx(ind_x_lb); + ubx_reduced = ubx(ind_x_ub); + g_sym = g; + % Generate casadi functions for objective and constraint function evaluations + nabla_f = f.jacobian(x)'; + % Zero order + g_eq = g(ind_g_eq)-lbg(ind_g_eq); % g_eq = g - g_lb = 0 + g_ineq_ub = ubg(ind_g_ineq_ub)-g(ind_g_ineq_ub); % g_ineq_ub = g_ub - g >= 0 + g_ineq_lb = g(ind_g_ineq_lb)-lbg(ind_g_ineq_lb); % g_ineq_lb = g - g_lb >= 0 + g_ineq = [ubg(ind_g_ineq_ub)-g(ind_g_ineq_ub);... + g(ind_g_ineq_lb)-lbg(ind_g_ineq_lb)]; % g_ineq = [g_ub; g_lb] + n_ineq = size(g_ineq,1); + % first-order Constraint Jacobians + if n_eq > 0 + nabla_g = g_sym.jacobian(x); + nabla_g_eq = g_eq.jacobian(x); + else + nabla_g = []; + nabla_g_eq = []; + end + if n_ineq > 0 + nabla_g_ineq_ub = g_ineq_ub.jacobian(x); + nabla_g_ineq_lb = g_ineq_lb.jacobian(x); + else + nabla_g_ineq_ub = []; + nabla_g_ineq_lb = []; + end + nabla_g_ineq = [nabla_g_ineq_ub; nabla_g_ineq_lb]; + + % CasADi functions of function evaluations and derivaties + % Zero order (objective and constraint function evaluations) + f_fun = Function('f_fun',{x,p},{f}); + g_eq_fun = Function('g_eq_fun',{x,p},{g_eq}); + g_ineq_ub_fun = Function('g_ineq_ub_fun',{x,p},{g_ineq_ub}); + g_ineq_lb_fun = Function('g_ineq_lb_fun',{x,p},{g_ineq_lb}); + g_ineq_fun = Function('g_ineq_lb_fun',{x,p},{g_ineq}); + % First order (Gradients and Jacobian) + nabla_f_fun = Function('nabla_f_fun',{x,p},{nabla_f}); + nabla_g_fun = Function('nabla_g_fun',{x,p},{nabla_g}); + nabla_g_eq_fun = Function('nabla_g_eq_fun',{x,p},{nabla_g_eq}); + nabla_g_ineq_ub_fun = Function('nabla_g_ineq_ub_fun',{x,p},{nabla_g_ineq_ub}); + nabla_g_ineq_lb_fun = Function('nabla_g_ineq_lb_fun',{x,p},{nabla_g_ineq_lb}); + nabla_g_ineq_fun = Function('nabla_g_ineq_lb_fun',{x,p},{nabla_g_ineq}); + + dims.n_primal = n_primal; + dims.n_comp = n_comp; + dims.n_eq = n_eq; + dims.n_ineq = n_ineq; + + dims.ind_x0 = ind_x0; + dims.ind_x1 = ind_x1; + dims.ind_x2 = ind_x2; + dims.n_slacks = 0; + + if strcmp(x_class,'casadi.SX') + M = SX.sym('M', 1); + y = SX.sym('y', dims.n_comp); % binary variablkes for comp. constraints + else + M = MX.sym('M', 1); + y = MX.sym('y', dims.n_comp); % binary variablkes for comp. constraints + end + + % Big M reformulation of complementarities + A_lpec_sym = [-x1+M*y; -x2-M*y]; + A_lpec = A_lpec_sym.jacobian([x;y]); + A_lpec_fun = Function('A_lpec_fun',{M},{A_lpec}); + b_res = [x1;x2-M]; + b_res_fun = Function('b_res_fun',{x,p,M},{b_res}); + % for initalzing of an lpec + sense_B = repmat('>',1,2*n_comp); + sense = [repmat('=',1,dims.n_eq), repmat('>',1,dims.n_ineq), sense_B]; + vtype = [repmat('C',1,n_primal), repmat('B',1,n_comp)]; + vtype_num = [repmat(0,1,n_primal), repmat(1,1,n_comp)]; + A_lpec_k = A_lpec; + % b_lpec_k = full(b_res_fun(x_k,p0)); + dims.n_auxiliary = n_comp; + lb_binary = 0*ones(n_comp,1); + ub_binary = 1*ones(n_comp,1); + lpec_functions.A_lpec_fun = A_lpec_fun; + lpec_functions.b_res_fun = b_res_fun; + lpec_functions.vtype = vtype; + lpec_functions.vtype_num = vtype_num; + lpec_functions.sense = sense; + lpec_functions.lb_binary = lb_binary; + lpec_functions.ub_binary = ub_binary; + lpec_functions.g_eq_fun = g_eq_fun; + lpec_functions.f_fun = f_fun; + lpec_functions.g_ineq_fun = g_ineq_fun; + lpec_functions.nabla_g_eq_fun = nabla_g_eq_fun; + lpec_functions.nabla_g_ineq_fun = nabla_g_ineq_fun; + lpec_functions.nabla_f_fun = nabla_f_fun; + lpec_functions.lbx = lbx; + lpec_functions.ubx = ubx; +end +% lpec_casadi = create_lpec_functions(mpec_casadi,dims,settings,solver_initalization); + +%% MINLP reformulation +if strcmp(x_class,'casadi.SX') + M_minlp = SX.sym('M_minlp', 1); + y_minlp = SX.sym('y_minlp', dims.n_comp); +else + M_minlp = MX.sym('M_minlp', 1); + y_minlp = MX.sym('y_minlp', dims.n_comp); +end +discrete = [false(n_primal,1); true(n_comp,1)]; +opts_minlp = settings.settings_casadi_nlp; +opts_minlp.discrete = discrete; +e = ones(n_comp,1); +g_comp = [x1-y_minlp*M_minlp; x2-(e-y_minlp)*M_minlp]; +lbg_comp = -inf(2*n_comp,1); +ubg_comp = zeros(2*n_comp,1); +p_minlp = [p; M_minlp]; +p0_minlp = [p0; settings.BigM_minlp]; +x_minlp = [x;y_minlp]; +lbx_minlp = [lbx;zeros(n_comp,1)]; +ubx_minlp = [ubx;ones(n_comp,1)]; +f_fun = Function('f_fun',{x,p},{f}); +h_std_fun = Function('h_std_fun',{x,p},{h_std}); +g_minlp = [g;g_comp]; +lbg_minlp = [lbg; lbg_comp]; +ubg_minlp = [ubg; ubg_comp]; +h_comp_constraints_tol_fun = Function('h_comp_constraints_tol_fun',{x,p},{max(min(abs(x1),abs(x2)))}); +if settings.comp_res_bilinear + h_comp_constraints_fun = Function('h_comp_constraints_fun',{x,p},{max(abs(x1).*abs(x2))}); +else + h_comp_constraints_fun = Function('h_comp_constraints_fun',{x,p},{max(min(abs(x1),abs(x2)))}); +end +%% minlp solver +minlp_prob = struct('x', x_minlp, 'f', f, 'g', g_minlp,'p',p_minlp); +solver = nlpsol('minlp_solver', 'bonmin', minlp_prob, opts_minlp); +n_nlp_total = 0; + +%% Tnlp solver +dummy_settings = HomotopySolverOptions(); +opts_tnlp = dummy_settings.settings_casadi_nlp; +tnlp_prob = struct('x', x, 'f', f, 'g', g,'p',p); +tnlp_solver = nlpsol('tnlp_solver', 'ipopt', tnlp_prob, opts_tnlp); + +%% Solve MINLP +cpu_time_iters = []; +success = false; +problem_infeasible = false; +max_iterations_reached = false; +if settings.verbose_solver + fprintf('----------------------------------- solving MINLP with bonmin -----------------------------------------------\n') +end +X_outer = [x_k]; +cpu_time_nlp_iter = []; +x_k_minlp = [x_k; x_k(ind_x1) > x_k(ind_x2)]; +%% Solve MINLP + +comp_res_ii = full(h_comp_constraints_fun(x_k,p0)); +f_k = full(f_fun(x_k,p0)); +inf_pr_ii = full(h_std_fun(x_k,p0)); +t_phase_ii_start = tic; +t_nlp_start_ii = tic; +solution = solver('x0',x_k_minlp,'p',p0_minlp,'lbx',lbx_minlp,'ubx',ubx_minlp,'lbg',lbg_minlp,'ubg',ubg_minlp); +t_nlp_end_ii = toc(t_nlp_start_ii); +cpu_time_phase_ii = toc(t_phase_ii_start); +n_nlp_total = n_nlp_total + 1; +cpu_time_nlp_iter = [cpu_time_nlp_iter,t_nlp_end_ii]; +stats = solver.stats(); +x_k_minlp = full(solution.x); +x_k = x_k_minlp(1:n_primal); +comp_res_ii = full(h_comp_constraints_fun(x_k,p0)); +f_k = full(f_fun(x_k,p0)); +X_outer = [X_outer, x_k]; + +%% status of last iter +if isequal(stats.return_status,'SUCCESS') + success = true; + solver_message = 'Stationary point was found successfuly.'; +else + success = false; + solver_message = stats.return_status; +end + +if isequal(stats.return_status,'Maximum_Iterations_Exceeded') + success = false; + solver_message = 'Last NLP in homotopy reached maximum number of iterations.'; +end + +if comp_res_ii > settings.comp_tol + success = false; + solver_message = 'Complementarity tolerance not satisfied.'; +end + +if isequal(stats.return_status,'INFEASIBLE') + problem_infeasible = true; + success = false; + solver_message = 'Infeasible problem detected.'; +elseif isequal(stats.return_status,'Restoration_Failed') + problem_infeasible = true; + success = false; + solver_message = 'Infeasible problem detected.'; +else + problem_infeasible = false; +end + +if full(h_comp_constraints_tol_fun(x_k,p0)) <= settings.tol && ~success + success = true; + solver_message = 'Feasible point found.'; +end + + + +%% biactive +G_res = full(G_fun(x_k(1:n_primal_non_lifted),p0)); +H_res = full(H_fun(x_k(1:n_primal_non_lifted),p0)); + +if settings.check_B_stationarity + active_set_estimate_k = find_active_sets(x_k, dims, settings.tol_active); + n_biactive = sum(active_set_estimate_k.I_00); +else + n_biactive = sum(G_res+H_res= 1e2 && settings.rescale_large_objective_gradients + nabla_f_k = nabla_f_k./(norm(nabla_f_k)); + lpec.f = nabla_f_k; + end + while ii <= N_LPEC + % full(h_comp_constraints_fun(x_k,p0)) + lpec.rho_TR = rho_TR; + [results_lpec,stats_lpec] = lpec_solver(lpec,settings_lpec); + f_lpec = results_lpec.f_opt; + d_lpec = results_lpec.d_lpec; + if abs(f_lpec) <= settings.tol_B_stationarity + % if norm((d_lpec),inf)<= settings.tol_B_stationarity + b_stationarity = true; + break; + end + rho_TR = 0.1*rho_TR; + ii = ii+1; + end + f_lpec = results_lpec.f_opt; +end + +% double check biactive set +if ~settings.problem_is_lpec && b_stationarity && success && ~strcmp(multiplier_based_stationarity,'S') + % active_set_estimate_k = find_active_sets(x_k(1:n_primal)+results_lpec.d_lpec, dims, settings.tol_active); + active_set_estimate_k = find_active_sets(x_tnlp(1:n_primal), dims, settings.tol_active); + n_biactive = sum(active_set_estimate_k.I_00); + if n_biactive == 0 + multiplier_based_stationarity = 'S'; + end +end + +% use LPEC active set, if difference not too big take this as solution +if ~settings.problem_is_lpec && success && ~b_stationarity + % if strcmp(multiplier_based_stationarity,'X') + try + % keyboard; + active_set_estimate_k = find_active_sets(x_k+results_lpec.d_lpec, dims, settings.tol_active); + ubx(dims.ind_x1(active_set_estimate_k.I_0_plus)) = 0; + ubx(dims.ind_x2(active_set_estimate_k.I_plus_0)) = 0; + ubx(dims.ind_x1(active_set_estimate_k.I_00)) = 0; + ubx(dims.ind_x2(active_set_estimate_k.I_00)) = 0; + + solution = solver('x0',x_k,'p',p0,'lbx',lbx,'ubx',ubx,'lbg',lbg,'ubg',ubg); + x_tnlp = full(solution.x); + f_tnlp = full(solution.f); + lambda_x_tnlp = full(solution.lam_x); + comp_res_tnlp = full(h_comp_constraints_fun(x_k,p0)); + inf_pr_tnlp = stats.iterations.inf_pr(end); + inf_du_tnlp = stats.iterations.inf_du(end); + fprintf('----------------------------------- lpec active set -----------------------------------------------\n') + if settings.verbose_solver + fprintf('%d \t %2.2e\t %2.2e\t %2.2e\t %2.2e\t %d \t\t %2.2e\t \t\t %s \n',ii,f_tnlp,comp_res_tnlp,inf_pr_tnlp,inf_du_tnlp,stats.iter_count,0,(stats.return_status)); + end + fprintf('\t\t ||x_tnlp - x_k|| = %2.2e, |f_tnlp-f_k| = %2.2e \n', norm(x_tnlp-x_k,inf),abs(f_tnlp-f_k)) + % Compute multipliers + ii = 1; + rho_TR = 1e-3; + if norm(x_tnlp-x_k,inf) <= 1e-6 || abs(f_tnlp-f_k)/abs(f_k+1e-16) <= 1e-6 + [multiplier_based_stationarity, ~] = determine_multipliers_based_stationary_point(x_tnlp,lambda_x_tnlp,dims,settings); + active_set_estimate_k = find_active_sets(x_tnlp, dims, settings.tol_active); + n_biactive = sum(active_set_estimate_k.I_00); + % check b stationarity + lpec = create_lpec_subproblem(x_tnlp(1:n_primal),p0 ,rho_TR, lpec_functions, dims, settings, settings.tol_active); + else + lpec = create_lpec_subproblem(x_k_before_tnlp(1:n_primal),p0 ,rho_TR, lpec_functions, dims, settings, settings.tol_active); + end + while ii <= N_LPEC + lpec.rho_TR = rho_TR; + [results_lpec, stats_lpec] = lpec_solver(lpec,settings_lpec); + f_lpec = results_lpec.f_opt; + d_lpec = results_lpec.d_lpec; + % if abs(f_lpec) <= settings.tol_B_stationarity + if abs(f_lpec) <= settings.tol_B_stationarity + b_stationarity = true; + x_k = x_tnlp; + break; + end + rho_TR = 0.1*rho_TR; + ii = ii+1; + end + catch + end +end + +if strcmp(multiplier_based_stationarity,'S') + b_stationarity = true; +end +if settings.verbose_summary + fprintf('\n'); + print_iter_summary_minlp(f_k,inf_pr_ii,comp_res_ii,solver_message,multiplier_based_stationarity,b_stationarity,n_biactive,f_lpec,rho_TR); +end + +% if strcmp(multiplier_based_stationarity,'X') +% keyboard; +% end + +% if ~b_stationarity +% keyboard; +% end +% if ~b_stationarity && success && ~settings.problem_is_lpec +% f_lpec +% keyboard; +% end +%% results +solution.f = f_k; +solution.x_lifted = x_k; +solution.x = x_k(1:n_primal_non_lifted); +solution.x1 = full(G_fun(x_k(1:n_primal_non_lifted),p0)); +solution.x2 = full(H_fun(x_k(1:n_primal_non_lifted),p0)); + +stats.comp_res = comp_res_ii; +stats.success = success; +stats.success_phase_i = true; +stats.f_lpec = f_lpec; +stats.problem_infeasible = problem_infeasible; +stats.max_iterations_reached = max_iterations_reached; +stats.solved_in_phase_i = false; % homotopy sovlers dont have a phase i +stats.multiplier_based_stationarity = multiplier_based_stationarity; +stats.b_stationarity = b_stationarity; +stats.n_biactive = n_biactive; +stats.success_despite_infeasiblity = success; +% total time per phase +stats.cpu_time_total = cpu_time_phase_ii; +stats.cpu_time_phase_i = 0; +stats.cpu_time_phase_ii = cpu_time_phase_ii; +% nlp time per phase +stats.cpu_time_nlp = sum(cpu_time_nlp_iter); +stats.cpu_time_nlp_iter = cpu_time_nlp_iter; +stats.cpu_time_nlp_phase_i = 0; +stats.cpu_time_nlp_phase_ii = sum(cpu_time_nlp_iter); +stats.n_nlp_total = n_nlp_total; +%lpec time +stats.cpu_time_lpec = 0; +stats.cpu_time_lpec_phase_i = 0; +stats.cpu_time_lpec_phase_ii = 0; + +% cpu time per iter +stats.iter.cpu_time_lpec_phase_i_iter = 0; +stats.iter.cpu_time_lpec_phase_ii_iter = 0; +stats.iter.cpu_time_nlp_phase_i_iter = 0; +stats.iter.cpu_time_nlp_phase_ii_iter = cpu_time_nlp_iter; + +stats.n_lpec_total = 0; + +if sum(cpu_time_nlp_iter)>0 + stats.iter.cpu_time_nlp_iter = cpu_time_nlp_iter; +else + stats.iter.cpu_time_nlp_iter = nan+cpu_time_nlp_iter; +end +stats.iter.active_set_changes = 0; +stats.iter.X_outer = X_outer; +end + diff --git a/src/options/HomotopySolverOptions.m b/src/options/HomotopySolverOptions.m index fbaff66..72550c4 100644 --- a/src/options/HomotopySolverOptions.m +++ b/src/options/HomotopySolverOptions.m @@ -9,7 +9,7 @@ % Stopping criteria/tolernaces max_iter(1,1) double {mustBeInteger, mustBePositive} = 15; tol(1,1) double {mustBeReal, mustBeNonnegative} = 1e-9; - tol_active(1,1) double {mustBeReal, mustBeNonnegative} = 1e-9; % below this treshold a constraint is considered to be active + tol_active(1,1) double {mustBeReal, mustBeNonnegative} = 1e-10; % below this treshold a constraint is considered to be active comp_tol(1,1) double {mustBeReal, mustBeNonnegative} = 1e-9; plot_mpec_multipliers(1,1) logical = false; initial_comp_all_zero(1,1) logical = false; diff --git a/src/options/LPECSolverOptions.m b/src/options/LPECSolverOptions.m index 7f28697..2f4b032 100644 --- a/src/options/LPECSolverOptions.m +++ b/src/options/LPECSolverOptions.m @@ -5,7 +5,7 @@ solver_name {mustBeTextScalar} = 'lpec_solver'; % MILP Solver for LPEC settings - lpec_solver(1,1) LpecSolver = LpecSolver.Highs; % LpecSolver.Gurobi + lpec_solver(1,1) LpecSolver = LpecSolver.Gurobi; % LpecSolver.Gurobi max_nodes(1,1) double {mustBeInteger, mustBePositive} = 5e2; max_time(1,1) double {mustBeReal, mustBePositive} = 2e2; % ~3 min time out; cutoff(1,1) double {mustBeReal} = 10; diff --git a/src/options/MINLPSolverOptions.m b/src/options/MINLPSolverOptions.m new file mode 100644 index 0000000..ee780d4 --- /dev/null +++ b/src/options/MINLPSolverOptions.m @@ -0,0 +1,95 @@ +% Copyright (c) 2025, Armin Nurkanović +classdef MINLPSolverOptions< handle + properties + % General + solver_name {mustBeTextScalar} = 'mpec_minlp'; + casadi_symbolic_mode {mustBeMember(casadi_symbolic_mode,{'casadi.SX', 'casadi.MX'})} = 'casadi.SX'; + comp_res_bilinear(1,1) logical = true; % if true comp_res = max(x1.*x2), if false, comp_res = max(min(x1,x2)); the bliniear shrinks faster, e.g. x1 = 1e-3,x2 = 1e-3, bilinear = 1e-6, std 1e-3; + + % Stopping criteria/tolernaces + max_iter(1,1) double {mustBeInteger, mustBePositive} = 15; + tol(1,1) double {mustBeReal, mustBeNonnegative} = 1e-9; + tol_active(1,1) double {mustBeReal, mustBeNonnegative} = 1e-10; % below this treshold a constraint is considered to be active + comp_tol(1,1) double {mustBeReal, mustBeNonnegative} = 1e-9; + plot_mpec_multipliers(1,1) logical = false; + initial_comp_all_zero(1,1) logical = false; + + success_only_if_s_stationary(1,1) logical = false; + compute_tnlp_stationary_point(1,1) logical = true; + check_B_stationarity(1,1) logical = true; + tol_B_stationarity(1,1) double {mustBeReal, mustBeNonnegative} = 1e-8; + consider_all_complementarities_in_lpec(1,1) logical = true; % for B stat check + rescale_large_objective_gradients (1,1) logical = true; + + lift_complementarities(1,1) logical = true; % smart lifting, dont introduce slack variable for those that are already scalar; + lift_complementarities_full(1,1) logical = false; % brute force lifting without checking are some parts of G or H already scalar; + aggregate_comps(1,1) logical = false; + + % LPEC settings (needed in B stationarity detection) + problem_is_lpec(1,1) logical = false; + update_comp_lower_bounds(1,1) logical = true; % if true set lbx and ubx of comp constraints to zero + BigM(1,1) double {mustBeReal, mustBeNonnegative} = 1e4; % LPEC + BigM_minlp(1,1) double {mustBeReal, mustBeNonnegative} = 1e4; % MINLP + + % verbose + verbose_solver(1,1) logical = true; + verbose_summary(1,1) logical = true; + + % NLP solver settings + settings_casadi_nlp + + end + + methods + function obj = MINLPSolverOptions() + % see + + + + default_tol = 1e-12; + % --------- MINLP Specific settings + % bonmin: https://www.coin-or.org/Bonmin/option_pages/options_list_bonmin.html + obj.settings_casadi_nlp.error_on_fail = false; + obj.settings_casadi_nlp.bonmin.algorithm = 'B-BB'; + obj.settings_casadi_nlp.bonmin.warm_start = 'interior_point'; + obj.settings_casadi_nlp.bonmin.node_limit = 75; + obj.settings_casadi_nlp.bonmin.time_limit = 600; + + % options for the algorithm + % B-BB % simple branch-and-bound algorithm, + % B-OA % OA Decomposition algorithm, + % B-QG % Quesada and Grossmann branch-and-cut algorithm, + % B-Hyb % hybrid outer approximation based branch-and-cut, + % B-Ecp % ECP cuts based branch-and-cut a la FilMINT. + % B-iFP % Iterated Feasibility Pump for MINLP. + + % ------ NLP Solver settings for ipopt --------------- + % ipopt: https://coin-or.github.io/Ipopt/OPTIONS.html + obj.settings_casadi_nlp.bonmin.print_level = 0; + obj.settings_casadi_nlp.bonmin.print_level = 0; + obj.settings_casadi_nlp.print_time = 0; + obj.settings_casadi_nlp.verbose = false; + obj.settings_casadi_nlp.bonmin.max_iter = 3000; + % obj.settings_casadi_nlp.bonmin.bound_relax_factor = 0; + obj.settings_casadi_nlp.bonmin.bound_relax_factor = default_tol; + obj.settings_casadi_nlp.bonmin.honor_original_bounds = 'yes'; + obj.settings_casadi_nlp.bonmin.tol = default_tol; + obj.settings_casadi_nlp.bonmin.dual_inf_tol = default_tol; + obj.settings_casadi_nlp.bonmin.dual_inf_tol = default_tol; + obj.settings_casadi_nlp.bonmin.compl_inf_tol = default_tol; + obj.settings_casadi_nlp.bonmin.acceptable_tol = 1e-9; + obj.settings_casadi_nlp.bonmin.mu_strategy = 'adaptive'; + obj.settings_casadi_nlp.bonmin.mu_oracle = 'quality-function'; + % obj.settings_casadi_nlp.bonmin.warm_start_init_point = 'yes'; + % obj.settings_casadi_nlp.bonmin.warm_start_entire_iterate = 'yes'; + obj.settings_casadi_nlp.bonmin.linear_solver = 'ma27'; % 'mumps'; ma57 + + % Some BonminSettings + % obj.opts_casadi_nlp.snopt = struct(); + % obj.opts_casadi_nlp.worhp = struct(); + % obj.opts_casadi_nlp.uno = struct(); + % obj.p_val = [obj.sigma_0]; + end + + end +end diff --git a/src/verbose/print_iter_summary_minlp.m b/src/verbose/print_iter_summary_minlp.m new file mode 100644 index 0000000..01e10a1 --- /dev/null +++ b/src/verbose/print_iter_summary_minlp.m @@ -0,0 +1,17 @@ +function [] = print_iter_summary_minlp(f,h_std,h_comp,solver_message,multiplier_based_stationarity,b_stationarity,n_biactive,f_lpec,rho_TR) + +print_iter_line() +fprintf('\n'); +fprintf('MPEC MINLP reformulation optimizer:\t %s\n',solver_message); +fprintf('Objective....................:\t %2.6e\n',f); +fprintf('Std. constraint violation....:\t %2.6e\n',h_std); +fprintf('Complementarity residual.....:\t %2.6e\n',h_comp); +% fprintf('Solver message...............:\t %s\n',solver_message) +fprintf('Mult. based stationarity.....:\t %s\n',multiplier_based_stationarity); +fprintf('B stationarity...............:\t %s\n',mat2str(b_stationarity)); +fprintf('Biactive constraints.........:\t %d\n',n_biactive); +fprintf('nabla_f(x)^T d...............:\t %d\n',f_lpec); +fprintf('Final rho_TR.................:\t %d\n',rho_TR); +print_iter_line() +fprintf('\n'); +end \ No newline at end of file diff --git a/src/verbose/print_iter_summary_scholtes.m b/src/verbose/print_iter_summary_scholtes.m index ddbfd7b..8fee74a 100644 --- a/src/verbose/print_iter_summary_scholtes.m +++ b/src/verbose/print_iter_summary_scholtes.m @@ -2,7 +2,7 @@ print_iter_line() fprintf('\n'); -fprintf('Scholtes_Homotopy_Opt:\t %s\n',solver_message); +fprintf('MPEC homotopy optimizer:\t %s\n',solver_message); fprintf('Objective....................:\t %2.6e\n',f); fprintf('Std. constraint violation....:\t %2.6e\n',h_std); fprintf('Complementarity residual.....:\t %2.6e\n',h_comp); From d2da8a50b75b02f2fc553ef04e548d7d9fac27c8 Mon Sep 17 00:00:00 2001 From: "armin.nurkanovic" Date: Mon, 1 Sep 2025 12:04:11 +0200 Subject: [PATCH 02/31] fixed some termination and initialization bugs --- benchmarks/macmpec/solve_single_macmpec_problem.m | 12 ++++++++++-- src/+mpecopt/Options.m | 2 +- src/+mpecopt/Solver.m | 5 +++-- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/benchmarks/macmpec/solve_single_macmpec_problem.m b/benchmarks/macmpec/solve_single_macmpec_problem.m index 723c8ab..1c586a6 100644 --- a/benchmarks/macmpec/solve_single_macmpec_problem.m +++ b/benchmarks/macmpec/solve_single_macmpec_problem.m @@ -12,8 +12,15 @@ % problame_name = 'pack-rig-8'; problame_name = 'gnash15m.nl'; problame_name = 'pack-rig2p-16.nl'; + +N_biactive = [168 80 83 71 85 73 120 117 105 108]; + + + % ii_prob = find(contains({macmpec_json.name},problame_name)); +ii_prob = N_biactive(10); % A stationarity in reg! looks problematic! +ii_prob = N_biactive(10); fname = fullfile(macmpec_json(ii_prob).folder, macmpec_json(ii_prob).name); @@ -71,6 +78,7 @@ %% Homotopy solver settings_homotopy = HomotopySolverOptions(); +settings_homotopy.homotopy_parameter_steering = 'Direct'; [result_homotopy,stats_homotopy] = mpec_homotopy_solver(mpec,solver_initalization,settings_homotopy); f_opt_homotopy = full(result_homotopy.f); w_opt_homotopy = full(result_homotopy.x); @@ -90,8 +98,8 @@ solver_settings.relax_and_project_homotopy_parameter_steering = "Direct"; solver_settings.settings_lpec.lpec_solver = 'Gurobi'; % solver_settings.initialization_strategy = "FeasibilityEll1General"; -solver_settings.rho_TR_phase_i_init = 10; -solver_settings.tol_active = 1e-6; +% solver_settings.rho_TR_phase_i_init = 10; +% solver_settings.tol_active = 1e-6; solver = mpecopt.Solver(mpec, solver_settings); [result_mpecopt,stats_mpecopt] = solver.solve(solver_initalization); w_opt_mpecopt = full(result_mpecopt.x); diff --git a/src/+mpecopt/Options.m b/src/+mpecopt/Options.m index 1353f29..2fe7106 100644 --- a/src/+mpecopt/Options.m +++ b/src/+mpecopt/Options.m @@ -146,7 +146,7 @@ % obj.settings_casadi_nlp.ipopt.warm_start_bound_frac = 1e-15; % obj.settings_casadi_nlp.ipopt.warm_start_bound_push = 1e-6; % obj.settings_casadi_nlp.ipopt.warm_start_bound_frac = 1e-6; - obj.settings_casadi_nlp.ipopt.warm_start_entire_iterate = 'yes'; + % obj.settings_casadi_nlp.ipopt.warm_start_entire_iterate = 'yes'; obj.settings_casadi_nlp.ipopt.linear_solver = 'ma27'; % mumps, ma27, ma57 obj.settings_casadi_nlp.ipopt.fixed_variable_treatment = 'make_parameter'; % make_parameter make_constraint relax_bounds diff --git a/src/+mpecopt/Solver.m b/src/+mpecopt/Solver.m index 9cb246c..8beba3e 100644 --- a/src/+mpecopt/Solver.m +++ b/src/+mpecopt/Solver.m @@ -809,8 +809,9 @@ function process_solver_initialization(obj, solver_initialization) solver_initialization.x0(dims.ind_x1) = G_eval; solver_initialization.x0(dims.ind_x2) = H_eval; else - solver_initialization.x0(dims.ind_nonscalar_x1) = G_eval(dims.ind_nonscalar_x1); - solver_initialization.x0(dims.ind_nonscalar_x2) = H_eval(dims.ind_nonscalar_x2); + % TODO: HERE is some BUG! + % solver_initialization.x0(dims.ind_nonscalar_x1) = G_eval(dims.ind_nonscalar_x1); + % solver_initialization.x0(dims.ind_nonscalar_x2) = H_eval(dims.ind_nonscalar_x2); end %% Split into equalites and inequalities % TODO@Anton?: Get rid of this unfold? From 7d7d6c405f6389c76419d2cdd5c463bf3862158a Mon Sep 17 00:00:00 2001 From: "armin.nurkanovic" Date: Mon, 1 Sep 2025 17:52:15 +0200 Subject: [PATCH 03/31] fixing termination bugs due to ipopt fixed variables, more benchmark infrastructure, correct early termination in phase I --- benchmarks/macmpec/histogram_on_lpecs.m | 140 ++++++++++++++++++ benchmarks/macmpec/load_and_plot_results.m | 1 - .../macmpec/mpec_benchmark_dtable_loop.m | 45 +++++- .../macmpec/run_macmpec_experiments_general.m | 42 ++++-- .../macmpec/solve_single_macmpec_problem.m | 55 ++++--- .../nonlinear_mpec_benchmark_dtable_loop.m | 23 ++- src/+mpecopt/Options.m | 9 +- src/+mpecopt/Solver.m | 69 ++++++--- src/lpec_solver.m | 52 ++++--- src/mpec_homotopy_solver.m | 7 + src/mpec_minlp_solver.m | 9 +- src/mpec_optimizer.m | 2 + src/options/HomotopySolverOptions.m | 2 + src/options/LPECSolverOptions.m | 2 + src/options/MINLPSolverOptions.m | 11 +- 15 files changed, 387 insertions(+), 82 deletions(-) create mode 100644 benchmarks/macmpec/histogram_on_lpecs.m diff --git a/benchmarks/macmpec/histogram_on_lpecs.m b/benchmarks/macmpec/histogram_on_lpecs.m new file mode 100644 index 0000000..aaf363e --- /dev/null +++ b/benchmarks/macmpec/histogram_on_lpecs.m @@ -0,0 +1,140 @@ +%% Evaluating LPEC MILP efficiency: +close all; clc; +dtable; +lpec_dstruct; + +% Filter by solver and success +solver = "MPECopt-Reg-Early"; +solver = "MPECopt-Reg-Gurobi"; +idx = strcmp(dtable.solver_name, solver) & dtable.success == 1; + +% ------------------------ +% Helper functions +% ------------------------ + +% Replace zeros by ones in a vector +function y = replace_zeros(x) + if all(x == 0) + y = ones(size(x)); + else + x(x == 0) = 1; + y = x; + end +end + +% Postprocess nodecount cells +process_cells = @(C) cellfun(@(x) ... + (isempty(x) * 0) + ... % empty → 0 + (~isempty(x) * (sum(replace_zeros(x)))), ... + C); + +% Postprocess cpu time cells +process_cpu = @(C) cellfun(@(x) ... + (isempty(x) * 0) + (~isempty(x) * sum(x)), ... + C); + +% Conversion of raw vector (per problem, for iteration plots) +function y = convert_vec(x) + if isempty(x) + y = 0; + else + if all(x == 0) + y = ones(size(x)); + else + x(x == 0) = 1; + y = x; + end + end +end + +% ------------------------ +% Nodecount statistics +% ------------------------ +nc_i = process_cells(lpec_dstruct.nodecount_phase_i(idx)); +nc_ii = process_cells(lpec_dstruct.nodecount_phase_ii(idx)); +nc_tot = nc_i + nc_ii; + +% Histograms (nodecounts) +figure; +histogram(nc_i, 'BinMethod', 'integers'); +title('Nodecount Phase I'); xlabel('Nodes'); ylabel('Frequency'); grid on; +% xticks(min(nc_i):max(nc_i)); + +figure; +histogram(nc_ii, 'BinMethod', 'integers'); +title('Nodecount Phase II'); xlabel('Nodes'); ylabel('Frequency'); grid on; +xticks(min(nc_ii):max(nc_ii)); + +figure; +histogram(nc_tot, 'BinMethod', 'integers'); +title('Total Nodecount'); xlabel('Nodes'); ylabel('Frequency'); grid on; +% xticks(min(nc_tot):max(nc_tot)); + +% Largest problem by total nodecount +[~, max_idx_rel] = max(nc_tot); +max_idx_all = find(idx); % indices in original table +max_idx = max_idx_all(max_idx_rel); + +fprintf('Problem with largest total nodecount is %s with %d nodes.\n', ... + dtable.problem_name{max_idx}, nc_tot(max_idx_rel)); + +% ------------------------ +% CPU time statistics +% ------------------------ +cpu_i = process_cpu(lpec_dstruct.cpu_time_lpec_phase_i(idx)); +cpu_ii = process_cpu(lpec_dstruct.cpu_time_lpec_phase_ii(idx)); +cpu_tot = cpu_i + cpu_ii; + +% Histograms (CPU times) +figure; +histogram(cpu_i); +title('CPU Time Phase I'); xlabel('Time [s]'); ylabel('Frequency'); grid on; + +figure; +histogram(cpu_ii); +title('CPU Time Phase II'); xlabel('Time [s]'); ylabel('Frequency'); grid on; + +figure; +histogram(cpu_tot); +title('Total CPU Time'); xlabel('Time [s]'); ylabel('Frequency'); grid on; + +% ------------------------ +% Iteration plot for max problem (nodecounts) +% ------------------------ +vec_i = lpec_dstruct.nodecount_phase_i{max_idx}; +vec_ii = lpec_dstruct.nodecount_phase_ii{max_idx}; + +vec_i = convert_vec(vec_i); +vec_ii = convert_vec(vec_ii); + +x_i = 1:length(vec_i); +x_ii = (length(vec_i)+1):(length(vec_i)+length(vec_ii)); + +figure; +semilogy(x_i, vec_i, '-o', 'LineWidth', 1.5, 'DisplayName','Phase I'); hold on; +semilogy(x_ii, vec_ii, '-s', 'LineWidth', 1.5, 'DisplayName','Phase II'); +ylim([0 (max([vec_i, vec_ii])+1)*1.1]) +xlabel('Iteration'); +ylabel('Nodecount'); +title(sprintf('Nodecount per Iteration (%s)', dtable.problem_name{max_idx})); +legend('show'); grid on; + +% ------------------------ +% Iteration plot for max problem (CPU times) +% ------------------------ +cpu_vec_i = lpec_dstruct.cpu_time_lpec_phase_i{max_idx}; +cpu_vec_ii = lpec_dstruct.cpu_time_lpec_phase_ii{max_idx}; + +if isempty(cpu_vec_i), cpu_vec_i = 0; end +if isempty(cpu_vec_ii), cpu_vec_ii = 0; end + +x_ci = 1:length(cpu_vec_i); +x_cii = (length(cpu_vec_i)+1):(length(cpu_vec_i)+length(cpu_vec_ii)); + +figure; +plot(x_ci, cpu_vec_i, '-o', 'LineWidth', 1.5, 'DisplayName','Phase I'); hold on; +plot(x_cii, cpu_vec_ii, '-s', 'LineWidth', 1.5, 'DisplayName','Phase II'); +xlabel('Iteration'); +ylabel('CPU Time [s]'); +title(sprintf('CPU Time per Iteration (%s)', dtable.problem_name{max_idx})); +legend('show'); grid on; diff --git a/benchmarks/macmpec/load_and_plot_results.m b/benchmarks/macmpec/load_and_plot_results.m index 7315d18..8e23f98 100644 --- a/benchmarks/macmpec/load_and_plot_results.m +++ b/benchmarks/macmpec/load_and_plot_results.m @@ -11,7 +11,6 @@ plot_settings.absolute_lpec = 0; plot_settings.success_fail_statistics = 1; - plot_settings.nlp_lpec_cpu_comparisson = 0; plot_settings.lpecs_solved = 1; diff --git a/benchmarks/macmpec/mpec_benchmark_dtable_loop.m b/benchmarks/macmpec/mpec_benchmark_dtable_loop.m index 3130c41..903ac35 100644 --- a/benchmarks/macmpec/mpec_benchmark_dtable_loop.m +++ b/benchmarks/macmpec/mpec_benchmark_dtable_loop.m @@ -31,6 +31,18 @@ dstruct.problem_infeasible = []; dstruct.f_lpec = []; +% LPEC solver statistics +lpec_dstruct = struct; +lpec_dstruct.solver_name = []; +lpec_dstruct.nodecount_phase_i = {}; +lpec_dstruct.nodecount_phase_ii= {}; +lpec_dstruct.baritercount_phase_i = {}; +lpec_dstruct.baritercount_phase_ii= {}; +lpec_dstruct.itercount_phase_i = {}; +lpec_dstruct.itercount_phase_ii= {}; +lpec_dstruct.cpu_time_lpec_phase_i = {}; +lpec_dstruct.cpu_time_lpec_phase_ii = {}; + % dstruct.solver_message = [;] dstruct.prob_num = []; dstruct.f = []; @@ -81,7 +93,16 @@ mpec_struct = struct('x',w,'f',f,'g',g,'G',G,'H',H); solver_initalization = struct('x0', w0, 'lbx',lbw, 'ubx',ubw,'lbg',lbg,'ubg',ubg); % to add non-mpecsol solver you can add ifs here - [result,stats] = solver_functions{ii}(mpec_struct,solver_initalization,options); + if isequal(solver_functions{ii},@mpec_optimizer) + solver = mpecopt.Solver(mpec_struct, options); + [result,stats] = solver.solve(solver_initalization); + else + [result,stats] = solver_functions{ii}(mpec_struct,solver_initalization,options); + end + + % if stats.success~=1 + % keyboard; + % end dstruct.solver_name = [dstruct.solver_name; string(name)]; dstruct.problem_name = [dstruct.problem_name; string(mpec_name)]; @@ -107,7 +128,25 @@ dstruct.cpu_time_lpec = [dstruct.cpu_time_lpec; stats.cpu_time_lpec]; dstruct.cpu_time_lpec_phase_i = [dstruct.cpu_time_lpec_phase_i; stats.cpu_time_lpec_phase_i]; dstruct.cpu_time_lpec_phase_ii = [dstruct.cpu_time_lpec_phase_ii; stats.cpu_time_lpec_phase_ii]; - + + % detailed lpec statistis + % lpec_dstruct.nodecount_phase_i = [lpec_dstruct.nodecount_phase_i; stats.iter.nodecount_phase_i]; + % lpec_dstruct.nodecount_phase_ii= [lpec_dstruct.nodecount_phase_ii; stats.iter.nodecount_phase_ii]; + % lpec_dstruct.baritercount_phase_i = [lpec_dstruct.baritercount_phase_i; stats.iter.baritercount_phase_i]; + % lpec_dstruct.baritercount_phase_ii = [lpec_dstruct.baritercount_phase_ii; stats.iter.baritercount_phase_ii]; + % lpec_dstruct.itercount_phase_i = [lpec_dstruct.itercount_phase_i; stats.iter.itercount_phase_i]; + % lpec_dstruct.itercount_phase_ii= [lpec_dstruct.itercount_phase_ii; stats.iter.itercount_phase_ii]; + lpec_dstruct.solver_name = [lpec_dstruct.solver_name; string(name)]; + lpec_dstruct.nodecount_phase_i{end+1} = stats.iter.nodecount_phase_i; + lpec_dstruct.nodecount_phase_ii{end+1} = stats.iter.nodecount_phase_ii; + lpec_dstruct.baritercount_phase_i{end+1} = stats.iter.baritercount_phase_i; + lpec_dstruct.baritercount_phase_ii{end+1} = stats.iter.baritercount_phase_ii; + lpec_dstruct.itercount_phase_i{end+1} = stats.iter.itercount_phase_i; + lpec_dstruct.itercount_phase_ii{end+1} = stats.iter.itercount_phase_ii; + lpec_dstruct.cpu_time_lpec_phase_i{end+1} = stats.iter.cpu_time_lpec_phase_i_iter; + lpec_dstruct.cpu_time_lpec_phase_ii{end+1} = stats.iter.cpu_time_lpec_phase_ii_iter; + + dstruct.n_biactive = [dstruct.n_biactive; stats.n_biactive]; dstruct.f_lpec = [dstruct.f_lpec; stats.f_lpec]; @@ -135,3 +174,5 @@ %% Check results and plot dtable = struct2table(dstruct); save(results_name,"dtable"); +save([results_name '_lpec_details'],"lpec_dstruct"); + diff --git a/benchmarks/macmpec/run_macmpec_experiments_general.m b/benchmarks/macmpec/run_macmpec_experiments_general.m index 5cfbe87..8531eca 100644 --- a/benchmarks/macmpec/run_macmpec_experiments_general.m +++ b/benchmarks/macmpec/run_macmpec_experiments_general.m @@ -5,7 +5,7 @@ %% Color order results_name = ['results/macmpec_general_' datestr(datetime("today"))]; % name for matlab .dat with results -filename = 'macmpec_general2'; % name for figures and excel table +filename = 'macmpec_general3'; % name for figures and excel table %% Load macmpec macmpec_json = dir('macMPEC/*.json'); @@ -22,7 +22,8 @@ N_almost_feasible = [114 68]; N_easy = [34 36 65 87 49 67 69 33 51 53 50 55 57 174 25 40 101 16 102 1 2 3 4]; % bunch of easy problems for more diversity % N_interesting = [N_failed_by_scohltes, N_biactive, N_non_S, N_qpecs, N_not_presolve, N_easy, N_infeasible, N_almost_feasible]; -N_interesting = N_easy; +N_interesting = [N_non_S, N_qpecs, N_not_presolve, N_not_B_scholtes]; +N_interesting = unique(N_interesting); % N_interesting = [N_easy, N_failed_by_scohltes, N_biactive]; % for ii = N_interesting @@ -43,14 +44,14 @@ mpecs = [mpecs,mpec]; end -% N_interesting = []; -% for ii=1:length(macmpec_json) -% if mpecs(ii).n_w <= 50 -% N_interesting = [N_interesting; ii]; -% end -% end +N_interesting = []; +for ii=1:length(macmpec_json) + if mpecs(ii).n_w <= 250 + N_interesting = [N_interesting; ii]; + end +end -% mpecs = mpecs(N_interesting); +mpecs = mpecs(N_interesting); %% Define list of solvers to use @@ -58,6 +59,12 @@ "Reg" , "NLP", ... "MINLP"]; + +solver_names = ["MPECopt-Reg-Gurobi", "MPECopt-Reg-Early", "MPECopt-$\ell_1$-Gurobi", ... + "Reg" , "NLP", ... + "MINLP"]; + + solver_functions = {@mpec_optimizer,@mpec_optimizer,@mpec_optimizer,... @mpec_homotopy_solver,@mpec_homotopy_solver,... @mpec_minlp_solver}; @@ -66,12 +73,22 @@ default_opts1.solver_name = solver_names{1}; default_opts1.settings_lpec.lpec_solver = "Gurobi"; default_opts1.relax_and_project_homotopy_parameter_steering = "Direct"; +% default_opts1.initialization_strategy = "FeasibilityEll1General"; + +% default_opts2 = mpecopt.Options(); +% default_opts2.solver_name = solver_names{2}; +% default_opts2.settings_lpec.lpec_solver = "Highs_casadi"; +% default_opts2.settings_lpec.stop_lpec_at_feasible = true; +% default_opts2.relax_and_project_homotopy_parameter_steering = "Direct"; +% default_opts2.rho_TR_phase_i_init = 1e-3; default_opts2 = mpecopt.Options(); default_opts2.solver_name = solver_names{2}; -default_opts2.settings_lpec.lpec_solver = "Highs_casadi"; +default_opts2.settings_lpec.lpec_solver = "Gurobi"; default_opts2.relax_and_project_homotopy_parameter_steering = "Direct"; -default_opts2.rho_TR_phase_i_init = 1e-3; +default_opts2.settings_lpec.stop_lpec_at_feasible = true; +% default_opts2.rho_TR_phase_i_init = 1e-3; + default_opts3 = mpecopt.Options(); default_opts3.solver_name = solver_names{3}; @@ -97,6 +114,7 @@ %% Create data struct N_experiments = [1, 3:6]; +N_experiments = [2]; mpec_benchmark_dtable_loop; % this script runs the experimetns, creates a dtable %% Pick which results to plot @@ -130,7 +148,7 @@ %% Plot results plot_settings.success_fail_statistics = 1; plot_settings.n_biactive = 0; -plot_settings.lpecs_solved = 0; +plot_settings.lpecs_solved = 1; plot_settings.active_set_changes = 0; plot_settings.lpecs_cpu_time = 0; plot_settings.bar_timing_plots = 0; diff --git a/benchmarks/macmpec/solve_single_macmpec_problem.m b/benchmarks/macmpec/solve_single_macmpec_problem.m index 1c586a6..c3dfc27 100644 --- a/benchmarks/macmpec/solve_single_macmpec_problem.m +++ b/benchmarks/macmpec/solve_single_macmpec_problem.m @@ -6,21 +6,24 @@ all_problem_names = {macmpec_json.name}; -problame_name = 'qpec-200-2'; -% problame_name = 'ralph1'; +% problame_name = 'qpec-200-2'; +problame_name = 'ralph1'; % problame_name = 'pack-rig-32'; % problame_name = 'pack-rig-8'; -problame_name = 'gnash15m.nl'; -problame_name = 'pack-rig2p-16.nl'; +% problame_name = 'gnash15m.nl'; +% problame_name = 'pack-rig2p-16.nl'; +problame_name = 'tap-09'; -N_biactive = [168 80 83 71 85 73 120 117 105 108]; +problame_name = 'qpec-200-3'; +% Some failures: gnash15m gnash16 gnash17m gnash18m gnash19m tap-09 +N_biactive = [168 80 83 71 85 73 120 117 105 108]; -% ii_prob = find(contains({macmpec_json.name},problame_name)); -ii_prob = N_biactive(10); % A stationarity in reg! looks problematic! -ii_prob = N_biactive(10); +% ii_prob = N_biactive(10); % A stationarity in reg! looks problematic! +% ii_prob = N_biactive(10); +% ii_prob = 8; fname = fullfile(macmpec_json(ii_prob).folder, macmpec_json(ii_prob).name); @@ -79,24 +82,25 @@ %% Homotopy solver settings_homotopy = HomotopySolverOptions(); settings_homotopy.homotopy_parameter_steering = 'Direct'; -[result_homotopy,stats_homotopy] = mpec_homotopy_solver(mpec,solver_initalization,settings_homotopy); -f_opt_homotopy = full(result_homotopy.f); -w_opt_homotopy = full(result_homotopy.x); +% [result_homotopy,stats_homotopy] = mpec_homotopy_solver(mpec,solver_initalization,settings_homotopy); +% f_opt_homotopy = full(result_homotopy.f); +% w_opt_homotopy = full(result_homotopy.x); %% MINLP solver settings_minlp = MINLPSolverOptions(); -settings_minlp.settings_casadi_nlp.bonmin.time_limit = 10; -% settings_minlp.settings_casadi_nlp.bonmin.node_limit = 5; -% settings_minlp.settings_casadi_nlp.bonmin.solution_limit = 5; -% settings_minlp.settings_casadi_nlp.bonmin.max_consecutive_failures = 5; -[result_minlp,stats_minlp] = mpec_minlp_solver(mpec,solver_initalization,settings_minlp); -f_opt_minlp = full(result_minlp.f); -w_opt_minlp = full(result_minlp.x); +settings_minlp.settings_casadi_nlp.bonmin.time_limit = 25; +settings_minlp.settings_casadi_nlp.bonmin.node_limit = 5; +settings_minlp.settings_casadi_nlp.bonmin.solution_limit = 5; +settings_minlp.settings_casadi_nlp.bonmin.max_consecutive_failures = 5; +% [result_minlp,stats_minlp] = mpec_minlp_solver(mpec,solver_initalization,settings_minlp); +% f_opt_minlp = full(result_minlp.f); +% w_opt_minlp = full(result_minlp.x); %% MPECopt solver solver_settings = mpecopt.Options(); solver_settings.relax_and_project_homotopy_parameter_steering = "Direct"; -solver_settings.settings_lpec.lpec_solver = 'Gurobi'; +solver_settings.settings_lpec.lpec_solver = "Highs_casadi"; +solver_settings.settings_lpec.stop_lpec_at_feasible = true; % solver_settings.initialization_strategy = "FeasibilityEll1General"; % solver_settings.rho_TR_phase_i_init = 10; % solver_settings.tol_active = 1e-6; @@ -109,10 +113,15 @@ fprintf('\n-------------------------------------------------------------------------------\n'); fprintf('Method \t\t Objective \t comp_res \t n_biactive \t CPU time (s)\t Success\t Stat. type\n') fprintf('-------------------------------------------------------------------------------\n'); -fprintf('Reg \t %2.2e \t %2.2e \t\t %d \t\t\t %2.2f \t\t\t\t %d\t %s\n',f_opt_homotopy,stats_homotopy.comp_res,stats_homotopy.n_biactive,stats_homotopy.cpu_time_total,stats_homotopy.success,stats_homotopy.multiplier_based_stationarity) -fprintf('MINLP \t\t %2.2e \t %2.2e \t\t %d \t\t\t %2.2f \t\t\t\t %d\t %s\n',f_opt_minlp,stats_minlp.comp_res,stats_minlp.n_biactive,stats_minlp.cpu_time_total,stats_minlp.success,stats_minlp.multiplier_based_stationarity) +% fprintf('Reg \t %2.2e \t %2.2e \t\t %d \t\t\t %2.2f \t\t\t\t %d\t %s\n',f_opt_homotopy,stats_homotopy.comp_res,stats_homotopy.n_biactive,stats_homotopy.cpu_time_total,stats_homotopy.success,stats_homotopy.multiplier_based_stationarity) +% fprintf('MINLP \t\t %2.2e \t %2.2e \t\t %d \t\t\t %2.2f \t\t\t\t %d\t %s\n',f_opt_minlp,stats_minlp.comp_res,stats_minlp.n_biactive,stats_minlp.cpu_time_total,stats_minlp.success,stats_minlp.multiplier_based_stationarity) fprintf('MPECopt \t %2.2e \t %2.2e \t\t %d \t\t\t %2.2f \t\t\t\t %d\t %s\n',f_opt_mpecopt,stats_mpecopt.comp_res,stats_mpecopt.n_biactive,stats_mpecopt.cpu_time_total,stats_mpecopt.success,stats_mpecopt.multiplier_based_stationarity) fprintf('-------------------------------------------------------------------------------\n'); -fprintf('||w_reg - w_mpec|| = %2.2e \n',norm(w_opt_homotopy-w_opt_mpecopt)); -fprintf('||w_minlp - w_mpec|| = %2.2e \n',norm(w_opt_minlp-w_opt_mpecopt)); +% fprintf('||w_reg - w_mpec|| = %2.2e \n',norm(w_opt_homotopy-w_opt_mpecopt)); +% fprintf('||w_minlp - w_mpec|| = %2.2e \n',norm(w_opt_minlp-w_opt_mpecopt)); + +stats_mpecopt.iter.nodecount_phase_i +stats_mpecopt.iter.nodecount_phase_ii +stats_mpecopt.iter.itercount_phase_i +stats_mpecopt.iter.itercount_phase_ii \ No newline at end of file diff --git a/benchmarks/nonlinear_mpec_benchmark/nonlinear_mpec_benchmark_dtable_loop.m b/benchmarks/nonlinear_mpec_benchmark/nonlinear_mpec_benchmark_dtable_loop.m index fc60729..75a6c74 100644 --- a/benchmarks/nonlinear_mpec_benchmark/nonlinear_mpec_benchmark_dtable_loop.m +++ b/benchmarks/nonlinear_mpec_benchmark/nonlinear_mpec_benchmark_dtable_loop.m @@ -30,6 +30,17 @@ dstruct.max_cpu_time_lpec = []; dstruct.problem_infeasible = []; dstruct.f_lpec = []; +% LPEC solver statistics +lpec_dstruct = struct; +lpec_dstruct.solver_name = []; +lpec_dstruct.nodecount_phase_i = {}; +lpec_dstruct.nodecount_phase_ii= {}; +lpec_dstruct.baritercount_phase_i = {}; +lpec_dstruct.baritercount_phase_ii= {}; +lpec_dstruct.itercount_phase_i = {}; +lpec_dstruct.itercount_phase_ii= {}; + + % dstruct.solver_message = [;] dstruct.prob_num = []; @@ -91,7 +102,16 @@ dstruct.cpu_time_lpec = [dstruct.cpu_time_lpec; stats.cpu_time_lpec]; dstruct.cpu_time_lpec_phase_i = [dstruct.cpu_time_lpec_phase_i; stats.cpu_time_lpec_phase_i]; dstruct.cpu_time_lpec_phase_ii = [dstruct.cpu_time_lpec_phase_ii; stats.cpu_time_lpec_phase_ii]; - + + % detailed lpec statistis + lpec_dstruct.solver_name = [lpec_dstruct.solver_name; string(name)]; + lpec_dstruct.nodecount_phase_i{end+1} = stats.iter.nodecount_phase_i; + lpec_dstruct.nodecount_phase_ii{end+1} = stats.iter.nodecount_phase_ii; + lpec_dstruct.baritercount_phase_i{end+1} = stats.iter.baritercount_phase_i; + lpec_dstruct.baritercount_phase_ii{end+1} = stats.iter.baritercount_phase_ii; + lpec_dstruct.itercount_phase_i{end+1} = stats.iter.itercount_phase_i; + lpec_dstruct.itercount_phase_ii{end+1} = stats.iter.itercount_phase_ii; + dstruct.n_biactive = [dstruct.n_biactive; stats.n_biactive]; dstruct.f_lpec = [dstruct.f_lpec; stats.f_lpec]; @@ -119,3 +139,4 @@ %% Check results and plot dtable = struct2table(dstruct); save(results_name,"dtable"); +save([results_name '_lpec_details'],"lpec_dstruct"); diff --git a/src/+mpecopt/Options.m b/src/+mpecopt/Options.m index 2fe7106..3fdb917 100644 --- a/src/+mpecopt/Options.m +++ b/src/+mpecopt/Options.m @@ -125,15 +125,14 @@ % obj.settings_lpec.abs_tol= 1e-8; default_tol = 1e-12; - obj.settings_casadi_nlp.detect_simple_bounds = true; obj.settings_casadi_nlp.ipopt.print_level = 0; obj.settings_casadi_nlp.print_time = 0; obj.settings_casadi_nlp.ipopt.sb = 'yes'; obj.settings_casadi_nlp.verbose = false; obj.settings_casadi_nlp.ipopt.max_iter = 3000; obj.settings_casadi_nlp.ipopt.bound_relax_factor = 0; - % obj.settings_casadi_nlp.ipopt.bound_relax_factor = 1e-16; - % obj.settings_casadi_nlp.ipopt.honor_original_bounds = 'yes'; + obj.settings_casadi_nlp.ipopt.bound_relax_factor = 1e-16; + obj.settings_casadi_nlp.ipopt.honor_original_bounds = 'yes'; obj.settings_casadi_nlp.ipopt.tol = default_tol; obj.settings_casadi_nlp.ipopt.dual_inf_tol = default_tol; obj.settings_casadi_nlp.ipopt.dual_inf_tol = default_tol; @@ -148,7 +147,9 @@ % obj.settings_casadi_nlp.ipopt.warm_start_bound_frac = 1e-6; % obj.settings_casadi_nlp.ipopt.warm_start_entire_iterate = 'yes'; obj.settings_casadi_nlp.ipopt.linear_solver = 'ma27'; % mumps, ma27, ma57 - obj.settings_casadi_nlp.ipopt.fixed_variable_treatment = 'make_parameter'; % make_parameter make_constraint relax_bounds + obj.settings_casadi_nlp.ipopt.fixed_variable_treatment = 'relax_bounds'; % make_parameter make_constraint relax_bounds + obj.settings_casadi_nlp.detect_simple_bounds = true; + % obj.settings_casadi_nlp.ipopt.fixed_variable_treatment = 'relax_bounds'; % make_parameter make_constraint relax_bounds % obj.opts_casadi_nlp.snopt = struct(); % obj.opts_casadi_nlp.worhp = struct(); diff --git a/src/+mpecopt/Solver.m b/src/+mpecopt/Solver.m index 8beba3e..c83c243 100644 --- a/src/+mpecopt/Solver.m +++ b/src/+mpecopt/Solver.m @@ -22,9 +22,8 @@ obj.create_mpec_functions(); obj.solver_initialization = struct(); obj.create_lpec_functions(); - cpu_time_prepare_mpec = toc(t_prepare_mpec); - obj.stats.cpu_time_prepare_mpec = cpu_time_prepare_mpec; + obj.stats.cpu_time_prepare_mpec = cpu_time_prepare_mpec; if strcmp(opts.initialization_strategy,"RelaxAndProject") t_generate_nlp_solvers = tic; @@ -75,11 +74,22 @@ stats.iter.f_iter = []; % objective of accepted steps stats.iter.h_std_iter = []; % standard infeasilibty stats.iter.h_comp_iter = []; % comp infeasilibty - stats.n_nlp_total = 0; - stats.n_lpec_total = 0; stats.iter.n_nlp_iter = []; stats.iter.n_lpec_iter = []; + + % collect details for every lpec solve via a milp + % BUG: calling phase II for + % feasiblity problems deletes everything): TODO! need a function to empty this for a very new solver call, but need to see how to detect this + stats.iter.nodecount_phase_i = []; + stats.iter.nodecount_phase_ii= []; + stats.iter.baritercount_phase_i = []; + stats.iter.baritercount_phase_ii= []; + stats.iter.itercount_phase_i = []; + stats.iter.itercount_phase_ii= []; + stats.iter.active_set_changes = []; + stats.n_nlp_total = 0; + stats.n_lpec_total = 0; stats.solver_message = 'Maximum number of iterations reached.'; stats.success = false; stats.success_phase_i = false; @@ -167,10 +177,14 @@ end lpec.rho_TR = rho_TR_k_l; stats.iter.rho_TR_iter = [stats.iter.rho_TR_iter, rho_TR_k_l]; % store TR radius - % Solve LPEC + % Solve LPEC [results_lpec,stats_lpec] = lpec_solver(lpec,opts.settings_lpec); stats.iter.cpu_time_lpec_phase_i_iter = [stats.iter.cpu_time_lpec_phase_i_iter, stats_lpec.cpu_time]; % stats stats.n_lpec_total = stats.n_lpec_total+1; + stats.iter.nodecount_phase_i = [stats.iter.nodecount_phase_i, stats_lpec.nodecount]; + stats.iter.baritercount_phase_i = [stats.iter.baritercount_phase_i, stats_lpec.baritercount]; + stats.iter.itercount_phase_i = [stats.iter.itercount_phase_i, stats_lpec.itercount]; + % extract results lpec_solution_exists = stats_lpec.lpec_solution_exists; d_lpec_k_l = results_lpec.d_lpec; @@ -207,13 +221,13 @@ f_lin_opt_k_l = 0; end end - if norm(d_lpec_k_l) <= opts.tol_B_stationarity - stats.stopping_criterion_fullfiled = true; % B-stationary point found, optimal solution found! - stats.solver_message = 'B-stationary point found sucessfully.'; - stats.success = true; - stats.b_stationarity = true; - stats.f_lpec = f_lin_opt_k_l; - end + % if norm(d_lpec_k_l) <= opts.tol_B_stationarity + % stats.stopping_criterion_fullfiled = true; % B-stationary point found, optimal solution found! + % stats.solver_message = 'B-stationary point found sucessfully.'; + % stats.success = true; + % stats.b_stationarity = true; + % stats.f_lpec = f_lin_opt_k_l; + % end stats.iter.X_lpec = [stats.iter.X_lpec, x_trail_lpec]; stats.iter.d_norm_lpec = [stats.iter.d_norm_lpec, norm(d_lpec_k_l)]; stats.iter.f_lpec = [stats.iter.f_lpec, f_lin_opt_k_l]; % store some stats @@ -275,7 +289,8 @@ if opts.verbose_solver print_iter_stats(1,ii,f_opt_k,h_std_k,h_comp_k,'BNLP',nlp_iters_k,stats_nlp.return_status,rho_TR_k_l,norm(x_k_init(1:dims.n_primal)-x_k),cpu_time_bnlp_ii ,1) end - if full(mpec_casadi.h_total_fun(x_k,solver_initialization.p0)) <= opts.tol_feasibility + % if full(mpec_casadi.h_total_fun(x_k,solver_initialization.p0)) <= opts.tol_B_stationarity + if ismember(stats_nlp.return_status, {'Solve_Succeeded', 'Search_Direction_Becomes_Too_Small', 'Solved_To_Acceptable_Level'}) || full(mpec_casadi.h_total_fun(x_k,solver_initialization.p0)) <= opts.tol_B_stationarity % if ismember(stats_nlp.return_status, {'Solve_Succeeded', 'Search_Direction_Becomes_Too_Small', 'Solved_To_Acceptable_Level'}) feasible_bnlp_found = true; else @@ -328,6 +343,12 @@ x_k_init = x_k; stats.n_nlp_total = stats.n_nlp_total+1; x_k = x_k_init(1:dims.n_primal); + if full(mpec_casadi.h_total_fun(x_k,solver_initialization.p0)) <= opts.tol_feasibility + % if ismember(stats_nlp.return_status, {'Solve_Succeeded', 'Search_Direction_Becomes_Too_Small', 'Solved_To_Acceptable_Level'}) + feasible_bnlp_found = true; + else + feasible_bnlp_found = false; + end end end y_lpec_k_l = abs(x_k(dims.ind_x1))>=abs(x_k(dims.ind_x2)); % inital guess for active set/binaries @@ -548,7 +569,7 @@ end stats.rho_TR_final = rho_TR_k_l; % ---- make sure feasible point is declared sucessful --- - if full(mpec_casadi.h_total_fun(x_k,solver_initialization.p0)) <= opts.tol_feasibility + if full(mpec_casadi.h_total_fun(x_k,solver_initialization.p0)) <= opts.tol_B_stationarity*10 stats.feasible_bnlp_found = true; stats.success_phase_i = true; else stats.feasible_bnlp_found = false; stats.success_phase_i = false; @@ -608,6 +629,7 @@ solver_initialization.y_lpec_k_l = y_lpec_k_l; solver_initialization.d_lpec_k_l = d_lpec_k_l; phase_ii = true; + opts.settings_lpec.is_in_phase_i = false; if opts.verbose_solver print_phase_ii(); print_iter_header(); @@ -618,6 +640,7 @@ stats.cpu_time_phase_ii = 1e-10; end + opts.settings_lpec.is_in_phase_i = true; % reset %% ------------------------ Final verbose ---------------------------------- fprintf('\n'); if opts.verbose_summary @@ -1426,11 +1449,17 @@ function create_lpec_functions(obj) lpec.rho_TR = rho_TR_k_l; % update trust region stats.iter.rho_TR_iter = [stats.iter.rho_TR_iter, rho_TR_k_l]; % store TR radius % Solve LPEC - [results_lpec,stats_lpec] = lpec_solver(lpec,opts.settings_lpec); + [results_lpec,stats_lpec] = lpec_solver(lpec, opts.settings_lpec); if ~phase_ii stats.iter.cpu_time_lpec_phase_i_iter = [stats.iter.cpu_time_lpec_phase_i_iter, stats_lpec.cpu_time]; % stats + stats.iter.nodecount_phase_i = [stats.iter.nodecount_phase_i, stats_lpec.nodecount]; + stats.iter.baritercount_phase_i = [stats.iter.baritercount_phase_i, stats_lpec.baritercount]; + stats.iter.itercount_phase_i = [stats.iter.itercount_phase_i, stats_lpec.itercount]; else stats.iter.cpu_time_lpec_phase_ii_iter = [stats.iter.cpu_time_lpec_phase_ii_iter, stats_lpec.cpu_time]; % stats + stats.iter.nodecount_phase_ii = [stats.iter.nodecount_phase_ii, stats_lpec.nodecount]; + stats.iter.baritercount_phase_ii = [stats.iter.baritercount_phase_ii, stats_lpec.baritercount]; + stats.iter.itercount_phase_ii = [stats.iter.itercount_phase_ii, stats_lpec.itercount]; end n_lpec_k = n_lpec_k + 1; stats.n_lpec_total = stats.n_lpec_total + 1; % extract LPEC results @@ -1452,8 +1481,14 @@ function create_lpec_functions(obj) plot_lpec(nabla_f_k, x_k, d_lpec_k_l, rho_TR_k_l) end % --------------------------- Check if B-stationary point found -------------------------- - h_total_k = full(mpec_casadi.h_total_fun(x_k,p0)); - if (h_total_k <= opts.tol) && ((abs(f_lin_opt_k_l) <= opts.tol_B_stationarity || norm(nabla_f_k) <= opts.tol_B_stationarity)) % if objective zero (either if cost gradient zero, or solution leads to it) = then set step to zero => B stationarity + % h_total_k = full(mpec_casadi.h_total_fun(x_k,p0)); + % if ismember(stats_nlp.return_status, {'Solve_Succeeded', 'Search_Direction_Becomes_Too_Small', 'Solved_To_Acceptable_Level'}) + % bnlp_solved = true; + % else + % bnlp_solved = false; + % end + % if (h_total_k <= opts.tol) && ((abs(f_lin_opt_k_l) <= opts.tol_B_stationarity || norm(nabla_f_k) <= opts.tol_B_stationarity)) % if objective zero (either if cost gradient zero, or solution leads to it) = then set step to zero => B stationarity + if ((abs(f_lin_opt_k_l) <= opts.tol_B_stationarity || norm(nabla_f_k) <= opts.tol_B_stationarity)) % if objective zero (either if cost gradient zero, or solution leads to it) = then set step to zero => B stationarity if opts.reset_lpec_objective d_lpec_k_l = d_lpec_k_l*0; % if the current point is feasible, and the objective is zero, then d = 0 is also a solution of the lpec (occurs if a solution is not on the verties of the lp) f_lin_opt_k_l = 0; diff --git a/src/lpec_solver.m b/src/lpec_solver.m index 5ecb2d4..4792d24 100644 --- a/src/lpec_solver.m +++ b/src/lpec_solver.m @@ -36,7 +36,10 @@ % lpec.vtype = vtype; % lpec.vtype_num = vtype_num; import casadi.* - +%% init some stats: +stats.itercount = 0; % number simplex iters +stats.baritercount = 0; % nuber of barrier iters +stats.nodecount = 0; % number of nodes in BnB %% Prepare LPEC % add the boundso of the inaries @@ -113,9 +116,9 @@ params.IntFeasTol = 1e-9; params.TimeLimit = settings.max_time; params.OptimalityTol = 1e-9; - if settings.stop_lpec_at_feasible - params.SolutionLimit = 1; - % params.MIPGap = 1; + if settings.stop_lpec_at_feasible && settings.is_in_phase_i + % params.SolutionLimit = 1; + params.MIPGap = 1; end % params.ObjScale = -0.5; % https://www.gurobi.com/documentation/current/refman/objscale.html#parameter:ObjScale % params.ScaleFlag=0; % default -1, % https://www.gurobi.com/documentation/current/refman/scaleflag.html#parameter:ScaleFlag @@ -145,12 +148,16 @@ if settings.solve_lpec_with_cutoff options.ObjectiveCutOff = settings.cutoff; end + if settings.stop_lpec_at_feasible && settings.is_in_phase_i + options.RelativeGapTolerance = 1; % stop at first integer feasible sol + end + % GapTolerance = 1e-16; - GapTolerance = 1e-4; + % GapTolerance = 1e-4; options.MaxNodes = settings.max_nodes; options.ConstraintTolerance = 1e-9; % options.AbsoluteGapTolerance = GapTolerance; - options.RelativeGapTolerance = GapTolerance; + % options.RelativeGapTolerance = GapTolerance; options.MaxTime = settings.max_time; model.options = options; model.solver = 'intlinprog'; @@ -175,6 +182,9 @@ highs_opts.highs.log_to_console = false; % highs_opts.highs.simplex_strategy = 4; % highs_opts.highs.error_on_fail = false; + if settings.stop_lpec_at_feasible && settings.is_in_phase_i + highs_opts.highs.mip_rel_gap = 1; + end % highs_opts.highs.mip_max_nodes = settings.max_nodes; % highs_opts.highs.time_limit = settings.max_time; @@ -184,6 +194,7 @@ % highs_opts.highs.primal_feasibility_tolerance = 1e-9; % highs_opts.highs.dual_feasibility_tolerance = 1e-9; highs_opts.highs.mip_heuristic_effort = 0.1; + % highs_opts.highs.simplex_strategy = 4; @@ -250,9 +261,14 @@ switch settings.lpec_solver case "Gurobi" try - t_gurobi_start = tic; + gurobi_time = tic; result_gurobi = gurobi(model, params); - cpu_time_gurobi = toc(t_gurobi_start); + stats.itercount = result_gurobi.itercount; + stats.baritercount = result_gurobi.baritercount; + stats.nodecount = result_gurobi.nodecount; + cpu_time_gurobi = toc(gurobi_time); + % todo; add node and itter coumt, and add them tho phase i iter + % and node, catch model; % keyboard; @@ -262,8 +278,7 @@ % results.y_lpec = lpec.y_lpec*nan; % results.f_opt = nan; % stats.lpec_solution_exists = false; - result_gurobi.nodecount = 0; - stats.solver_message = 'error in lpec'; + result_gurobi.nodecount = nan; result_gurobi.runtime = nan; cpu_time_gurobi = nan; end @@ -273,23 +288,23 @@ results.y_lpec = result_gurobi.x(end-lpec.dims.n_auxiliary+1:end); results.f_opt = result_gurobi.objval; stats.lpec_solution_exists = true; - stats.nodecount = result_gurobi.nodecount; + % stats.nodecount = result_gurobi.nodecount; else results.d_lpec = lpec.d_lpec*nan; results.y_lpec = lpec.y_lpec*nan; results.f_opt = nan; stats.lpec_solution_exists = false; - stats.nodecount = result_gurobi.nodecount; + % stats.nodecount = result_gurobi.nodecount; end stats.solver_message = result_gurobi.status; % stats.cpu_time = result_gurobi.runtime; stats.cpu_time = cpu_time_gurobi; case {"Highs", "Matlab"} try - tic + intlinprog_time = tic; % [x,f_opt,statsu,output] = intlinprog(model); [x,f_opt,status,output] = intlinprog(f_lpec,intcon,A_ineq_matlab,b_ineq_matlab,A_eq_matlab,b_eq_matlab,lb,ub,x0,options); - cpu_time = toc; + cpu_time = toc(intlinprog_time); catch model; % keyboard; @@ -324,14 +339,14 @@ stats.solver_message_extended = output.message; stats.cpu_time = cpu_time; case "Highs_casadi" - tic + higs_casadi_time = tic; try r = lpsol('g', c_highs, 'a', A_highs, 'lbx', lb, 'ubx', ub, 'lba', lbA_highs, 'uba', ubA_highs); highs_success = strcmp(lpsol.stats.return_status, 'Optimal'); catch highs_success = false; end - cpu_time = toc; + cpu_time = toc(higs_casadi_time); if highs_success @@ -350,8 +365,11 @@ stats.lpec_solution_exists = false; end stats.nodecount = lpsol.stats.n_call_solver; + stats.itercount = lpsol.stats.iter_count; + stats.baritercount = lpsol.stats.ipm_iteration_count; % stats.cpu_time = lpsol.stats.t_wall_solver; stats.cpu_time = cpu_time; + stats.solver_message = lpsol.stats.unified_return_status; case {'Reg','Ell_1','Ell_inf','Nlp'} % reg @@ -390,7 +408,7 @@ end stats.cpu_time = stats.cpu_time_total; stats.solver_message = stats.return_status; - stats.nodecount = 0; + % stats.nodecount = 0; case {"Projected_Gradient"} d_lpec = zeros(lpec.dims.n_primal,1); d0 = zeros(lpec.dims.n_primal-2*lpec.dims.n_comp,1); diff --git a/src/mpec_homotopy_solver.m b/src/mpec_homotopy_solver.m index ef1b18a..54ffd4f 100644 --- a/src/mpec_homotopy_solver.m +++ b/src/mpec_homotopy_solver.m @@ -718,7 +718,14 @@ stats.iter.cpu_time_nlp_phase_i_iter = 0; stats.iter.cpu_time_nlp_phase_ii_iter = cpu_time_nlp_iter; +% Dummy Lpec data stats.n_lpec_total = 0; +stats.iter.nodecount_phase_i = 0; +stats.iter.nodecount_phase_ii= 0; +stats.iter.baritercount_phase_i = 0; +stats.iter.baritercount_phase_ii= 0; +stats.iter.itercount_phase_i = 0; +stats.iter.itercount_phase_ii= 0; if sum(cpu_time_nlp_iter)>0 stats.iter.cpu_time_nlp_iter = cpu_time_nlp_iter; diff --git a/src/mpec_minlp_solver.m b/src/mpec_minlp_solver.m index a1521bb..6604b4f 100644 --- a/src/mpec_minlp_solver.m +++ b/src/mpec_minlp_solver.m @@ -450,7 +450,7 @@ problem_infeasible = false; end -if full(h_comp_constraints_tol_fun(x_k,p0)) <= settings.tol && ~success +if max(full(h_std_fun(x_k,p0)),full(h_comp_constraints_tol_fun(x_k,p0))) <= settings.tol && ~success success = true; solver_message = 'Feasible point found.'; end @@ -692,7 +692,14 @@ stats.iter.cpu_time_nlp_phase_i_iter = 0; stats.iter.cpu_time_nlp_phase_ii_iter = cpu_time_nlp_iter; +% Dummy Lpec data stats.n_lpec_total = 0; +stats.iter.nodecount_phase_i = 0; +stats.iter.nodecount_phase_ii= 0; +stats.iter.baritercount_phase_i = 0; +stats.iter.baritercount_phase_ii= 0; +stats.iter.itercount_phase_i = 0; +stats.iter.itercount_phase_ii= 0; if sum(cpu_time_nlp_iter)>0 stats.iter.cpu_time_nlp_iter = cpu_time_nlp_iter; diff --git a/src/mpec_optimizer.m b/src/mpec_optimizer.m index 8542f6c..afe0aa6 100644 --- a/src/mpec_optimizer.m +++ b/src/mpec_optimizer.m @@ -1,4 +1,6 @@ function [solution,stats] = mpec_optimizer(mpec,solver_initalization,settings) +warning("This is the legacy version of the solver!") +warning("Use the updated version by creating a solver object: solver = mpecopt.Solver(mpec, solver_settings); [result_mpecopt,stats_mpecopt] = solver.solve(solver_initalization);") %% % MPEC Opt - a globally convergent method for computing B-stationary % point of MPECs. diff --git a/src/options/HomotopySolverOptions.m b/src/options/HomotopySolverOptions.m index 72550c4..db66dc3 100644 --- a/src/options/HomotopySolverOptions.m +++ b/src/options/HomotopySolverOptions.m @@ -63,6 +63,8 @@ obj.settings_casadi_nlp.ipopt.warm_start_init_point = 'yes'; obj.settings_casadi_nlp.ipopt.warm_start_entire_iterate = 'yes'; obj.settings_casadi_nlp.ipopt.linear_solver = 'ma27'; % 'mumps'; ma57 + obj.settings_casadi_nlp.detect_simple_bounds = true; + obj.settings_casadi_nlp.ipopt.fixed_variable_treatment = 'relax_bounds'; % make_parameter make_constraint relax_bounds % obj.opts_casadi_nlp.snopt = struct(); % obj.opts_casadi_nlp.worhp = struct(); % obj.opts_casadi_nlp.uno = struct(); diff --git a/src/options/LPECSolverOptions.m b/src/options/LPECSolverOptions.m index 2f4b032..66e8073 100644 --- a/src/options/LPECSolverOptions.m +++ b/src/options/LPECSolverOptions.m @@ -14,6 +14,7 @@ solve_lpec_with_cutoff (1,1) logical = false; stop_lpec_at_feasible (1,1) logical = false; trust_region_on_slacks (1,1) logical = false; % Do the slack variables for the feasbility trasformation have a TR constraint? + is_in_phase_i (1,1) logical = true; % tell lpec solver which phase it is homotopy_solver_settings % settings for custom nlp based lpec methods (Scholtes, Ell1, Ell_inf) @@ -23,6 +24,7 @@ sigma0(1,1) double {mustBeReal, mustBeNonnegative} = 1; kappa(1,1) double {mustBeReal, mustBePositive} = 0.1; + end methods diff --git a/src/options/MINLPSolverOptions.m b/src/options/MINLPSolverOptions.m index ee780d4..f8d1f35 100644 --- a/src/options/MINLPSolverOptions.m +++ b/src/options/MINLPSolverOptions.m @@ -43,18 +43,18 @@ methods function obj = MINLPSolverOptions() % see - - + + default_tol = 1e-12; - % --------- MINLP Specific settings + % --------- MINLP Specific settings % bonmin: https://www.coin-or.org/Bonmin/option_pages/options_list_bonmin.html obj.settings_casadi_nlp.error_on_fail = false; obj.settings_casadi_nlp.bonmin.algorithm = 'B-BB'; obj.settings_casadi_nlp.bonmin.warm_start = 'interior_point'; obj.settings_casadi_nlp.bonmin.node_limit = 75; obj.settings_casadi_nlp.bonmin.time_limit = 600; - + % options for the algorithm % B-BB % simple branch-and-bound algorithm, % B-OA % OA Decomposition algorithm, @@ -84,6 +84,9 @@ % obj.settings_casadi_nlp.bonmin.warm_start_entire_iterate = 'yes'; obj.settings_casadi_nlp.bonmin.linear_solver = 'ma27'; % 'mumps'; ma57 + obj.settings_casadi_nlp.detect_simple_bounds = true; + obj.settings_casadi_nlp.bonmin.fixed_variable_treatment = 'relax_bounds'; % make_parameter make_constraint relax_bounds + % Some BonminSettings % obj.opts_casadi_nlp.snopt = struct(); % obj.opts_casadi_nlp.worhp = struct(); From 763a28c94131146a98ba7cac5a6dbcbcf6062b65 Mon Sep 17 00:00:00 2001 From: "armin.nurkanovic" Date: Mon, 1 Sep 2025 22:46:51 +0200 Subject: [PATCH 04/31] fixed quite some stuff to get stationarity detection straight --- benchmarks/macmpec/histogram_on_lpecs.m | 85 +++++++++++++------ benchmarks/macmpec/load_and_plot_results.m | 27 ++++-- .../macmpec/mpec_benchmark_dtable_loop.m | 2 +- .../macmpec/run_macmpec_experiments_general.m | 8 +- .../macmpec/solve_single_macmpec_problem.m | 46 ++++++---- .../generate_nonlinear_mpec_problem_set.m | 4 +- .../nonlinear_mpec_benchmark_dtable_loop.m | 18 +++- .../run_nonlinear_mpec_benchmark_large.m | 29 +++++-- src/+mpecopt/Options.m | 6 +- src/+mpecopt/Solver.m | 78 +++++++++++------ ...rmine_multipliers_based_stationary_point.m | 32 +++++-- src/misc/latexify_plot.m | 38 +++++++++ src/mpec_homotopy_solver.m | 24 ++++-- src/options/HomotopySolverOptions.m | 5 +- src/options/MINLPSolverOptions.m | 6 +- 15 files changed, 301 insertions(+), 107 deletions(-) create mode 100644 src/misc/latexify_plot.m diff --git a/benchmarks/macmpec/histogram_on_lpecs.m b/benchmarks/macmpec/histogram_on_lpecs.m index aaf363e..cd81ad3 100644 --- a/benchmarks/macmpec/histogram_on_lpecs.m +++ b/benchmarks/macmpec/histogram_on_lpecs.m @@ -3,6 +3,7 @@ dtable; lpec_dstruct; +plot_cpu = false; % Filter by solver and success solver = "MPECopt-Reg-Early"; solver = "MPECopt-Reg-Gurobi"; @@ -23,11 +24,16 @@ end % Postprocess nodecount cells -process_cells = @(C) cellfun(@(x) ... +process_cells_cummulative = @(C) cellfun(@(x) ... (isempty(x) * 0) + ... % empty → 0 (~isempty(x) * (sum(replace_zeros(x)))), ... C); +process_cells = @(C) cellfun(@(x) ... + (isempty(x) * 0) + ... % empty → 0 + (~isempty(x) * ((replace_zeros(x)))), ... + C, UniformOutput=false); + % Postprocess cpu time cells process_cpu = @(C) cellfun(@(x) ... (isempty(x) * 0) + (~isempty(x) * sum(x)), ... @@ -48,24 +54,51 @@ end % ------------------------ -% Nodecount statistics +% Nodecount statistics (for each lpec call seperatalyover solver calls) % ------------------------ -nc_i = process_cells(lpec_dstruct.nodecount_phase_i(idx)); -nc_ii = process_cells(lpec_dstruct.nodecount_phase_ii(idx)); +nc_i = cell2mat(process_cells(lpec_dstruct.nodecount_phase_i(idx))); +nc_ii = cell2mat(process_cells(lpec_dstruct.nodecount_phase_ii(idx))); +nc_tot = [nc_i, nc_ii]; + +% Histograms (nodecounts) +figure; +subplot(131) +histogram(nc_i, 'BinMethod', 'integers'); +title('Nodecount Phase I - each LPEC call'); xlabel('Nodes'); ylabel('Frequency'); grid on; +% xticks(min(nc_i):max(nc_i)); + +subplot(132) +histogram(nc_ii, 'BinMethod', 'integers'); +title('Nodecount Phase II - each LPEC call'); xlabel('Nodes'); ylabel('Frequency'); grid on; +xticks(min(nc_ii):max(nc_ii)); + +subplot(133) +histogram(nc_tot, 'BinMethod', 'integers'); +title('Total Nodecount - each LPEC call'); xlabel('Nodes'); ylabel('Frequency'); grid on; +% xticks(min(nc_tot):max(nc_tot)); + + + +% ------------------------ +% Nodecount statistics (cummulative over solver calls) +% ------------------------ +nc_i = process_cells_cummulative(lpec_dstruct.nodecount_phase_i(idx)); +nc_ii = process_cells_cummulative(lpec_dstruct.nodecount_phase_ii(idx)); nc_tot = nc_i + nc_ii; % Histograms (nodecounts) figure; +subplot(131) histogram(nc_i, 'BinMethod', 'integers'); title('Nodecount Phase I'); xlabel('Nodes'); ylabel('Frequency'); grid on; % xticks(min(nc_i):max(nc_i)); -figure; +subplot(132) histogram(nc_ii, 'BinMethod', 'integers'); title('Nodecount Phase II'); xlabel('Nodes'); ylabel('Frequency'); grid on; xticks(min(nc_ii):max(nc_ii)); -figure; +subplot(133) histogram(nc_tot, 'BinMethod', 'integers'); title('Total Nodecount'); xlabel('Nodes'); ylabel('Frequency'); grid on; % xticks(min(nc_tot):max(nc_tot)); @@ -86,20 +119,23 @@ cpu_tot = cpu_i + cpu_ii; % Histograms (CPU times) -figure; -histogram(cpu_i); -title('CPU Time Phase I'); xlabel('Time [s]'); ylabel('Frequency'); grid on; - -figure; -histogram(cpu_ii); -title('CPU Time Phase II'); xlabel('Time [s]'); ylabel('Frequency'); grid on; - -figure; -histogram(cpu_tot); -title('Total CPU Time'); xlabel('Time [s]'); ylabel('Frequency'); grid on; +if plot_cpu + figure; + subplot(131) + histogram(cpu_i); + title('CPU Time Phase I'); xlabel('Time [s]'); ylabel('Frequency'); grid on; + + subplot(132) + histogram(cpu_ii); + title('CPU Time Phase II'); xlabel('Time [s]'); ylabel('Frequency'); grid on; + + subplot(133) + histogram(cpu_tot); + title('Total CPU Time'); xlabel('Time [s]'); ylabel('Frequency'); grid on; +end % ------------------------ -% Iteration plot for max problem (nodecounts) +%% Iteration plot for max problem (nodecounts) % ------------------------ vec_i = lpec_dstruct.nodecount_phase_i{max_idx}; vec_ii = lpec_dstruct.nodecount_phase_ii{max_idx}; @@ -111,12 +147,13 @@ x_ii = (length(vec_i)+1):(length(vec_i)+length(vec_ii)); figure; -semilogy(x_i, vec_i, '-o', 'LineWidth', 1.5, 'DisplayName','Phase I'); hold on; -semilogy(x_ii, vec_ii, '-s', 'LineWidth', 1.5, 'DisplayName','Phase II'); +subplot(121) +stairs(x_i, vec_i, '-o', 'LineWidth', 1.5, 'DisplayName','Phase I'); hold on; +stairs(x_ii, vec_ii, '-s', 'LineWidth', 1.5, 'DisplayName','Phase II'); ylim([0 (max([vec_i, vec_ii])+1)*1.1]) xlabel('Iteration'); ylabel('Nodecount'); -title(sprintf('Nodecount per Iteration (%s)', dtable.problem_name{max_idx})); +% title(sprintf('Nodecount per Iteration (%s)', dtable.problem_name{max_idx})); legend('show'); grid on; % ------------------------ @@ -131,9 +168,9 @@ x_ci = 1:length(cpu_vec_i); x_cii = (length(cpu_vec_i)+1):(length(cpu_vec_i)+length(cpu_vec_ii)); -figure; -plot(x_ci, cpu_vec_i, '-o', 'LineWidth', 1.5, 'DisplayName','Phase I'); hold on; -plot(x_cii, cpu_vec_ii, '-s', 'LineWidth', 1.5, 'DisplayName','Phase II'); +subplot(122) +stairs(x_ci, cpu_vec_i, '-o', 'LineWidth', 1.5, 'DisplayName','Phase I'); hold on; +stairs(x_cii, cpu_vec_ii, '-s', 'LineWidth', 1.5, 'DisplayName','Phase II'); xlabel('Iteration'); ylabel('CPU Time [s]'); title(sprintf('CPU Time per Iteration (%s)', dtable.problem_name{max_idx})); diff --git a/benchmarks/macmpec/load_and_plot_results.m b/benchmarks/macmpec/load_and_plot_results.m index 8e23f98..f0f7f8a 100644 --- a/benchmarks/macmpec/load_and_plot_results.m +++ b/benchmarks/macmpec/load_and_plot_results.m @@ -50,16 +50,29 @@ results = 1; switch results case 1 - S = load('macmpec_general_30-Oct-2024'); - - S = load('macmpec_general_07-Nov-2024'); + % S = load('macmpec_general_30-Oct-2024'); + % S = load('macmpec_general_07-Nov-2024'); + S = load('macmpec_general_31-Aug-2025'); dtable = S.dtable; % solver_names = ["MPECopt-Gurobi", "MPECopt-HiGHS",... % "MPECopt-Simple", "Reg" , "Pen-$\ell_{\infty}$", "Pen-$\ell_{1}$"]; - solver_names = ["MPECopt-Reg-Gurobi", "MPECopt-Reg-HiGHS",... - "MPECopt-$\ell_1$-Gurobi", "Reg" , "Pen-$\ell_{\infty}$", "Pen-$\ell_{1}$"]; + % solver_names = ["MPECopt-Reg-Gurobi", "MPECopt-Reg-HiGHS",... + % "MPECopt-$\ell_1$-Gurobi", "Reg" , "Pen-$\ell_{\infty}$", "Pen-$\ell_{1}$"]; + + solver_names = ["MPECopt-Reg-Gurobi", "MPECopt-Reg-HiGHS", "MPECopt-$\ell_1$-Gurobi", ... + "Reg" , "NLP", ... + "MINLP"]; + + solver_names = ["MINLP" + "MPECopt-$\ell_1$-Gurobi" + "MPECopt-Reg-Gurobi" + "NLP" + "Reg" + "MINLP" + ]; + % solver_names = ["MPECopt-Gurobi", "MPECopt-Reg-HiGHS",... % "MPECopt-$\ell_1$-Gurobi", "Reg" , "Pen-$\ell_{\infty}$", "Pen-$\ell_{1}$"]; @@ -71,7 +84,7 @@ plot_settings.solved_in_phase_i = 1; %% Who is not B stationary? dtable_ii = dtable(dtable.solver_name == solver_names{1},:); - dtable_jj = dtable(dtable.solver_name == solver_names{4},:); + dtable_jj = dtable(dtable.solver_name == solver_names{3},:); ind_not_B = find(dtable_jj.success == 1 & dtable_jj.b_stationarity == 0); % For Reg % diff in obj @@ -100,7 +113,7 @@ %% Solved by gurobi and not by highs dtable_ii = dtable(dtable.solver_name == solver_names{1},:); dtable_jj = dtable(dtable.solver_name == solver_names{2},:); - dtable_jj.problem_name(dtable_ii.success == 1 & dtable_jj.success == 0) + % dtable_jj.problem_name(dtable_ii.success == 1 & dtable_jj.success == 0) case 2 diff --git a/benchmarks/macmpec/mpec_benchmark_dtable_loop.m b/benchmarks/macmpec/mpec_benchmark_dtable_loop.m index 903ac35..8b331ef 100644 --- a/benchmarks/macmpec/mpec_benchmark_dtable_loop.m +++ b/benchmarks/macmpec/mpec_benchmark_dtable_loop.m @@ -99,7 +99,7 @@ else [result,stats] = solver_functions{ii}(mpec_struct,solver_initalization,options); end - + % % if stats.success~=1 % keyboard; % end diff --git a/benchmarks/macmpec/run_macmpec_experiments_general.m b/benchmarks/macmpec/run_macmpec_experiments_general.m index 8531eca..c933c61 100644 --- a/benchmarks/macmpec/run_macmpec_experiments_general.m +++ b/benchmarks/macmpec/run_macmpec_experiments_general.m @@ -46,7 +46,7 @@ N_interesting = []; for ii=1:length(macmpec_json) - if mpecs(ii).n_w <= 250 + if mpecs(ii).n_w <= 150 N_interesting = [N_interesting; ii]; end end @@ -71,7 +71,7 @@ default_opts1 = mpecopt.Options(); default_opts1.solver_name = solver_names{1}; -default_opts1.settings_lpec.lpec_solver = "Gurobi"; +default_opts1.settings_lpec.lpec_solver = "Highs_casadi"; default_opts1.relax_and_project_homotopy_parameter_steering = "Direct"; % default_opts1.initialization_strategy = "FeasibilityEll1General"; @@ -84,7 +84,7 @@ default_opts2 = mpecopt.Options(); default_opts2.solver_name = solver_names{2}; -default_opts2.settings_lpec.lpec_solver = "Gurobi"; +default_opts2.settings_lpec.lpec_solver = "Highs_casadi"; default_opts2.relax_and_project_homotopy_parameter_steering = "Direct"; default_opts2.settings_lpec.stop_lpec_at_feasible = true; % default_opts2.rho_TR_phase_i_init = 1e-3; @@ -114,7 +114,7 @@ %% Create data struct N_experiments = [1, 3:6]; -N_experiments = [2]; +N_experiments = [1 2 4]; mpec_benchmark_dtable_loop; % this script runs the experimetns, creates a dtable %% Pick which results to plot diff --git a/benchmarks/macmpec/solve_single_macmpec_problem.m b/benchmarks/macmpec/solve_single_macmpec_problem.m index c3dfc27..cf99bff 100644 --- a/benchmarks/macmpec/solve_single_macmpec_problem.m +++ b/benchmarks/macmpec/solve_single_macmpec_problem.m @@ -6,23 +6,31 @@ all_problem_names = {macmpec_json.name}; -% problame_name = 'qpec-200-2'; -problame_name = 'ralph1'; +problame_name = 'qpec-200-2'; +% problame_name = 'ralph1'; % problame_name = 'pack-rig-32'; % problame_name = 'pack-rig-8'; % problame_name = 'gnash15m.nl'; % problame_name = 'pack-rig2p-16.nl'; -problame_name = 'tap-09'; +% problame_name = 'nash1a'; +% problame_name = 'nash1c'; +% problame_name = 'tap-09'; -problame_name = 'qpec-200-3'; +% problame_name = 'pack-rig2p-16'; -% Some failures: gnash15m gnash16 gnash17m gnash18m gnash19m tap-09 +% problame_name = 'pack-rig2p-8'; + + +% Infesaile : gnash15m gnash16m gnash17m gnash18m gnash19m +% Cyicling: tap-09? +% A-stats: pack-rig2p-16.nl +% siouxfls1 and siouxfls % bad tolerances? N_biactive = [168 80 83 71 85 73 120 117 105 108]; ii_prob = find(contains({macmpec_json.name},problame_name)); % ii_prob = N_biactive(10); % A stationarity in reg! looks problematic! -% ii_prob = N_biactive(10); +% ii_prob = N_biactive(9); % ii_prob = 8; @@ -82,9 +90,9 @@ %% Homotopy solver settings_homotopy = HomotopySolverOptions(); settings_homotopy.homotopy_parameter_steering = 'Direct'; -% [result_homotopy,stats_homotopy] = mpec_homotopy_solver(mpec,solver_initalization,settings_homotopy); -% f_opt_homotopy = full(result_homotopy.f); -% w_opt_homotopy = full(result_homotopy.x); +[result_homotopy,stats_homotopy] = mpec_homotopy_solver(mpec,solver_initalization,settings_homotopy); +f_opt_homotopy = full(result_homotopy.f); +w_opt_homotopy = full(result_homotopy.x); %% MINLP solver settings_minlp = MINLPSolverOptions(); @@ -99,13 +107,17 @@ %% MPECopt solver solver_settings = mpecopt.Options(); solver_settings.relax_and_project_homotopy_parameter_steering = "Direct"; -solver_settings.settings_lpec.lpec_solver = "Highs_casadi"; +solver_settings.settings_lpec.lpec_solver = "Gurobi"; +solver_settings.settings_casadi_nlp.ipopt.max_iter = 4000; solver_settings.settings_lpec.stop_lpec_at_feasible = true; +% solver_settings.settings_casadi_nlp.ipopt.fixed_variable_treatment = 'relax_bounds'; % solver_settings.initialization_strategy = "FeasibilityEll1General"; % solver_settings.rho_TR_phase_i_init = 10; % solver_settings.tol_active = 1e-6; solver = mpecopt.Solver(mpec, solver_settings); [result_mpecopt,stats_mpecopt] = solver.solve(solver_initalization); + +% [solution,stats] = mpec_optimizer(mpec, solver_initalization, solver_settings); w_opt_mpecopt = full(result_mpecopt.x); f_opt_mpecopt = full(result_mpecopt.f); @@ -113,15 +125,15 @@ fprintf('\n-------------------------------------------------------------------------------\n'); fprintf('Method \t\t Objective \t comp_res \t n_biactive \t CPU time (s)\t Success\t Stat. type\n') fprintf('-------------------------------------------------------------------------------\n'); -% fprintf('Reg \t %2.2e \t %2.2e \t\t %d \t\t\t %2.2f \t\t\t\t %d\t %s\n',f_opt_homotopy,stats_homotopy.comp_res,stats_homotopy.n_biactive,stats_homotopy.cpu_time_total,stats_homotopy.success,stats_homotopy.multiplier_based_stationarity) +fprintf('Reg \t %2.2e \t %2.2e \t\t %d \t\t\t %2.2f \t\t\t\t %d\t %s\n',f_opt_homotopy,stats_homotopy.comp_res,stats_homotopy.n_biactive,stats_homotopy.cpu_time_total,stats_homotopy.success,stats_homotopy.multiplier_based_stationarity) % fprintf('MINLP \t\t %2.2e \t %2.2e \t\t %d \t\t\t %2.2f \t\t\t\t %d\t %s\n',f_opt_minlp,stats_minlp.comp_res,stats_minlp.n_biactive,stats_minlp.cpu_time_total,stats_minlp.success,stats_minlp.multiplier_based_stationarity) fprintf('MPECopt \t %2.2e \t %2.2e \t\t %d \t\t\t %2.2f \t\t\t\t %d\t %s\n',f_opt_mpecopt,stats_mpecopt.comp_res,stats_mpecopt.n_biactive,stats_mpecopt.cpu_time_total,stats_mpecopt.success,stats_mpecopt.multiplier_based_stationarity) fprintf('-------------------------------------------------------------------------------\n'); -% fprintf('||w_reg - w_mpec|| = %2.2e \n',norm(w_opt_homotopy-w_opt_mpecopt)); +fprintf('||w_reg - w_mpec|| = %2.2e \n',norm(w_opt_homotopy-w_opt_mpecopt)); % fprintf('||w_minlp - w_mpec|| = %2.2e \n',norm(w_opt_minlp-w_opt_mpecopt)); -stats_mpecopt.iter.nodecount_phase_i -stats_mpecopt.iter.nodecount_phase_ii - -stats_mpecopt.iter.itercount_phase_i -stats_mpecopt.iter.itercount_phase_ii \ No newline at end of file +% stats_mpecopt.iter.nodecount_phase_i +% stats_mpecopt.iter.nodecount_phase_ii +% +% stats_mpecopt.iter.itercount_phase_i +% stats_mpecopt.iter.itercount_phase_ii \ No newline at end of file diff --git a/benchmarks/nonlinear_mpec_benchmark/generate_nonlinear_mpec_problem_set.m b/benchmarks/nonlinear_mpec_benchmark/generate_nonlinear_mpec_problem_set.m index 505686d..c7b32db 100644 --- a/benchmarks/nonlinear_mpec_benchmark/generate_nonlinear_mpec_problem_set.m +++ b/benchmarks/nonlinear_mpec_benchmark/generate_nonlinear_mpec_problem_set.m @@ -77,9 +77,9 @@ scatter(n_var,n_y_vec); hold on scatter(n_var,n_ineq_vec*(1+settings.s_ineq_copy)+n_x_vec); - hold on + scatter(n_var,n_x_vec); xlabel('$n+2m$ - number of variables') - legend({'$m$ number of complementairt variables','Total num. of constraints'},'Location','northwest') + legend({'$m$ number of complementairt variables','Total num. of constraints', 'Non. comp. vars.'},'Location','northwest') axis equal % elseif settings.random_problem_sizes_individual % n_x_vec = round(n_x_min+n_x_max*rand(N_rand_prob,1)); diff --git a/benchmarks/nonlinear_mpec_benchmark/nonlinear_mpec_benchmark_dtable_loop.m b/benchmarks/nonlinear_mpec_benchmark/nonlinear_mpec_benchmark_dtable_loop.m index 75a6c74..d40a769 100644 --- a/benchmarks/nonlinear_mpec_benchmark/nonlinear_mpec_benchmark_dtable_loop.m +++ b/benchmarks/nonlinear_mpec_benchmark/nonlinear_mpec_benchmark_dtable_loop.m @@ -30,7 +30,8 @@ dstruct.max_cpu_time_lpec = []; dstruct.problem_infeasible = []; dstruct.f_lpec = []; -% LPEC solver statistics + +%% Lpec details lpec_dstruct = struct; lpec_dstruct.solver_name = []; lpec_dstruct.nodecount_phase_i = {}; @@ -39,6 +40,8 @@ lpec_dstruct.baritercount_phase_ii= {}; lpec_dstruct.itercount_phase_i = {}; lpec_dstruct.itercount_phase_ii= {}; +lpec_dstruct.cpu_time_lpec_phase_i = {}; +lpec_dstruct.cpu_time_lpec_phase_ii = {}; @@ -76,7 +79,16 @@ mpec_struct = struct('x',w,'f',f,'g',g,'G',G,'H',H); solver_initalization = struct('x0', w0, 'lbx',lbw, 'ubx',ubw,'lbg',lbg,'ubg',ubg); % to add non-mpecsol solver you can add ifs here - [result,stats] = solver_functions{ii}(mpec_struct,solver_initalization,options); + if isequal(solver_functions{ii},@mpec_optimizer) + solver = mpecopt.Solver(mpec_struct, options); + [result,stats] = solver.solve(solver_initalization); + else + [result,stats] = solver_functions{ii}(mpec_struct,solver_initalization,options); + end + + % if stats.success~=1 + % keyboard; + % end dstruct.solver_name = [dstruct.solver_name; string(name)]; dstruct.problem_name = [dstruct.problem_name; string(mpec_name)]; @@ -111,6 +123,8 @@ lpec_dstruct.baritercount_phase_ii{end+1} = stats.iter.baritercount_phase_ii; lpec_dstruct.itercount_phase_i{end+1} = stats.iter.itercount_phase_i; lpec_dstruct.itercount_phase_ii{end+1} = stats.iter.itercount_phase_ii; + lpec_dstruct.cpu_time_lpec_phase_i{end+1} = stats.iter.cpu_time_lpec_phase_i_iter; + lpec_dstruct.cpu_time_lpec_phase_ii{end+1} = stats.iter.cpu_time_lpec_phase_ii_iter; dstruct.n_biactive = [dstruct.n_biactive; stats.n_biactive]; dstruct.f_lpec = [dstruct.f_lpec; stats.f_lpec]; diff --git a/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_large.m b/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_large.m index 92c8ccf..5ec91ee 100644 --- a/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_large.m +++ b/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_large.m @@ -62,8 +62,8 @@ % dimensions.n_x_min = 10; % % -dimensions.N_rand_prob = 2; % number of problems per objective -dimensions.n_x_max = 30; +dimensions.N_rand_prob = 1; % number of problems per objective +dimensions.n_x_max = 100; dimensions.n_x_min = 10; dimensions.n_fraction_of_x = 0.5; % n_y = round(n_x/n_fraction_of_x) @@ -78,25 +78,40 @@ %% Solver settings -%% Define list of solvers to use + solver_names = ["MPECopt-Reg-Gurobi", "MPECopt-Reg-HiGHS", "MPECopt-$\ell_1$-Gurobi", ... "Reg" , "NLP", ... "MINLP"]; + + +solver_names = ["MPECopt-Reg-Gurobi", "MPECopt-Reg-Early", "MPECopt-$\ell_1$-Gurobi", ... + "Reg" , "NLP", ... + "MINLP"]; + solver_functions = {@mpec_optimizer,@mpec_optimizer,@mpec_optimizer,... @mpec_homotopy_solver,@mpec_homotopy_solver,... @mpec_minlp_solver}; - default_opts1 = mpecopt.Options(); default_opts1.solver_name = solver_names{1}; default_opts1.settings_lpec.lpec_solver = "Gurobi"; default_opts1.relax_and_project_homotopy_parameter_steering = "Direct"; +% default_opts1.initialization_strategy = "FeasibilityEll1General"; + +% default_opts2 = mpecopt.Options(); +% default_opts2.solver_name = solver_names{2}; +% default_opts2.settings_lpec.lpec_solver = "Highs_casadi"; +% default_opts2.settings_lpec.stop_lpec_at_feasible = true; +% default_opts2.relax_and_project_homotopy_parameter_steering = "Direct"; +% default_opts2.rho_TR_phase_i_init = 1e-3; default_opts2 = mpecopt.Options(); default_opts2.solver_name = solver_names{2}; -default_opts2.settings_lpec.lpec_solver = "Highs_casadi"; +default_opts2.settings_lpec.lpec_solver = "Gurobi"; default_opts2.relax_and_project_homotopy_parameter_steering = "Direct"; -default_opts2.rho_TR_phase_i_init = 1e-3; +default_opts2.settings_lpec.stop_lpec_at_feasible = true; +% default_opts2.rho_TR_phase_i_init = 1e-3; + default_opts3 = mpecopt.Options(); default_opts3.solver_name = solver_names{3}; @@ -121,7 +136,7 @@ minlp_opts}; % list of options to pass to mpecsol (option structs) %% Create data struct -N_experiments = [1 5]; +N_experiments = [1 2]; nonlinear_mpec_benchmark_dtable_loop; % this script runs the experimetns, creates a dtable %% Pick which results to plot diff --git a/src/+mpecopt/Options.m b/src/+mpecopt/Options.m index 3fdb917..2316a61 100644 --- a/src/+mpecopt/Options.m +++ b/src/+mpecopt/Options.m @@ -131,7 +131,7 @@ obj.settings_casadi_nlp.verbose = false; obj.settings_casadi_nlp.ipopt.max_iter = 3000; obj.settings_casadi_nlp.ipopt.bound_relax_factor = 0; - obj.settings_casadi_nlp.ipopt.bound_relax_factor = 1e-16; + % obj.settings_casadi_nlp.ipopt.bound_relax_factor = 1e-16; obj.settings_casadi_nlp.ipopt.honor_original_bounds = 'yes'; obj.settings_casadi_nlp.ipopt.tol = default_tol; obj.settings_casadi_nlp.ipopt.dual_inf_tol = default_tol; @@ -147,9 +147,9 @@ % obj.settings_casadi_nlp.ipopt.warm_start_bound_frac = 1e-6; % obj.settings_casadi_nlp.ipopt.warm_start_entire_iterate = 'yes'; obj.settings_casadi_nlp.ipopt.linear_solver = 'ma27'; % mumps, ma27, ma57 - obj.settings_casadi_nlp.ipopt.fixed_variable_treatment = 'relax_bounds'; % make_parameter make_constraint relax_bounds - obj.settings_casadi_nlp.detect_simple_bounds = true; % obj.settings_casadi_nlp.ipopt.fixed_variable_treatment = 'relax_bounds'; % make_parameter make_constraint relax_bounds + obj.settings_casadi_nlp.detect_simple_bounds = true; + obj.settings_casadi_nlp.ipopt.fixed_variable_treatment = 'make_parameter'; % make_parameter make_constraint relax_bounds % obj.opts_casadi_nlp.snopt = struct(); % obj.opts_casadi_nlp.worhp = struct(); diff --git a/src/+mpecopt/Solver.m b/src/+mpecopt/Solver.m index c83c243..f22c305 100644 --- a/src/+mpecopt/Solver.m +++ b/src/+mpecopt/Solver.m @@ -1482,13 +1482,17 @@ function create_lpec_functions(obj) end % --------------------------- Check if B-stationary point found -------------------------- % h_total_k = full(mpec_casadi.h_total_fun(x_k,p0)); - % if ismember(stats_nlp.return_status, {'Solve_Succeeded', 'Search_Direction_Becomes_Too_Small', 'Solved_To_Acceptable_Level'}) - % bnlp_solved = true; - % else - % bnlp_solved = false; - % end + if l_k > 1 + if ismember(stats_nlp.return_status, {'Solve_Succeeded', 'Search_Direction_Becomes_Too_Small', 'Solved_To_Acceptable_Level'}) || (h_total_k <= opts.tol) + bnlp_solved = true; % succes of bnlp iter in phase ii + else + bnlp_solved = false; % failed in phase ii or not good enough + end + else + bnlp_solved = true; % from phase 1 + end % if (h_total_k <= opts.tol) && ((abs(f_lin_opt_k_l) <= opts.tol_B_stationarity || norm(nabla_f_k) <= opts.tol_B_stationarity)) % if objective zero (either if cost gradient zero, or solution leads to it) = then set step to zero => B stationarity - if ((abs(f_lin_opt_k_l) <= opts.tol_B_stationarity || norm(nabla_f_k) <= opts.tol_B_stationarity)) % if objective zero (either if cost gradient zero, or solution leads to it) = then set step to zero => B stationarity + if bnlp_solved && ((abs(f_lin_opt_k_l) <= opts.tol_B_stationarity || norm(nabla_f_k) <= opts.tol_B_stationarity)) % if objective zero (either if cost gradient zero, or solution leads to it) = then set step to zero => B stationarity if opts.reset_lpec_objective d_lpec_k_l = d_lpec_k_l*0; % if the current point is feasible, and the objective is zero, then d = 0 is also a solution of the lpec (occurs if a solution is not on the verties of the lp) f_lin_opt_k_l = 0; @@ -1762,28 +1766,54 @@ function create_lpec_functions(obj) % --------------- compute multiplier-based stationary points -------------- multiplier_based_stationarity_debug = stats.multiplier_based_stationarity; + tol_active = 1e-6; + N_TNLP = 6; % max tnlp solves + ii = 1; + tol_active_default = opts.tol_active; + opts.tol_active = tol_active; if (stats.success || k==opts.max_iter) && opts.compute_tnlp_stationary_point && phase_ii % if ~strcmp(opts.piece_nlp_strategy,'TNLP') % resolve TNLP for correct multipliers - lbx_bnlp_k = solver_initialization.lbx; ubx_bnlp_k = solver_initialization.ubx; % reset bounds of bnlp. - lbg_tnlp_k = solver_initialization.lbg; ubg_tnlp_k = solver_initialization.ubg; - initial_strategy = opts.piece_nlp_strategy; - opts.piece_nlp_strategy = 'TNLP'; % used to get tnlp active sets - nabla_f_k = full(mpec_casadi.nabla_f_fun(x_k,p0)); - active_set_estimate_k = find_active_sets_piece_nlp(x_k,nabla_f_k,y_lpec_k_l,dims,opts,opts.tol_active); - ubx_bnlp_k(dims.ind_x1(active_set_estimate_k.I_0_plus)) = 0; - ubx_bnlp_k(dims.ind_x2(active_set_estimate_k.I_plus_0)) = 0; - ubx_bnlp_k(dims.ind_x1(active_set_estimate_k.I_00)) = 0; - ubx_bnlp_k(dims.ind_x2(active_set_estimate_k.I_00)) = 0; - opts.piece_nlp_strategy = initial_strategy; - % end - t_nlp_start = tic; - results_nlp = mpec_casadi.solver('x0',x_k,'p', p0, 'lbx', lbx_bnlp_k, 'ubx', ubx_bnlp_k,'lbg', lbg_tnlp_k, 'ubg', ubg_tnlp_k); - cpu_time_nlp_k_l = toc(t_nlp_start); - x_k_multi = full(results_nlp.x); - lambda_x_k = full(results_nlp.lam_x); - [stats.multiplier_based_stationarity, ~] = determine_multipliers_based_stationary_point(x_k_multi,lambda_x_k,dims,opts); + while ii <= N_TNLP + lbx_bnlp_k = solver_initialization.lbx; ubx_bnlp_k = solver_initialization.ubx; % reset bounds of bnlp. + lbg_tnlp_k = solver_initialization.lbg; ubg_tnlp_k = solver_initialization.ubg; + initial_strategy = opts.piece_nlp_strategy; + opts.piece_nlp_strategy = 'TNLP'; % used to get tnlp active sets + nabla_f_k = full(mpec_casadi.nabla_f_fun(x_k,p0)); + active_set_estimate_k = find_active_sets_piece_nlp(x_k,nabla_f_k,y_lpec_k_l,dims,opts,opts.tol_active); + ubx_bnlp_k(dims.ind_x1(active_set_estimate_k.I_0_plus)) = 0; + ubx_bnlp_k(dims.ind_x2(active_set_estimate_k.I_plus_0)) = 0; + ubx_bnlp_k(dims.ind_x1(active_set_estimate_k.I_00)) = 0; + ubx_bnlp_k(dims.ind_x2(active_set_estimate_k.I_00)) = 0; + opts.piece_nlp_strategy = initial_strategy; + % end + t_nlp_start = tic; + results_nlp = mpec_casadi.solver('x0',x_k,'p', p0, 'lbx', lbx_bnlp_k, 'ubx', ubx_bnlp_k,'lbg', lbg_tnlp_k, 'ubg', ubg_tnlp_k); + cpu_time_nlp_k_l = toc(t_nlp_start); + x_k_multi = full(results_nlp.x); + f_tnlp = full(results_nlp.f); + lambda_x_k = full(results_nlp.lam_x); + % if settings.verbose_solver + % fprintf('%d \t %2.2e\t %2.2e\t %2.2e\t %2.2e\t %d \t\t %2.2e\t \t\t %s \n',ii,f_tnlp,comp_res_tnlp,inf_pr_tnlp,inf_du_tnlp,stats.iter_count,0,(stats.return_status)); + % end + fprintf('\t\t ||x_tnlp - x_k|| = %2.2e, |f_tnlp-f_k| = %2.2e \n', norm(x_k_multi-x_k,inf),abs(f_tnlp-f_k)) + if norm(x_k_multi-x_k,inf) <= 1e-6 || abs(f_tnlp-f_k)/abs(f_k) <= 1e-3 || ii == N_TNLP + [stats.multiplier_based_stationarity, ~] = determine_multipliers_based_stationary_point(x_k_multi,lambda_x_k,dims,opts); + n_biactive = sum(active_set_estimate_k.I_00); + % if ii~=N_TNLP + % x_k = x_tnlp; + % end + break; + else + tol_active = 0.1*tol_active; + opts.tol_active = tol_active; + ii = ii+1; + end + % [stats.multiplier_based_stationarity, ~] = determine_multipliers_based_stationary_point(x_k_multi,lambda_x_k,dims,opts); + end end + settings.tol_active = tol_active_default; % reset + % Debug falure of stationary point computation if ~strcmp(multiplier_based_stationarity_debug,'X') && opts.debug_mode_on && ~isequal(multiplier_based_stationarity_debug,stats.multiplier_based_stationarity) diff --git a/src/determine_multipliers_based_stationary_point.m b/src/determine_multipliers_based_stationary_point.m index ff7f6c3..98711da 100644 --- a/src/determine_multipliers_based_stationary_point.m +++ b/src/determine_multipliers_based_stationary_point.m @@ -12,11 +12,31 @@ active_set_estimate_k = find_active_sets(x, dims, settings.tol_active); ind_I_00 = find(active_set_estimate_k.I_00); + +% Isolate multipliers for biactives + +eta_x1_biactive = eta_x1(ind_I_00); +eta_x2_biactive = eta_x2(ind_I_00); +max_mult = max(norm([eta_x1_biactive;eta_x2_biactive],inf), 1e-10); + +% Rescale to max (sensitive for M-stationarity) +eta_x1_biactive = eta_x1_biactive./max_mult; +eta_x2_biactive = eta_x2_biactive./max_mult; + +if max_mult > 1e3 + tol_M = 1e-3; +else + tol_M = settings.tol_B_stationarity*10; +end + + +% R + % print_iter_line(); if isempty(ind_I_00) output_message = sprintf('S - stationary, %d biactive constraints. ', length(ind_I_00)); multiplier_based_stationarity = 'S'; -elseif (all(eta_x1(ind_I_00) >= 0 & eta_x2(ind_I_00) >= 0) ) +elseif (all(eta_x1_biactive >= 0 & eta_x2_biactive >= 0) ) output_message = sprintf('S - stationary, %d biactive constraints. ', length(ind_I_00)); multiplier_based_stationarity = 'S'; if all(eta_x1.*eta_x2 > 0) @@ -25,13 +45,15 @@ output_message = [output_message ,' Upper level strict complementarity does not hold.']; end else - if all( (eta_x1(ind_I_00) > 0 & eta_x2(ind_I_00) > 0) | (eta_x1(ind_I_00).*eta_x2(ind_I_00) == 0)) + % if all( (eta_x1_biactive > 0 & eta_x2_biactive > 0) | (eta_x1_biactive.*eta_x2_biactive == 0)) + if all( (eta_x1_biactive > 0 & eta_x2_biactive > 0) | abs(eta_x1_biactive.*eta_x2_biactive)<= tol_M) + % account for imperfect zeros output_message = sprintf('M - stationary, %d biactive constraints.', length(ind_I_00)); multiplier_based_stationarity = 'M'; - elseif all(eta_x1(ind_I_00).*eta_x2(ind_I_00) >=0) + elseif all(eta_x1_biactive.*eta_x2_biactive >=0) output_message = sprintf('C - stationary, %d biactive constraints.', length(ind_I_00)); multiplier_based_stationarity = 'C'; - elseif all(eta_x1(ind_I_00) >= 0 | eta_x2(ind_I_00) >=0) + elseif all(eta_x1_biactive >= 0 | eta_x2_biactive >=0) output_message = sprintf('A - stationary, %d biactive constraints.', length(ind_I_00)); multiplier_based_stationarity = 'A'; else @@ -88,7 +110,7 @@ hh = patch(hh_x,hh_y,'k'); hh.FaceAlpha = 0.1; end - plot(eta_x1(ind_I_00),eta_x2(ind_I_00),'.','LineWidth',2.5,'MarkerSize',15) + plot(eta_x1_biactive,eta_x2_biactive,'.','LineWidth',2.5,'MarkerSize',15) grid on xlabel('$\nu$','Interpreter','latex') ylabel('$\xi$','Interpreter','latex') diff --git a/src/misc/latexify_plot.m b/src/misc/latexify_plot.m new file mode 100644 index 0000000..1cbd74b --- /dev/null +++ b/src/misc/latexify_plot.m @@ -0,0 +1,38 @@ +% BSD 2-Clause License + +% Copyright (c) 2022, Armin Nurkanović, Jonathan Frey, Anton Pozharskiy, Moritz Diehl + +% Redistribution and use in source and binary forms, with or without +% modification, are permitted provided that the following conditions are met: + +% 1. Redistributions of source code must retain the above copyright notice, this +% list of conditions and the following disclaimer. + +% 2. Redistributions in binary form must reproduce the above copyright notice, +% this list of conditions and the following disclaimer in the documentation +% and/or other materials provided with the distribution. + +% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +% DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +% FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +% DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +% SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +% CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +% OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +% OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +% This file is part of NOSNOC. + + +% https://www.mathworks.com/matlabcentral/answers/183311-setting-default-interpreter-to-latex + +function [] = latexify_plot() + list_factory = fieldnames(get(groot,'factory')); + index_interpreter = find(contains(list_factory,'Interpreter')); + for i = 1:length(index_interpreter) + default_name = strrep(list_factory{index_interpreter(i)},'factory','default'); + set(groot, default_name,'latex'); + end +end \ No newline at end of file diff --git a/src/mpec_homotopy_solver.m b/src/mpec_homotopy_solver.m index 54ffd4f..da607ec 100644 --- a/src/mpec_homotopy_solver.m +++ b/src/mpec_homotopy_solver.m @@ -513,11 +513,13 @@ x_k_before_tnlp = x_k; %% % ----------------------------------- MULTIPLIER BASED STAT ------------------------------------- -N_TNLP = 3; % max attemps to solve a TNLP; +N_TNLP = 6; % max attemps to solve a TNLP; ii = 1; tol_ell_inf = full(h_comp_constraints_tol_fun(x_k,p0)); % tol_active = max(2*settings.tol_active*1e3,2*tol_ell_inf); -tol_active = settings.tol_active; +tol_active_default = settings.tol_active; +tol_active = 1e-6; % tnlp +% tol_active = 1e2; % tnlp if settings.compute_tnlp_stationary_point && success && settings.lift_complementarities && ~settings.problem_is_lpec fprintf('----------------------------------- determining stationarity -----------------------------------------------\n') @@ -526,6 +528,7 @@ if strcmp(settings.homotopy_parameter_steering,'Ell_1') || strcmp(settings.homotopy_parameter_steering,'Ell_inf') ubx(end) = +inf; end + while ii <= N_TNLP active_set_estimate_k = find_active_sets(x_k, dims, tol_active); ubx(dims.ind_x1(active_set_estimate_k.I_0_plus)) = 0; @@ -545,7 +548,8 @@ if settings.verbose_solver fprintf('%d \t %2.2e\t %2.2e\t %2.2e\t %2.2e\t %d \t\t %2.2e\t \t\t %s \n',ii,f_tnlp,comp_res_tnlp,inf_pr_tnlp,inf_du_tnlp,stats.iter_count,0,(stats.return_status)); end - % fprintf('\t\t ||x_tnlp - x_k|| = %2.2e, |f_tnlp-f_k| = %2.2e \n', norm(x_tnlp-x_k,inf),abs(f_tnlp-f_k)) + fprintf('\t\t ||x_tnlp - x_k|| = %2.2e, |f_tnlp-f_k| = %2.2e \n', norm(x_tnlp-x_k,inf),abs(f_tnlp-f_k)) + % Terminate TNLP loop if norm(x_tnlp-x_k,inf) <= 1e-6 || abs(f_tnlp-f_k)/abs(f_k) <= 1e-3 || ii == N_TNLP [multiplier_based_stationarity, ~] = determine_multipliers_based_stationary_point(x_tnlp,lambda_x_tnlp,dims,settings); n_biactive = sum(active_set_estimate_k.I_00); @@ -554,14 +558,24 @@ end break; else + % drop out greatest biactive + % tol_active = (1-1e-9)*max(max(abs(x_k(dims.ind_x1(active_set_estimate_k.I_00))),x_k(dims.ind_x2(active_set_estimate_k.I_00)))); tol_active = 0.1*tol_active; ii = ii+1; end end - if ~strcmp(multiplier_based_stationarity,'X') + % if ~strcmp(multiplier_based_stationarity,'X') + % end +end + +if success + if ~(norm(x_tnlp-x_k,inf) <= 1e-6 || abs(f_tnlp-f_k)/abs(f_k) <= 1e-3) + multiplier_based_stationarity = 'X'; % failed to achieve accuracy end end +settings.tol_active = tol_active_default; % reset + % if strcmp(multiplier_based_stationarity,'W') % keyboard; % end @@ -617,7 +631,7 @@ ubx(dims.ind_x2(active_set_estimate_k.I_plus_0)) = 0; ubx(dims.ind_x1(active_set_estimate_k.I_00)) = 0; ubx(dims.ind_x2(active_set_estimate_k.I_00)) = 0; - + solution = solver('x0',x_k,'p',p0,'lbx',lbx,'ubx',ubx,'lbg',lbg,'ubg',ubg); x_tnlp = full(solution.x); f_tnlp = full(solution.f); diff --git a/src/options/HomotopySolverOptions.m b/src/options/HomotopySolverOptions.m index db66dc3..5cea88f 100644 --- a/src/options/HomotopySolverOptions.m +++ b/src/options/HomotopySolverOptions.m @@ -64,7 +64,10 @@ obj.settings_casadi_nlp.ipopt.warm_start_entire_iterate = 'yes'; obj.settings_casadi_nlp.ipopt.linear_solver = 'ma27'; % 'mumps'; ma57 obj.settings_casadi_nlp.detect_simple_bounds = true; - obj.settings_casadi_nlp.ipopt.fixed_variable_treatment = 'relax_bounds'; % make_parameter make_constraint relax_bounds + obj.settings_casadi_nlp.ipopt.max_wall_time = 600; + + % obj.settings_casadi_nlp.ipopt.fixed_variable_treatment = 'relax_bounds'; % make_parameter make_constraint relax_bounds + obj.settings_casadi_nlp.ipopt.fixed_variable_treatment = 'make_parameter'; % obj.opts_casadi_nlp.snopt = struct(); % obj.opts_casadi_nlp.worhp = struct(); % obj.opts_casadi_nlp.uno = struct(); diff --git a/src/options/MINLPSolverOptions.m b/src/options/MINLPSolverOptions.m index f8d1f35..c8f8c28 100644 --- a/src/options/MINLPSolverOptions.m +++ b/src/options/MINLPSolverOptions.m @@ -42,11 +42,7 @@ methods function obj = MINLPSolverOptions() - % see - - - - default_tol = 1e-12; + default_tol = 1e-10; % --------- MINLP Specific settings % bonmin: https://www.coin-or.org/Bonmin/option_pages/options_list_bonmin.html obj.settings_casadi_nlp.error_on_fail = false; From 68fa48322c86aa24c614bb069fb59a158f6d797d Mon Sep 17 00:00:00 2001 From: "armin.nurkanovic" Date: Tue, 2 Sep 2025 09:43:14 +0200 Subject: [PATCH 05/31] fixed missing implementaiton of ell 1 in class version --- src/+mpecopt/Solver.m | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/+mpecopt/Solver.m b/src/+mpecopt/Solver.m index f22c305..04a888d 100644 --- a/src/+mpecopt/Solver.m +++ b/src/+mpecopt/Solver.m @@ -1936,6 +1936,9 @@ function create_lpec_functions(obj) lbg_comp_relaxed = -inf*ones(dims.n_comp,1); ubg_comp_relaxed = 0*ones(dims.n_comp,1); end + case "Ell_1" + lbg_comp_relaxed = []; + ubg_comp_relaxed = []; end lbg_relaxed = [lbg_relaxed;lbg_comp_relaxed]; From dd45c27714066e409c1dcab76c4a315758d728fb Mon Sep 17 00:00:00 2001 From: "armin.nurkanovic" Date: Sat, 6 Sep 2025 15:51:32 +0200 Subject: [PATCH 06/31] more lpec results details, more benchmark problems, more solution stats --- benchmarks/macmpec/histogram_on_lpecs.m | 47 ++-- .../macmpec/run_macmpec_experiments_general.m | 33 ++- .../run_macmpec_experiments_lpec_histogram.m | 169 ++++++++++++ .../macmpec/solve_single_macmpec_problem.m | 17 +- .../generate_nonlinear_mpec_problem_set.m | 48 +++- .../histogram_on_lpecs.m | 243 ++++++++++++++++++ .../run_nonlinear_mpec_benchmark_large.m | 41 +-- ..._nonlinear_mpec_benchmark_lpec_histogram.m | 216 ++++++++++++++++ .../nonlinear_mpec_benchmark/sparsity.m | 72 ++++++ src/+mpecopt/Solver.m | 19 +- src/lpec_solver.m | 82 +++--- 11 files changed, 876 insertions(+), 111 deletions(-) create mode 100644 benchmarks/macmpec/run_macmpec_experiments_lpec_histogram.m create mode 100644 benchmarks/nonlinear_mpec_benchmark/histogram_on_lpecs.m create mode 100644 benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_lpec_histogram.m create mode 100644 benchmarks/nonlinear_mpec_benchmark/sparsity.m diff --git a/benchmarks/macmpec/histogram_on_lpecs.m b/benchmarks/macmpec/histogram_on_lpecs.m index cd81ad3..ce91e96 100644 --- a/benchmarks/macmpec/histogram_on_lpecs.m +++ b/benchmarks/macmpec/histogram_on_lpecs.m @@ -1,12 +1,16 @@ %% Evaluating LPEC MILP efficiency: close all; clc; -dtable; -lpec_dstruct; +% dtable; +% lpec_dstruct; + +S = load('macmpec_general_04-Sep-2025_lpec_details'); +lpec_dstruct = S.lpec_dstruct; +S = load('macmpec_general_04-Sep-2025'); +dtable = S.dtable; plot_cpu = false; -% Filter by solver and success -solver = "MPECopt-Reg-Early"; -solver = "MPECopt-Reg-Gurobi"; +solver_names = ["Gurobi", "Gurobi-early", "Highs", "Highs-early"]; +solver = solver_names{2}; idx = strcmp(dtable.solver_name, solver) & dtable.success == 1; % ------------------------ @@ -15,12 +19,12 @@ % Replace zeros by ones in a vector function y = replace_zeros(x) - if all(x == 0) - y = ones(size(x)); - else - x(x == 0) = 1; - y = x; - end +if all(x == 0) + y = ones(size(x)); +else + x(x == 0) = 1; + y = x; +end end % Postprocess nodecount cells @@ -41,17 +45,17 @@ % Conversion of raw vector (per problem, for iteration plots) function y = convert_vec(x) - if isempty(x) - y = 0; +if isempty(x) + y = 0; +else + if all(x == 0) + y = ones(size(x)); else - if all(x == 0) - y = ones(size(x)); - else - x(x == 0) = 1; - y = x; - end + x(x == 0) = 1; + y = x; end end +end % ------------------------ % Nodecount statistics (for each lpec call seperatalyover solver calls) @@ -61,7 +65,7 @@ nc_tot = [nc_i, nc_ii]; % Histograms (nodecounts) -figure; +figure; subplot(131) histogram(nc_i, 'BinMethod', 'integers'); title('Nodecount Phase I - each LPEC call'); xlabel('Nodes'); ylabel('Frequency'); grid on; @@ -78,7 +82,6 @@ % xticks(min(nc_tot):max(nc_tot)); - % ------------------------ % Nodecount statistics (cummulative over solver calls) % ------------------------ @@ -87,7 +90,7 @@ nc_tot = nc_i + nc_ii; % Histograms (nodecounts) -figure; +figure; subplot(131) histogram(nc_i, 'BinMethod', 'integers'); title('Nodecount Phase I'); xlabel('Nodes'); ylabel('Frequency'); grid on; diff --git a/benchmarks/macmpec/run_macmpec_experiments_general.m b/benchmarks/macmpec/run_macmpec_experiments_general.m index c933c61..e92f680 100644 --- a/benchmarks/macmpec/run_macmpec_experiments_general.m +++ b/benchmarks/macmpec/run_macmpec_experiments_general.m @@ -5,7 +5,7 @@ %% Color order results_name = ['results/macmpec_general_' datestr(datetime("today"))]; % name for matlab .dat with results -filename = 'macmpec_general3'; % name for figures and excel table +filename = 'macmpec_general4'; % name for figures and excel table %% Load macmpec macmpec_json = dir('macMPEC/*.json'); @@ -44,25 +44,25 @@ mpecs = [mpecs,mpec]; end -N_interesting = []; -for ii=1:length(macmpec_json) - if mpecs(ii).n_w <= 150 - N_interesting = [N_interesting; ii]; - end -end +% N_interesting = []; +% for ii=1:length(macmpec_json) +% if mpecs(ii).n_w <= 150 +% N_interesting = [N_interesting; ii]; +% end +% end -mpecs = mpecs(N_interesting); +% mpecs = mpecs(N_interesting); %% Define list of solvers to use -solver_names = ["MPECopt-Reg-Gurobi", "MPECopt-Reg-HiGHS", "MPECopt-$\ell_1$-Gurobi", ... +solver_names = ["MPECopt-Reg-Gurobi", "MPECopt-Reg-Gurobi-ET", "MPECopt-$\ell_1$-Gurobi", ... "Reg" , "NLP", ... "MINLP"]; -solver_names = ["MPECopt-Reg-Gurobi", "MPECopt-Reg-Early", "MPECopt-$\ell_1$-Gurobi", ... - "Reg" , "NLP", ... - "MINLP"]; +% solver_names = ["MPECopt-Reg-Gurobi", "MPECopt-Reg-Early", "MPECopt-$\ell_1$-Gurobi", ... +% "Reg" , "NLP", ... +% "MINLP"]; solver_functions = {@mpec_optimizer,@mpec_optimizer,@mpec_optimizer,... @@ -71,7 +71,7 @@ default_opts1 = mpecopt.Options(); default_opts1.solver_name = solver_names{1}; -default_opts1.settings_lpec.lpec_solver = "Highs_casadi"; +default_opts1.settings_lpec.lpec_solver = "Gurobi"; default_opts1.relax_and_project_homotopy_parameter_steering = "Direct"; % default_opts1.initialization_strategy = "FeasibilityEll1General"; @@ -84,7 +84,7 @@ default_opts2 = mpecopt.Options(); default_opts2.solver_name = solver_names{2}; -default_opts2.settings_lpec.lpec_solver = "Highs_casadi"; +default_opts2.settings_lpec.lpec_solver = "Gurobi"; default_opts2.relax_and_project_homotopy_parameter_steering = "Direct"; default_opts2.settings_lpec.stop_lpec_at_feasible = true; % default_opts2.rho_TR_phase_i_init = 1e-3; @@ -105,7 +105,6 @@ scholtes_opts2.sigma0 = 0; minlp_opts = MINLPSolverOptions(); -minlp_opts.settings_casadi_nlp.bonmin.time_limit = 900; opts = {default_opts1, default_opts2, default_opts3, ... scholtes_opts1, scholtes_opts2,... @@ -113,8 +112,8 @@ %% Create data struct -N_experiments = [1, 3:6]; -N_experiments = [1 2 4]; +% N_experiments = [1, 3:6]; +N_experiments = [1:6]; mpec_benchmark_dtable_loop; % this script runs the experimetns, creates a dtable %% Pick which results to plot diff --git a/benchmarks/macmpec/run_macmpec_experiments_lpec_histogram.m b/benchmarks/macmpec/run_macmpec_experiments_lpec_histogram.m new file mode 100644 index 0000000..d9a8d1b --- /dev/null +++ b/benchmarks/macmpec/run_macmpec_experiments_lpec_histogram.m @@ -0,0 +1,169 @@ +%% load macmpec problems +clear ; clc; close all; +import casadi.* + +%% Color order + +results_name = ['results/macmpec_general_' datestr(datetime("today"))]; % name for matlab .dat with results +filename = 'macmpec_general4'; % name for figures and excel table + +%% Load macmpec +macmpec_json = dir('macMPEC/*.json'); +mpecs = []; +n_problems = length(macmpec_json); + +N_not_B_scholtes = [164 166 183 184 133]; % first four low b stat coef +N_biactive = [168 80 83 71 85 73 120 117 105 108]; +N_non_S = [84 72 141 123 132 144 147 125 143 134 35 170 181 169]; +N_qpecs = [160 161 162 163 164 165 166 167]; +N_failed_by_scohltes = [94 95 110 113]; +N_not_presolve = [45 46 47 191]; +N_infeasible = [60 66]; +N_almost_feasible = [114 68]; +N_easy = [34 36 65 87 49 67 69 33 51 53 50 55 57 174 25 40 101 16 102 1 2 3 4]; % bunch of easy problems for more diversity +% N_interesting = [N_failed_by_scohltes, N_biactive, N_non_S, N_qpecs, N_not_presolve, N_easy, N_infeasible, N_almost_feasible]; +N_interesting = [N_non_S, N_qpecs, N_not_presolve, N_not_B_scholtes]; +N_interesting = unique(N_interesting); +% N_interesting = [N_easy, N_failed_by_scohltes, N_biactive]; + +% for ii = N_interesting +for ii=1:length(macmpec_json) + fname = fullfile(macmpec_json(ii).folder, macmpec_json(ii).name); + fid = fopen(fname); + raw = fread(fid,inf); + str = char(raw'); + fclose(fid); + mpec = jsondecode(str); + mpec.w = SX.deserialize(mpec.w); + mpec.f_fun = Function.deserialize(mpec.f_fun); + mpec.g_fun = Function.deserialize(mpec.g_fun); + mpec.G_fun = Function.deserialize(mpec.G_fun); + mpec.H_fun = Function.deserialize(mpec.H_fun); + mpec.n_w = length(mpec.w); + mpec.n_comp = length(length(mpec.H_fun(mpec.w))); + mpecs = [mpecs,mpec]; +end + +% N_interesting = []; +% for ii=1:length(macmpec_json) +% if mpecs(ii).n_w <= 400 && mpecs(ii).n_w > 100 +% N_interesting = [N_interesting; ii]; +% end +% end +% mpecs = mpecs(N_interesting); + + +%% Define list of solvers to use +solver_names = ["Gurobi", "Gurobi-early", "Highs", "Highs-early"]; + +solver_functions = {@mpec_optimizer,@mpec_optimizer,@mpec_optimizer,@mpec_optimizer}; + +opts1 = mpecopt.Options(); +opts1.solver_name = solver_names{1}; +opts1.settings_lpec.lpec_solver = "Gurobi"; +opts1.relax_and_project_homotopy_parameter_steering = "Direct"; +opts1.settings_lpec.stop_lpec_at_feasible = false; + + +opts2 = mpecopt.Options(); +opts2.solver_name = solver_names{2}; +opts2.settings_lpec.lpec_solver = "Gurobi"; +opts2.relax_and_project_homotopy_parameter_steering = "Direct"; +opts2.settings_lpec.stop_lpec_at_feasible = true; + + +opts3 = mpecopt.Options(); +opts3.solver_name = solver_names{3}; +opts3.settings_lpec.lpec_solver = "Highs_casadi"; +opts3.relax_and_project_homotopy_parameter_steering = "Direct"; +opts3.settings_lpec.stop_lpec_at_feasible = false; + + +opts4 = mpecopt.Options(); +opts4.solver_name = solver_names{4}; +opts4.settings_lpec.lpec_solver = "Highs_casadi"; +opts4.relax_and_project_homotopy_parameter_steering = "Direct"; +opts4.settings_lpec.stop_lpec_at_feasible = true; + +opts = {opts1, opts2, opts3, opts4}; % list of options to pass to mpecsol (option structs) + + +%% Create data struct +N_experiments = [1:4]; +mpec_benchmark_dtable_loop; % this script runs the experimetns, creates a dtable + +%% Pick which results to plot +dtable = dtable1; +% Modift possibly which solvers are plotted +[~,temp_b] = unique(dtable.solver_name); +solver_name = dtable.solver_name(sort(temp_b)); +% solver_tab = table(solver_name); +N_plot = N_experiments; +% N_plot = [1,2]; + +dtable_plot = []; +for ii = N_plot + dtable_plot = [dtable_plot; dtable(dtable.solver_name == solver_names(ii),:)]; +end + +%% Export to excel +write_in_separate_sheets = true; +filename_excel = [filename '_details.xlsx']; +if write_in_separate_sheets + solver_tab = pivot(dtable_plot, Rows= "solver_name"); + for ii = 1:length(solver_tab.solver_name) + solver_name =solver_tab.solver_name{ii}; + dtable_ii = dtable_plot(dtable_plot.solver_name == solver_name,:); + writetable(dtable_ii,filename_excel,'Sheet',ii) + end +else + writetable(dtable_plot,filename_excel,'Sheet',1) +end + +%% Plot results +plot_settings.success_fail_statistics = 1; +plot_settings.n_biactive = 0; +plot_settings.lpecs_solved = 0; +plot_settings.active_set_changes = 0; +plot_settings.lpecs_cpu_time = 1; +plot_settings.bar_timing_plots = 1; +plot_settings.nlps_solved = 0; +plot_settings.max_nlp_cpu_time = 0; +plot_settings.max_nlp_cpu_time_phase_i = 0; +plot_settings.max_nlp_cpu_time_phase_ii = 0; +plot_settings.nlp_cpu_time = 0; % aggegated nlp times phase I and II +plot_settings.nlp_lpec_cpu_comparisson = 1; +plot_settings.objective = 0; +plot_settings.objective_rescaled = 0; +plot_settings.max_lpec_cpu_time = 0; + +plot_settings.relative = 1; +plot_settings.relative_phase_i = 0; +plot_settings.relative_phase_ii = 0; +plot_settings.relative_lpec = 0; + +plot_settings.absolute = 1; +plot_settings.absolute_phase_i = 0; +plot_settings.absolute_phase_ii = 0; +plot_settings.absolute_lpec = 0; + +plot_settings.solved_in_phase_i = 1; + +% split cpu time among phases and show in bar plot (just report for mpecopt +% guroi) +plot_settings.nlp_phases_cpu_time = 0; +plot_settings.lpec_phases_cpu_time = 0; + +plot_settings.max_lpec_cpu_time = 0; +plot_settings.stationary_points = 1; +plot_settings.b_stationarity = 1; +plot_settings.b_stationarty_as_success_criterion = 1; +plot_settings.plot_only_sucessful = 1; +plot_settings.bar_comparisson_plots = 0; + +plot_settings.save_plot = 1; + +close all; +plot_mpec_benchmark_result(dtable_plot,filename,plot_settings) + + diff --git a/benchmarks/macmpec/solve_single_macmpec_problem.m b/benchmarks/macmpec/solve_single_macmpec_problem.m index cf99bff..9f07911 100644 --- a/benchmarks/macmpec/solve_single_macmpec_problem.m +++ b/benchmarks/macmpec/solve_single_macmpec_problem.m @@ -6,7 +6,7 @@ all_problem_names = {macmpec_json.name}; -problame_name = 'qpec-200-2'; +problame_name = 'qpec-200-3'; % problame_name = 'ralph1'; % problame_name = 'pack-rig-32'; % problame_name = 'pack-rig-8'; @@ -20,6 +20,8 @@ % problame_name = 'pack-rig2p-8'; + % problame_name = 'pack-rig1p-32'; % lot of lpec itters + % Infesaile : gnash15m gnash16m gnash17m gnash18m gnash19m % Cyicling: tap-09? @@ -30,8 +32,8 @@ ii_prob = find(contains({macmpec_json.name},problame_name)); % ii_prob = N_biactive(10); % A stationarity in reg! looks problematic! -% ii_prob = N_biactive(9); -% ii_prob = 8; +ii_prob = N_biactive(9); +% ii_prob = 183; fname = fullfile(macmpec_json(ii_prob).folder, macmpec_json(ii_prob).name); @@ -46,6 +48,7 @@ mpec.g_fun = Function.deserialize(mpec.g_fun); mpec.G_fun = Function.deserialize(mpec.G_fun); mpec.H_fun = Function.deserialize(mpec.H_fun); +name = mpec.name %% create casadi problem w = mpec.w; @@ -107,7 +110,7 @@ %% MPECopt solver solver_settings = mpecopt.Options(); solver_settings.relax_and_project_homotopy_parameter_steering = "Direct"; -solver_settings.settings_lpec.lpec_solver = "Gurobi"; +solver_settings.settings_lpec.lpec_solver = "Highs"; solver_settings.settings_casadi_nlp.ipopt.max_iter = 4000; solver_settings.settings_lpec.stop_lpec_at_feasible = true; % solver_settings.settings_casadi_nlp.ipopt.fixed_variable_treatment = 'relax_bounds'; @@ -121,6 +124,9 @@ w_opt_mpecopt = full(result_mpecopt.x); f_opt_mpecopt = full(result_mpecopt.f); +stats_mpecopt.iter.nodecount_phase_i +stats_mpecopt.iter.nodecount_phase_ii + %% Results comparison fprintf('\n-------------------------------------------------------------------------------\n'); fprintf('Method \t\t Objective \t comp_res \t n_biactive \t CPU time (s)\t Success\t Stat. type\n') @@ -132,8 +138,7 @@ fprintf('||w_reg - w_mpec|| = %2.2e \n',norm(w_opt_homotopy-w_opt_mpecopt)); % fprintf('||w_minlp - w_mpec|| = %2.2e \n',norm(w_opt_minlp-w_opt_mpecopt)); -% stats_mpecopt.iter.nodecount_phase_i -% stats_mpecopt.iter.nodecount_phase_ii + % % stats_mpecopt.iter.itercount_phase_i % stats_mpecopt.iter.itercount_phase_ii \ No newline at end of file diff --git a/benchmarks/nonlinear_mpec_benchmark/generate_nonlinear_mpec_problem_set.m b/benchmarks/nonlinear_mpec_benchmark/generate_nonlinear_mpec_problem_set.m index c7b32db..c5f7887 100644 --- a/benchmarks/nonlinear_mpec_benchmark/generate_nonlinear_mpec_problem_set.m +++ b/benchmarks/nonlinear_mpec_benchmark/generate_nonlinear_mpec_problem_set.m @@ -268,7 +268,6 @@ for i = 1:floor(n_obj/2) f = f+(w(2*i-1)^2+w(2*i)^2+w(2*i-1)*w(2*i))^2+sin(w(2*i-1))^2+cos(w(2*i))^2; end - case 'Fletcvb3' p = 1/1e8; c = 1; @@ -332,6 +331,53 @@ for i = n_obj-1 f = f + sin(-0.5*w(i+1)+w(i)^2); end + + % last four are from the cutest https://ccom.ucsd.edu/~optimizers/cutest/problems/ + case 'LUKVLE10' + f = 0; + for i = 1:(n_obj/2) + xi = w(2*i-1); + yi = w(2*i); + f = f + (xi^2)^(yi^2 + 1) + (yi^2)^(xi^2 + 1); + end + case 'CURLY20' + K = 20; + f = 0; + for i = 1:n_obj + if i <= n_obj-K + q = sum(w(i:(i+K))); + else + q = sum(w(i:n_obj)); + end + f = f + q * (q * (q^2 - 20) - 0.1); + end + case 'SCURLY30' + K = 30; + SCAL = 12.0; + f = 0; + % scale factors as column vector + S = exp(((0:(n_obj-1))/(n_obj-1)) * SCAL)'; + for i = 1:n_obj + if i <= n_obj-K + q = sum(S(i:(i+K)) .* w(i:(i+K))); + else + q = sum(S(i:end) .* w(i:end)); + end + f = f + q * (q * (q^2 - 20) - 0.1); + end + case 'FLETBV3M' + f = 0; + for i = 1:n_obj + % finite-difference like group (difference with next) + if i < n_obj + q = w(i+1) - w(i); + else + q = w(i); + end + % element contribution (scaled sin/cos) + f = f + 0.5 * q^2 + 100*sin(0.01*w(i)) + 1e8*cos(w(i)); + end + % case 'Hager' %very similar to Raydan 2 diff --git a/benchmarks/nonlinear_mpec_benchmark/histogram_on_lpecs.m b/benchmarks/nonlinear_mpec_benchmark/histogram_on_lpecs.m new file mode 100644 index 0000000..5448ba4 --- /dev/null +++ b/benchmarks/nonlinear_mpec_benchmark/histogram_on_lpecs.m @@ -0,0 +1,243 @@ +%% Evaluating LPEC MILP efficiency: +close all; clc; +dtable; +lpec_dstruct; + +plot_cpu = false; +% Filter by solver and success +solver = "Gurobi"; +solver = "Gurobi-Early"; +solver_names = ["Gurobi", "Gurobi-early", "Highs", "Highs-early"]; + +solver = solver_names{1} +idx = strcmp(dtable.solver_name, solver) & dtable.success == 1; + +% ------------------------ +% Helper functions +% ------------------------ + +% Replace zeros by ones in a vector +function y = replace_zeros(x) + if all(x == 0) + y = ones(size(x)); + else + x(x == 0) = 1; + y = x; + end +end + +% Postprocess nodecount cells +process_cells_cummulative = @(C) cellfun(@(x) ... + (isempty(x) * 0) + ... % empty → 0 + (~isempty(x) * (sum(replace_zeros(x)))), ... + C); + +process_cells = @(C) cellfun(@(x) ... + (isempty(x) * 0) + ... % empty → 0 + (~isempty(x) * ((replace_zeros(x)))), ... + C, UniformOutput=false); + +% Postprocess cpu time cells +process_cpu = @(C) cellfun(@(x) ... + (isempty(x) * 0) + (~isempty(x) * sum(x)), ... + C); + +% Conversion of raw vector (per problem, for iteration plots) +function y = convert_vec(x) + if isempty(x) + y = 0; + else + if all(x == 0) + y = ones(size(x)); + else + x(x == 0) = 1; + y = x; + end + end +end + +% ------------------------ +% Nodecount statistics (for each lpec call seperatalyover solver calls) +% ------------------------ +nc_i = cell2mat(process_cells(lpec_dstruct.nodecount_phase_i(idx))); +nc_ii = cell2mat(process_cells(lpec_dstruct.nodecount_phase_ii(idx))); +nc_tot = [nc_i, nc_ii]; + +% Histograms (nodecounts) +figure; +subplot(131) +histogram(nc_i, 'BinMethod', 'integers'); +title('Nodecount Phase I - each LPEC call'); xlabel('Nodes'); ylabel('Frequency'); grid on; +% xticks(min(nc_i):max(nc_i)); + +subplot(132) +histogram(nc_ii, 'BinMethod', 'integers'); +title('Nodecount Phase II - each LPEC call'); xlabel('Nodes'); ylabel('Frequency'); grid on; +xticks(min(nc_ii):max(nc_ii)); + +subplot(133) +histogram(nc_tot, 'BinMethod', 'integers'); +title('Total Nodecount - each LPEC call'); xlabel('Nodes'); ylabel('Frequency'); grid on; +% xticks(min(nc_tot):max(nc_tot)); + + + +% ------------------------ +% Nodecount statistics (cummulative over solver calls) +% ------------------------ +nc_i = process_cells_cummulative(lpec_dstruct.nodecount_phase_i(idx)); +nc_ii = process_cells_cummulative(lpec_dstruct.nodecount_phase_ii(idx)); +nc_tot = nc_i + nc_ii; +if 0 +% Histograms (nodecounts) +figure; +subplot(131) +histogram(nc_i, 'BinMethod', 'integers'); +title('Nodecount Phase I'); xlabel('Nodes'); ylabel('Frequency'); grid on; +% xticks(min(nc_i):max(nc_i)); + +subplot(132) +histogram(nc_ii, 'BinMethod', 'integers'); +title('Nodecount Phase II'); xlabel('Nodes'); ylabel('Frequency'); grid on; +xticks(min(nc_ii):max(nc_ii)); + +subplot(133) +histogram(nc_tot, 'BinMethod', 'integers'); +title('Total Nodecount'); xlabel('Nodes'); ylabel('Frequency'); grid on; +% xticks(min(nc_tot):max(nc_tot)); +end +% Largest problem by total nodecount +[~, max_idx_rel] = max(nc_tot); +max_idx_all = find(idx); % indices in original table +max_idx = max_idx_all(max_idx_rel); + +fprintf('Problem with largest total nodecount is %s with %d nodes.\n', ... + dtable.problem_name{max_idx}, nc_tot(max_idx_rel)); + +% ------------------------ +% CPU time statistics +% ------------------------ +cpu_i = process_cpu(lpec_dstruct.cpu_time_lpec_phase_i(idx)); +cpu_ii = process_cpu(lpec_dstruct.cpu_time_lpec_phase_ii(idx)); +cpu_tot = cpu_i + cpu_ii; + +% Histograms (CPU times) +if plot_cpu + figure; + subplot(131) + histogram(cpu_i); + title('CPU Time Phase I'); xlabel('Time [s]'); ylabel('Frequency'); grid on; + + subplot(132) + histogram(cpu_ii); + title('CPU Time Phase II'); xlabel('Time [s]'); ylabel('Frequency'); grid on; + + subplot(133) + histogram(cpu_tot); + title('Total CPU Time'); xlabel('Time [s]'); ylabel('Frequency'); grid on; +end + +% ------------------------ +%% Iteration plot for max problem (nodecounts) +% ------------------------ +vec_i = cell2mat(process_cells(lpec_dstruct.nodecount_phase_i(max_idx))); +vec_ii = cell2mat(process_cells(lpec_dstruct.nodecount_phase_ii(max_idx))); +% vec_i = convert_vec(vec_i); +% vec_ii = convert_vec(vec_ii); +x_i = 1:length(vec_i); +x_ii = (length(vec_i)+1):(length(vec_i)+length(vec_ii)); +figure; +subplot(121) +plot(x_i, vec_i, '-o', 'LineWidth', 1.5, 'Color', [0 0.4470 0.7410], 'DisplayName','Phase I'); hold on; +plot(x_ii, vec_ii, '-s', 'LineWidth', 1.5, 'Color', [0.8500 0.3250 0.0980], 'DisplayName','Phase II'); +% Connect the two phases +if ~isempty(vec_i) && ~isempty(vec_ii) + plot([x_i(end), x_ii(1)], [vec_i(end), vec_ii(1)], '--', 'LineWidth', 1, 'Color', [0.5 0.5 0.5]); +end +ylim([0 (max([vec_i, vec_ii])+1)*1.1]) +xlabel('Iteration'); +ylabel('Nodecount'); +% title(sprintf('Nodecount per Iteration (%s)', dtable.problem_name{max_idx})); +legend('show'); grid on; +% ------------------------ +% Iteration plot for max problem (CPU times) +% ------------------------ +cpu_vec_i = lpec_dstruct.cpu_time_lpec_phase_i{max_idx}; +cpu_vec_ii = lpec_dstruct.cpu_time_lpec_phase_ii{max_idx}; +if isempty(cpu_vec_i), cpu_vec_i = 0; end +if isempty(cpu_vec_ii), cpu_vec_ii = 0; end +x_ci = 1:length(cpu_vec_i); +x_cii = (length(cpu_vec_i)+1):(length(cpu_vec_i)+length(cpu_vec_ii)); +subplot(122) +plot(x_ci, cpu_vec_i, '-o', 'LineWidth', 1.5, 'Color', [0 0.4470 0.7410], 'DisplayName','Phase I'); hold on; +plot(x_cii, cpu_vec_ii, '-s', 'LineWidth', 1.5, 'Color', [0.8500 0.3250 0.0980], 'DisplayName','Phase II'); +% Connect the two phases +if ~isempty(cpu_vec_i) && ~isempty(cpu_vec_ii) + plot([x_ci(end), x_cii(1)], [cpu_vec_i(end), cpu_vec_ii(1)], '--', 'LineWidth', 1, 'Color', [0.5 0.5 0.5]); +end +xlabel('Iteration'); +ylabel('CPU Time [s]'); +legend('show'); grid on; +%% +%% Iteration plot for max problem (nodecounts) +max_idx = 7 +% ------------------------ +vec_i = lpec_dstruct.nodecount_phase_i{max_idx}; +vec_ii = lpec_dstruct.nodecount_phase_ii{max_idx}; +vec_i = convert_vec(vec_i); +vec_ii = convert_vec(vec_ii); + +% Combine vectors and create x-axis +combined_vec = [vec_i, vec_ii]; +x_combined = 1:length(combined_vec); + +% Create color array - blue for Phase I, orange for Phase II +colors = [repmat([0 0.4470 0.7410], length(vec_i), 1); ... + repmat([0.8500 0.3250 0.0980], length(vec_ii), 1)]; + +figure; +subplot(121) +b = bar(x_combined, combined_vec, 'FaceColor', 'flat'); +b.CData = colors; +ylim([0 (max(combined_vec)+1)*1.1]) +xlabel('Iteration'); +ylabel('Nodecount'); +% title(sprintf('Nodecount per Iteration (%s)', dtable.problem_name{max_idx})); +grid on; + +% Add legend manually +hold on; +h1 = bar(NaN, NaN, 'FaceColor', [0 0.4470 0.7410]); +h2 = bar(NaN, NaN, 'FaceColor', [0.8500 0.3250 0.0980]); +legend([h1, h2], {'Phase I', 'Phase II'}, 'Location', 'best'); + +% ------------------------ +% Iteration plot for max problem (CPU times) +% ------------------------ +cpu_vec_i = lpec_dstruct.cpu_time_lpec_phase_i{max_idx}; +cpu_vec_ii = lpec_dstruct.cpu_time_lpec_phase_ii{max_idx}; +if isempty(cpu_vec_i), cpu_vec_i = 0; end +if isempty(cpu_vec_ii), cpu_vec_ii = 0; end + +% Ensure CPU vectors are column vectors and combine them +cpu_vec_i = cpu_vec_i(:); % Convert to column vector +cpu_vec_ii = cpu_vec_ii(:); % Convert to column vector +combined_cpu = [cpu_vec_i; cpu_vec_ii]; +x_cpu_combined = 1:length(combined_cpu); + +% Create color array for CPU plot +cpu_colors = [repmat([0 0.4470 0.7410], length(cpu_vec_i), 1); ... + repmat([0.8500 0.3250 0.0980], length(cpu_vec_ii), 1)]; + +subplot(122) +b2 = bar(x_cpu_combined, combined_cpu, 'FaceColor', 'flat'); +b2.CData = cpu_colors; +xlabel('Iteration'); +ylabel('CPU Time [s]'); +grid on; + +% Add legend manually +hold on; +h3 = bar(NaN, NaN, 'FaceColor', [0 0.4470 0.7410]); +h4 = bar(NaN, NaN, 'FaceColor', [0.8500 0.3250 0.0980]); +legend([h3, h4], {'Phase I', 'Phase II'}, 'Location', 'best'); \ No newline at end of file diff --git a/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_large.m b/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_large.m index 5ec91ee..fdb28d3 100644 --- a/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_large.m +++ b/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_large.m @@ -8,7 +8,7 @@ settings.nonlinear_eq_constraints = 1; settings.nonlinear_ineq_constraints = 1; settings.s_nonlinear_eq = 0.05; -settings.s_nonlinear_ineq = 0.1; +settings.s_nonlinear_ineq = 0.33; settings.include_lifted_variables_in_objective = 0; settings.copy_ineq = 1; %have two copies of the inequality constrants -> violate licq if active settings.s_ineq_copy = 0.25; @@ -28,7 +28,28 @@ 'Fletcvb3','Bdqrtic','Tridia',... 'EG2','Edensch','Indef',... 'Cube','Bdexp','Genhumps',... - 'Arwhead', 'Quartc', 'Cosine', 'Sine'}; + 'Arwhead', 'Quartc', 'Cosine', 'Sine',... + 'LUKVLE10', 'CURLY20', 'SCURLY30', 'FLETBV3M' + }; + + +settings.objective_functions = {'Quadratic_psd',... + 'Fletcher','Himmelblau','McCormick',... + 'Powell','Rosenbrock',... + 'Raydan1','Raydan2',... + 'Diagonal3','Diagonal4',... + 'Extended_Trigiaonal',... + 'Generalized_PSC1','Extended_PSC1',... + 'Fletcvb3','Bdqrtic','Tridia',... + 'EG2','Edensch','Indef',... + 'Cube','Bdexp','Genhumps',... + 'Arwhead', 'Quartc', 'Cosine'}; + + +settings.objective_functions = {'Quadratic_psd',... + 'Fletcher','Himmelblau','McCormick'}; + + settings.rescale_factor = 1; settings.round_all_data = 1; @@ -53,16 +74,8 @@ settings.n_ineq_lb = 0.1; % Problem size -dimensions.N_rand_prob = 3; % number of problems per objective -dimensions.n_x_max = 100; -dimensions.n_x_min = 10; -% dimensions.N_rand_prob = 4; % number of problems per objective -% dimensions.n_x_max = 50; -% dimensions.n_x_min = 10; -% -% -dimensions.N_rand_prob = 1; % number of problems per objective +dimensions.N_rand_prob = 4; % number of problems per objective dimensions.n_x_max = 100; dimensions.n_x_min = 10; @@ -83,8 +96,6 @@ "Reg" , "NLP", ... "MINLP"]; - - solver_names = ["MPECopt-Reg-Gurobi", "MPECopt-Reg-Early", "MPECopt-$\ell_1$-Gurobi", ... "Reg" , "NLP", ... "MINLP"]; @@ -94,7 +105,7 @@ @mpec_minlp_solver}; default_opts1 = mpecopt.Options(); default_opts1.solver_name = solver_names{1}; -default_opts1.settings_lpec.lpec_solver = "Gurobi"; +default_opts1.settings_lpec.lpec_solver = "Highs_casadi"; default_opts1.relax_and_project_homotopy_parameter_steering = "Direct"; % default_opts1.initialization_strategy = "FeasibilityEll1General"; @@ -107,7 +118,7 @@ default_opts2 = mpecopt.Options(); default_opts2.solver_name = solver_names{2}; -default_opts2.settings_lpec.lpec_solver = "Gurobi"; +default_opts2.settings_lpec.lpec_solver = "Highs_casadi"; default_opts2.relax_and_project_homotopy_parameter_steering = "Direct"; default_opts2.settings_lpec.stop_lpec_at_feasible = true; % default_opts2.rho_TR_phase_i_init = 1e-3; diff --git a/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_lpec_histogram.m b/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_lpec_histogram.m new file mode 100644 index 0000000..ab27700 --- /dev/null +++ b/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_lpec_histogram.m @@ -0,0 +1,216 @@ +clear; clc; close all; +problem_set_name = 'nonlinear_mpec2'; % (add large, degenerate, nonlinear, include lifted) + + +%% file names +filename = 'random_lpecs'; % name for figures and excel table +results_name = ['results/' filename '_' datestr(datetime("today"))]; % name for matlab .dat with results +%% Generate test set +settings.nonlinear_eq_constraints = 1; +settings.nonlinear_ineq_constraints = 1; +settings.s_nonlinear_eq = 0.05; +settings.s_nonlinear_ineq = 1/3; +settings.include_lifted_variables_in_objective = 0; +settings.copy_ineq = 1; %have two copies of the inequality constrants -> violate licq if active +settings.s_ineq_copy = 0.25; +settings.copy_eq = 0; %have two copies of the inequality constrants -> violate licq if active +settings.s_eq_copy = 0.0; + +settings.inequality_constraints = 1; +settings.inequality_constraints_coupling_terms = 1; % if 0, then in Ax+By >=f, B = 0; + +settings.objective_functions = {'Quadratic_psd','Quadratic_ind',... + 'Fletcher','Himmelblau','McCormick',... + 'Powell','Trigonometric','Rosenbrock',... + 'Raydan1','Raydan2',... + 'Diagonal3','Diagonal4','Diagonal5',... + 'Extended_Trigiaonal','Three_Exponential_Terms',... + 'Generalized_PSC1','Extended_PSC1',... + 'Fletcvb3','Bdqrtic','Tridia',... + 'EG2','Edensch','Indef',... + 'Cube','Bdexp','Genhumps',... + 'Arwhead', 'Quartc', 'Cosine', 'Sine'}; + + +settings.objective_functions = {'Quadratic_psd',... + 'Fletcher','Himmelblau','McCormick',... + 'Powell','Rosenbrock',... + 'Raydan1','Raydan2',... + 'Diagonal3','Diagonal4',... + 'Extended_Trigiaonal',... + 'Generalized_PSC1','Extended_PSC1',... + 'Fletcvb3','Bdqrtic','Tridia',... + 'EG2','Edensch','Indef',... + 'Cube','Bdexp','Genhumps',... + 'Arwhead', 'Quartc', 'Cosine'}; + + +settings.rescale_factor = 1; +settings.round_all_data = 1; +settings.n_digits = 4; +settings.bounded_w = 1; +settings.use_normal_distributions_for_feasible_point = 1; +settings.alpha_lin = 10; % f + alpha_lin*[c;d]'*[x;y]' +settings.n_non_zero_E = 2000; +settings.symmetric_psd_M_matrix = 0; + +settings.s_density_A_B = 0.1; % all same or change +settings.s_density_M = 0.5; + +settings.s_density_A_B = 0.05; % all same or change +settings.s_density_M = 0.1; + +settings.variable_density = 1; +settings.range_s_density = [0.1 0.3]; +settings.random_problem_sizes = 1; + +settings.n_ineq_ub = 3; % n_ineq = n_ineq_ub*n_x % max relative number of ineq w.r.t x +settings.n_ineq_lb = 0.1; + +% Problem size + +dimensions.N_rand_prob = 4; % number of problems per objective +dimensions.n_x_max = 400; +dimensions.n_x_min = 100; + +dimensions.n_fraction_of_x = 0.5; % n_y = round(n_x/n_fraction_of_x) + +% example for nonradnom problem sizes +dimensions.n_x_vec = [10,20,50,75,100,100]; +dimensions.n_y_vec = [10,10,25,40,50,70]; +dimensions.n_ineq_vec = [10,10,20,50,40,250]; + +mpecs = generate_nonlinear_mpec_problem_set(problem_set_name,settings,dimensions); +length(mpecs) + + +%% Solver settings +%% Define list of solvers to use +solver_names = ["Gurobi", "Gurobi-early", "Highs", "Highs-early"]; + +solver_functions = {@mpec_optimizer,@mpec_optimizer,@mpec_optimizer,@mpec_optimizer}; + +opts1 = mpecopt.Options(); +opts1.solver_name = solver_names{1}; +opts1.settings_lpec.lpec_solver = "Gurobi"; +opts1.relax_and_project_homotopy_parameter_steering = "Direct"; +opts1.settings_lpec.stop_lpec_at_feasible = false; + + +opts2 = mpecopt.Options(); +opts2.solver_name = solver_names{2}; +opts2.settings_lpec.lpec_solver = "Gurobi"; +opts2.relax_and_project_homotopy_parameter_steering = "Direct"; +opts2.settings_lpec.stop_lpec_at_feasible = true; + + +opts3 = mpecopt.Options(); +opts3.solver_name = solver_names{3}; +opts3.settings_lpec.lpec_solver = "Highs"; +opts3.relax_and_project_homotopy_parameter_steering = "Direct"; +opts3.settings_lpec.stop_lpec_at_feasible = false; + + +opts4 = mpecopt.Options(); +opts4.solver_name = solver_names{4}; +opts4.settings_lpec.lpec_solver = "Highs"; +opts4.relax_and_project_homotopy_parameter_steering = "Direct"; +opts4.settings_lpec.stop_lpec_at_feasible = true; + +opts = {opts1, opts2, opts3, opts4}; % list of options to pass to mpecsol (option structs) + +%% Create data struct +N_experiments = [1 2 3 4]; + +nonlinear_mpec_benchmark_dtable_loop; % this script runs the experimetns, creates a dtable +%% Pick which results to plot +dtable = dtable1; +% Modift possibly which solvers are plotted +[~,temp_b] = unique(dtable.solver_name); +solver_name = dtable.solver_name(sort(temp_b)); +% solver_tab = table(solver_name); +N_plot = N_experiments; +% N_plot = [1,2]; + +dtable_plot = []; +for ii = N_plot + dtable_plot = [dtable_plot; dtable(dtable.solver_name == solver_names(ii),:)]; +end + +%% Export to excel +write_in_separate_sheets = true; +filename_excel = [filename '_details.xlsx']; +if write_in_separate_sheets + solver_tab = pivot(dtable_plot, Rows= "solver_name"); + for ii = 1:length(solver_tab.solver_name) + solver_name =solver_tab.solver_name{ii}; + dtable_ii = dtable_plot(dtable_plot.solver_name == solver_name,:); + writetable(dtable_ii,filename_excel,'Sheet',ii) + end +else + writetable(dtable_plot,filename_excel,'Sheet',1) +end + +%% Plot results +plot_settings.success_fail_statistics = 1; +plot_settings.n_biactive = 1; +plot_settings.lpecs_solved = 0; +plot_settings.active_set_changes = 0; +plot_settings.lpecs_cpu_time = 0; +plot_settings.bar_timing_plots = 0; +plot_settings.nlps_solved = 1; +plot_settings.max_nlp_cpu_time = 0; +plot_settings.max_nlp_cpu_time_phase_i = 0; +plot_settings.max_nlp_cpu_time_phase_ii = 0; +plot_settings.nlp_cpu_time = 0; % aggegated nlp times phase I and II +plot_settings.nlp_lpec_cpu_comparisson = 0; +plot_settings.objective = 0; +plot_settings.objective_rescaled = 1; +plot_settings.max_lpec_cpu_time = 0; + +plot_settings.relative = 1; +plot_settings.relative_phase_i = 0; +plot_settings.relative_phase_ii = 0; +plot_settings.relative_lpec = 0; + +plot_settings.absolute = 1; +plot_settings.absolute_phase_i = 0; +plot_settings.absolute_phase_ii = 0; +plot_settings.absolute_lpec = 0; + +plot_settings.solved_in_phase_i = 0; + +% split cpu time among phases and show in bar plot (just report for mpecopt +% guroi) +plot_settings.nlp_phases_cpu_time = 0; +plot_settings.lpec_phases_cpu_time = 0; + +plot_settings.max_lpec_cpu_time = 0; +plot_settings.stationary_points = 1; +plot_settings.b_stationarity = 1; +plot_settings.b_stationarty_as_success_criterion = 0; +plot_settings.plot_only_sucessful = 1; +plot_settings.bar_comparisson_plots = 0; + +plot_settings.save_plot = 1; + +close all; +plot_mpec_benchmark_result(dtable_plot,filename,plot_settings) +%% +dtable_ii = dtable_plot(dtable_plot.solver_name == solver_names{1},:); +dtable_jj = dtable_plot(dtable_plot.solver_name == solver_names{4},:); +hh = find(dtable_jj.b_stationarity == 0 & dtable_jj.success == 1); +% dtable_ii.f_lpec((hh)) +% dtable_jj.f((hh)) +% dtable_jj.f_lpec((hh)) +% abs((dtable_ii.f((hh))-dtable_jj.f((hh))))./abs(dtable_ii.f((hh)))>1e-3 + +%% +% figure +% semilogy(abs(dtable_ii.f_lpec)+1e-16,'LineWidth',1.5); +% hold on +% semilogy(abs(dtable_jj.f_lpec)+1e-16,'LineWidth',1.5); +% ylabel('$|\nabla f(x^*)^\top d|$') +% xlabel('Problem instance') +% grid on +% yline(1e-8,'LineWidth',2) \ No newline at end of file diff --git a/benchmarks/nonlinear_mpec_benchmark/sparsity.m b/benchmarks/nonlinear_mpec_benchmark/sparsity.m new file mode 100644 index 0000000..b1f3142 --- /dev/null +++ b/benchmarks/nonlinear_mpec_benchmark/sparsity.m @@ -0,0 +1,72 @@ +import casadi.* + +n_obj = 100; % number of variables +w = SX.sym('w', n_obj, 1); % decision variables + +% case 'CHEBYQAD' + % % Parameters + % H = 1/(n_obj+1); + % H2 = H^2; + % KAPPA = 1.0; + % KAPPAH2 = KAPPA*H2; + % + % % Residuals (gradient-like) + % r = SX.zeros(n_obj,1); + % r(1) = w(1) - 0; % G(0) + % for i=1:n_obj-1 + % r(i+1) = w(i) - w(i+1); % G(i) + % end + % r = r - 2*H2; % L(i) + % r(n_obj) = r(n_obj) - (1 + 2*H2); % L(N) + % + % % Function + % f = 0.5 * r.' * r; + + +f = 0; +for i = 1:n_obj + % finite-difference like group (difference with next) + if i < n_obj + q = w(i+1) - w(i); + else + q = w(i); + end + % element contribution (scaled sin/cos) + f = f + 0.5 * q^2 + 100*sin(0.01*w(i)) + 1e8*cos(w(i)); +end + + +H = hessian(f, w); % symbolic Hessian +H_fun = Function('H_fun', {w}, {H}); + +% evaluate at a dummy point +w0 = 1*ones(n_obj,1); +H_val = full(H_fun(w0)); + +% plot sparsity pattern +figure; spy(H_val); +%% +import casadi.* + +N = 10; % adjust N as needed +KAPPA = 1.0; +OBJSCALE = 1e8; + +w = SX.sym('w', N, 1); + +% Define individual contributions +S = 100.0*sin(0.01*w); +C = OBJSCALE*cos(w); + +% Define groups (HALFL2 structure) +f = 0.5*OBJSCALE*sum((diff([0; w])).^2); % finite-difference quadratic +f = f + sum(C) + sum(S); % add individual element contributions + +% Hessian +H_f = hessian(f, w); + +% Function to evaluate and plot +H_fun = Function('H_fun', {w}, {H_f}); +H_val = full(H_fun(zeros(N,1))); + +figure; spy(H_val); title('Hessian sparsity of FLETBV3M'); diff --git a/src/+mpecopt/Solver.m b/src/+mpecopt/Solver.m index 04a888d..82caf7a 100644 --- a/src/+mpecopt/Solver.m +++ b/src/+mpecopt/Solver.m @@ -23,7 +23,7 @@ obj.solver_initialization = struct(); obj.create_lpec_functions(); cpu_time_prepare_mpec = toc(t_prepare_mpec); - obj.stats.cpu_time_prepare_mpec = cpu_time_prepare_mpec; + obj.stats.cpu_time_prepare_mpec = cpu_time_prepare_mpec; if strcmp(opts.initialization_strategy,"RelaxAndProject") t_generate_nlp_solvers = tic; @@ -80,6 +80,8 @@ % collect details for every lpec solve via a milp % BUG: calling phase II for % feasiblity problems deletes everything): TODO! need a function to empty this for a very new solver call, but need to see how to detect this + stats.iter.gap_phase_i = []; + stats.iter.gap_phase_ii = []; stats.iter.nodecount_phase_i = []; stats.iter.nodecount_phase_ii= []; stats.iter.baritercount_phase_i = []; @@ -113,12 +115,12 @@ x_k_init = solver_initialization_relaxed.x0; end - stats.iter.cpu_time_nlp_phase_i_iter = [0]; - stats.iter.cpu_time_nlp_phase_ii_iter = [0]; - stats.iter.cpu_time_lpec_phase_i_iter = [0]; - stats.iter.cpu_time_lpec_phase_ii_iter = [0]; + stats.iter.cpu_time_nlp_phase_i_iter = []; + stats.iter.cpu_time_nlp_phase_ii_iter = []; + stats.iter.cpu_time_lpec_phase_i_iter = []; + stats.iter.cpu_time_lpec_phase_ii_iter = []; stats.iter.cpu_time_lpec_preparation_iter = []; - stats.iter.cpu_time_nlp_iter = [0]; + stats.iter.cpu_time_nlp_iter = []; stats.iter.cpu_time_globalization_iter = []; %% ---------------------- Phase I - compute feasible point ------------------------ @@ -179,9 +181,10 @@ stats.iter.rho_TR_iter = [stats.iter.rho_TR_iter, rho_TR_k_l]; % store TR radius % Solve LPEC [results_lpec,stats_lpec] = lpec_solver(lpec,opts.settings_lpec); - stats.iter.cpu_time_lpec_phase_i_iter = [stats.iter.cpu_time_lpec_phase_i_iter, stats_lpec.cpu_time]; % stats stats.n_lpec_total = stats.n_lpec_total+1; + stats.iter.cpu_time_lpec_phase_i_iter = [stats.iter.cpu_time_lpec_phase_i_iter, stats_lpec.cpu_time]; stats.iter.nodecount_phase_i = [stats.iter.nodecount_phase_i, stats_lpec.nodecount]; + stats.iter.gap_phase_i = [stats.iter.gap_phase_i , stats_lpec.gap]; stats.iter.baritercount_phase_i = [stats.iter.baritercount_phase_i, stats_lpec.baritercount]; stats.iter.itercount_phase_i = [stats.iter.itercount_phase_i, stats_lpec.itercount]; @@ -1453,11 +1456,13 @@ function create_lpec_functions(obj) if ~phase_ii stats.iter.cpu_time_lpec_phase_i_iter = [stats.iter.cpu_time_lpec_phase_i_iter, stats_lpec.cpu_time]; % stats stats.iter.nodecount_phase_i = [stats.iter.nodecount_phase_i, stats_lpec.nodecount]; + stats.iter.gap_phase_i = [stats.iter.gap_phase_i, stats_lpec.gap]; stats.iter.baritercount_phase_i = [stats.iter.baritercount_phase_i, stats_lpec.baritercount]; stats.iter.itercount_phase_i = [stats.iter.itercount_phase_i, stats_lpec.itercount]; else stats.iter.cpu_time_lpec_phase_ii_iter = [stats.iter.cpu_time_lpec_phase_ii_iter, stats_lpec.cpu_time]; % stats stats.iter.nodecount_phase_ii = [stats.iter.nodecount_phase_ii, stats_lpec.nodecount]; + stats.iter.gap_phase_ii = [stats.iter.gap_phase_ii, stats_lpec.gap]; stats.iter.baritercount_phase_ii = [stats.iter.baritercount_phase_ii, stats_lpec.baritercount]; stats.iter.itercount_phase_ii = [stats.iter.itercount_phase_ii, stats_lpec.itercount]; end diff --git a/src/lpec_solver.m b/src/lpec_solver.m index 4792d24..926dd8a 100644 --- a/src/lpec_solver.m +++ b/src/lpec_solver.m @@ -1,45 +1,38 @@ function [results,stats] = lpec_solver(lpec,settings) -% This functions solves Linear Programs with Complementarity Constraints -% (LPECs) that arise as subproblems of the MPEC_Opt method, and they have -% the form: -% min_d f'*d -% s.t. A_eq*d+b_eq = 0 -% A_ineq*d+b_ineq >= 0 -% lb <= d + x_lin <= ub -% 0<= d_1 + x_lin_1 _|_ d_2 + x_lin_2 >= 0 -% -rho_TR <= d <= rho_TR; -% The problems are solved either a Big M reformulation and mixed-integer -% linear programing solvers: Gurobi, Highs or Matlab, or via the Scholtes -% relaxation, Ell_1 or Ell_infity penalty reformulations and Ipopt - within -% a homotopy loop. - -% Inputs: dims (contains important dimenisons, and index vectors of -% d0_,d_1 and d_2 -% BigM -% lpec.x_lin = x_lin; % linearization point -% lpec.dims = dims; % -% lpec.d_lpec = w0; % initial guess for cont. variables -% lpec.y_lpec = w0(ind_x1)>w0(ind_x2); % inital guess for bin. variablels. -% lpec.rho_TR = rho_TR; -% lpec.lb = lbw; -% lpec.ub = ubw; -% lpec.lb_binary = []; % relevant if the lpec is reduced -% lpec.ub_binary = []; -% lpec.f = f; % cost gradient -% lpec.A_eq = A_eq; -% lpec.b_eq = b_eq; -% lpec.A_ineq = A_ineq; -% lpec.b_ineq = b_ineq; -% lpec.A_lpec = A_lpec; % constraint matrices for the binary constraint to model complementarities -% lpec.b_lpec = b_lpec; -% lpec.sense = sense; -% lpec.vtype = vtype; -% lpec.vtype_num = vtype_num; +%LPEC_SOLVER Solves Linear Programs with Complementarity Constraints +% +% [RESULTS, STATS] = LPEC_SOLVER(LPEC, SETTINGS) solves LPECs of the form: +% min_d f' * d +% s.t. A_eq * d + b_eq = 0 +% A_ineq * d + b_ineq >= 0 +% lb <= d + x_lin <= ub +% 0 <= d_1 + x_lin_1 ⊥ d_2 + x_lin_2 >= 0 +% -rho_TR <= d <= rho_TR +% +% METHODS: Big-M with MILP solvers (Gurobi, HiGHS, MATLAB) or continuous +% reformulations (Scholtes, L1/L∞ penalty) with Ipopt via homotopy. +% +% INPUTS: +% lpec - Structure with fields: +% .x_lin, .d_lpec, .y_lpec - Linearization point and initial guesses +% .rho_TR, .lb, .ub - Trust region radius and bounds +% .f, .A_eq, .b_eq - Objective and equality constraints +% .A_ineq, .b_ineq - Inequality constraints +% .A_lpec, .b_lpec - Complementarity constraint data +% .dims, .sense, .vtype - Problem dimensions and variable types +% +% settings - Solver options structure +% +% OUTPUTS: +% results - Solution: .d_lpec (cont. variables), y_lpec (binaries encoding active set), .f_opt (lpec objective) +% stats - Statistics: .solve_time, .iterations, .nodecount, .itercount, .gap +%% import casadi.* %% init some stats: stats.itercount = 0; % number simplex iters stats.baritercount = 0; % nuber of barrier iters stats.nodecount = 0; % number of nodes in BnB +stats.gap = inf; % integer gap %% Prepare LPEC % add the boundso of the inaries @@ -194,8 +187,6 @@ % highs_opts.highs.primal_feasibility_tolerance = 1e-9; % highs_opts.highs.dual_feasibility_tolerance = 1e-9; highs_opts.highs.mip_heuristic_effort = 0.1; - - % highs_opts.highs.simplex_strategy = 4; % highs_opts.error_on_fail = false; @@ -266,6 +257,7 @@ stats.itercount = result_gurobi.itercount; stats.baritercount = result_gurobi.baritercount; stats.nodecount = result_gurobi.nodecount; + stats.gap = result_gurobi.mipgap; cpu_time_gurobi = toc(gurobi_time); % todo; add node and itter coumt, and add them tho phase i iter % and node, @@ -280,6 +272,8 @@ % stats.lpec_solution_exists = false; result_gurobi.nodecount = nan; result_gurobi.runtime = nan; + result_gurobi.mipgap = nan; + cpu_time_gurobi = nan; end @@ -317,6 +311,7 @@ results.f_opt = f_opt; stats.lpec_solution_exists = true; stats.nodecount = output.numnodes; + stats.gap = output.relativegap; stats.solver_message = 'OPTIMAL'; if status == 2 stats.solver_message = 'NODE_LIMIT'; % node limit but solution exists @@ -329,6 +324,7 @@ results.f_opt = nan; stats.lpec_solution_exists = false; stats.nodecount = output.numnodes; + stats.gap = nan; if status == 0 stats.solver_message = 'NODE_LIMIT'; else @@ -348,11 +344,10 @@ end cpu_time = toc(higs_casadi_time); - if highs_success x = full(r.x); - results.d_lpec = x(1:lpec.dims.n_primal); - results.y_lpec = x(end-lpec.dims.n_auxiliary+1:end); + results.d_lpec = x(lpec.vtype_num==0); + results.y_lpec = round(x(lpec.vtype_num==1)); results.f_opt = full(r.cost); stats.lpec_solution_exists = true; @@ -361,14 +356,15 @@ results.d_lpec = lpec.d_lpec*nan; results.y_lpec = lpec.y_lpec*nan; results.f_opt = nan; - stats.lpec_solution_exists = false; end stats.nodecount = lpsol.stats.n_call_solver; - stats.itercount = lpsol.stats.iter_count; + stats.itercount = lpsol.stats.simplex_iteration_count; stats.baritercount = lpsol.stats.ipm_iteration_count; + stats.gap = lpsol.stats.mip_gap; % stats.cpu_time = lpsol.stats.t_wall_solver; stats.cpu_time = cpu_time; + stats.solver_message = lpsol.stats.unified_return_status; case {'Reg','Ell_1','Ell_inf','Nlp'} From ce348aa9ccb02dd6f36985d4dc34f46374ac9c57 Mon Sep 17 00:00:00 2001 From: "armin.nurkanovic" Date: Sun, 7 Sep 2025 10:41:49 +0200 Subject: [PATCH 07/31] early termination ph ii gurobi, mx support, typofix in verbose, one solver use support --- src/+mpecopt/Options.m | 6 +- src/+mpecopt/Solver.m | 1125 +++++++++-------- ...rmine_multipliers_based_stationary_point.m | 3 +- src/lpec_solver.m | 6 +- src/mpec_homotopy_solver.m | 46 +- 5 files changed, 648 insertions(+), 538 deletions(-) diff --git a/src/+mpecopt/Options.m b/src/+mpecopt/Options.m index 2316a61..0de7929 100644 --- a/src/+mpecopt/Options.m +++ b/src/+mpecopt/Options.m @@ -24,7 +24,8 @@ stop_if_S_stationary (1,1) logical = false; % S stationarity is sufficent for B % --- BNLP/TNLP settings---- - piece_nlp_strategy PieceNLPStartegy = "BNLP_integer"; + piece_nlp_strategy PieceNLPStartegy = "BNLP_integer" + use_one_nlp_solver (1,1) logical = true; % if true create only one NLP solver for phase i with reg, and phase ii for bnlp and tnlp check (but set sigma to inf) - saves lot of time for large problems; Option supportedo only for RelaxAndProject Phase I % Multiplier-based stationarity compute_tnlp_stationary_point (1,1) logical = true; % todo: reduce this and the one below settings to one @@ -119,7 +120,8 @@ % obj.settings_lpec.solver_name = 'lpec_solver'; % obj.settings_lpec.lpec_solver= LpecSolver.Gurobi; % obj.settings_lpec.max_nodes = 2e3; - obj.settings_lpec.stop_lpec_at_feasible = false; + obj.settings_lpec.stop_lpec_at_feasible = false; % Stop at a feasible point in Phase I lpecs (sufficent for pogress) + obj.settings_lpec.stop_lpec_at_descent = false; % Stop at descent direction in Phase II lepcs (sufficent for pogress) (supported only for gurobi atm) obj.settings_lpec.trust_region_on_slacks = false; % trust region for slack variables in fesability problem; % obj.settings_lpec.rel_tol= 1e-6; % obj.settings_lpec.abs_tol= 1e-8; diff --git a/src/+mpecopt/Solver.m b/src/+mpecopt/Solver.m index 82caf7a..6ccea8a 100644 --- a/src/+mpecopt/Solver.m +++ b/src/+mpecopt/Solver.m @@ -48,8 +48,8 @@ lpec_casadi = obj.lpec_casadi; dims = obj.dims; stats = obj.stats; - - + + %% Ininitialization k = 1; x_k = solver_initialization.x0; @@ -88,7 +88,7 @@ stats.iter.baritercount_phase_ii= []; stats.iter.itercount_phase_i = []; stats.iter.itercount_phase_ii= []; - + stats.iter.active_set_changes = []; stats.n_nlp_total = 0; stats.n_lpec_total = 0; @@ -137,331 +137,407 @@ t_phase_i_start = tic; switch opts.initialization_strategy - case 'RelaxAndProject' - h_comp_ii = 1; - ii = 1; - y_lpec_k_l = double(x_k(dims.ind_x1)> x_k(dims.ind_x2)); - d_lpec_k_l = zeros(dims.n_primal,1); - while (ii <= opts.max_recovery_iters) && ~stats.problem_infeasible && ~feasible_bnlp_found - switch opts.bnlp_projection_strategy - case {'LPEC'} - if opts.lpec_solve_if_comp_feasible - rho_min = 1.01*max(abs(min(x_k(dims.ind_x1),x_k(dims.ind_x2)))); - if rho_min > opts.rho_TR_phase_i_init || ii == 1 - solve_lpec = false; - else - solve_lpec = true; - end - else - solve_lpec = true; - end - if solve_lpec - % prepare and solve lpec; - t_lpec_preparation_iter = tic; - lpec = create_lpec_subproblem(x_k,solver_initialization.p0,rho_TR_k_l,lpec_casadi,dims,opts,opts.tol_active); - stats.iter.cpu_time_lpec_preparation_iter = [stats.iter.cpu_time_lpec_preparation_iter;toc(t_lpec_preparation_iter)]; - % Initial guess and TR for the LPEC - y_lpec_k_previous = y_lpec_k_l; % to keep track of active set chnages - if opts.warm_start_lpec_phase_i - lpec.d_lpec = d_lpec_k_l; - lpec.y_lpec = y_lpec_k_l; % inital guess for bin. variablels. - end + case 'RelaxAndProject' + h_comp_ii = 1; + ii = 1; + y_lpec_k_l = double(x_k(dims.ind_x1)> x_k(dims.ind_x2)); + d_lpec_k_l = zeros(dims.n_primal,1); + while (ii <= opts.max_recovery_iters) && ~stats.problem_infeasible && ~feasible_bnlp_found + switch opts.bnlp_projection_strategy + case {'LPEC'} + if opts.lpec_solve_if_comp_feasible + rho_min = 1.01*max(abs(min(x_k(dims.ind_x1),x_k(dims.ind_x2)))); + if rho_min > opts.rho_TR_phase_i_init || ii == 1 + solve_lpec = false; + else + solve_lpec = true; + end + else + solve_lpec = true; + end + if solve_lpec + % prepare and solve lpec; + t_lpec_preparation_iter = tic; + lpec = create_lpec_subproblem(x_k,solver_initialization.p0,rho_TR_k_l,lpec_casadi,dims,opts,opts.tol_active); + stats.iter.cpu_time_lpec_preparation_iter = [stats.iter.cpu_time_lpec_preparation_iter;toc(t_lpec_preparation_iter)]; + % Initial guess and TR for the LPEC + y_lpec_k_previous = y_lpec_k_l; % to keep track of active set chnages + if opts.warm_start_lpec_phase_i + lpec.d_lpec = d_lpec_k_l; + lpec.y_lpec = y_lpec_k_l; % inital guess for bin. variablels. + end - rho_TR_k_l_lb = opts.realx_and_project_scale_factor_rho_tr*max(abs(min(x_k(dims.ind_x1),x_k(dims.ind_x2)))); - rho_TR_k_l_ub = opts.realx_and_project_scale_factor_rho_tr*max(max(abs(x_k(dims.ind_x1)),abs(x_k(dims.ind_x2)))); + rho_TR_k_l_lb = opts.realx_and_project_scale_factor_rho_tr*max(abs(min(x_k(dims.ind_x1),x_k(dims.ind_x2)))); + rho_TR_k_l_ub = opts.realx_and_project_scale_factor_rho_tr*max(max(abs(x_k(dims.ind_x1)),abs(x_k(dims.ind_x2)))); - if opts.relax_and_project_tighter_TR - rho_TR_k_l = min(rho_TR_k_l_lb, opts.rho_TR_phase_i_init); - rho_TR_k_l = max(1e-4,rho_TR_k_l); % to avoid unerasonably small TR at almost fesaible points; - % [rho_TR_k_l_lb, p0_relaxed(end) ] - else - rho_TR_k_l = max(rho_TR_k_l_lb, opts.rho_TR_phase_i_init); - end - lpec.rho_TR = rho_TR_k_l; - stats.iter.rho_TR_iter = [stats.iter.rho_TR_iter, rho_TR_k_l]; % store TR radius - % Solve LPEC - [results_lpec,stats_lpec] = lpec_solver(lpec,opts.settings_lpec); - stats.n_lpec_total = stats.n_lpec_total+1; - stats.iter.cpu_time_lpec_phase_i_iter = [stats.iter.cpu_time_lpec_phase_i_iter, stats_lpec.cpu_time]; - stats.iter.nodecount_phase_i = [stats.iter.nodecount_phase_i, stats_lpec.nodecount]; - stats.iter.gap_phase_i = [stats.iter.gap_phase_i , stats_lpec.gap]; - stats.iter.baritercount_phase_i = [stats.iter.baritercount_phase_i, stats_lpec.baritercount]; - stats.iter.itercount_phase_i = [stats.iter.itercount_phase_i, stats_lpec.itercount]; - - % extract results - lpec_solution_exists = stats_lpec.lpec_solution_exists; - d_lpec_k_l = results_lpec.d_lpec; - y_lpec_k_l = results_lpec.y_lpec; - f_lin_opt_k_l = results_lpec.f_opt; - stats.f_lpec = f_lin_opt_k_l; - x_trail_lpec = x_k + d_lpec_k_l; - % Infeasibility check - h_comp_lpec_k_l = full(mpec_casadi.h_comp_fun(x_trail_lpec,solver_initialization.p0)); - h_std_lpec_k_l = full(mpec_casadi.h_std_fun(x_trail_lpec,solver_initialization.p0)); - if opts.verbose_solver - print_iter_stats(1,ii,f_lin_opt_k_l,h_std_lpec_k_l,h_comp_lpec_k_l,'LPEC',stats_lpec.nodecount,stats_lpec.solver_message,lpec.rho_TR,norm(d_lpec_k_l),stats_lpec.cpu_time,' ') - end - if opts.use_lpec_fallback_strategy_phase_i && ii > ceil(opts.lpec_recovery_start*opts.max_recovery_iters) - if strcmp(stats_lpec.solver_message,'INFEASIBLE') || (strcmp(stats_lpec.solver_message,'NODE_LIMIT') && isnan(results_lpec.f_opt)) - if opts.debug_mode_on - keyboard; + if opts.relax_and_project_tighter_TR + rho_TR_k_l = min(rho_TR_k_l_lb, opts.rho_TR_phase_i_init); + rho_TR_k_l = max(1e-4,rho_TR_k_l); % to avoid unerasonably small TR at almost fesaible points; + % [rho_TR_k_l_lb, p0_relaxed(end) ] + else + rho_TR_k_l = max(rho_TR_k_l_lb, opts.rho_TR_phase_i_init); end - [results_lpec,stats_lpec] = lpec_fallback_strategy(lpec,opts,results_lpec,stats_lpec); - stats.n_lpec_total = stats.n_lpec_total + results_lpec.n_lpecs_solved; + lpec.rho_TR = rho_TR_k_l; + stats.iter.rho_TR_iter = [stats.iter.rho_TR_iter, rho_TR_k_l]; % store TR radius + % Solve LPEC + [results_lpec,stats_lpec] = lpec_solver(lpec,opts.settings_lpec); + stats.n_lpec_total = stats.n_lpec_total+1; + stats.iter.cpu_time_lpec_phase_i_iter = [stats.iter.cpu_time_lpec_phase_i_iter, stats_lpec.cpu_time]; + stats.iter.nodecount_phase_i = [stats.iter.nodecount_phase_i, stats_lpec.nodecount]; + stats.iter.gap_phase_i = [stats.iter.gap_phase_i , stats_lpec.gap]; + stats.iter.baritercount_phase_i = [stats.iter.baritercount_phase_i, stats_lpec.baritercount]; + stats.iter.itercount_phase_i = [stats.iter.itercount_phase_i, stats_lpec.itercount]; + + % extract results lpec_solution_exists = stats_lpec.lpec_solution_exists; - d_lpec_k_l = results_lpec.d_lpec; y_lpec_k_l = results_lpec.y_lpec; f_lin_opt_k_l = results_lpec.f_opt; + d_lpec_k_l = results_lpec.d_lpec; + y_lpec_k_l = results_lpec.y_lpec; + f_lin_opt_k_l = results_lpec.f_opt; + stats.f_lpec = f_lin_opt_k_l; x_trail_lpec = x_k + d_lpec_k_l; - end - end - % - if lpec_solution_exists - % --------------------------- Check if B-stationary point found -------------------------- - h_total_k = full(mpec_casadi.h_total_fun(x_k,solver_initialization.p0)); - nabla_f_k = full(mpec_casadi.nabla_f_fun(x_k,solver_initialization.p0)); - if (h_total_k <= opts.tol) && ((abs(f_lin_opt_k_l) <= opts.tol_B_stationarity || norm(nabla_f_k) <= opts.tol_B_stationarity)) % if objective zero (either if cost gradient zero, or solution leads to it) = then set step to zero => B stationarity - if opts.reset_lpec_objective - d_lpec_k_l = d_lpec_k_l*0; % if the current point is feasible, and the objective is zero, then d = 0 is also a solution of the lpec (occurs if a solution is not on the verties of the lp) - f_lin_opt_k_l = 0; + % Infeasibility check + h_comp_lpec_k_l = full(mpec_casadi.h_comp_fun(x_trail_lpec,solver_initialization.p0)); + h_std_lpec_k_l = full(mpec_casadi.h_std_fun(x_trail_lpec,solver_initialization.p0)); + if opts.verbose_solver + print_iter_stats(1,ii,f_lin_opt_k_l,h_std_lpec_k_l,h_comp_lpec_k_l,'LPEC',stats_lpec.nodecount,stats_lpec.solver_message,lpec.rho_TR,norm(d_lpec_k_l),stats_lpec.cpu_time,' ') + end + if opts.use_lpec_fallback_strategy_phase_i && ii > ceil(opts.lpec_recovery_start*opts.max_recovery_iters) + if strcmp(stats_lpec.solver_message,'INFEASIBLE') || (strcmp(stats_lpec.solver_message,'NODE_LIMIT') && isnan(results_lpec.f_opt)) + if opts.debug_mode_on + keyboard; + end + [results_lpec,stats_lpec] = lpec_fallback_strategy(lpec,opts,results_lpec,stats_lpec); + stats.n_lpec_total = stats.n_lpec_total + results_lpec.n_lpecs_solved; + lpec_solution_exists = stats_lpec.lpec_solution_exists; + d_lpec_k_l = results_lpec.d_lpec; y_lpec_k_l = results_lpec.y_lpec; f_lin_opt_k_l = results_lpec.f_opt; + x_trail_lpec = x_k + d_lpec_k_l; + end + end + % + if lpec_solution_exists + % --------------------------- Check if B-stationary point found -------------------------- + h_total_k = full(mpec_casadi.h_total_fun(x_k,solver_initialization.p0)); + nabla_f_k = full(mpec_casadi.nabla_f_fun(x_k,solver_initialization.p0)); + if (h_total_k <= opts.tol) && ((abs(f_lin_opt_k_l) <= opts.tol_B_stationarity || norm(nabla_f_k) <= opts.tol_B_stationarity)) % if objective zero (either if cost gradient zero, or solution leads to it) = then set step to zero => B stationarity + if opts.reset_lpec_objective + d_lpec_k_l = d_lpec_k_l*0; % if the current point is feasible, and the objective is zero, then d = 0 is also a solution of the lpec (occurs if a solution is not on the verties of the lp) + f_lin_opt_k_l = 0; + end + end + % if norm(d_lpec_k_l) <= opts.tol_B_stationarity + % stats.stopping_criterion_fullfiled = true; % B-stationary point found, optimal solution found! + % stats.solver_message = 'B-stationary point found successfully.'; + % stats.success = true; + % stats.b_stationarity = true; + % stats.f_lpec = f_lin_opt_k_l; + % end + stats.iter.X_lpec = [stats.iter.X_lpec, x_trail_lpec]; + stats.iter.d_norm_lpec = [stats.iter.d_norm_lpec, norm(d_lpec_k_l)]; + stats.iter.f_lpec = [stats.iter.f_lpec, f_lin_opt_k_l]; % store some stats + I_0_plus = y_lpec_k_l==0; + I_plus_0 = y_lpec_k_l==1; + % I_00 = []; + active_set_guess_exists = true; + else + active_set_guess_exists = false; end + else + active_set_guess_exists = false; end - % if norm(d_lpec_k_l) <= opts.tol_B_stationarity - % stats.stopping_criterion_fullfiled = true; % B-stationary point found, optimal solution found! - % stats.solver_message = 'B-stationary point found sucessfully.'; - % stats.success = true; - % stats.b_stationarity = true; - % stats.f_lpec = f_lin_opt_k_l; - % end - stats.iter.X_lpec = [stats.iter.X_lpec, x_trail_lpec]; - stats.iter.d_norm_lpec = [stats.iter.d_norm_lpec, norm(d_lpec_k_l)]; - stats.iter.f_lpec = [stats.iter.f_lpec, f_lin_opt_k_l]; % store some stats - I_0_plus = y_lpec_k_l==0; - I_plus_0 = y_lpec_k_l==1; - % I_00 = []; + + case 'Simple' + I_0_plus = x_k(dims.ind_x1)<=x_k(dims.ind_x2); + I_plus_0 = x_k(dims.ind_x1)>x_k(dims.ind_x2); active_set_guess_exists = true; + case 'ActiveSetFunction' + % TODO: use some fancy function like Lin Fukushima 2006 + error('not supported at the moment, choos different option'); + % active_set_identification_fun; + % active_set_identification_fun = + % tau > 0, eta \in (0,1) + tau = 1e-1;eta = 0.5; + active_set_identification = tau*norm(min(x(dims.ind_x1),x(dims.ind_x2)))^eta; + active_set_identification_fun = Function('active_set_identification_fun',{x},{active_set_identification}); + % alpha = I_+0 , beta I_00, gamma I_0+ + rho_as = full(active_set_identification_fun(x_k)); + I_0_plus = x_k(dims.ind_x1) <= rho_as & x_k(dims.ind_x2) > rho_as ; + I_plus_0 = x_k(dims.ind_x1) > rho_as & x_k(dims.ind_x2) <= rho_as ; + I_00 = x_k(dims.ind_x1) <= rho_as & x_k(dims.ind_x2) <= rho_as ; + % split I_00 + I_0_plus((x_k(dims.ind_x1(I_00))>x_k(dims.ind_x2(I_00)))) = 1; + I_plus_0((x_k(dims.ind_x1(I_00))<=x_k(dims.ind_x2(I_00)))) = 1; + active_set_guess_exists = true; + end + % solve BNLP/NLP + h_total_k = full(mpec_casadi.h_total_fun(x_k,solver_initialization.p0)); + if h_total_k <= opts.tol_feasibility && ii>1 + feasible_bnlp_found = true; + end + + if active_set_guess_exists && ~feasible_bnlp_found + + if opts.use_one_nlp_solver + lbx_bnlp_k = solver_initialization_relaxed.lbx; + ubx_bnlp_k = solver_initialization_relaxed.ubx; + ubx_bnlp_k(dims.ind_x1(I_0_plus)) = 0; + ubx_bnlp_k(dims.ind_x2(I_plus_0)) = 0; + t_presolve_nlp_iter = tic; + results_nlp = obj.solver_relaxed('x0',x_k,'p', [1e9], 'lbx', lbx_bnlp_k, 'ubx', ubx_bnlp_k,'lbg', solver_initialization_relaxed.lbg, 'ubg', solver_initialization_relaxed.ubg); + stats_nlp = obj.solver_relaxed.stats(); else - active_set_guess_exists = false; + lbx_bnlp_k = solver_initialization.lbx; + ubx_bnlp_k = solver_initialization.ubx; + ubx_bnlp_k(dims.ind_x1(I_0_plus)) = 0; + ubx_bnlp_k(dims.ind_x2(I_plus_0)) = 0; + t_presolve_nlp_iter = tic; + results_nlp = mpec_casadi.solver('x0',x_k,'p',solver_initialization.p0,'lbx',lbx_bnlp_k,'ubx',ubx_bnlp_k,'lbg',solver_initialization.lbg,'ubg',solver_initialization.ubg); + stats_nlp = mpec_casadi.solver.stats(); end - else - active_set_guess_exists = false; - end + cpu_time_bnlp_ii = toc(t_presolve_nlp_iter); - case 'Simple' - I_0_plus = x_k(dims.ind_x1)<=x_k(dims.ind_x2); - I_plus_0 = x_k(dims.ind_x1)>x_k(dims.ind_x2); - active_set_guess_exists = true; - case 'ActiveSetFunction' - % TODO: use some fancy function like Lin Fukushima 2006 - error('not supported at the moment, choos different option'); - % active_set_identification_fun; - % active_set_identification_fun = - % tau > 0, eta \in (0,1) - tau = 1e-1;eta = 0.5; - active_set_identification = tau*norm(min(x(dims.ind_x1),x(dims.ind_x2)))^eta; - active_set_identification_fun = Function('active_set_identification_fun',{x},{active_set_identification}); - % alpha = I_+0 , beta I_00, gamma I_0+ - rho_as = full(active_set_identification_fun(x_k)); - I_0_plus = x_k(dims.ind_x1) <= rho_as & x_k(dims.ind_x2) > rho_as ; - I_plus_0 = x_k(dims.ind_x1) > rho_as & x_k(dims.ind_x2) <= rho_as ; - I_00 = x_k(dims.ind_x1) <= rho_as & x_k(dims.ind_x2) <= rho_as ; - % split I_00 - I_0_plus((x_k(dims.ind_x1(I_00))>x_k(dims.ind_x2(I_00)))) = 1; - I_plus_0((x_k(dims.ind_x1(I_00))<=x_k(dims.ind_x2(I_00)))) = 1; - active_set_guess_exists = true; - end - % solve BNLP/NLP - h_total_k = full(mpec_casadi.h_total_fun(x_k,solver_initialization.p0)); - if h_total_k <= opts.tol_feasibility && ii>1 - feasible_bnlp_found = true; - end - if active_set_guess_exists && ~feasible_bnlp_found - lbx_bnlp_k = solver_initialization.lbx; - ubx_bnlp_k = solver_initialization.ubx; - ubx_bnlp_k(dims.ind_x1(I_0_plus)) = 0; - ubx_bnlp_k(dims.ind_x2(I_plus_0)) = 0; - t_presolve_nlp_iter = tic; - results_nlp = mpec_casadi.solver('x0',x_k,'p',solver_initialization.p0,'lbx',lbx_bnlp_k,'ubx',ubx_bnlp_k,'lbg',solver_initialization.lbg,'ubg',solver_initialization.ubg); - cpu_time_bnlp_ii = toc(t_presolve_nlp_iter); - stats.iter.cpu_time_nlp_phase_i_iter = [stats.iter.cpu_time_nlp_phase_i_iter; cpu_time_bnlp_ii]; - x_k = full(results_nlp.x); - lambda_x_k = full(results_nlp.lam_x); - stats_nlp = mpec_casadi.solver.stats(); nlp_iters_k = stats_nlp.iter_count; stats.n_nlp_total = stats.n_nlp_total + 1; - h_comp_k = full(mpec_casadi.h_comp_fun(x_k,solver_initialization.p0)); - h_std_k = full(mpec_casadi.h_std_fun(x_k,solver_initialization.p0)); - f_opt_k = full(results_nlp.f); - if opts.verbose_solver - print_iter_stats(1,ii,f_opt_k,h_std_k,h_comp_k,'BNLP',nlp_iters_k,stats_nlp.return_status,rho_TR_k_l,norm(x_k_init(1:dims.n_primal)-x_k),cpu_time_bnlp_ii ,1) + stats.iter.cpu_time_nlp_phase_i_iter = [stats.iter.cpu_time_nlp_phase_i_iter; cpu_time_bnlp_ii]; + x_k = full(results_nlp.x); + lambda_x_k = full(results_nlp.lam_x); + + nlp_iters_k = stats_nlp.iter_count; stats.n_nlp_total = stats.n_nlp_total + 1; + h_comp_k = full(mpec_casadi.h_comp_fun(x_k,solver_initialization.p0)); + h_std_k = full(mpec_casadi.h_std_fun(x_k,solver_initialization.p0)); + f_opt_k = full(results_nlp.f); + if opts.verbose_solver + print_iter_stats(1,ii,f_opt_k,h_std_k,h_comp_k,'BNLP',nlp_iters_k,stats_nlp.return_status,rho_TR_k_l,norm(x_k_init(1:dims.n_primal)-x_k),cpu_time_bnlp_ii ,1) + end + % if full(mpec_casadi.h_total_fun(x_k,solver_initialization.p0)) <= opts.tol_B_stationarity + if ismember(stats_nlp.return_status, {'Solve_Succeeded', 'Search_Direction_Becomes_Too_Small', 'Solved_To_Acceptable_Level'}) || full(mpec_casadi.h_total_fun(x_k,solver_initialization.p0)) <= opts.tol_B_stationarity + % if ismember(stats_nlp.return_status, {'Solve_Succeeded', 'Search_Direction_Becomes_Too_Small', 'Solved_To_Acceptable_Level'}) + feasible_bnlp_found = true; + else + feasible_bnlp_found = false; + end end - % if full(mpec_casadi.h_total_fun(x_k,solver_initialization.p0)) <= opts.tol_B_stationarity - if ismember(stats_nlp.return_status, {'Solve_Succeeded', 'Search_Direction_Becomes_Too_Small', 'Solved_To_Acceptable_Level'}) || full(mpec_casadi.h_total_fun(x_k,solver_initialization.p0)) <= opts.tol_B_stationarity - % if ismember(stats_nlp.return_status, {'Solve_Succeeded', 'Search_Direction_Becomes_Too_Small', 'Solved_To_Acceptable_Level'}) - feasible_bnlp_found = true; + % If not sucessful do one more relaxation step until max reached + if feasible_bnlp_found + % if we have a feasible BNLP already papulate the multipliers as we may not have a chance to later. + stats.lambda_g_opt = full(results_nlp.lam_g(dims.map_g(1:dims.n_g_non_lifted))); + stats.lambda_x_opt = full(results_nlp.lam_x(dims.map_w(1:dims.n_primal_non_lifted))); + stats.success_phase_i = true; + break; else - feasible_bnlp_found = false; + t_presolve_nlp_iter = tic; + solver_initialization_relaxed.x0 = x_k_init; + results_nlp = obj.solver_relaxed('x0',solver_initialization_relaxed.x0,'p',solver_initialization_relaxed.p0,'lbx',solver_initialization_relaxed.lbx,'ubx',solver_initialization_relaxed.ubx,'lbg',solver_initialization_relaxed.lbg,'ubg',solver_initialization_relaxed.ubg); + % results_nlp = solver_relaxed(solver_initialization_relaxed); % this would need conversion of doubles in argument to DMs. + % Cpu times + cpu_time_presolve_nlp_ii = toc(t_presolve_nlp_iter); + stats.iter.cpu_time_nlp_phase_i_iter = [stats.iter.cpu_time_nlp_phase_i_iter;cpu_time_presolve_nlp_ii]; + stats_nlp = obj.solver_relaxed.stats(); + nlp_iters_ii = stats_nlp.iter_count; + % Extract results and compute objective, infeasibility ect. + x_k = full(results_nlp.x); + lambda_x_k = full(results_nlp.lam_x); + h_comp_ii = full(mpec_casadi.h_comp_fun(x_k(1:dims.n_primal),solver_initialization.p0)); + h_std_ii= full(mpec_casadi.h_std_fun(x_k(1:dims.n_primal),solver_initialization.p0)); + f_opt_ii = full(mpec_casadi.f_fun(x_k(1:dims.n_primal),solver_initialization.p0)); + stats.iter.X_outer = [stats.iter.X_outer, x_k(1:dims.n_primal)]; + if opts.verbose_solver + if strcmp(opts.relax_and_project_homotopy_parameter_steering,"Direct") + print_iter_stats(1,ii,f_opt_ii,h_std_ii,h_comp_ii,'NLP (Reg)',nlp_iters_ii,stats_nlp.return_status,solver_initialization_relaxed.p0(end),norm(x_k_init-x_k),cpu_time_presolve_nlp_ii,1) + else + print_iter_stats(1,ii,f_opt_ii,h_std_ii,h_comp_ii,'NLP (Pen)',nlp_iters_ii,stats_nlp.return_status,solver_initialization_relaxed.p0(end),norm(x_k_init-x_k),cpu_time_presolve_nlp_ii,1) + end + end + if isequal(stats_nlp.return_status,'Infeasible_Problem_Detected') + stats.phase_i_infeasibility_detected = true; + if opts.stop_if_nlp_infeasible + stats.problem_infeasible = true; + stats.success = false; + stats.success_phase_i = false; + stats.stopping_criterion_fullfiled = true; + stats.solver_message = 'MPEC is locally infeasible.'; + end + end + solver_initialization_relaxed.p0(end) = opts.relax_and_project_kappa*solver_initialization_relaxed.p0(end); + ii = ii+1; + x_k_init = x_k; + stats.n_nlp_total = stats.n_nlp_total+1; + x_k = x_k_init(1:dims.n_primal); + if full(mpec_casadi.h_total_fun(x_k,solver_initialization.p0)) <= opts.tol_feasibility + % if ismember(stats_nlp.return_status, {'Solve_Succeeded', 'Search_Direction_Becomes_Too_Small', 'Solved_To_Acceptable_Level'}) + feasible_bnlp_found = true; + else + feasible_bnlp_found = false; + end end end - % If not sucessful do one more relaxation step until max reached - if feasible_bnlp_found - % if we have a feasible BNLP already papulate the multipliers as we may not have a chance to later. - stats.lambda_g_opt = full(results_nlp.lam_g(dims.map_g(1:dims.n_g_non_lifted))); - stats.lambda_x_opt = full(results_nlp.lam_x(dims.map_w(1:dims.n_primal_non_lifted))); + y_lpec_k_l = abs(x_k(dims.ind_x1))>=abs(x_k(dims.ind_x2)); % inital guess for active set/binaries + tol_active_set = max(opts.tol_active,min(h_comp_ii,solver_initialization_relaxed.p0(end))); + case {'FeasibilityEllInfGeneral','FeasibilityEll1General'} + n_g = length(mpec_casadi.g); + % chekc if inital point already feasible + h_total_k = full(mpec_casadi.h_total_fun(x_k,solver_initialization.p0)); + if h_total_k <= opts.tol_feasibility + % TODO: add this maybe before any possible Phase I method; + stats.feasible_bnlp_found = true; stats.success_phase_i = true; - break; + x_k = project_to_bounds(x_k,solver_initialization.lbx,solver_initialization.ubx,dims); + fprintf('\n MPECopt: initial guess already feasible. \n') else - t_presolve_nlp_iter = tic; - solver_initialization_relaxed.x0 = x_k_init; - results_nlp = obj.solver_relaxed('x0',solver_initialization_relaxed.x0,'p',solver_initialization_relaxed.p0,'lbx',solver_initialization_relaxed.lbx,'ubx',solver_initialization_relaxed.ubx,'lbg',solver_initialization_relaxed.lbg,'ubg',solver_initialization_relaxed.ubg); - % results_nlp = solver_relaxed(solver_initialization_relaxed); % this would need conversion of doubles in argument to DMs. - % Cpu times - cpu_time_presolve_nlp_ii = toc(t_presolve_nlp_iter); - stats.iter.cpu_time_nlp_phase_i_iter = [stats.iter.cpu_time_nlp_phase_i_iter;cpu_time_presolve_nlp_ii]; - stats_nlp = obj.solver_relaxed.stats(); - nlp_iters_ii = stats_nlp.iter_count; - % Extract results and compute objective, infeasibility ect. - x_k = full(results_nlp.x); - lambda_x_k = full(results_nlp.lam_x); - h_comp_ii = full(mpec_casadi.h_comp_fun(x_k(1:dims.n_primal),solver_initialization.p0)); - h_std_ii= full(mpec_casadi.h_std_fun(x_k(1:dims.n_primal),solver_initialization.p0)); - f_opt_ii = full(mpec_casadi.f_fun(x_k(1:dims.n_primal),solver_initialization.p0)); - stats.iter.X_outer = [stats.iter.X_outer, x_k(1:dims.n_primal)]; - if opts.verbose_solver - if strcmp(opts.relax_and_project_homotopy_parameter_steering,"Direct") - print_iter_stats(1,ii,f_opt_ii,h_std_ii,h_comp_ii,'NLP (Reg)',nlp_iters_ii,stats_nlp.return_status,solver_initialization_relaxed.p0(end),norm(x_k_init-x_k),cpu_time_presolve_nlp_ii,1) + if n_g > 0 + if strcmp(opts.initialization_strategy,"FeasibilityEll1General") + if strcmp(class(obj.mpec_casadi.x),'casadi.SX') + s = SX.sym('s',n_g); %define slack variables for ell_1 norm of generla constraints; + else + s = MX.sym('s',n_g); %define slack variables for ell_1 norm of generla constraints; + end + + n_slacks = n_g; else - print_iter_stats(1,ii,f_opt_ii,h_std_ii,h_comp_ii,'NLP (Pen)',nlp_iters_ii,stats_nlp.return_status,solver_initialization_relaxed.p0(end),norm(x_k_init-x_k),cpu_time_presolve_nlp_ii,1) + if strcmp(class(obj.mpec_casadi.x),'casadi.SX') + s = SX.sym('s',1); %define slack variables for ell_inf norm of generla constraints; + else + s = MX.sym('s',1); + end + n_slacks = 1; end - end - if isequal(stats_nlp.return_status,'Infeasible_Problem_Detected') - stats.phase_i_infeasibility_detected = true; - if opts.stop_if_nlp_infeasible + % do a for loop for index updates to preserve the structure to some extent + g_s = []; + lbg_s = []; + ubg_s = []; + % (TODO: vectorize the for loop above for better perfomance) + for ii = 1:n_g + if strcmp(opts.initialization_strategy,"FeasibilityEll1General") + s_ii = s(ii); + else + s_ii = s; + end + % lower bound; + if solver_initialization.ubg(ii)~= -inf + g_s = [g_s; mpec_casadi.g(ii)+s_ii]; + lbg_s = [lbg_s; solver_initialization.lbg(ii)]; + ubg_s = [ubg_s; inf]; + end + % upper bound; + if solver_initialization.ubg(ii)~= inf + g_s = [g_s; mpec_casadi.g(ii)-s_ii]; + lbg_s = [lbg_s; -inf]; + ubg_s = [ubg_s; solver_initialization.ubg(ii)]; + end + end + if opts.feasibility_project_to_bounds + x_projected = project_to_bounds(solver_initialization.x0,solver_initialization.lbx,solver_initialization.ubx,dims); + solver_initialization.x0 = x_projected; + end + s_max = full(mpec_casadi.h_std_fun(solver_initialization.x0,solver_initialization.p0))+1e-1; + s_lb = opts.feasibility_s_lower_bound*ones(n_slacks,1); + s_ub = 1.1*s_max*ones(n_slacks,1); + s0 = 0.9*s_max*ones(n_slacks,1); + + % new mpec for feasibility + mpec_feas = struct; + mpec_feas.f = sum(s); + mpec_feas.g = g_s; + mpec_feas.x = [s;mpec_casadi.x]; + mpec_feas.G = mpec_casadi.x1; + mpec_feas.H = mpec_casadi.x2; + solver_initialization_feas = solver_initialization; + solver_initialization_feas.lbg = lbg_s; + solver_initialization_feas.ubg = ubg_s; + solver_initialization_feas.lbx = [s_lb;solver_initialization.lbx]; + solver_initialization_feas.ubx = [s_ub; solver_initialization.ubx]; + solver_initialization_feas.x0 = [s0;solver_initialization.x0]; + [mpec_feas_casadi, dims_feas, solver_initialization_feas] = create_mpec_functions(mpec_feas,solver_initialization_feas,opts); + lpec_feas_casadi = create_lpec_functions(mpec_feas_casadi,dims_feas,opts,solver_initialization_feas); + solver_initialization_feas.y_lpec_k_l = y_lpec_k_l; + solver_initialization_feas.d_lpec_k_l = nan; + phase_ii = false; + dims_feas.map_w = 1:dims_feas.n_primal_non_lifted; + dims_feas.map_g = 1:length(mpec_feas_casadi.g); + [solution_feas,stats_feas] = obj.phase_II(mpec_feas_casadi,lpec_feas_casadi,dims_feas,opts,solver_initialization_feas,stats,phase_ii); + s_k = solution_feas.x(1:n_slacks); + % get cpu times from phase i + stats.iter.cpu_time_lpec_phase_i_iter = stats_feas.iter.cpu_time_lpec_phase_i_iter; + stats.iter.cpu_time_nlp_phase_i_iter = stats_feas.iter.cpu_time_nlp_phase_i_iter; + s_infeasiblity = max(s_k); + if s_infeasiblity >= opts.tol_feasibility + stats.stopping_criterion_fullfiled = true; stats.problem_infeasible = true; stats.success = false; stats.success_phase_i = false; - stats.stopping_criterion_fullfiled = true; stats.solver_message = 'MPEC is locally infeasible.'; + x_k = solution_feas.x(1+n_slacks:end); + else + stats.feasible_bnlp_found = true; + stats.success_phase_i = true; + x_k = solution_feas.x(n_slacks+1:end); + if full(mpec_casadi.h_comp_fun(x_k,solver_initialization.p0)) > 1e-3 && opts.debug_mode_on + keyboard; + end end - end - solver_initialization_relaxed.p0(end) = opts.relax_and_project_kappa*solver_initialization_relaxed.p0(end); - ii = ii+1; - x_k_init = x_k; - stats.n_nlp_total = stats.n_nlp_total+1; - x_k = x_k_init(1:dims.n_primal); - if full(mpec_casadi.h_total_fun(x_k,solver_initialization.p0)) <= opts.tol_feasibility - % if ismember(stats_nlp.return_status, {'Solve_Succeeded', 'Search_Direction_Becomes_Too_Small', 'Solved_To_Acceptable_Level'}) - feasible_bnlp_found = true; else - feasible_bnlp_found = false; + stats.feasible_bnlp_found = true; + stats.success_phase_i = true; + x_k = project_to_bounds(x_k,solver_initialization.lbx,solver_initialization.ubx,dims); + fprintf('\n MPECopt: MPEC has only complementarity and bound constraints, feasible point found by projection to bounds.\n') end end - end - y_lpec_k_l = abs(x_k(dims.ind_x1))>=abs(x_k(dims.ind_x2)); % inital guess for active set/binaries - tol_active_set = max(opts.tol_active,min(h_comp_ii,solver_initialization_relaxed.p0(end))); - case {'FeasibilityEllInfGeneral','FeasibilityEll1General'} - n_g = length(mpec_casadi.g); - % chekc if inital point already feasible - h_total_k = full(mpec_casadi.h_total_fun(x_k,solver_initialization.p0)); - if h_total_k <= opts.tol_feasibility - % TODO: add this maybe before any possible Phase I method; - stats.feasible_bnlp_found = true; - stats.success_phase_i = true; - x_k = project_to_bounds(x_k,solver_initialization.lbx,solver_initialization.ubx,dims); - fprintf('\n MPECopt: initial guess already feasible. \n') - else - if n_g > 0 - if strcmp(opts.initialization_strategy,"FeasibilityEll1General") - s = SX.sym('s',n_g); %define slack variables for ell_1 norm of generla constraints; - n_slacks = n_g; - else - s = SX.sym('s',1); %define slack variables for ell_inf norm of generla constraints; - n_slacks = 1; - end - % do a for loop for index updates to preserve the structure to some extent - g_s = []; - lbg_s = []; - ubg_s = []; - % (TODO: vectorize the for loop above for better perfomance) - for ii = 1:n_g - if strcmp(opts.initialization_strategy,"FeasibilityEll1General") - s_ii = s(ii); - else - s_ii = s; - end - % lower bound; - if solver_initialization.ubg(ii)~= -inf - g_s = [g_s; mpec_casadi.g(ii)+s_ii]; - lbg_s = [lbg_s; solver_initialization.lbg(ii)]; - ubg_s = [ubg_s; inf]; - end - % upper bound; - if solver_initialization.ubg(ii)~= inf - g_s = [g_s; mpec_casadi.g(ii)-s_ii]; - lbg_s = [lbg_s; -inf]; - ubg_s = [ubg_s; solver_initialization.ubg(ii)]; - end - end - if opts.feasibility_project_to_bounds - x_projected = project_to_bounds(solver_initialization.x0,solver_initialization.lbx,solver_initialization.ubx,dims); - solver_initialization.x0 = x_projected; + case 'RandomActiveSet' % just take the user provided x0; + % rng(1,"twister"); % to have reproducable problem sets + ii = 1; + while (ii <= opts.max_recovery_iters) && ~stats.problem_infeasible + I_0_plus = boolean(round(rand(dims.n_comp,1))); + I_plus_0 = ~I_0_plus; + lbx_bnlp_k = solver_initialization.lbx; + ubx_bnlp_k = solver_initialization.ubx; + ubx_bnlp_k(dims.ind_x1(I_0_plus)) = 0; + ubx_bnlp_k(dims.ind_x2(I_plus_0)) = 0; + % solve BNLP/NLP + x_k_init = x_k; + t_presolve_nlp_iter = tic; + results_nlp = mpec_casadi.solver('x0',x_k,'p',solver_initialization.p0,'lbx',lbx_bnlp_k,'ubx',ubx_bnlp_k,'lbg',solver_initialization.lbg,'ubg',solver_initialization.ubg); + cpu_time_bnlp_k = toc(t_presolve_nlp_iter); + stats.iter.cpu_time_nlp_phase_i_iter = [stats.iter.cpu_time_nlp_phase_i_iter;cpu_time_bnlp_k ]; + x_k = full(results_nlp.x); lambda_x_k = full(results_nlp.lam_x); + stats_nlp = mpec_casadi.solver.stats(); nlp_iters_k = stats_nlp.iter_count; stats.n_nlp_total = stats.n_nlp_total + 1; + h_comp_k = full(mpec_casadi.h_comp_fun(x_k,solver_initialization.p0)); h_std_k = full(mpec_casadi.h_std_fun(x_k,solver_initialization.p0)); f_opt_k = full(results_nlp.f); + if opts.verbose_solver + print_iter_stats(1,ii,f_opt_k,h_std_k,h_comp_k,'BNLP',nlp_iters_k,stats_nlp.return_status,nan,norm(x_k_init-x_k),cpu_time_bnlp_k,1) end - s_max = full(mpec_casadi.h_std_fun(solver_initialization.x0,solver_initialization.p0))+1e-1; - s_lb = opts.feasibility_s_lower_bound*ones(n_slacks,1); - s_ub = 1.1*s_max*ones(n_slacks,1); - s0 = 0.9*s_max*ones(n_slacks,1); - - % new mpec for feasibility - mpec_feas = struct; - mpec_feas.f = sum(s); - mpec_feas.g = g_s; - mpec_feas.x = [s;mpec_casadi.x]; - mpec_feas.G = mpec_casadi.x1; - mpec_feas.H = mpec_casadi.x2; - solver_initialization_feas = solver_initialization; - solver_initialization_feas.lbg = lbg_s; - solver_initialization_feas.ubg = ubg_s; - solver_initialization_feas.lbx = [s_lb;solver_initialization.lbx]; - solver_initialization_feas.ubx = [s_ub; solver_initialization.ubx]; - solver_initialization_feas.x0 = [s0;solver_initialization.x0]; - [mpec_feas_casadi, dims_feas, solver_initialization_feas] = create_mpec_functions(mpec_feas,solver_initialization_feas,opts); - lpec_feas_casadi = create_lpec_functions(mpec_feas_casadi,dims_feas,opts,solver_initialization_feas); - solver_initialization_feas.y_lpec_k_l = y_lpec_k_l; - solver_initialization_feas.d_lpec_k_l = nan; - phase_ii = false; - dims_feas.map_w = 1:dims_feas.n_primal_non_lifted; - dims_feas.map_g = 1:length(mpec_feas_casadi.g); - [solution_feas,stats_feas] = obj.phase_II(mpec_feas_casadi,lpec_feas_casadi,dims_feas,opts,solver_initialization_feas,stats,phase_ii); - s_k = solution_feas.x(1:n_slacks); - % get cpu times from phase i - stats.iter.cpu_time_lpec_phase_i_iter = stats_feas.iter.cpu_time_lpec_phase_i_iter; - stats.iter.cpu_time_nlp_phase_i_iter = stats_feas.iter.cpu_time_nlp_phase_i_iter; - s_infeasiblity = max(s_k); - if s_infeasiblity >= opts.tol_feasibility - stats.stopping_criterion_fullfiled = true; - stats.problem_infeasible = true; - stats.success = false; - stats.success_phase_i = false; - stats.solver_message = 'MPEC is locally infeasible.'; - x_k = solution_feas.x(1+n_slacks:end); - else + ii = ii+1; + stats.iter.X_outer = [stats.iter.X_outer, x_k]; + if ismember(stats_nlp.return_status, {'Solve_Succeeded', 'Search_Direction_Becomes_Too_Small', 'Solved_To_Acceptable_Level'}) stats.feasible_bnlp_found = true; stats.success_phase_i = true; - x_k = solution_feas.x(n_slacks+1:end); - if full(mpec_casadi.h_comp_fun(x_k,solver_initialization.p0)) > 1e-3 && opts.debug_mode_on - keyboard; - end + break; end - else - stats.feasible_bnlp_found = true; - stats.success_phase_i = true; + end + % TODO: IF INFEASIBLE QUIT + case 'AllBiactive' + lbx_bnlp_k = solver_initialization.lbx; ubx_bnlp_k = solver_initialization.ubx; x_k_init = x_k; + lbx_bnlp_k(dims.ind_x1) = 0; ubx_bnlp_k(dims.ind_x1) = 0; lbx_bnlp_k(dims.ind_x2) = 0; ubx_bnlp_k(dims.ind_x2) = 0; + t_presolve_nlp_iter = tic; + results_nlp = mpec_casadi.solver('x0', x_k, 'p',solver_initialization.p0,'lbx',lbx_bnlp_k,'ubx',ubx_bnlp_k,'lbg',solver_initialization.lbg,'ubg',solver_initialization.ubg); % solve BNLP/NLP + cpu_time_bnlp_k = toc(t_presolve_nlp_iter); + stats.iter.cpu_time_nlp_phase_i_iter = [stats.iter.cpu_time_nlp_phase_i_iter;cpu_time_bnlp_k]; + x_k = full(results_nlp.x); + lambda_x_k = full(results_nlp.lam_x); + stats_nlp = mpec_casadi.solver.stats(); + nlp_iters_k = stats_nlp.iter_count; + h_comp_k = full(mpec_casadi.h_comp_fun(x_k,solver_initialization.p0)); h_std_k = full(mpec_casadi.h_std_fun(x_k,solver_initialization.p0)); f_opt_k = full(results_nlp.f); + if opts.verbose_solver + print_iter_stats('BNLP',1,f_opt_k,h_std_k,h_comp_k,'NLP',nlp_iters_k,stats_nlp.return_status,nan,norm(x_k_init-x_k),cpu_time_bnlp_k,1) + end + stats.iter.X_outer = [stats.iter.X_outer, x_k]; + stats.n_nlp_total = stats.n_nlp_total + 1; + + % TODO: IF INFEASIBLE QUIT + case 'TakeInitialGuessDirectly' + if opts.project_guess_to_bounds x_k = project_to_bounds(x_k,solver_initialization.lbx,solver_initialization.ubx,dims); - fprintf('\n MPECopt: MPEC has only complementarity and bound constraints, feasible point found by projection to bounds.\n') end - end - case 'RandomActiveSet' % just take the user provided x0; - % rng(1,"twister"); % to have reproducable problem sets - ii = 1; - while (ii <= opts.max_recovery_iters) && ~stats.problem_infeasible - I_0_plus = boolean(round(rand(dims.n_comp,1))); - I_plus_0 = ~I_0_plus; + case 'TakeInitialGuessActiveSet' + % Make active set guess from x_k and solve corresponding BNLP; + I_0_plus = x_k(dims.ind_x1)<=x_k(dims.ind_x2); + I_plus_0 = x_k(dims.ind_x1)>x_k(dims.ind_x2); lbx_bnlp_k = solver_initialization.lbx; ubx_bnlp_k = solver_initialization.ubx; ubx_bnlp_k(dims.ind_x1(I_0_plus)) = 0; @@ -472,103 +548,51 @@ results_nlp = mpec_casadi.solver('x0',x_k,'p',solver_initialization.p0,'lbx',lbx_bnlp_k,'ubx',ubx_bnlp_k,'lbg',solver_initialization.lbg,'ubg',solver_initialization.ubg); cpu_time_bnlp_k = toc(t_presolve_nlp_iter); stats.iter.cpu_time_nlp_phase_i_iter = [stats.iter.cpu_time_nlp_phase_i_iter;cpu_time_bnlp_k ]; - x_k = full(results_nlp.x); lambda_x_k = full(results_nlp.lam_x); - stats_nlp = mpec_casadi.solver.stats(); nlp_iters_k = stats_nlp.iter_count; stats.n_nlp_total = stats.n_nlp_total + 1; + x_k = full(results_nlp.x); + lambda_x_k = full(results_nlp.lam_x); + stats_nlp = mpec_casadi.solver.stats(); + nlp_iters_k = stats_nlp.iter_count; h_comp_k = full(mpec_casadi.h_comp_fun(x_k,solver_initialization.p0)); h_std_k = full(mpec_casadi.h_std_fun(x_k,solver_initialization.p0)); f_opt_k = full(results_nlp.f); if opts.verbose_solver - print_iter_stats(1,ii,f_opt_k,h_std_k,h_comp_k,'BNLP',nlp_iters_k,stats_nlp.return_status,nan,norm(x_k_init-x_k),cpu_time_bnlp_k,1) + print_iter_stats(1,1,f_opt_k,h_std_k,h_comp_k,'BNLP',nlp_iters_k,stats_nlp.return_status,nan,norm(x_k_init-x_k),cpu_time_bnlp_k,1) end - ii = ii+1; stats.iter.X_outer = [stats.iter.X_outer, x_k]; - if ismember(stats_nlp.return_status, {'Solve_Succeeded', 'Search_Direction_Becomes_Too_Small', 'Solved_To_Acceptable_Level'}) - stats.feasible_bnlp_found = true; - stats.success_phase_i = true; - break; - end - end - % TODO: IF INFEASIBLE QUIT - case 'AllBiactive' - lbx_bnlp_k = solver_initialization.lbx; ubx_bnlp_k = solver_initialization.ubx; x_k_init = x_k; - lbx_bnlp_k(dims.ind_x1) = 0; ubx_bnlp_k(dims.ind_x1) = 0; lbx_bnlp_k(dims.ind_x2) = 0; ubx_bnlp_k(dims.ind_x2) = 0; - t_presolve_nlp_iter = tic; - results_nlp = mpec_casadi.solver('x0', x_k, 'p',solver_initialization.p0,'lbx',lbx_bnlp_k,'ubx',ubx_bnlp_k,'lbg',solver_initialization.lbg,'ubg',solver_initialization.ubg); % solve BNLP/NLP - cpu_time_bnlp_k = toc(t_presolve_nlp_iter); - stats.iter.cpu_time_nlp_phase_i_iter = [stats.iter.cpu_time_nlp_phase_i_iter;cpu_time_bnlp_k]; - x_k = full(results_nlp.x); - lambda_x_k = full(results_nlp.lam_x); - stats_nlp = mpec_casadi.solver.stats(); - nlp_iters_k = stats_nlp.iter_count; - h_comp_k = full(mpec_casadi.h_comp_fun(x_k,solver_initialization.p0)); h_std_k = full(mpec_casadi.h_std_fun(x_k,solver_initialization.p0)); f_opt_k = full(results_nlp.f); - if opts.verbose_solver - print_iter_stats('BNLP',1,f_opt_k,h_std_k,h_comp_k,'NLP',nlp_iters_k,stats_nlp.return_status,nan,norm(x_k_init-x_k),cpu_time_bnlp_k,1) - end - stats.iter.X_outer = [stats.iter.X_outer, x_k]; - stats.n_nlp_total = stats.n_nlp_total + 1; - - % TODO: IF INFEASIBLE QUIT - case 'TakeInitialGuessDirectly' - if opts.project_guess_to_bounds - x_k = project_to_bounds(x_k,solver_initialization.lbx,solver_initialization.ubx,dims); - end - case 'TakeInitialGuessActiveSet' - % Make active set guess from x_k and solve corresponding BNLP; - I_0_plus = x_k(dims.ind_x1)<=x_k(dims.ind_x2); - I_plus_0 = x_k(dims.ind_x1)>x_k(dims.ind_x2); - lbx_bnlp_k = solver_initialization.lbx; - ubx_bnlp_k = solver_initialization.ubx; - ubx_bnlp_k(dims.ind_x1(I_0_plus)) = 0; - ubx_bnlp_k(dims.ind_x2(I_plus_0)) = 0; - % solve BNLP/NLP - x_k_init = x_k; - t_presolve_nlp_iter = tic; - results_nlp = mpec_casadi.solver('x0',x_k,'p',solver_initialization.p0,'lbx',lbx_bnlp_k,'ubx',ubx_bnlp_k,'lbg',solver_initialization.lbg,'ubg',solver_initialization.ubg); - cpu_time_bnlp_k = toc(t_presolve_nlp_iter); - stats.iter.cpu_time_nlp_phase_i_iter = [stats.iter.cpu_time_nlp_phase_i_iter;cpu_time_bnlp_k ]; - x_k = full(results_nlp.x); - lambda_x_k = full(results_nlp.lam_x); - stats_nlp = mpec_casadi.solver.stats(); - nlp_iters_k = stats_nlp.iter_count; - h_comp_k = full(mpec_casadi.h_comp_fun(x_k,solver_initialization.p0)); h_std_k = full(mpec_casadi.h_std_fun(x_k,solver_initialization.p0)); f_opt_k = full(results_nlp.f); - if opts.verbose_solver - print_iter_stats(1,1,f_opt_k,h_std_k,h_comp_k,'BNLP',nlp_iters_k,stats_nlp.return_status,nan,norm(x_k_init-x_k),cpu_time_bnlp_k,1) - end - stats.iter.X_outer = [stats.iter.X_outer, x_k]; - stats.n_nlp_total = stats.n_nlp_total + 1; - % stats.succes+p - case 'TakeProvidedActiveSet' - if isfield(solver_initialization,'y0') - if isequal(length(solver_initialization.y0),dims.n_comp) - y_lpec_k_l = solver_initialization.y0; - I_plus_0 = y_lpec_k_l ==1; - I_0_plus = y_lpec_k_l == 0; + stats.n_nlp_total = stats.n_nlp_total + 1; + % stats.succes+p + case 'TakeProvidedActiveSet' + if isfield(solver_initialization,'y0') + if isequal(length(solver_initialization.y0),dims.n_comp) + y_lpec_k_l = solver_initialization.y0; + I_plus_0 = y_lpec_k_l ==1; + I_0_plus = y_lpec_k_l == 0; + else + warning('TakeProvidedActiveSet: lenth of y0 is %d, but required is %d',length(solver_initialization.y0)) + I_0_plus = x_k(dims.ind_x1)<=x_k(dims.ind_x2); + I_plus_0 = x_k(dims.ind_x1)>x_k(dims.ind_x2); + end else - warning('TakeProvidedActiveSet: lenth of y0 is %d, but required is %d',length(solver_initialization.y0)) + warning('\n TakeProvidedActiveSet as initialization strategy chosen, but no y0 provided. Resporting to TakeInitialGuessActiveSet') I_0_plus = x_k(dims.ind_x1)<=x_k(dims.ind_x2); I_plus_0 = x_k(dims.ind_x1)>x_k(dims.ind_x2); end - else - warning('\n TakeProvidedActiveSet as initialization strategy chosen, but no y0 provided. Resporting to TakeInitialGuessActiveSet') - I_0_plus = x_k(dims.ind_x1)<=x_k(dims.ind_x2); - I_plus_0 = x_k(dims.ind_x1)>x_k(dims.ind_x2); - end - lbx_bnlp_k = solver_initialization.lbx; - ubx_bnlp_k = solver_initialization.ubx; - ubx_bnlp_k(dims.ind_x1(I_0_plus)) = 0; - ubx_bnlp_k(dims.ind_x2(I_plus_0)) = 0; - % solve BNLP/NLP - x_k_init = x_k; - t_presolve_nlp_iter = tic; - results_nlp = mpec_casadi.solver('x0',x_k,'p',solver_initialization.p0,'lbx',lbx_bnlp_k,'ubx',ubx_bnlp_k,'lbg',solver_initialization.lbg,'ubg',solver_initialization.ubg); - cpu_time_bnlp_k = toc(t_presolve_nlp_iter); - stats.iter.cpu_time_nlp_phase_i_iter = [stats.iter.cpu_time_nlp_phase_i_iter; cpu_time_bnlp_k]; - x_k = full(results_nlp.x); lambda_x_k = full(results_nlp.lam_x); - stats_nlp = mpec_casadi.solver.stats(); nlp_iters_k = stats_nlp.iter_count; stats.n_nlp_total = stats.n_nlp_total + 1; - h_comp_k = full(mpec_casadi.h_comp_fun(x_k,solver_initialization.p0)); h_std_k = full(mpec_casadi.h_std_fun(x_k,solver_initialization.p0)); f_opt_k = full(results_nlp.f); - if opts.verbose_solver - print_iter_stats(1,1,f_opt_k,h_std_k,h_comp_k,'BNLP',nlp_iters_k,stats_nlp.return_status,nan,norm(x_k_init-x_k),cpu_time_bnlp_k,1) - end - stats.iter.X_outer = [stats.iter.X_outer, x_k]; - stats.success_phase_i = true; + lbx_bnlp_k = solver_initialization.lbx; + ubx_bnlp_k = solver_initialization.ubx; + ubx_bnlp_k(dims.ind_x1(I_0_plus)) = 0; + ubx_bnlp_k(dims.ind_x2(I_plus_0)) = 0; + % solve BNLP/NLP + x_k_init = x_k; + t_presolve_nlp_iter = tic; + results_nlp = mpec_casadi.solver('x0',x_k,'p',solver_initialization.p0,'lbx',lbx_bnlp_k,'ubx',ubx_bnlp_k,'lbg',solver_initialization.lbg,'ubg',solver_initialization.ubg); + cpu_time_bnlp_k = toc(t_presolve_nlp_iter); + stats.iter.cpu_time_nlp_phase_i_iter = [stats.iter.cpu_time_nlp_phase_i_iter; cpu_time_bnlp_k]; + x_k = full(results_nlp.x); lambda_x_k = full(results_nlp.lam_x); + stats_nlp = mpec_casadi.solver.stats(); nlp_iters_k = stats_nlp.iter_count; stats.n_nlp_total = stats.n_nlp_total + 1; + h_comp_k = full(mpec_casadi.h_comp_fun(x_k,solver_initialization.p0)); h_std_k = full(mpec_casadi.h_std_fun(x_k,solver_initialization.p0)); f_opt_k = full(results_nlp.f); + if opts.verbose_solver + print_iter_stats(1,1,f_opt_k,h_std_k,h_comp_k,'BNLP',nlp_iters_k,stats_nlp.return_status,nan,norm(x_k_init-x_k),cpu_time_bnlp_k,1) + end + stats.iter.X_outer = [stats.iter.X_outer, x_k]; + stats.success_phase_i = true; end stats.rho_TR_final = rho_TR_k_l; % ---- make sure feasible point is declared sucessful --- @@ -587,7 +611,7 @@ if sum(active_set_estimate_k.I_00) == 0 stats.stopping_criterion_fullfiled = true; stats.multiplier_based_stationarity = 'S'; - stats.solver_message = 'S-stationary point found sucessfully in Phase I.'; + stats.solver_message = 'S-stationary point found successfully in Phase I.'; stats.solved_in_phase_i = true; stats.b_stationarity = true; stats.success = true; @@ -615,7 +639,7 @@ [multiplier_based_stationarity, solver_message] = determine_multipliers_based_stationary_point(x_k,lambda_x_k,dims,opts); if strcmp(stats.multiplier_based_stationarity,'S') stats.stopping_criterion_fullfiled = true; - stats.solver_message = 'S-stationary point found sucessfully in presolve.'; + stats.solver_message = 'S-stationary point found successfully in presolve.'; stats.success = true; stats.b_stationarity = true; stats.solved_in_phase_i = true; @@ -628,6 +652,10 @@ % ------------------------------- end of Phase I ----------------------------------------------- %% ---------------------- Main optimization loop of Phase II ----------------------------------- + if opts.use_one_nlp_solver + % needs for dimension match + solver_initialization = solver_initialization_relaxed; + end solver_initialization.x0 = x_k; solver_initialization.y_lpec_k_l = y_lpec_k_l; solver_initialization.d_lpec_k_l = d_lpec_k_l; @@ -637,6 +665,7 @@ print_phase_ii(); print_iter_header(); end + [solution,stats] = obj.phase_II(mpec_casadi,lpec_casadi,dims,opts,solver_initialization,stats,phase_ii); if stats.solved_in_phase_i @@ -695,14 +724,14 @@ end function varargout = size(obj,varargin) - % This is a required overload for matlab.mixin.indexing.RedefinesParen. - % In the case of a scalar like this class returning 1 or throwing an error is prefered. + % This is a required overload for matlab.mixin.indexing.RedefinesParen. + % In the case of a scalar like this class returning 1 or throwing an error is prefered. varargout = {1}; end - + function ind = end(obj,k,n) - % This is a required overload for matlab.mixin.indexing.RedefinesParen. - % In the case of a scalar like this class returning 1 or throwing an error is prefered. + % This is a required overload for matlab.mixin.indexing.RedefinesParen. + % In the case of a scalar like this class returning 1 or throwing an error is prefered. ind = 1; end end @@ -774,14 +803,14 @@ end function obj = parenAssign(obj,index_op,varargin) - % nosnoc.error('invalid', 'Invalid operation'); - % TODO: there is no nosnoc in mpecopt - adjust errors messages + % nosnoc.error('invalid', 'Invalid operation'); + % TODO: there is no nosnoc in mpecopt - adjust errors messages error('mpecopt: Invalid operation.') end - + function obj = parenDelete(obj,index_op) - % nosnoc.error('invalid', 'Invalid operation') - % TODO: there is no nosnoc in mpecopt - adjust errors messages + % nosnoc.error('invalid', 'Invalid operation') + % TODO: there is no nosnoc in mpecopt - adjust errors messages error('mpecopt: Invalid operation.') end @@ -802,7 +831,7 @@ function process_solver_initialization(obj, solver_initialization) if ~isfield(solver_initialization,"p0") solver_initialization.p0 = []; end - + G_fun = mpec_casadi.G_fun; H_fun = mpec_casadi.H_fun; if opts.initial_comp_all_zero @@ -835,7 +864,7 @@ function process_solver_initialization(obj, solver_initialization) solver_initialization.x0(dims.ind_x1) = G_eval; solver_initialization.x0(dims.ind_x2) = H_eval; else - % TODO: HERE is some BUG! + % TODO: HERE is some BUG! % solver_initialization.x0(dims.ind_nonscalar_x1) = G_eval(dims.ind_nonscalar_x1); % solver_initialization.x0(dims.ind_nonscalar_x2) = H_eval(dims.ind_nonscalar_x2); end @@ -847,7 +876,7 @@ function process_solver_initialization(obj, solver_initialization) x1 = mpec_casadi.x1; x2 = mpec_casadi.x2; p = mpec_casadi.p; - + ind_g_eq = find(solver_initialization.lbg == solver_initialization.ubg); ind_g_ineq = find(solver_initialization.lbg < solver_initialization.ubg); @@ -902,9 +931,9 @@ function process_solver_initialization(obj, solver_initialization) if dims.n_comp > 0 if opts.comp_res_bilinear h_comp= max(abs(x1).*abs(x2)); % here kappa =0.1 to have reduce of the value by factor of 10 - % h_comp= sqrt(max(abs(min((x1),(x2))))); % here kappa =0.01 to reduce the value above by factor of 10 + % h_comp= sqrt(max(abs(min((x1),(x2))))); % here kappa =0.01 to reduce the value above by factor of 10 else - h_comp= max(min(abs(x1),abs(x2))); % + h_comp= max(min(abs(x1),abs(x2))); % % h_comp = max(abs(min(x1,x2))); end else @@ -915,7 +944,7 @@ function process_solver_initialization(obj, solver_initialization) mpec_casadi.h_std_fun = Function('h_std_fun',{x,p},{h_std}); mpec_casadi.h_comp_fun = Function('h_comp_fun',{x,p},{h_comp}); mpec_casadi.h_total_fun = Function('h_comp_fun',{x,p},{max(h_comp,h_std)}); - + %% generate required functions % Zero order mpec_casadi.g_eq_fun = Function('g_eq_fun',{x,p},{g_eq}); @@ -939,7 +968,7 @@ function process_solver_initialization(obj, solver_initialization) lpec_casadi.g_ineq_fun = mpec_casadi.g_ineq_fun; lpec_casadi.nabla_g_eq_fun = mpec_casadi.nabla_g_eq_fun; lpec_casadi.nabla_g_ineq_fun = mpec_casadi.nabla_g_ineq_fun; - + %% Update dims dims.n_eq = n_eq; dims.n_ineq = n_ineq; @@ -948,7 +977,7 @@ function process_solver_initialization(obj, solver_initialization) dims.ind_g_ineq = ind_g_ineq; dims.ind_g_ineq_lb = ind_g_ineq_lb; dims.ind_g_ineq_ub = ind_g_ineq_lb; % if the last two have same indicies then it is a two sided ineq; - + %% update structs obj.solver_initialization = solver_initialization; obj.mpec_casadi = mpec_casadi; @@ -986,20 +1015,20 @@ function create_mpec_functions(obj) H_var = mpec.H.(name); G_curr = mpec.G.(name)(); H_curr = mpec.H.(name)(); - + [ind_scalar_G,ind_nonscalar_G, ind_map_G] = find_nonscalar(G_curr, mpec.w.sym, mpec.p.sym); [ind_scalar_H,ind_nonscalar_H, ind_map_H] = find_nonscalar(H_curr, mpec.w.sym, mpec.p.sym); - + mpec.w.([name '_G_lift']) = {{'G', length(ind_nonscalar_G)}, 0, inf}; G_lift = G_curr(ind_nonscalar_G); G_curr(ind_nonscalar_G) = mpec.w.([name '_G_lift'])(); G_var().sym = G_curr; - + mpec.w.([name '_H_lift']) = {{'H', length(ind_nonscalar_H)}, 0, inf}; H_lift = H_curr(ind_nonscalar_H); H_curr(ind_nonscalar_H) = mpec.w.([name '_H_lift'])(); H_var().sym = H_curr; - + mpec.g.([name '_G_lift']) = {mpec.w.([name '_G_lift'])()-G_lift}; mpec.g.([name '_H_lift']) = {mpec.w.([name '_H_lift'])()-H_lift}; @@ -1010,7 +1039,7 @@ function create_mpec_functions(obj) n_lift_x1 = n_lift_x1 + length(ind_nonscalar_G); n_lift_x2 = n_lift_x2 + length(ind_nonscalar_H); else % Do the same behavior as before excep this time for each var(i,j,k,...) index for each variable 'var'. - % Get indices that we will need to get all the casadi vars for the vdx.Variable + % Get indices that we will need to get all the casadi vars for the vdx.Variable indices = {}; for len=size(mpec.G.(name).indices) indices = horzcat(indices, {1:len}); @@ -1034,7 +1063,7 @@ function create_mpec_functions(obj) % however it is likely that this can be done in 1 shot? [ind_scalar_G,ind_nonscalar_G, ind_map_G] = find_nonscalar(G_curr, mpec.w.sym, mpec.p.sym); [ind_scalar_H,ind_nonscalar_H, ind_map_H] = find_nonscalar(H_curr, mpec.w.sym, mpec.p.sym); - + mpec.w.([name '_G_lift'])(curr{:}) = {{'G', length(ind_nonscalar_G)}, 0, inf}; G_lift = G_curr(ind_nonscalar_G); G_curr(ind_nonscalar_G) = mpec.w.([name '_G_lift'])(curr{:}); @@ -1044,7 +1073,7 @@ function create_mpec_functions(obj) H_lift = H_curr(ind_nonscalar_H); H_curr(ind_nonscalar_H) = mpec.w.([name '_H_lift'])(curr{:}); H_var(curr{:}).sym = H_curr; - + mpec.g.([name '_G_lift'])(curr{:}) = {mpec.w.([name '_G_lift'])(curr{:})-G_lift}; mpec.g.([name '_H_lift'])(curr{:}) = {mpec.w.([name '_H_lift'])(curr{:})-H_lift}; @@ -1056,7 +1085,7 @@ function create_mpec_functions(obj) ind_nonscalar_x2 = [ind_nonscalar_x2, ind_H(ind_nonscalar_H)]; n_lift_x1 = n_lift_x1 + length(ind_nonscalar_G); n_lift_x2 = n_lift_x2 + length(ind_nonscalar_H); - + % if ~opts.assume_lower_bounds % Lower bounds on G, H, not already present in MPCC % if ~opts.lift_complementarities % if ~isempty(ind_nonscalar_G) @@ -1101,7 +1130,7 @@ function create_mpec_functions(obj) x1 = G; x2 = H; n_comp = length(x2); - + if n_comp > 0 % TODO(@anton) do we actually need this here or can we calculate these "analytically" ind_x1 = []; @@ -1132,7 +1161,7 @@ function create_mpec_functions(obj) ind_x1 = []; ind_x2 = []; end - + n_primal = length(x); n_primal_x0 = n_primal - 2*n_comp; % primal variables excluding the complementarity variables; ind_x0 = [1:n_primal]'; @@ -1164,7 +1193,7 @@ function create_mpec_functions(obj) G_fun = Function('G_fun',{x,p},{G}); H_fun = Function('H_fun',{x,p},{H}); - + dims.ind_x = 1:n_primal_non_lifted; dims.ind_g = 1:n_g_non_lifted; @@ -1174,7 +1203,11 @@ function create_mpec_functions(obj) % check if the comps are expressions or just subvectors of w. if opts.lift_complementarities_full % define lift vairables - x1 = SX.sym('x1',n_comp); + if strcmp(class(x),'casadi.SX') + x1 = SX.sym('x1',n_comp); + else + x1 = MX.sym('x1',n_comp); + end % update x and init guess x = [x;x1]; % lift @@ -1195,7 +1228,11 @@ function create_mpec_functions(obj) end end if n_lift_x1 > 0 - x1_lift = SX.sym('x1_lift',n_lift_x1); + if strcmp(class(x),'casadi.SX') + x1_lift = SX.sym('x1_lift',n_lift_x1); + else + x1_lift = MX.sym('x1_lift',n_lift_x1); + end % x1 = [x(ind_scalar);x1_lift]; % update x and init guess x = [x;x1_lift]; @@ -1211,7 +1248,11 @@ function create_mpec_functions(obj) if opts.lift_complementarities_full % define lift vairables - x2 = SX.sym('x2',n_comp); + if strcmp(class(x),'casadi.SX') + x2 = SX.sym('x2',n_comp); + else + x2 = MX.sym('x2',n_comp); + end % update x and init guess x = [x;x2]; % lift @@ -1231,7 +1272,11 @@ function create_mpec_functions(obj) end end if n_lift_x2 > 0 - x2_lift = SX.sym('x2_lift',n_lift_x2); + if strcmp(class(x),'casadi.SX') + x2_lift = SX.sym('x2_lift',n_lift_x2); + else + x2_lift = MX.sym('x2_lift',n_lift_x2); + end % x2 = [x(ind_scalar);x2_lift]; % update x and init guess x = [x;x2_lift]; @@ -1289,7 +1334,7 @@ function create_mpec_functions(obj) dims.map_w = 1:n_primal; dims.map_g = 1:n_g; end - + nabla_f = f.jacobian(x)'; nabla_g = g.jacobian(x); %% CasADi functions for constraint evaluations and their derivaties @@ -1324,7 +1369,7 @@ function create_mpec_functions(obj) dims.n_lift_x2 = n_lift_x2; dims.n_auxiliary = dims.n_comp; % number of binary variables in LPEC dims.n_g = n_g; - + % indices for lifting dims.ind_nonscalar_x1 = ind_nonscalar_x1; dims.ind_nonscalar_x2 = ind_nonscalar_x2; @@ -1333,13 +1378,16 @@ function create_mpec_functions(obj) % TODO(@anton) actually build the scholtes relaxation here and store the scholtes indices. t_generate_nlp_solvers = tic; nlp = struct('x', x,'f', f,'g', g,'p',p); - solver = nlpsol('solver', 'ipopt', nlp, opts.settings_casadi_nlp); + if ~obj.opts.use_one_nlp_solver + solver = nlpsol('solver', 'ipopt', nlp, opts.settings_casadi_nlp); + end stats.cpu_time_generate_nlp_solvers = toc(t_generate_nlp_solvers); - mpec_casadi.solver = solver; - + if ~obj.opts.use_one_nlp_solver + mpec_casadi.solver = solver; + end %% Generate scholtes - - + + %% Store structs obj.mpec_casadi = mpec_casadi; obj.dims = dims; @@ -1358,9 +1406,14 @@ function create_lpec_functions(obj) % x1 = x(dims.ind_x1); % x2 = x(dims.ind_x2); p = mpec_casadi.p; - M = SX.sym('M', 1); - y = SX.sym('y', dims.n_comp); % binary variablkes for comp. constraints - + if strcmp(class(x),'casadi.SX') + M = SX.sym('M', 1); + y = SX.sym('y', dims.n_comp); % binary variablkes for comp. constraints + else + M = MX.sym('M', 1); + y = MX.sym('y', dims.n_comp); % binary variablkes for comp. constraints + end + % Big M reformulation of complementarities A_lpec_sym = [-x1+M*y; -x2-M*y]; b_res = [x1;x2-M]; @@ -1389,7 +1442,7 @@ function create_lpec_functions(obj) end function [solution,stats] = phase_II(obj,mpec_casadi,lpec_casadi,dims,opts,solver_initialization,stats,phase_ii) - % This function implements the Phase II algorithm of MPECppt. + % This function implements the Phase II algorithm of MPECppt. k = 1; n_cycles = 0; resolve_nlp = true; @@ -1418,7 +1471,7 @@ function create_lpec_functions(obj) l_k = 1; % minor/inner iter counter in k-th major/outer iteration n_nlp_k = 0; n_lpec_k = 0; % lpecs/nlps solved in iteration k accept_trail_step = false; - if opts.reset_TR_radius && k > 1 + if opts.reset_TR_radius && k > 1 if phase_ii rho_TR_k_l = opts.rho_TR_phase_ii_init; else @@ -1451,7 +1504,7 @@ function create_lpec_functions(obj) end lpec.rho_TR = rho_TR_k_l; % update trust region stats.iter.rho_TR_iter = [stats.iter.rho_TR_iter, rho_TR_k_l]; % store TR radius - % Solve LPEC + % Solve LPEC [results_lpec,stats_lpec] = lpec_solver(lpec, opts.settings_lpec); if ~phase_ii stats.iter.cpu_time_lpec_phase_i_iter = [stats.iter.cpu_time_lpec_phase_i_iter, stats_lpec.cpu_time]; % stats @@ -1472,7 +1525,7 @@ function create_lpec_functions(obj) d_lpec_k_l = results_lpec.d_lpec; y_lpec_k_l = results_lpec.y_lpec; f_lin_opt_k_l = results_lpec.f_opt; - stats.f_lpec = results_lpec.f_opt; + stats.f_lpec = results_lpec.f_opt; x_trail_lpec = x_k + d_lpec_k_l; % Infeasiblity check h_comp_lpec_k_l = full(mpec_casadi.h_comp_fun(x_trail_lpec,p0)); @@ -1487,7 +1540,7 @@ function create_lpec_functions(obj) end % --------------------------- Check if B-stationary point found -------------------------- % h_total_k = full(mpec_casadi.h_total_fun(x_k,p0)); - if l_k > 1 + if l_k > 1 if ismember(stats_nlp.return_status, {'Solve_Succeeded', 'Search_Direction_Becomes_Too_Small', 'Solved_To_Acceptable_Level'}) || (h_total_k <= opts.tol) bnlp_solved = true; % succes of bnlp iter in phase ii else @@ -1504,9 +1557,9 @@ function create_lpec_functions(obj) end end if norm(d_lpec_k_l) <= opts.tol_B_stationarity - % if abs(f_lin_opt_k_l) <= settings.tol_B_stationarity + % if abs(f_lin_opt_k_l) <= settings.tol_B_stationarity stats.stopping_criterion_fullfiled = true; % B-stationary point found, optimal solution found! - stats.solver_message = 'B-stationary point found sucessfully.'; + stats.solver_message = 'B-stationary point found successfully.'; stats.success = true; stats.b_stationarity = true; resolve_nlp = false; @@ -1518,7 +1571,7 @@ function create_lpec_functions(obj) stats.iter.X_lpec = [stats.iter.X_lpec, x_trail_lpec]; stats.iter.d_norm_lpec = [stats.iter.d_norm_lpec, norm(d_lpec_k_l)]; stats.iter.f_lpec = [stats.iter.f_lpec, f_lin_opt_k_l]; % store some stats - % --------------------------- set up piece NLP with new active set------------------------- + % --------------------------- set up piece NLP with new active set------------------------- if opts.compute_bnlp_step && ~stats.stopping_criterion_fullfiled lbx_bnlp_k = solver_initialization.lbx; ubx_bnlp_k = solver_initialization.ubx; % reset bounds of bnlp. lbg_tnlp_k = solver_initialization.lbg; ubg_tnlp_k = solver_initialization.ubg; @@ -1539,11 +1592,19 @@ function create_lpec_functions(obj) % --------------------------- solve piece NLP ------------------------- if resolve_nlp t_nlp_start = tic; - results_nlp = mpec_casadi.solver('x0',x_k,'p', p0, 'lbx', lbx_bnlp_k, 'ubx', ubx_bnlp_k,'lbg', lbg_tnlp_k, 'ubg', ubg_tnlp_k); + if opts.use_one_nlp_solver + % Remark: this will not work if phase i solves a feasbility problem, there it is necessary to create two solvers; + results_nlp = obj.solver_relaxed('x0',x_k,'p', [1e64], 'lbx', lbx_bnlp_k, 'ubx', ubx_bnlp_k,'lbg', lbg_tnlp_k, 'ubg', ubg_tnlp_k); + stats_nlp = obj.solver_relaxed.stats(); + else + results_nlp = mpec_casadi.solver('x0',x_k,'p', p0, 'lbx', lbx_bnlp_k, 'ubx', ubx_bnlp_k,'lbg', lbg_tnlp_k, 'ubg', ubg_tnlp_k); + stats_nlp = mpec_casadi.solver.stats(); + end + cpu_time_nlp_k_l = toc(t_nlp_start); x_trail_nlp = full(results_nlp.x); lambda_x_trail_nlp = full(results_nlp.lam_x); - stats_nlp = mpec_casadi.solver.stats(); + nlp_iters_k_l = stats_nlp.iter_count; h_comp_k_l = full(mpec_casadi.h_comp_fun(x_trail_nlp,p0)); h_std_k_l = full(mpec_casadi.h_std_fun(x_trail_nlp,p0)); @@ -1560,9 +1621,9 @@ function create_lpec_functions(obj) stats.iter.X_all = [stats.iter.X_all, x_trail_nlp]; nlp_step_sucessful = true; - if isequal(stats_nlp.return_status,'Infeasible_Problem_Detected') && opts.debug_mode_on + if isequal(stats_nlp.return_status,'Infeasible_Problem_Detected') && opts.debug_mode_on % this may hapen if lpec makes big jump and xk not fesaible for new bnlp - keyboard; + keyboard; end if ~ismember(stats_nlp.return_status, {'Solve_Succeeded', 'Search_Direction_Becomes_Too_Small', 'Solved_To_Acceptable_Level'}) @@ -1630,7 +1691,7 @@ function create_lpec_functions(obj) end % --------------------------- Globalization: check step acceptence ------------------------- t_globalization_iter = tic; - if ~stats.stopping_criterion_fullfiled + if ~stats.stopping_criterion_fullfiled % Current iterate f_k = full(mpec_casadi.f_fun(x_k, p0)); h_std_k = full(mpec_casadi.h_std_fun(x_k, p0)); @@ -1665,9 +1726,9 @@ function create_lpec_functions(obj) keyboard; end end - + % Update TR: - if accept_trail_step + if accept_trail_step rho_TR_k_l = opts.TR_increasing_factor*rho_TR_k_l; else rho_TR_k_l = opts.TR_reducing_factor*rho_TR_k_l; @@ -1677,7 +1738,7 @@ function create_lpec_functions(obj) n_cycles = n_cycles+1; break; % avoid cyclin with rho_TR_min end - + stats.iter.cpu_time_globalization_iter = [stats.iter.cpu_time_globalization_iter; toc(t_globalization_iter)]; % ------------------- Debug mode - catch some unexpected behaviour --------------------- if ~feasible_enough && k>1 && opts.debug_mode_on @@ -1754,7 +1815,7 @@ function create_lpec_functions(obj) % ------------- max iteration but early terminaton tolorance achieved?--------------------- if ((stats.max_iterations_reached && stats.success == 0) || n_cycles == 3)&& opts.allow_early_termination if (h_total_k <= opts.tol_B_stationarity_early_term) && ((abs(f_lin_opt_k_l) <= opts.tol_B_stationarity_early_term|| norm(nabla_f_k) <= opts.tol_B_stationarity_early_term)) % if objective zero (either if cost gradient zero, or solution leads to it) = then set step to zero => B stationarity - % B-stationary point found, optimal solution found! + % B-stationary point found, optimal solution found! if n_cycles == 3 stats.solver_message = 'Major loop was cycling due to bad problem scaling or too low tolerances. B-stationary point found at lower tolerance.'; else @@ -1772,7 +1833,7 @@ function create_lpec_functions(obj) % --------------- compute multiplier-based stationary points -------------- multiplier_based_stationarity_debug = stats.multiplier_based_stationarity; tol_active = 1e-6; - N_TNLP = 6; % max tnlp solves + N_TNLP = 6; % max tnlp solves ii = 1; tol_active_default = opts.tol_active; opts.tol_active = tol_active; @@ -1793,7 +1854,14 @@ function create_lpec_functions(obj) opts.piece_nlp_strategy = initial_strategy; % end t_nlp_start = tic; - results_nlp = mpec_casadi.solver('x0',x_k,'p', p0, 'lbx', lbx_bnlp_k, 'ubx', ubx_bnlp_k,'lbg', lbg_tnlp_k, 'ubg', ubg_tnlp_k); + % solve tnlp + if opts.use_one_nlp_solver + % Remark: this will not work if phase i solves a feasbility problem, there it is necessary to create two solvers; + results_nlp = obj.solver_relaxed('x0',x_k,'p', [1e64], 'lbx', lbx_bnlp_k, 'ubx', ubx_bnlp_k,'lbg', lbg_tnlp_k, 'ubg', ubg_tnlp_k); + else + results_nlp = mpec_casadi.solver('x0',x_k,'p', p0, 'lbx', lbx_bnlp_k, 'ubx', ubx_bnlp_k,'lbg', lbg_tnlp_k, 'ubg', ubg_tnlp_k); + end + cpu_time_nlp_k_l = toc(t_nlp_start); x_k_multi = full(results_nlp.x); f_tnlp = full(results_nlp.f); @@ -1806,7 +1874,7 @@ function create_lpec_functions(obj) [stats.multiplier_based_stationarity, ~] = determine_multipliers_based_stationary_point(x_k_multi,lambda_x_k,dims,opts); n_biactive = sum(active_set_estimate_k.I_00); % if ii~=N_TNLP - % x_k = x_tnlp; + % x_k = x_tnlp; % end break; else @@ -1862,46 +1930,53 @@ function create_lpec_functions(obj) import casadi.* % symbolics dims = obj.dims; - sigma_relaxed = SX.sym('sigma_relaxed',1); - x_relaxed = obj.mpec_casadi.x; + if strcmp(class(obj.mpec_casadi.x),'casadi.SX') + sigma_relaxed = SX.sym('sigma_relaxed',1); + else + sigma_relaxed = MX.sym('sigma_relaxed',1); + end + x_relaxed = obj.mpec_casadi.x; x1 = obj.mpec_casadi.x1; x2 = obj.mpec_casadi.x2; f_relaxed = obj.mpec_casadi.f; g = obj.mpec_casadi.g; - + % Parameters - p_relaxed = [obj.mpec_casadi.p;sigma_relaxed]; - + p_relaxed = [obj.mpec_casadi.p;sigma_relaxed]; + % relaxed complementarity constraints; - g_comp_relaxed = []; - lbg_comp_relaxed = []; + g_comp_relaxed = []; + lbg_comp_relaxed = []; ubg_comp_relaxed = []; - - switch obj.opts.relax_and_project_homotopy_parameter_steering - case "Direct" - if obj.opts.relax_and_project_comps_aggregated - g_comp_relaxed = x1'*x2-sigma_relaxed*dims.n_comp; - else - g_comp_relaxed = x1.*x2-sigma_relaxed; - end - case "Ell_1" - f_relaxed = f_relaxed+(x1'*x2)*(sigma_relaxed)^(-1); - case "Ell_inf" - % x_k_relaxed = project_to_bounds(x_k_relaxed ,lbx,ubx,dims); - s_eleastic = SX.sym('s_eleastic',1); - f_relaxed = f_relaxed+(s_eleastic)*(sigma_relaxed)^(-1); - x_relaxed = [x_relaxed;s_eleastic]; - if obj.opts.relax_and_project_comps_aggregated - g_comp_relaxed = x1'*x2-s_eleastic*dims.n_comp; - else - g_comp_relaxed = x1.*x2-s_eleastic; - end + case "Direct" + if obj.opts.relax_and_project_comps_aggregated + g_comp_relaxed = x1'*x2-sigma_relaxed*dims.n_comp; + else + g_comp_relaxed = x1.*x2-sigma_relaxed; + end + case "Ell_1" + f_relaxed = f_relaxed+(x1'*x2)*(sigma_relaxed)^(-1); + case "Ell_inf" + % x_k_relaxed = project_to_bounds(x_k_relaxed ,lbx,ubx,dims); + if strcmp(class(obj.mpec_casadi.x),'casadi.SX') + s_eleastic = SX.sym('s_eleastic',1); + else + s_eleastic = MX.sym('s_eleastic',1); + end + + f_relaxed = f_relaxed+(s_eleastic)*(sigma_relaxed)^(-1); + x_relaxed = [x_relaxed;s_eleastic]; + if obj.opts.relax_and_project_comps_aggregated + g_comp_relaxed = x1'*x2-s_eleastic*dims.n_comp; + else + g_comp_relaxed = x1.*x2-s_eleastic; + end end - g_relaxed = [g;g_comp_relaxed]; + g_relaxed = [g;g_comp_relaxed]; %% --------- create solver for Phase I ----------------------------------- @@ -1914,39 +1989,39 @@ function create_lpec_functions(obj) dims = obj.dims; x_k_relaxed = solver_initialization.x0; p0_relaxed = [solver_initialization.p0; obj.opts.relax_and_project_sigma0]; - % bounds - lbx_relaxed = solver_initialization.lbx; + % bounds + lbx_relaxed = solver_initialization.lbx; ubx_relaxed = solver_initialization.ubx; - lbg_relaxed = solver_initialization.lbg; + lbg_relaxed = solver_initialization.lbg; ubg_relaxed = solver_initialization.ubg; switch obj.opts.relax_and_project_homotopy_parameter_steering - case "Direct" - if obj.opts.relax_and_project_comps_aggregated - lbg_comp_relaxed = -inf; - ubg_comp_relaxed = 0; - else - lbg_comp_relaxed = -inf*ones(dims.n_comp,1); - ubg_comp_relaxed = 0*ones(dims.n_comp,1); - end - case "Ell_inf" - % x_k_relaxed = project_to_bounds(x_k_relaxed ,lbx,ubx,dims); - lbx_relaxed = [lbx_relaxed;0]; - ubx_relaxed = [ubx_relaxed;max(10,obj.opts.relax_and_project_sigma0*10)]; - x_k_relaxed = [x_k_relaxed;obj.opts.relax_and_project_sigma0]; - if obj.opts.relax_and_project_comps_aggregated - lbg_comp_relaxed = -inf; - ubg_comp_relaxed = 0; - else - lbg_comp_relaxed = -inf*ones(dims.n_comp,1); - ubg_comp_relaxed = 0*ones(dims.n_comp,1); - end + case "Direct" + if obj.opts.relax_and_project_comps_aggregated + lbg_comp_relaxed = -inf; + ubg_comp_relaxed = 0; + else + lbg_comp_relaxed = -inf*ones(dims.n_comp,1); + ubg_comp_relaxed = 0*ones(dims.n_comp,1); + end + case "Ell_inf" + % x_k_relaxed = project_to_bounds(x_k_relaxed ,lbx,ubx,dims); + lbx_relaxed = [lbx_relaxed;0]; + ubx_relaxed = [ubx_relaxed;max(10,obj.opts.relax_and_project_sigma0*10)]; + x_k_relaxed = [x_k_relaxed;obj.opts.relax_and_project_sigma0]; + if obj.opts.relax_and_project_comps_aggregated + lbg_comp_relaxed = -inf; + ubg_comp_relaxed = 0; + else + lbg_comp_relaxed = -inf*ones(dims.n_comp,1); + ubg_comp_relaxed = 0*ones(dims.n_comp,1); + end case "Ell_1" lbg_comp_relaxed = []; ubg_comp_relaxed = []; end - lbg_relaxed = [lbg_relaxed;lbg_comp_relaxed]; + lbg_relaxed = [lbg_relaxed;lbg_comp_relaxed]; ubg_relaxed = [ubg_relaxed;ubg_comp_relaxed]; % ind_comp = ind_comp:1:length(g_relaxed); % Output solver initalization @@ -1962,31 +2037,31 @@ function create_lpec_functions(obj) function X = all_combinations(varargin) - numSets = length(varargin); - for i=1:numSets, - thisSet = sort(varargin{i}); - if ~isequal(prod(size(thisSet)),length(thisSet)), - nosnoc.error('combinations_not_vectors', 'All inputs must be vectors.') - end - if ~isnumeric(thisSet), - nosnoc.error('cominations_not_numeric','All inputs must be numeric.') - end - sizeThisSet(i) = length(thisSet); - varargin{i} = thisSet; +numSets = length(varargin); +for i=1:numSets, + thisSet = sort(varargin{i}); + if ~isequal(prod(size(thisSet)),length(thisSet)), + nosnoc.error('combinations_not_vectors', 'All inputs must be vectors.') end - X = zeros(prod(sizeThisSet),numSets); - for i=1:size(X,1) - ixVect = cell(length(sizeThisSet),1); - sz = flip(sizeThisSet); - if length(sz) == 1 - sz = [sz,1]; - end - [ixVect{:}] = ind2sub(sz,i); - ixVect = flip([ixVect{:}]); - vect = zeros(1, numSets); - for jj=1:numSets - vect(jj) = varargin{jj}(ixVect(jj)); - end - X(i,:) = vect; + if ~isnumeric(thisSet), + nosnoc.error('cominations_not_numeric','All inputs must be numeric.') end + sizeThisSet(i) = length(thisSet); + varargin{i} = thisSet; +end +X = zeros(prod(sizeThisSet),numSets); +for i=1:size(X,1) + ixVect = cell(length(sizeThisSet),1); + sz = flip(sizeThisSet); + if length(sz) == 1 + sz = [sz,1]; + end + [ixVect{:}] = ind2sub(sz,i); + ixVect = flip([ixVect{:}]); + vect = zeros(1, numSets); + for jj=1:numSets + vect(jj) = varargin{jj}(ixVect(jj)); + end + X(i,:) = vect; +end end diff --git a/src/determine_multipliers_based_stationary_point.m b/src/determine_multipliers_based_stationary_point.m index 98711da..fd673c7 100644 --- a/src/determine_multipliers_based_stationary_point.m +++ b/src/determine_multipliers_based_stationary_point.m @@ -17,7 +17,8 @@ eta_x1_biactive = eta_x1(ind_I_00); eta_x2_biactive = eta_x2(ind_I_00); -max_mult = max(norm([eta_x1_biactive;eta_x2_biactive],inf), 1e-10); +max_mult = max(norm([eta_x1_biactive;eta_x2_biactive],inf), 1e-12); +% max_mult = 1; % Rescale to max (sensitive for M-stationarity) eta_x1_biactive = eta_x1_biactive./max_mult; diff --git a/src/lpec_solver.m b/src/lpec_solver.m index 926dd8a..67c1340 100644 --- a/src/lpec_solver.m +++ b/src/lpec_solver.m @@ -110,9 +110,13 @@ params.TimeLimit = settings.max_time; params.OptimalityTol = 1e-9; if settings.stop_lpec_at_feasible && settings.is_in_phase_i - % params.SolutionLimit = 1; + % terminate phase i lpecs at a feasible point params.MIPGap = 1; end + if settings.stop_lpec_at_descent && ~settings.is_in_phase_i + % terminate phase ii lpecs at a dscent direction + params.BestObjStop = -0.5*norm(f_lpec)*lpec.rho_TR; + end % params.ObjScale = -0.5; % https://www.gurobi.com/documentation/current/refman/objscale.html#parameter:ObjScale % params.ScaleFlag=0; % default -1, % https://www.gurobi.com/documentation/current/refman/scaleflag.html#parameter:ScaleFlag if settings.solve_lpec_with_cutoff diff --git a/src/mpec_homotopy_solver.m b/src/mpec_homotopy_solver.m index da607ec..edb603d 100644 --- a/src/mpec_homotopy_solver.m +++ b/src/mpec_homotopy_solver.m @@ -24,7 +24,11 @@ end %% homotopy parameters -sigma = SX.sym('sigma',1); +if strcmp(class(x),'casadi.SX') + sigma = SX.sym('sigma',1); +else + sigma = MX.sym('sigma',1); +end ii = 1; comp_res_ii = 1e3; p = [p;sigma]; @@ -55,7 +59,11 @@ % Lift complemetraties G if settings.lift_complementarities_full % full lifting with duplicatse; - x1 = SX.sym('x1',n_comp); + if strcmp(class(x),'casadi.SX') + x1 = SX.sym('x1',n_comp); + else + x1 = MX.sym('x1',n_comp); + end lbx = [lbx;0*ones(n_comp,1)]; ubx = [ubx;inf*ones(n_comp,1)]; x = [x;x1]; @@ -80,7 +88,11 @@ end end if n_lift_x1 > 0 - x1_lift = SX.sym('x1_lift',n_lift_x1); + if strcmp(class(x),'casadi.SX') + x1_lift = SX.sym('x1_lift',n_lift_x1); + else + x1_lift = MX.sym('x1_lift',n_lift_x1); + end lbx = [lbx;0*ones(n_lift_x1,1)]; ubx = [ubx;inf*ones(n_lift_x1 ,1)]; x = [x;x1_lift]; @@ -110,7 +122,11 @@ % Lift complemetraties H if settings.lift_complementarities_full % full lifting with duplicatse; - x2 = SX.sym('x2',n_comp); + if strcmp(class(x),'casadi.SX') + x2 = SX.sym('x2',n_comp); + else + x2 = MX.sym('x2',n_comp); + end lbx = [lbx;0*ones(n_comp,1)]; ubx = [ubx;inf*ones(n_comp,1)]; x = [x;x2]; @@ -136,7 +152,11 @@ end end if n_lift_x2 > 0 - x2_lift = SX.sym('x2_lift',n_lift_x2); + if strcmp(class(x),'casadi.SX') + x2_lift = SX.sym('x2_lift',n_lift_x2); + else + x2_lift = MX.sym('x2_lift',n_lift_x2); + end lbx = [lbx;0*ones(n_lift_x2,1)]; ubx = [ubx;inf*ones(n_lift_x2 ,1)]; x = [x;x2_lift]; @@ -290,9 +310,13 @@ dims.ind_x1 = ind_x1; dims.ind_x2 = ind_x2; dims.n_slacks = 0; - - M = SX.sym('M', 1); - y = SX.sym('y', dims.n_comp); % binary variablkes for comp. constraints + if strcmp(class(x),'casadi.SX') + M = SX.sym('M', 1); + y = SX.sym('y', dims.n_comp); % binary variablkes for comp. constraints + else + M = MX.sym('M', 1); + y = MX.sym('y', dims.n_comp); % binary variablkes for comp. constraints + end % Big M reformulation of complementarities A_lpec_sym = [-x1+M*y; -x2-M*y]; A_lpec = A_lpec_sym.jacobian([x;y]); @@ -356,7 +380,11 @@ ubg_comp = []; f = f+(x1'*x2)*(sigma)^(-1); case 'Ell_inf' - s_eleastic = SX.sym('s_eleastic',1); + if strcmp(class(x),'casadi.SX') + s_eleastic = SX.sym('s_eleastic',1); + else + s_eleastic = MX.sym('s_eleastic',1); + end f = f+(s_eleastic)*(sigma)^(-1); x = [x;s_eleastic]; lbx = [lbx;0]; From 57f0e411f2753bd0b6a053877721b5dd65a3c1ef Mon Sep 17 00:00:00 2001 From: "armin.nurkanovic" Date: Mon, 8 Sep 2025 14:19:25 +0200 Subject: [PATCH 08/31] preparing next set of large benchmarks --- .../histogram_on_lpecs.m | 169 ++-- benchmarks/macmpec/histogram_on_lpecs.m | 180 ---- benchmarks/macmpec/load_and_plot_results.m | 2 +- .../macmpec/mpec_benchmark_dtable_loop.m | 6 +- .../macmpec/run_macmpec_experiments_general.m | 72 +- .../run_macmpec_experiments_lpec_histogram.m | 6 +- .../macmpec/solve_single_macmpec_problem.m | 16 +- .../current_problem.txt | 1 + .../generate_nonlinear_mpec_problem_set.m | 931 ++++++++++-------- .../nonlinear_mpec_benchmark_dtable_loop.m | 19 +- .../run_nonlinear_mpec_benchmark_large.m | 126 ++- ..._nonlinear_mpec_benchmark_lpec_histogram.m | 94 +- .../nonlinear_mpec_benchmark/sparsity.m | 66 +- 13 files changed, 799 insertions(+), 889 deletions(-) rename benchmarks/{nonlinear_mpec_benchmark => }/histogram_on_lpecs.m (69%) delete mode 100644 benchmarks/macmpec/histogram_on_lpecs.m create mode 100644 benchmarks/nonlinear_mpec_benchmark/current_problem.txt diff --git a/benchmarks/nonlinear_mpec_benchmark/histogram_on_lpecs.m b/benchmarks/histogram_on_lpecs.m similarity index 69% rename from benchmarks/nonlinear_mpec_benchmark/histogram_on_lpecs.m rename to benchmarks/histogram_on_lpecs.m index 5448ba4..e10fa3f 100644 --- a/benchmarks/nonlinear_mpec_benchmark/histogram_on_lpecs.m +++ b/benchmarks/histogram_on_lpecs.m @@ -1,16 +1,23 @@ %% Evaluating LPEC MILP efficiency: close all; clc; -dtable; -lpec_dstruct; +% dtable; +% lpec_dstruct; + +S = load('macmpec_general_01-Sep-2025_lpec_details'); +lpec_dstruct = S.lpec_dstruct; +S = load('macmpec_general_01-Sep-2025'); +dtable = S.dtable; + plot_cpu = false; % Filter by solver and success -solver = "Gurobi"; -solver = "Gurobi-Early"; -solver_names = ["Gurobi", "Gurobi-early", "Highs", "Highs-early"]; -solver = solver_names{1} -idx = strcmp(dtable.solver_name, solver) & dtable.success == 1; +% solver_names = ["Gurobi", "Gurobi-early-I", "Gurobi-early-I-II", "Highs", "Highs-early"]; +solver_names = unique(dtable.solver_name); + +solver = solver_names{2}; +idx = (dtable.solver_name == solver) & dtable.success == 1; + % ------------------------ % Helper functions @@ -18,12 +25,12 @@ % Replace zeros by ones in a vector function y = replace_zeros(x) - if all(x == 0) - y = ones(size(x)); - else - x(x == 0) = 1; - y = x; - end +if all(x == 0) + y = ones(size(x)); +else + x(x == 0) = 1; + y = x; +end end % Postprocess nodecount cells @@ -44,17 +51,17 @@ % Conversion of raw vector (per problem, for iteration plots) function y = convert_vec(x) - if isempty(x) - y = 0; +if isempty(x) + y = 0; +else + if all(x == 0) + y = ones(size(x)); else - if all(x == 0) - y = ones(size(x)); - else - x(x == 0) = 1; - y = x; - end + x(x == 0) = 1; + y = x; end end +end % ------------------------ % Nodecount statistics (for each lpec call seperatalyover solver calls) @@ -63,23 +70,58 @@ nc_ii = cell2mat(process_cells(lpec_dstruct.nodecount_phase_ii(idx))); nc_tot = [nc_i, nc_ii]; -% Histograms (nodecounts) -figure; -subplot(131) -histogram(nc_i, 'BinMethod', 'integers'); -title('Nodecount Phase I - each LPEC call'); xlabel('Nodes'); ylabel('Frequency'); grid on; -% xticks(min(nc_i):max(nc_i)); - -subplot(132) -histogram(nc_ii, 'BinMethod', 'integers'); -title('Nodecount Phase II - each LPEC call'); xlabel('Nodes'); ylabel('Frequency'); grid on; -xticks(min(nc_ii):max(nc_ii)); - -subplot(133) -histogram(nc_tot, 'BinMethod', 'integers'); -title('Total Nodecount - each LPEC call'); xlabel('Nodes'); ylabel('Frequency'); grid on; -% xticks(min(nc_tot):max(nc_tot)); +figure; + +% --- Phase I --- +subplot(1,2,1) +h1 = histogram(nc_i, 'BinMethod', 'integers'); +title('Nodecount Phase I - each LPEC call'); +xlabel('Nodes'); ylabel('Frequency'); grid on; + +% annotate counts +for k = 1:numel(h1.BinEdges)-1 + if h1.Values(k) > 0 + x = (h1.BinEdges(k) + h1.BinEdges(k+1))/2; + y = h1.Values(k); + text(x, y, num2str(y), 'HorizontalAlignment','center','VerticalAlignment','bottom'); + end +end + +% adjust ylim +ymax1 = max(h1.Values); +ylim([0 ymax1 + max(2,ceil(0.2*ymax1))]) +% --- Phase II --- +subplot(1,2,2) +h2 = histogram(nc_ii, 'BinMethod', 'integers'); +title('Nodecount Phase II - each LPEC call'); +xlabel('Nodes'); ylabel('Frequency'); grid on; + +% enforce nicer axis if only one bar +if numel(unique(nc_ii)) == 1 + xlim([0 10]) +end + +% annotate counts +for k = 1:numel(h2.BinEdges)-1 + if h2.Values(k) > 0 + x = (h2.BinEdges(k) + h2.BinEdges(k+1))/2; + y = h2.Values(k); + text(x, y, num2str(y), 'HorizontalAlignment','center','VerticalAlignment','bottom'); + end +end + +% adjust ylim +ymax2 = max(h2.Values); +ylim([0 ymax2 + max(2,ceil(0.2*ymax2))]) + + +% xticks(min(nc_ii):max(nc_ii)); + +% subplot(133) +% histogram(nc_tot, 'BinMethod', 'integers'); +% title('Total Nodecount - each LPEC call'); xlabel('Nodes'); ylabel('Frequency'); grid on; +% xticks(min(nc_tot):max(nc_tot)); % ------------------------ @@ -89,25 +131,27 @@ nc_ii = process_cells_cummulative(lpec_dstruct.nodecount_phase_ii(idx)); nc_tot = nc_i + nc_ii; if 0 -% Histograms (nodecounts) -figure; -subplot(131) -histogram(nc_i, 'BinMethod', 'integers'); -title('Nodecount Phase I'); xlabel('Nodes'); ylabel('Frequency'); grid on; -% xticks(min(nc_i):max(nc_i)); - -subplot(132) -histogram(nc_ii, 'BinMethod', 'integers'); -title('Nodecount Phase II'); xlabel('Nodes'); ylabel('Frequency'); grid on; -xticks(min(nc_ii):max(nc_ii)); - -subplot(133) -histogram(nc_tot, 'BinMethod', 'integers'); -title('Total Nodecount'); xlabel('Nodes'); ylabel('Frequency'); grid on; -% xticks(min(nc_tot):max(nc_tot)); + % Histograms (nodecounts) + figure; + subplot(131) + histogram(nc_i, 'BinMethod', 'integers'); + title('Nodecount Phase I'); xlabel('Nodes'); ylabel('Frequency'); grid on; + % xticks(min(nc_i):max(nc_i)); + + subplot(132) + histogram(nc_ii, 'BinMethod', 'integers'); + title('Nodecount Phase II'); xlabel('Nodes'); ylabel('Frequency'); grid on; + % xticks(min(nc_ii):max(nc_ii)); + + subplot(133) + histogram(nc_tot, 'BinMethod', 'integers'); + title('Total Nodecount'); xlabel('Nodes'); ylabel('Frequency'); grid on; + % xticks(min(nc_tot):max(nc_tot)); end % Largest problem by total nodecount -[~, max_idx_rel] = max(nc_tot); +[sort_val, sort_idx_rel] = sort(nc_tot); +max_idx_rel = sort_idx_rel(end-1); +% [~, max_idx_rel] = max(nc_tot); max_idx_all = find(idx); % indices in original table max_idx = max_idx_all(max_idx_rel); @@ -138,9 +182,10 @@ end % ------------------------ +% max_idx = 14; %% Iteration plot for max problem (nodecounts) % ------------------------ -vec_i = cell2mat(process_cells(lpec_dstruct.nodecount_phase_i(max_idx))); +vec_i = cell2mat(process_cells(lpec_dstruct.nodecount_phase_i(max_idx))); vec_ii = cell2mat(process_cells(lpec_dstruct.nodecount_phase_ii(max_idx))); % vec_i = convert_vec(vec_i); % vec_ii = convert_vec(vec_ii); @@ -180,7 +225,6 @@ legend('show'); grid on; %% %% Iteration plot for max problem (nodecounts) -max_idx = 7 % ------------------------ vec_i = lpec_dstruct.nodecount_phase_i{max_idx}; vec_ii = lpec_dstruct.nodecount_phase_ii{max_idx}; @@ -193,8 +237,8 @@ % Create color array - blue for Phase I, orange for Phase II colors = [repmat([0 0.4470 0.7410], length(vec_i), 1); ... - repmat([0.8500 0.3250 0.0980], length(vec_ii), 1)]; - + repmat([0.8500 0.3250 0.0980], length(vec_ii), 1)]; +%% figure; subplot(121) b = bar(x_combined, combined_vec, 'FaceColor', 'flat'); @@ -216,6 +260,10 @@ % ------------------------ cpu_vec_i = lpec_dstruct.cpu_time_lpec_phase_i{max_idx}; cpu_vec_ii = lpec_dstruct.cpu_time_lpec_phase_ii{max_idx}; + +cpu_vec_i(cpu_vec_i==0) = []; +cpu_vec_ii(cpu_vec_ii==0) = []; + if isempty(cpu_vec_i), cpu_vec_i = 0; end if isempty(cpu_vec_ii), cpu_vec_ii = 0; end @@ -227,7 +275,7 @@ % Create color array for CPU plot cpu_colors = [repmat([0 0.4470 0.7410], length(cpu_vec_i), 1); ... - repmat([0.8500 0.3250 0.0980], length(cpu_vec_ii), 1)]; + repmat([0.8500 0.3250 0.0980], length(cpu_vec_ii), 1)]; subplot(122) b2 = bar(x_cpu_combined, combined_cpu, 'FaceColor', 'flat'); @@ -240,4 +288,5 @@ hold on; h3 = bar(NaN, NaN, 'FaceColor', [0 0.4470 0.7410]); h4 = bar(NaN, NaN, 'FaceColor', [0.8500 0.3250 0.0980]); -legend([h3, h4], {'Phase I', 'Phase II'}, 'Location', 'best'); \ No newline at end of file +legend([h3, h4], {'Phase I', 'Phase II'}, 'Location', 'best'); + diff --git a/benchmarks/macmpec/histogram_on_lpecs.m b/benchmarks/macmpec/histogram_on_lpecs.m deleted file mode 100644 index ce91e96..0000000 --- a/benchmarks/macmpec/histogram_on_lpecs.m +++ /dev/null @@ -1,180 +0,0 @@ -%% Evaluating LPEC MILP efficiency: -close all; clc; -% dtable; -% lpec_dstruct; - -S = load('macmpec_general_04-Sep-2025_lpec_details'); -lpec_dstruct = S.lpec_dstruct; -S = load('macmpec_general_04-Sep-2025'); -dtable = S.dtable; - -plot_cpu = false; -solver_names = ["Gurobi", "Gurobi-early", "Highs", "Highs-early"]; -solver = solver_names{2}; -idx = strcmp(dtable.solver_name, solver) & dtable.success == 1; - -% ------------------------ -% Helper functions -% ------------------------ - -% Replace zeros by ones in a vector -function y = replace_zeros(x) -if all(x == 0) - y = ones(size(x)); -else - x(x == 0) = 1; - y = x; -end -end - -% Postprocess nodecount cells -process_cells_cummulative = @(C) cellfun(@(x) ... - (isempty(x) * 0) + ... % empty → 0 - (~isempty(x) * (sum(replace_zeros(x)))), ... - C); - -process_cells = @(C) cellfun(@(x) ... - (isempty(x) * 0) + ... % empty → 0 - (~isempty(x) * ((replace_zeros(x)))), ... - C, UniformOutput=false); - -% Postprocess cpu time cells -process_cpu = @(C) cellfun(@(x) ... - (isempty(x) * 0) + (~isempty(x) * sum(x)), ... - C); - -% Conversion of raw vector (per problem, for iteration plots) -function y = convert_vec(x) -if isempty(x) - y = 0; -else - if all(x == 0) - y = ones(size(x)); - else - x(x == 0) = 1; - y = x; - end -end -end - -% ------------------------ -% Nodecount statistics (for each lpec call seperatalyover solver calls) -% ------------------------ -nc_i = cell2mat(process_cells(lpec_dstruct.nodecount_phase_i(idx))); -nc_ii = cell2mat(process_cells(lpec_dstruct.nodecount_phase_ii(idx))); -nc_tot = [nc_i, nc_ii]; - -% Histograms (nodecounts) -figure; -subplot(131) -histogram(nc_i, 'BinMethod', 'integers'); -title('Nodecount Phase I - each LPEC call'); xlabel('Nodes'); ylabel('Frequency'); grid on; -% xticks(min(nc_i):max(nc_i)); - -subplot(132) -histogram(nc_ii, 'BinMethod', 'integers'); -title('Nodecount Phase II - each LPEC call'); xlabel('Nodes'); ylabel('Frequency'); grid on; -xticks(min(nc_ii):max(nc_ii)); - -subplot(133) -histogram(nc_tot, 'BinMethod', 'integers'); -title('Total Nodecount - each LPEC call'); xlabel('Nodes'); ylabel('Frequency'); grid on; -% xticks(min(nc_tot):max(nc_tot)); - - -% ------------------------ -% Nodecount statistics (cummulative over solver calls) -% ------------------------ -nc_i = process_cells_cummulative(lpec_dstruct.nodecount_phase_i(idx)); -nc_ii = process_cells_cummulative(lpec_dstruct.nodecount_phase_ii(idx)); -nc_tot = nc_i + nc_ii; - -% Histograms (nodecounts) -figure; -subplot(131) -histogram(nc_i, 'BinMethod', 'integers'); -title('Nodecount Phase I'); xlabel('Nodes'); ylabel('Frequency'); grid on; -% xticks(min(nc_i):max(nc_i)); - -subplot(132) -histogram(nc_ii, 'BinMethod', 'integers'); -title('Nodecount Phase II'); xlabel('Nodes'); ylabel('Frequency'); grid on; -xticks(min(nc_ii):max(nc_ii)); - -subplot(133) -histogram(nc_tot, 'BinMethod', 'integers'); -title('Total Nodecount'); xlabel('Nodes'); ylabel('Frequency'); grid on; -% xticks(min(nc_tot):max(nc_tot)); - -% Largest problem by total nodecount -[~, max_idx_rel] = max(nc_tot); -max_idx_all = find(idx); % indices in original table -max_idx = max_idx_all(max_idx_rel); - -fprintf('Problem with largest total nodecount is %s with %d nodes.\n', ... - dtable.problem_name{max_idx}, nc_tot(max_idx_rel)); - -% ------------------------ -% CPU time statistics -% ------------------------ -cpu_i = process_cpu(lpec_dstruct.cpu_time_lpec_phase_i(idx)); -cpu_ii = process_cpu(lpec_dstruct.cpu_time_lpec_phase_ii(idx)); -cpu_tot = cpu_i + cpu_ii; - -% Histograms (CPU times) -if plot_cpu - figure; - subplot(131) - histogram(cpu_i); - title('CPU Time Phase I'); xlabel('Time [s]'); ylabel('Frequency'); grid on; - - subplot(132) - histogram(cpu_ii); - title('CPU Time Phase II'); xlabel('Time [s]'); ylabel('Frequency'); grid on; - - subplot(133) - histogram(cpu_tot); - title('Total CPU Time'); xlabel('Time [s]'); ylabel('Frequency'); grid on; -end - -% ------------------------ -%% Iteration plot for max problem (nodecounts) -% ------------------------ -vec_i = lpec_dstruct.nodecount_phase_i{max_idx}; -vec_ii = lpec_dstruct.nodecount_phase_ii{max_idx}; - -vec_i = convert_vec(vec_i); -vec_ii = convert_vec(vec_ii); - -x_i = 1:length(vec_i); -x_ii = (length(vec_i)+1):(length(vec_i)+length(vec_ii)); - -figure; -subplot(121) -stairs(x_i, vec_i, '-o', 'LineWidth', 1.5, 'DisplayName','Phase I'); hold on; -stairs(x_ii, vec_ii, '-s', 'LineWidth', 1.5, 'DisplayName','Phase II'); -ylim([0 (max([vec_i, vec_ii])+1)*1.1]) -xlabel('Iteration'); -ylabel('Nodecount'); -% title(sprintf('Nodecount per Iteration (%s)', dtable.problem_name{max_idx})); -legend('show'); grid on; - -% ------------------------ -% Iteration plot for max problem (CPU times) -% ------------------------ -cpu_vec_i = lpec_dstruct.cpu_time_lpec_phase_i{max_idx}; -cpu_vec_ii = lpec_dstruct.cpu_time_lpec_phase_ii{max_idx}; - -if isempty(cpu_vec_i), cpu_vec_i = 0; end -if isempty(cpu_vec_ii), cpu_vec_ii = 0; end - -x_ci = 1:length(cpu_vec_i); -x_cii = (length(cpu_vec_i)+1):(length(cpu_vec_i)+length(cpu_vec_ii)); - -subplot(122) -stairs(x_ci, cpu_vec_i, '-o', 'LineWidth', 1.5, 'DisplayName','Phase I'); hold on; -stairs(x_cii, cpu_vec_ii, '-s', 'LineWidth', 1.5, 'DisplayName','Phase II'); -xlabel('Iteration'); -ylabel('CPU Time [s]'); -title(sprintf('CPU Time per Iteration (%s)', dtable.problem_name{max_idx})); -legend('show'); grid on; diff --git a/benchmarks/macmpec/load_and_plot_results.m b/benchmarks/macmpec/load_and_plot_results.m index f0f7f8a..634ab8c 100644 --- a/benchmarks/macmpec/load_and_plot_results.m +++ b/benchmarks/macmpec/load_and_plot_results.m @@ -52,7 +52,7 @@ case 1 % S = load('macmpec_general_30-Oct-2024'); % S = load('macmpec_general_07-Nov-2024'); - S = load('macmpec_general_31-Aug-2025'); + S = load('macmpec_general_06-Sep-2025'); dtable = S.dtable; % solver_names = ["MPECopt-Gurobi", "MPECopt-HiGHS",... % "MPECopt-Simple", "Reg" , "Pen-$\ell_{\infty}$", "Pen-$\ell_{1}$"]; diff --git a/benchmarks/macmpec/mpec_benchmark_dtable_loop.m b/benchmarks/macmpec/mpec_benchmark_dtable_loop.m index 8b331ef..d575248 100644 --- a/benchmarks/macmpec/mpec_benchmark_dtable_loop.m +++ b/benchmarks/macmpec/mpec_benchmark_dtable_loop.m @@ -114,8 +114,8 @@ dstruct.n_lpec_total = [dstruct.n_lpec_total; stats.n_lpec_total]; dstruct.max_cpu_time_nlp = [dstruct.max_cpu_time_nlp; max([stats.iter.cpu_time_nlp_phase_i_iter(:);stats.iter.cpu_time_nlp_phase_ii_iter(:)])]; - dstruct.max_cpu_time_nlp_phase_i = [dstruct.max_cpu_time_nlp_phase_i; max(stats.iter.cpu_time_nlp_phase_i_iter)]; - dstruct.max_cpu_time_nlp_phase_ii = [dstruct.max_cpu_time_nlp_phase_ii; max(stats.iter.cpu_time_nlp_phase_ii_iter)]; + dstruct.max_cpu_time_nlp_phase_i = [dstruct.max_cpu_time_nlp_phase_i; max([stats.iter.cpu_time_nlp_phase_i_iter;0])]; + dstruct.max_cpu_time_nlp_phase_ii = [dstruct.max_cpu_time_nlp_phase_ii; max([stats.iter.cpu_time_nlp_phase_ii_iter,0])]; dstruct.cpu_time = [dstruct.cpu_time; stats.cpu_time_total]; dstruct.cpu_time_phase_i = [dstruct.cpu_time_phase_i; stats.cpu_time_phase_i]; dstruct.cpu_time_phase_ii = [dstruct.cpu_time_phase_ii; stats.cpu_time_phase_ii]; @@ -124,7 +124,7 @@ dstruct.cpu_time_nlp_phase_i = [dstruct.cpu_time_nlp_phase_i; stats.cpu_time_nlp_phase_i]; dstruct.cpu_time_nlp_phase_ii = [dstruct.cpu_time_nlp_phase_ii; stats.cpu_time_nlp_phase_ii]; - dstruct.max_cpu_time_lpec = [dstruct.max_cpu_time_lpec; max([stats.iter.cpu_time_lpec_phase_i_iter';stats.iter.cpu_time_lpec_phase_ii_iter'])]; + dstruct.max_cpu_time_lpec = [dstruct.max_cpu_time_lpec; max([stats.iter.cpu_time_lpec_phase_i_iter';stats.iter.cpu_time_lpec_phase_ii_iter';0])]; dstruct.cpu_time_lpec = [dstruct.cpu_time_lpec; stats.cpu_time_lpec]; dstruct.cpu_time_lpec_phase_i = [dstruct.cpu_time_lpec_phase_i; stats.cpu_time_lpec_phase_i]; dstruct.cpu_time_lpec_phase_ii = [dstruct.cpu_time_lpec_phase_ii; stats.cpu_time_lpec_phase_ii]; diff --git a/benchmarks/macmpec/run_macmpec_experiments_general.m b/benchmarks/macmpec/run_macmpec_experiments_general.m index e92f680..1f72d2f 100644 --- a/benchmarks/macmpec/run_macmpec_experiments_general.m +++ b/benchmarks/macmpec/run_macmpec_experiments_general.m @@ -5,7 +5,7 @@ %% Color order results_name = ['results/macmpec_general_' datestr(datetime("today"))]; % name for matlab .dat with results -filename = 'macmpec_general4'; % name for figures and excel table +filename = 'macmpec_general'; % name for figures and excel table %% Load macmpec macmpec_json = dir('macMPEC/*.json'); @@ -44,56 +44,47 @@ mpecs = [mpecs,mpec]; end -% N_interesting = []; -% for ii=1:length(macmpec_json) -% if mpecs(ii).n_w <= 150 -% N_interesting = [N_interesting; ii]; -% end -% end +N_interesting = []; +for ii=1:length(macmpec_json) + if mpecs(ii).n_w <= 100 + N_interesting = [N_interesting; ii]; + end +end -% mpecs = mpecs(N_interesting); +mpecs = mpecs(N_interesting); %% Define list of solvers to use -solver_names = ["MPECopt-Reg-Gurobi", "MPECopt-Reg-Gurobi-ET", "MPECopt-$\ell_1$-Gurobi", ... - "Reg" , "NLP", ... +solver_names = ["MPECopt-Reg-Gurobi", "MPECopt-$\ell_1$-Gurobi", "MPECopt-Reg-Highs" + "Reg", "NLP", ... "MINLP"]; - -% solver_names = ["MPECopt-Reg-Gurobi", "MPECopt-Reg-Early", "MPECopt-$\ell_1$-Gurobi", ... -% "Reg" , "NLP", ... -% "MINLP"]; - - solver_functions = {@mpec_optimizer,@mpec_optimizer,@mpec_optimizer,... @mpec_homotopy_solver,@mpec_homotopy_solver,... @mpec_minlp_solver}; -default_opts1 = mpecopt.Options(); -default_opts1.solver_name = solver_names{1}; -default_opts1.settings_lpec.lpec_solver = "Gurobi"; -default_opts1.relax_and_project_homotopy_parameter_steering = "Direct"; -% default_opts1.initialization_strategy = "FeasibilityEll1General"; +opts1 = mpecopt.Options(); +opts1.solver_name = solver_names{1}; +opts1.settings_lpec.lpec_solver = "Gurobi"; +opts1.relax_and_project_homotopy_parameter_steering = "Direct"; +opts1.use_one_nlp_solver = false; +% opts1.initialization_strategy = "FeasibilityEll1General"; -% default_opts2 = mpecopt.Options(); -% default_opts2.solver_name = solver_names{2}; -% default_opts2.settings_lpec.lpec_solver = "Highs_casadi"; -% default_opts2.settings_lpec.stop_lpec_at_feasible = true; -% default_opts2.relax_and_project_homotopy_parameter_steering = "Direct"; -% default_opts2.rho_TR_phase_i_init = 1e-3; -default_opts2 = mpecopt.Options(); -default_opts2.solver_name = solver_names{2}; -default_opts2.settings_lpec.lpec_solver = "Gurobi"; -default_opts2.relax_and_project_homotopy_parameter_steering = "Direct"; -default_opts2.settings_lpec.stop_lpec_at_feasible = true; -% default_opts2.rho_TR_phase_i_init = 1e-3; +opts2 = mpecopt.Options(); +opts2.solver_name = solver_names{2}; +opts2.settings_lpec.lpec_solver = "Gurobi"; +opts2.relax_and_project_homotopy_parameter_steering = "Ell_1"; +opts2.use_one_nlp_solver = false; +% opts2.settings_lpec.stop_lpec_at_feasible = true; +% opts2.rho_TR_phase_i_init = 1e-3; -default_opts3 = mpecopt.Options(); -default_opts3.solver_name = solver_names{3}; -default_opts3.settings_lpec.lpec_solver = "Gurobi"; -default_opts3.relax_and_project_homotopy_parameter_steering = "Ell_1"; +opts3 = mpecopt.Options(); +opts3.solver_name = solver_names{1}; +opts3.settings_lpec.lpec_solver = "Highs"; +opts3.relax_and_project_homotopy_parameter_steering = "Direct"; +opts3.use_one_nlp_solver = false; scholtes_opts1 = HomotopySolverOptions(); @@ -105,8 +96,9 @@ scholtes_opts2.sigma0 = 0; minlp_opts = MINLPSolverOptions(); +minlp_opts.settings_casadi_nlp.bonmin.time_limit = 600; -opts = {default_opts1, default_opts2, default_opts3, ... +opts = {opts1, opts2, opts3, ... scholtes_opts1, scholtes_opts2,... minlp_opts}; % list of options to pass to mpecsol (option structs) @@ -114,6 +106,8 @@ %% Create data struct % N_experiments = [1, 3:6]; N_experiments = [1:6]; +N_experiments = [1, 4, 6]; + mpec_benchmark_dtable_loop; % this script runs the experimetns, creates a dtable %% Pick which results to plot @@ -181,7 +175,7 @@ plot_settings.max_lpec_cpu_time = 0; plot_settings.stationary_points = 1; plot_settings.b_stationarity = 1; -plot_settings.b_stationarty_as_success_criterion = 1; +plot_settings.b_stationarty_as_success_criterion = 0; plot_settings.plot_only_sucessful = 1; plot_settings.bar_comparisson_plots = 0; diff --git a/benchmarks/macmpec/run_macmpec_experiments_lpec_histogram.m b/benchmarks/macmpec/run_macmpec_experiments_lpec_histogram.m index d9a8d1b..33f55cd 100644 --- a/benchmarks/macmpec/run_macmpec_experiments_lpec_histogram.m +++ b/benchmarks/macmpec/run_macmpec_experiments_lpec_histogram.m @@ -4,8 +4,8 @@ %% Color order -results_name = ['results/macmpec_general_' datestr(datetime("today"))]; % name for matlab .dat with results -filename = 'macmpec_general4'; % name for figures and excel table +results_name = ['results/macmpec_lpec_' datestr(datetime("today"))]; % name for matlab .dat with results +filename = 'macmpec_lpec'; % name for figures and excel table %% Load macmpec macmpec_json = dir('macMPEC/*.json'); @@ -89,7 +89,7 @@ %% Create data struct -N_experiments = [1:4]; +N_experiments = [1:2]; mpec_benchmark_dtable_loop; % this script runs the experimetns, creates a dtable %% Pick which results to plot diff --git a/benchmarks/macmpec/solve_single_macmpec_problem.m b/benchmarks/macmpec/solve_single_macmpec_problem.m index 9f07911..555c935 100644 --- a/benchmarks/macmpec/solve_single_macmpec_problem.m +++ b/benchmarks/macmpec/solve_single_macmpec_problem.m @@ -15,6 +15,7 @@ % problame_name = 'nash1a'; % problame_name = 'nash1c'; % problame_name = 'tap-09'; +problame_name = ' design-cent-31'; % problame_name = 'pack-rig2p-16'; @@ -32,8 +33,8 @@ ii_prob = find(contains({macmpec_json.name},problame_name)); % ii_prob = N_biactive(10); % A stationarity in reg! looks problematic! -ii_prob = N_biactive(9); -% ii_prob = 183; +% ii_prob = N_biactive(10); +% ii_prob = 17; fname = fullfile(macmpec_json(ii_prob).folder, macmpec_json(ii_prob).name); @@ -103,21 +104,24 @@ settings_minlp.settings_casadi_nlp.bonmin.node_limit = 5; settings_minlp.settings_casadi_nlp.bonmin.solution_limit = 5; settings_minlp.settings_casadi_nlp.bonmin.max_consecutive_failures = 5; -% [result_minlp,stats_minlp] = mpec_minlp_solver(mpec,solver_initalization,settings_minlp); +[result_minlp,stats_minlp] = mpec_minlp_solver(mpec,solver_initalization,settings_minlp); % f_opt_minlp = full(result_minlp.f); % w_opt_minlp = full(result_minlp.x); %% MPECopt solver solver_settings = mpecopt.Options(); solver_settings.relax_and_project_homotopy_parameter_steering = "Direct"; -solver_settings.settings_lpec.lpec_solver = "Highs"; -solver_settings.settings_casadi_nlp.ipopt.max_iter = 4000; +solver_settings.settings_lpec.lpec_solver = "Gurobi"; +% solver_settings.settings_casadi_nlp.ipopt.max_iter = 4000; solver_settings.settings_lpec.stop_lpec_at_feasible = true; % solver_settings.settings_casadi_nlp.ipopt.fixed_variable_treatment = 'relax_bounds'; % solver_settings.initialization_strategy = "FeasibilityEll1General"; % solver_settings.rho_TR_phase_i_init = 10; % solver_settings.tol_active = 1e-6; +solver_settings.use_one_nlp_solver = true; +tic solver = mpecopt.Solver(mpec, solver_settings); +toc [result_mpecopt,stats_mpecopt] = solver.solve(solver_initalization); % [solution,stats] = mpec_optimizer(mpec, solver_initalization, solver_settings); @@ -126,6 +130,8 @@ stats_mpecopt.iter.nodecount_phase_i stats_mpecopt.iter.nodecount_phase_ii +stats_mpecopt.iter.cpu_time_lpec_phase_i_iter +stats_mpecopt.iter.cpu_time_lpec_phase_ii_iter %% Results comparison fprintf('\n-------------------------------------------------------------------------------\n'); diff --git a/benchmarks/nonlinear_mpec_benchmark/current_problem.txt b/benchmarks/nonlinear_mpec_benchmark/current_problem.txt new file mode 100644 index 0000000..ddb8b13 --- /dev/null +++ b/benchmarks/nonlinear_mpec_benchmark/current_problem.txt @@ -0,0 +1 @@ +nonlinear_mpec2_EDENSCH_Var1840_Comp736_Eq736_Ineq538 diff --git a/benchmarks/nonlinear_mpec_benchmark/generate_nonlinear_mpec_problem_set.m b/benchmarks/nonlinear_mpec_benchmark/generate_nonlinear_mpec_problem_set.m index c5f7887..19c55f7 100644 --- a/benchmarks/nonlinear_mpec_benchmark/generate_nonlinear_mpec_problem_set.m +++ b/benchmarks/nonlinear_mpec_benchmark/generate_nonlinear_mpec_problem_set.m @@ -42,6 +42,15 @@ unfold_struct(settings,'caller') unfold_struct(dimensions,'caller') + +if ~isfield(settings,'casadi_variable_type') + casadi_variable_type = 'SX'; % default; +else + if ~ismember(settings.casadi_variable_type,{'SX','MX'}) + error('casadi_variable_type must be = ''SX'' or ''MX'' ') + end +end + eps_prec = 10^(-settings.n_digits); if settings.round_all_data @@ -56,46 +65,46 @@ latexify_plot(); N_problems = N_objectives*N_rand_prob; n_x_max = max(N_problems+n_x_min,n_x_max); - n_x_vec = n_x_min+randperm(n_x_max-n_x_min,N_problems); - n_y_vec = round(n_x_vec/n_fraction_of_x); + n_x_vec = n_x_min+randperm(n_x_max-n_x_min,N_problems); % num of non comp vars + n_y_vec = round(n_x_vec/n_fraction_of_x); % num of comp vars n_ineq_vec = round((settings.n_ineq_lb+(settings.n_ineq_ub-settings.n_ineq_lb)*(rand(1,N_problems)).*n_x_vec)); % n_ineq_vec = round((settings.n_ineq_lb+(settings.n_ineq_ub-settings.n_ineq_lb)*(rand(1,N_problems)).*n_x_vec))*3; - + N_per_problem = N_rand_prob; figure - n_var = n_x_vec+2*n_y_vec; - subplot(121) - scatter(n_var,n_ineq_vec*(1*settings.s_ineq_copy)); + n_var = n_x_vec+2*n_y_vec; % total num of vars, non comp, comp1 + comp2 + lift + scatter(n_var,n_ineq_vec*(1+settings.s_ineq_copy)); hold on scatter(n_var,n_x_vec); hold on - xlabel('$n+2m$ - number of variables') - ylabel('Number of constraints') - legend({'Inequality constraints','Equality constraints'},'Location','northwest') - axis equal - subplot(122) scatter(n_var,n_y_vec); - hold on - scatter(n_var,n_ineq_vec*(1+settings.s_ineq_copy)+n_x_vec); - scatter(n_var,n_x_vec); xlabel('$n+2m$ - number of variables') - legend({'$m$ number of complementairt variables','Total num. of constraints', 'Non. comp. vars.'},'Location','northwest') + ylabel('Number of constraints') + legend({'Inequality constraints','Equality constraints', 'Comp. constraints'},'Location','northwest') axis equal -% elseif settings.random_problem_sizes_individual -% n_x_vec = round(n_x_min+n_x_max*rand(N_rand_prob,1)); -% n_x_vec = sort(n_x_vec); -% n_y_vec = round(n_x_vec/n_fraction_of_x); -% n_ineq_vec = round((settings.n_ineq_lb+(settings.n_ineq_ub-settings.n_ineq_lb)*(rand(1,N_problems)).*n_x_vec)); -% -% n_x_vec = repmat(n_x_vec',1,N_objectives); -% n_y_vec = repmat(n_y_vec',1,N_objectives); -% n_ineq_vec = repmat(n_ineq_vec',1,N_objectives); -% -% % Vectors stored in dimensions, example for the format; -% % n_x_vec = [20,50,100]; -% % n_y_vec = [20,50,100]; -% % k_vec = [10,35,90]; -% N_per_problem = N_rand_prob; + % subplot(122) + % scatter(n_var,n_y_vec); + % hold on + % scatter(n_var,n_ineq_vec*(1+settings.s_ineq_copy)+n_x_vec); + % scatter(n_var,n_x_vec); + % xlabel('$n+2m$ - number of variables') + % legend({'$m$ number of complementairt variables','Total num. of constraints', 'Non. comp. vars.'},'Location','northwest') + % axis equal + % elseif settings.random_problem_sizes_individual + % n_x_vec = round(n_x_min+n_x_max*rand(N_rand_prob,1)); + % n_x_vec = sort(n_x_vec); + % n_y_vec = round(n_x_vec/n_fraction_of_x); + % n_ineq_vec = round((settings.n_ineq_lb+(settings.n_ineq_ub-settings.n_ineq_lb)*(rand(1,N_problems)).*n_x_vec)); + % + % n_x_vec = repmat(n_x_vec',1,N_objectives); + % n_y_vec = repmat(n_y_vec',1,N_objectives); + % n_ineq_vec = repmat(n_ineq_vec',1,N_objectives); + % + % % Vectors stored in dimensions, example for the format; + % % n_x_vec = [20,50,100]; + % % n_y_vec = [20,50,100]; + % % k_vec = [10,35,90]; + % N_per_problem = N_rand_prob; else N_per_problem = length(n_x_vec); n_x_vec = repmat(n_x_vec',1,N_objectives); @@ -103,6 +112,7 @@ n_ineq_vec = repmat(n_ineq_vec',1,N_objectives); end +fprintf('Largerst problem: n_var = %d, n_comp = %d \n',max(n_x_vec+2*n_y_vec), max(n_y_vec)) % range for feasible points range_x = [0,1]; range_y = [0,1]; @@ -141,421 +151,494 @@ for kk = 1:length(objective_functions) objective_type = objective_functions{kk}; for jj = 1:N_per_problem - % dimensions - % ii_jj = N_per_problem*(jj-1)+ii; - ii_kk = N_per_problem*(kk-1)+jj; - n_x = n_x_vec(ii_kk); - n_y = n_y_vec(ii_kk); - n_ineq = n_ineq_vec(ii_kk); - % Ax+By >= f; x is of n_x, y is of n_y, f is of n_ineq; - n = n_x+n_y+n_y; % total number of vairables [x,y,z] ; z = q+Nx+My; - n_comp = n_y; - n_non_lifted = n_x+n_y; - % store dimensions: - n_var_vec = [n_var_vec, n]; - n_comp_vec = [n_comp_vec, n_comp]; - % Define symbolic variables: + % dimensions + % ii_jj = N_per_problem*(jj-1)+ii; + ii_kk = N_per_problem*(kk-1)+jj; + n_x = n_x_vec(ii_kk); + n_y = n_y_vec(ii_kk); + n_ineq = n_ineq_vec(ii_kk); + % Ax+By >= f; x is of n_x, y is of n_y, f is of n_ineq; + n = n_x+n_y+n_y; % total number of vairables [x,y,z] ; z = q+Nx+My; + n_comp = n_y; + n_non_lifted = n_x+n_y; + % store dimensions: + n_var_vec = [n_var_vec, n]; + n_comp_vec = [n_comp_vec, n_comp]; + % Define symbolic variables: + if isequal(casadi_variable_type,'SX') x = SX.sym('x', n_x); y = SX.sym('y', n_y); z = SX.sym('z', n_y); - w = [x;y;z]; - v = [x;y]; - - if settings.include_lifted_variables_in_objective - n_obj = n_x+n_y+n_y; - else - n_obj = n_non_lifted; - end - - c = range_c(1)+(range_c(2)-range_c(1)).*rand(n_x,1); - d = range_d(1)+(range_d(2)-range_d(1)).*rand(n_y,1); - - - f_lin = [c;d]'*v; - f = alpha_lin*f_lin; - - if settings.variable_density - s_density_A_B = range_s_density(1)+(range_s_density(2)-range_s_density(1)).*rand(1); - end - - switch objective_type - case 'Linear' - % Objective - % c = range_c(1)+(range_c(2)-range_c(1)).*rand(n_x,1); - % d = range_x(1)+(range_x(2)-range_x(1)).*rand(n_y,1); - % f = f+[c;d]'*v; - - case 'Quadratic_psd' - % Quadratic - % ... generate sparse Hessian matrix H (and make sure it it psd if needed) - % Quadratic - H = full( sprandsym(n_obj, s_density_A_B) ); % ... full matrix for easier printing - H = round( H , n_digits_data); % ... round matrix to nDigits - min_eig = max( 0, -min( eig( H ) - eps_prec) ); % ... -epsPrec to ensure H is p.s.d. - H = H + 2*round( min_eig*eye(n_obj), n_digits); % ... make trailing matrix p.s.d. - grad_vec = round( range_grad(1) + rand(n_obj,1) * (range_grad(2) - range_grad(1)) , n_digits ); - f = 0.5*w(1:n_obj)'*H*w(1:n_obj)+grad_vec'*w(1:n_obj); - % f = 0.5*v'*H*v+grad_vec'*w(1:n_obj); % +[c;d]'*v; - case 'Quadratic_ind' - H = full( sprandsym(n_obj, s_density_A_B) ); - H = round( H , n_digits_data); - grad_vec = round( range_grad(1) + rand(n_obj,1) * (range_grad(2) - range_grad(1)) , n_digits ); - f = 0.5*w(1:n_obj)'*H*w(1:n_obj)+grad_vec'*w(1:n_obj); - % f = 0.5*w(1:n_obj)'*H*w(1:n_obj)+[c;d]'*v; - % f = 0.5*v'*H*v+grad_vec'*v; % +[c;d]'*v; - case 'Fletcher' - for i = 1:n_obj-1 - f = f+100*(w(i+1)-w(i)+1-w(i)^2)^2; - end - case 'Himmelblau' - for i = floor(n_obj/2) - f = f+(w(2*i-1)-w(2*i)-11)^2+(w(2*i-1)+w(2*i)^2-7)^2; - end + else + x = MX.sym('x', n_x); + y = MX.sym('y', n_y); + z = MX.sym('z', n_y); + end + w = [x;y;z]; + v = [x;y]; + + if settings.include_lifted_variables_in_objective + n_obj = n_x+n_y+n_y; + else + n_obj = n_non_lifted; + end + + c = range_c(1)+(range_c(2)-range_c(1)).*rand(n_x,1); + d = range_d(1)+(range_d(2)-range_d(1)).*rand(n_y,1); + + + f_lin = [c;d]'*v; + f = alpha_lin*f_lin; + + if settings.variable_density + s_density_A_B = range_s_density(1)+(range_s_density(2)-range_s_density(1)).*rand(1); + end + + switch objective_type + case 'Linear' + % Objective + % c = range_c(1)+(range_c(2)-range_c(1)).*rand(n_x,1); + % d = range_x(1)+(range_x(2)-range_x(1)).*rand(n_y,1); + % f = f+[c;d]'*v; + + case 'Quadratic_psd' + % Quadratic + % ... generate sparse Hessian matrix H (and make sure it it psd if needed) + % Quadratic + H = full( sprandsym(n_obj, s_density_A_B) ); % ... full matrix for easier printing + H = round( H , n_digits_data); % ... round matrix to nDigits + min_eig = max( 0, -min( eig( H ) - eps_prec) ); % ... -epsPrec to ensure H is p.s.d. + H = H + 2*round( min_eig*eye(n_obj), n_digits); % ... make trailing matrix p.s.d. + grad_vec = round( range_grad(1) + rand(n_obj,1) * (range_grad(2) - range_grad(1)) , n_digits ); + f = 0.5*w(1:n_obj)'*H*w(1:n_obj)+grad_vec'*w(1:n_obj); + % f = 0.5*v'*H*v+grad_vec'*w(1:n_obj); % +[c;d]'*v; + case 'Quadratic_ind' + H = full( sprandsym(n_obj, s_density_A_B) ); + H = round( H , n_digits_data); + grad_vec = round( range_grad(1) + rand(n_obj,1) * (range_grad(2) - range_grad(1)) , n_digits ); + f = 0.5*w(1:n_obj)'*H*w(1:n_obj)+grad_vec'*w(1:n_obj); + % f = 0.5*w(1:n_obj)'*H*w(1:n_obj)+[c;d]'*v; + % f = 0.5*v'*H*v+grad_vec'*v; % +[c;d]'*v; + case 'Fletcher' + for i = 1:n_obj-1 + f = f+100*(w(i+1)-w(i)+1-w(i)^2)^2; + end + case 'Himmelblau' + for i = floor(n_obj/2) + f = f+(w(2*i-1)-w(2*i)-11)^2+(w(2*i-1)+w(2*i)^2-7)^2; + end - case 'McCormick' - for i = 1:n_obj-1 - f = f+(-1.5*w(i)+2.5*w(i+1)+1+(w(i)-w(i+1))^2+sin(w(i)+w(i+1))); - end - case 'Powell' - for i = 1:floor(n_obj/4) - f = f+ ( (w(4*i-3)+10*w(4*i-2))^2 + 5*(w(4*i-1)-w(4*i))^2 + (w(4*i-2)-2*w(4*i-1))^4 + 10*(w(4*i-3)-w(4*i))^4) ; - end - case 'Trigonometric' - % for i = 1:n_obj-1 - % f = f+(-1.5*cos(w(i))+2.5*tanh(w(i+1)/0.1)+1+(w(i)-w(i+1))^4+exp(w(i)+w(i+1))); - % end - for i = 1:n_obj - f = f+ ((n_obj-sum(cos(w))) + i*(1-cos(w(i)))-sin(w(i)))^2; - end - case 'Rosenbrock' - for i = 1:n_obj-1 - f = f+100*(w(i+1)-w(i)^2)^2+(1-w(i))^2; - end - case 'Raydan1' - for i = 1:n_obj - f = f+i/10*(exp(w(i))-w(i)); - end - case 'Raydan2' - for i = 1:n_obj - f = f+(exp(w(i))-w(i)); - end + case 'McCormick' + for i = 1:n_obj-1 + f = f+(-1.5*w(i)+2.5*w(i+1)+1+(w(i)-w(i+1))^2+sin(w(i)+w(i+1))); + end + case 'Powell' + for i = 1:floor(n_obj/4) + f = f+ ( (w(4*i-3)+10*w(4*i-2))^2 + 5*(w(4*i-1)-w(4*i))^2 + (w(4*i-2)-2*w(4*i-1))^4 + 10*(w(4*i-3)-w(4*i))^4) ; + end + case 'Trigonometric' + % for i = 1:n_obj-1 + % f = f+(-1.5*cos(w(i))+2.5*tanh(w(i+1)/0.1)+1+(w(i)-w(i+1))^4+exp(w(i)+w(i+1))); + % end + for i = 1:n_obj + f = f+ ((n_obj-sum(cos(w))) + i*(1-cos(w(i)))-sin(w(i)))^2; + end + case 'Rosenbrock' + for i = 1:n_obj-1 + f = f+100*(w(i+1)-w(i)^2)^2+(1-w(i))^2; + end + case 'Raydan1' + for i = 1:n_obj + f = f+i/10*(exp(w(i))-w(i)); + end + case 'Raydan2' + for i = 1:n_obj + f = f+(exp(w(i))-w(i)); + end - case 'Diagonal3' - for i = 1:n_obj - f = f+(exp(w(i))-i*sin(w(i))); - end - case 'Diagonal4' - for i = 1:floor(n_obj/2) - f = f+0.5*(w(2*i-1)^2+100*w(2*i)^2); - end - case 'Diagonal5' - for i = 1:n_obj - f = f+log(exp(w(i))-exp(-w(i))); - end - case 'Extended_Trigiaonal' - for i = 1:floor(n_obj/2) - f = f+(w(2*i-1)+w(2*i)-3)^2+(w(2*i-1)-w(2*i)-3)^4; - end - case 'Three_Exponential_Terms' - for i = 1:floor(n_obj/2) - f = f+exp(w(2*i-1)+3*w(2*i)-0.1)+exp(w(2*i-1)-3*w(2*i)-0.1)+exp(-w(2*i-1)-0.1); - end - case 'Generalized_PSC1' - for i = 1:n_obj-1 - f = f+(w(i)^2+w(i+1)^2+w(i)*w(i+1))^2+sin(w(i))^2+cos(w(i))^2; - end - case 'Extended_PSC1' - for i = 1:floor(n_obj/2) - f = f+(w(2*i-1)^2+w(2*i)^2+w(2*i-1)*w(2*i))^2+sin(w(2*i-1))^2+cos(w(2*i))^2; - end - case 'Fletcvb3' - p = 1/1e8; - c = 1; - f = f+ 0.5*p*(w(1)^2+w(end)^2); - for i = 1:n_obj-1 - f = f+0.5*p*(w(i)-w(i+1))^2; - end - for i = 1:n_obj - h = 1/(i+1); - f = f-(((p*(h^2+2))/h^2)*w(i)+(c*p/h^2)*cos(w(i))); - end - case 'Bdqrtic' - for i = 1:n_obj-4 - f = f+ (-4*w(i)+3)^2+(w(i)^2+2*w(i+1)^2+3*w(i+2)^2+4*w(i+3)^2+5*w(i+4)^2); - end - case 'Tridia' - f = f+(w(1)-1)^2; - for i = 2:n_obj - f = f+ i*(2*w(i)-w(i-1))^2; - end - case 'EG2' - for i = 1:n_obj-1 - f = f+ sin(w(i)+w(i)^2-1)+0.5*sin(w(i)^2); - end - case 'Edensch' - f = f+16; - for i = 1:n_obj-1 - f = f+ (w(i)-2)^4+(w(i)*w(i+1)-2*w(i+1))^2+(w(i+1)+1)^2; - end - case 'Indef' - f = f+sum(w); - for i = 2:n_obj-1 - f = f+ 0.5*cos(2*w(i)-w(end)-w(1)); - end - case 'Cube' - f = f+(w(1)-1)^2; - for i = 2:n_obj - f = f+ 100*(w(i)-w(i-1)^3)^2; - end - case 'Bdexp' - for i = n_obj-2 - f = f+(w(i)-w(i+1))*exp(-w(i+2)*(w(i)+w(i+1))); - end - case 'Genhumps' - for i = n_obj-1 - f = f+sin(2*w(i))^2*sin(2*w(i+1))^2+0.05*(w(i)^2+w(i+1)^2); - end - case 'Arwhead' - for i = n_obj-1 - f = f + (-4*w(i)+3)+(w(i)^2+w(end)^2)^2; - end - case 'Quartc' - for i = n_obj - f = f + (w(i)-1)^4; - end - case 'Cosine' - for i = n_obj-1 - f = f + cos(-0.5*w(i+1)+w(i)^2); - end - case 'Sine' - for i = n_obj-1 - f = f + sin(-0.5*w(i+1)+w(i)^2); - end + case 'Diagonal3' + for i = 1:n_obj + f = f+(exp(w(i))-i*sin(w(i)))/1e3; + end + case 'Diagonal4' + for i = 1:floor(n_obj/2) + f = f+0.5*(w(2*i-1)^2+100*w(2*i)^2); + end + case 'Diagonal5' + for i = 1:n_obj + f = f+log(exp(w(i))-exp(-w(i))); + end + case 'Extended_Trigiaonal' + for i = 1:floor(n_obj/2) + f = f+(w(2*i-1)+w(2*i)-3)^2+(w(2*i-1)-w(2*i)-3)^4; + end + case 'Three_Exponential_Terms' + for i = 1:floor(n_obj/2) + f = f+exp(w(2*i-1)+3*w(2*i)-0.1)+exp(w(2*i-1)-3*w(2*i)-0.1)+exp(-w(2*i-1)-0.1); + end + case 'Generalized_PSC1' + for i = 1:n_obj-1 + f = f+(w(i)^2+w(i+1)^2+w(i)*w(i+1))^2+sin(w(i))^2+cos(w(i))^2; + end + case 'Extended_PSC1' + for i = 1:floor(n_obj/2) + f = f+(w(2*i-1)^2+w(2*i)^2+w(2*i-1)*w(2*i))^2+sin(w(2*i-1))^2+cos(w(2*i))^2; + end + case 'Fletcvb3' + p = 1/1e8; + c = 1; + f = f+ 0.5*p*(w(1)^2+w(end)^2); + for i = 1:n_obj-1 + f = f+0.5*p*(w(i)-w(i+1))^2; + end + for i = 1:n_obj + h = 1/(i+1); + f = f-(((p*(h^2+2))/h^2)*w(i)+(c*p/h^2)*cos(w(i))); + end + case 'Bdqrtic' + for i = 1:n_obj-4 + f = f+ (-4*w(i)+3)^2+(w(i)^2+2*w(i+1)^2+3*w(i+2)^2+4*w(i+3)^2+5*w(i+4)^2); + end + case 'Tridia' + f = f+(w(1)-1)^2; + for i = 2:n_obj + f = f+ i*(2*w(i)-w(i-1))^2; + end + case 'EG2' + for i = 1:n_obj-1 + f = f+ sin(w(i)+w(i)^2-1)+0.5*sin(w(i)^2); + end + case 'Edensch' + f = f+16; + for i = 1:n_obj-1 + f = f+ (w(i)-2)^4+(w(i)*w(i+1)-2*w(i+1))^2+(w(i+1)+1)^2; + end + case 'Indef' + f = f+sum(w); + for i = 2:n_obj-1 + f = f+ 0.5*cos(2*w(i)-w(end)-w(1)); + end + case 'Cube' + f = f+(w(1)-1)^2; + for i = 2:n_obj + f = f+ 100*(w(i)-w(i-1)^3)^2; + end + case 'Bdexp' + for i = n_obj-2 + f = f+(w(i)-w(i+1))*exp(-w(i+2)*(w(i)+w(i+1))); + end + case 'Genhumps' + for i = n_obj-1 + f = f+sin(2*w(i))^2*sin(2*w(i+1))^2+0.05*(w(i)^2+w(i+1)^2); + end + case 'Arwhead' + for i = n_obj-1 + f = f + (-4*w(i)+3)+(w(i)^2+w(end)^2)^2; + end + case 'Quartc' + for i = n_obj + f = f + (w(i)-1)^4; + end + case 'Cosine' + for i = n_obj-1 + f = f + cos(-0.5*w(i+1)+w(i)^2); + end + case 'Sine' + for i = n_obj-1 + f = f + sin(-0.5*w(i+1)+w(i)^2); + end % last four are from the cutest https://ccom.ucsd.edu/~optimizers/cutest/problems/ - case 'LUKVLE10' - f = 0; - for i = 1:(n_obj/2) - xi = w(2*i-1); - yi = w(2*i); - f = f + (xi^2)^(yi^2 + 1) + (yi^2)^(xi^2 + 1); - end - case 'CURLY20' - K = 20; - f = 0; - for i = 1:n_obj - if i <= n_obj-K - q = sum(w(i:(i+K))); - else - q = sum(w(i:n_obj)); - end - f = f + q * (q * (q^2 - 20) - 0.1); - end - case 'SCURLY30' + case 'LUKVLE10' + f = 0; + for i = 1:(n_obj/2) + xi = w(2*i-1); + yi = w(2*i); + f = f + (xi^2)^(yi^2 + 1) + (yi^2)^(xi^2 + 1); + end + case 'CURLY20' + K = 20; + f = 0; + for i = 1:n_obj + if i <= n_obj-K + q = sum(w(i:(i+K))); + else + q = sum(w(i:n_obj)); + end + f = f + q * (q * (q^2 - 20) - 0.1); + end + case 'SCURLY30' + if n_obj > 30 K = 30; - SCAL = 12.0; - f = 0; - % scale factors as column vector - S = exp(((0:(n_obj-1))/(n_obj-1)) * SCAL)'; - for i = 1:n_obj - if i <= n_obj-K - q = sum(S(i:(i+K)) .* w(i:(i+K))); - else - q = sum(S(i:end) .* w(i:end)); - end - f = f + q * (q * (q^2 - 20) - 0.1); - end - case 'FLETBV3M' - f = 0; - for i = 1:n_obj - % finite-difference like group (difference with next) - if i < n_obj - q = w(i+1) - w(i); - else - q = w(i); - end - % element contribution (scaled sin/cos) - f = f + 0.5 * q^2 + 100*sin(0.01*w(i)) + 1e8*cos(w(i)); - end - - - % case 'Hager' - %very similar to Raydan 2 - % for i = 1:n_obj-1 - % f = f+(exp(w(i))-sqrt(i)*w(i)); - % end - % case 'Diagonal1' - % %very similar to Raydan 2 - % for i = 1:n_obj - % f = f+(exp(w(i))-i*w(i)); - % end - % case 'Diagonal2' - % very similar to Raydan 1 - % for i = 1:n_obj-1 - % f = f+(exp(w(i))-w(i)/i); - % end - end - - % Generate problem matrices - if settings.inequality_constraints - % Ax + By >= f - A = (range_A(2)-range_A(1))*sprand(n_ineq,n_x,s_density_A_B); - A(A~=0) = range_A(1)+A(A~=0); - A = round(A,n_digits_data); - if settings.inequality_constraints_coupling_terms - B = (range_B(2)-range_B(1))*sprand(n_ineq,n_y,s_density_A_B); - B(B~=0) = range_B(1)+B(B~=0); - B = round(B,n_digits_data); else - B = zeros(n_ineq,n_y); + K = n_obj; end - end - r = round(1+(n_y-1)*rand(1)); % pick number between - % s_density_M = (n_non_zero_E-n_y)/n_y^2; - if settings.variable_density - s_density_M = range_s_density(1)+(range_s_density(2)-range_s_density(1)).*rand(1); - else - s_density_M = settings.s_density_M; - end - % s_density_M = settings.s_density_M; + % SCAL = 12.0; + SCAL = 0.01; + f = 0; + % scale factors as column vector + S = exp(((0:(n_obj-1))/(n_obj-1)) * SCAL)'; + for i = 1:n_obj + if i <= n_obj-K + q = sum(S(i:(i+K)) .* w(i:(i+K))); + % else + % q = sum(S(i:end) .* w(i:end)); + end + f = f + q * (q * (q^2 - 20) - 0.1); + end + case 'FLETBV3M' + f = 0; + for i = 1:n_obj + % finite-difference like group (difference with next) + if i < n_obj + q = w(i+1) - w(i); + else + q = w(i); + end + % element contribution (scaled sin/cos) + f = f + 0.5 * q^2 + 100*sin(0.01*w(i)) + 1e2*cos(w(i)); + end + case 'NCVXQP6' - E = (range_E(2)-range_E(1))*sprand(r,n_y-r,s_density_M); - E(E~=0) = range_E(1)+E(E~=0); + % number of positive/negative rank-one terms + nplus = floor(0.75 * n_obj); - d1 = range_d1(1)+(range_d1(2)-range_d1(1)).*rand(r,1); - d2 = range_d2(1)+(range_d2(2)-range_d2(1)).*rand(n_y-r,1); - D1 = diag(d1); - D2 = diag(d2); - if settings.symmetric_psd_M_matrix - M = [D1 E;... - E' D2]; - [V,D] = eig(full(M)); - M = V*abs(D)*V'; - else - M = [D1 E;... - -E' D2]; - end - % M = (range_A(2)+1)*sprand(n_y,n_y,s_density_A_B); - % M (M ~=0) = -1+M(M~=0); - - M = round(M,n_digits_data); - - % N = sprand(n_y,n_x,s_density); - % r = a + (b-a).*rand(100,1); - N = (range_N(2)-range_N(1))*sprand(n_y,n_x,s_density_A_B); - N(N~=0) = range_N(1)+N(N~=0); - N = round(N,n_digits_data); - - q = range_q(1)+(range_q(2)-range_q(1))*rand(n_y,1); - q = round(q,n_digits_data); - - % feasible_ineq; - if settings.inequality_constraints - if settings.use_normal_distributions_for_feasible_point - x_bar = abs(normrnd(0,1,[n_x 1])); - % x_bar(x_bar<1) = 0; - y_bar = max(0,normrnd(0,1,[n_y 1])); - % y_bar = max(0,normrnd(-0.5,1,[n_y 1])); - else - x_bar = range_x(1)+(range_x(2)-range_x(1)).*rand(n_x,1); - y_bar = range_y(1)+(range_y(2)-range_y(1)).*rand(n_y,1); - end - f_ineq_eps = abs(normrnd(0,1,[n_ineq, 1])); - % f_ineq_eps = 0; - f_ineq = A*x_bar+B*y_bar-f_ineq_eps; - f_ineq = round(f_ineq,n_digits_data); - end - % Inequality constraints - if settings.inequality_constraints - g_ineq = A*x+B*y-f_ineq; - lbg_ineq = zeros(n_ineq,1); - ubg_ineq = inf*ones(n_ineq,1); - else - g_ineq = []; - lbg_ineq = []; - ubg_ineq = []; - end + % Objective: quadratic form with sign-flipped rank-one terms + f = 0; + for i = 1:n_obj + % each group uses w(i), w(mod(2i-1,n)+1), w(mod(3i-1,n)+1) + j = mod(2*i-1, n_obj) + 1; + k = mod(3*i-1, n_obj) + 1; - if nonlinear_ineq_constraints && ~strcmp(objective_type,'Linear') && ~strcmp(objective_type,'Quadratic_psd') && ~strcmp(objective_type,'Quadratic_ind') - ind_nonlinear_ineq = sort(randperm(n_ineq,round(settings.s_nonlinear_ineq*n_ineq))); - if ~isempty(ind_nonlinear_ineq) - g_ineq(ind_nonlinear_ineq) = g_ineq(ind_nonlinear_ineq)+g_ineq(ind_nonlinear_ineq).^2+g_ineq(ind_nonlinear_ineq).^4; - end - end - % - if settings.copy_ineq - ind_ineq_copy = sort(randperm(n_ineq,round(settings.s_ineq_copy*n_ineq))); - g_ineq = [g_ineq;g_ineq(ind_ineq_copy)]; - lbg_ineq = [lbg_ineq;lbg_ineq(ind_ineq_copy)]; - ubg_ineq = [ubg_ineq;ubg_ineq(ind_ineq_copy)]; - end + alpha = w(i) + w(j) + w(k); - % Equality constraints (lifted complementarity constraints) - g_eq = N*x+M*y-z; - if nonlinear_eq_constraints && ~strcmp(objective_type,'Linear') && ~strcmp(objective_type,'Quadratic_psd') && ~strcmp(objective_type,'Quadratic_ind') - ind_nonlinear_eq = sort(randperm(n_y,round(settings.s_nonlinear_eq*n_y))); - if ~isempty(ind_nonlinear_eq) - g_eq(ind_nonlinear_eq) = g_eq(ind_nonlinear_eq)+g_eq(ind_nonlinear_eq).^2+g_eq(ind_nonlinear_eq).^4; + if i <= nplus + f = f + 0.5 * (alpha^2); % positive term + else + f = f - 0.5 * (alpha^2); % negative term + end + end + case 'DIXCHLNV' + % w = decision vector of length n_obj + % SIF requires n_obj even and >= 4 + f = 0; + for i = 1:(n_obj-3) + % elements (from SIF): + % XSQ(k) = w(k)^2 for k = 1..n-1 + xsq_i = w(i)^2; % XSQ(i) + xsq_ip1 = w(i+1)^2; % XSQ(i+1) + xsq_ip2 = w(i+2)^2; % XSQ(i+2) + xsq_ip3 = w(i+3)^2; % XSQ(i+3) + % PR(i) = (w(i+1)-1)*(w(i+3)-1) + pr_i = (w(i+1)-1)*(w(i+3)-1); + + % group A: scale 0.01, uses -XSQ(i) -> (0.01 * (-xsq_i))^2 + f = f + (0.01 * (-xsq_i))^2; + + % group B: (unscaled) use xsq_i -> (xsq_i)^2 + f = f + (xsq_i)^2; + + % group C: scale 1/90 on x(i+3) element -> ((1/90)*xsq_ip3)^2 + f = f + ((1/90.0) * xsq_ip3)^2; + + % group D: use xsq(i+2) + f = f + (xsq_ip2)^2; + + % group E: scale 1/10.1 on x(i+1) -> ((1/10.1)*xsq_ip1)^2 + f = f + ((1/10.1) * xsq_ip1)^2; + + % group F: scale 1/10.1 on x(i+3) -> ((1/10.1)*xsq_ip3)^2 + f = f + ((1/10.1) * xsq_ip3)^2; + + % group G: scale 1/19.8 on PR(i) -> ((1/19.8)*pr_i)^2 + f = f + ((1/19.8) * pr_i)^2; + end + case 'EDENSCH' + for i = 1:n_obj-1 + r = w(i)*w(i+1); % pairwise product + f = f + r^2 + r^4; % L2 + L4 contributions end - end - lbg_eq = zeros(n_y,1); - ubg_eq = zeros(n_y,1); - - % Copy equality constraints - if settings.copy_eq - ind_eq_copy = sort(randperm(n_y,round(settings.s_eq_copy*n_y))); - g_eq = [g_eq;g_eq(ind_eq_copy)]; - lbg_eq = [lbg_eq;lbg_eq(ind_eq_copy)]; - ubg_eq = [ubg_eq;ubg_eq(ind_eq_copy)]; - end - - - n_eq = length(g_eq); - n_ineq = length(g_ineq); - lbw = [zeros(n_x+n_y,1);-inf*ones(n_y,1)]; - if bounded_w - ubw = range_ubw(1)+(range_ubw(2)-range_ubw(1)).*rand(n_x+n_y,1); - ubw = round(ubw,n_digits_data); - ubw = [ubw;inf*ones(n_y,1)]; + % case 'Hager' + %very similar to Raydan 2 + % for i = 1:n_obj-1 + % f = f+(exp(w(i))-sqrt(i)*w(i)); + % end + % case 'Diagonal1' + % %very similar to Raydan 2 + % for i = 1:n_obj + % f = f+(exp(w(i))-i*w(i)); + % end + % case 'Diagonal2' + % very similar to Raydan 1 + % for i = 1:n_obj-1 + % f = f+(exp(w(i))-w(i)/i); + % end + end + + % Generate problem matrices + if settings.inequality_constraints + % Ax + By >= f + A = (range_A(2)-range_A(1))*sprand(n_ineq,n_x,s_density_A_B); + A(A~=0) = range_A(1)+A(A~=0); + A = round(A,n_digits_data); + if settings.inequality_constraints_coupling_terms + B = (range_B(2)-range_B(1))*sprand(n_ineq,n_y,s_density_A_B); + B(B~=0) = range_B(1)+B(B~=0); + B = round(B,n_digits_data); else - ubw = inf*ones(n,1); + B = zeros(n_ineq,n_y); + end + end + + r = round(1+(n_y-1)*rand(1)); % pick number between + % s_density_M = (n_non_zero_E-n_y)/n_y^2; + if settings.variable_density + s_density_M = range_s_density(1)+(range_s_density(2)-range_s_density(1)).*rand(1); + else + s_density_M = settings.s_density_M; + end + % s_density_M = settings.s_density_M; + + + E = (range_E(2)-range_E(1))*sprand(r,n_y-r,s_density_M); + E(E~=0) = range_E(1)+E(E~=0); + + d1 = range_d1(1)+(range_d1(2)-range_d1(1)).*rand(r,1); + d2 = range_d2(1)+(range_d2(2)-range_d2(1)).*rand(n_y-r,1); + D1 = diag(d1); + D2 = diag(d2); + if settings.symmetric_psd_M_matrix + M = [D1 E;... + E' D2]; + [V,D] = eig(full(M)); + M = V*abs(D)*V'; + else + M = [D1 E;... + -E' D2]; + end + % M = (range_A(2)+1)*sprand(n_y,n_y,s_density_A_B); + % M (M ~=0) = -1+M(M~=0); + + M = round(M,n_digits_data); + + % N = sprand(n_y,n_x,s_density); + % r = a + (b-a).*rand(100,1); + N = (range_N(2)-range_N(1))*sprand(n_y,n_x,s_density_A_B); + N(N~=0) = range_N(1)+N(N~=0); + N = round(N,n_digits_data); + + q = range_q(1)+(range_q(2)-range_q(1))*rand(n_y,1); + q = round(q,n_digits_data); + + % feasible_ineq; + if settings.inequality_constraints + if settings.use_normal_distributions_for_feasible_point + x_bar = abs(normrnd(0,1,[n_x 1])); + % x_bar(x_bar<1) = 0; + y_bar = max(0,normrnd(0,1,[n_y 1])); + % y_bar = max(0,normrnd(-0.5,1,[n_y 1])); + else + x_bar = range_x(1)+(range_x(2)-range_x(1)).*rand(n_x,1); + y_bar = range_y(1)+(range_y(2)-range_y(1)).*rand(n_y,1); + end + f_ineq_eps = abs(normrnd(0,1,[n_ineq, 1])); + % f_ineq_eps = 0; + f_ineq = A*x_bar+B*y_bar-f_ineq_eps; + f_ineq = round(f_ineq,n_digits_data); + end + % Inequality constraints + if settings.inequality_constraints + g_ineq = A*x+B*y-f_ineq; + lbg_ineq = zeros(n_ineq,1); + ubg_ineq = inf*ones(n_ineq,1); + else + g_ineq = []; + lbg_ineq = []; + ubg_ineq = []; + end + + if nonlinear_ineq_constraints && ~strcmp(objective_type,'Linear') && ~strcmp(objective_type,'Quadratic_psd') && ~strcmp(objective_type,'Quadratic_ind') + ind_nonlinear_ineq = sort(randperm(n_ineq,round(settings.s_nonlinear_ineq*n_ineq))); + if ~isempty(ind_nonlinear_ineq) + g_ineq(ind_nonlinear_ineq) = g_ineq(ind_nonlinear_ineq)+g_ineq(ind_nonlinear_ineq).^2+g_ineq(ind_nonlinear_ineq).^4; + end + end + % + if settings.copy_ineq + ind_ineq_copy = sort(randperm(n_ineq,round(settings.s_ineq_copy*n_ineq))); + g_ineq = [g_ineq;g_ineq(ind_ineq_copy)]; + lbg_ineq = [lbg_ineq;lbg_ineq(ind_ineq_copy)]; + ubg_ineq = [ubg_ineq;ubg_ineq(ind_ineq_copy)]; + end + + % Equality constraints (lifted complementarity constraints) + g_eq = N*x+M*y-z; + if nonlinear_eq_constraints && ~strcmp(objective_type,'Linear') && ~strcmp(objective_type,'Quadratic_psd') && ~strcmp(objective_type,'Quadratic_ind') + ind_nonlinear_eq = sort(randperm(n_y,round(settings.s_nonlinear_eq*n_y))); + if ~isempty(ind_nonlinear_eq) + g_eq(ind_nonlinear_eq) = g_eq(ind_nonlinear_eq)+g_eq(ind_nonlinear_eq).^2+g_eq(ind_nonlinear_eq).^4; end - % create problem functions - % f = f; - G = y; - H = z; - g = [g_eq;g_ineq]; - lbg = [lbg_eq;lbg_ineq]; - ubg = [ubg_eq;ubg_ineq]; - - G_fun = Function('G_fun',{w},{G}); - H_fun = Function('H_fun',{w},{H}); - f_fun = Function('f_fun',{w},{f}); - g_fun = Function('g_fun',{w},{g}); - - name = [problem_set_name '_' objective_type '_Var' num2str(n) '_Comp' num2str(n_comp) '_Eq' num2str(n_eq) '_Ineq' num2str(n_ineq)]; - w0 = [x_bar;y_bar;q+N*x_bar+M*y_bar]; - % save problem - mpec.w = w; - mpec.f_fun = f_fun; - mpec.g_fun = g_fun; - mpec.G_fun = G_fun; - mpec.H_fun = H_fun; - mpec.f = f; - mpec.g = g; - mpec.G = G; - mpec.H = H; - mpec.w0 = w0; - mpec.lbw = lbw; - mpec.ubw = ubw; - mpec.lbg = lbg; - mpec.ubg = ubg; - mpec.name = name; - mpecs = [mpecs, mpec]; + end + lbg_eq = zeros(n_y,1); + ubg_eq = zeros(n_y,1); + + % Copy equality constraints + if settings.copy_eq + ind_eq_copy = sort(randperm(n_y,round(settings.s_eq_copy*n_y))); + g_eq = [g_eq;g_eq(ind_eq_copy)]; + lbg_eq = [lbg_eq;lbg_eq(ind_eq_copy)]; + ubg_eq = [ubg_eq;ubg_eq(ind_eq_copy)]; + end + + + + n_eq = length(g_eq); + n_ineq = length(g_ineq); + + lbw = [zeros(n_x+n_y,1);-inf*ones(n_y,1)]; + if bounded_w + ubw = range_ubw(1)+(range_ubw(2)-range_ubw(1)).*rand(n_x+n_y,1); + ubw = round(ubw,n_digits_data); + ubw = [ubw;inf*ones(n_y,1)]; + else + ubw = inf*ones(n,1); + end + % create problem functions + % f = f; + G = y; + H = z; + g = [g_eq;g_ineq]; + lbg = [lbg_eq;lbg_ineq]; + ubg = [ubg_eq;ubg_ineq]; + + G_fun = Function('G_fun',{w},{G}); + H_fun = Function('H_fun',{w},{H}); + f_fun = Function('f_fun',{w},{f}); + g_fun = Function('g_fun',{w},{g}); + + name = [problem_set_name '_' objective_type '_Var' num2str(n) '_Comp' num2str(n_comp) '_Eq' num2str(n_eq) '_Ineq' num2str(n_ineq)]; + w0 = [x_bar;y_bar;q+N*x_bar+M*y_bar]; + % save problem + mpec.w = w; + mpec.f_fun = f_fun; + mpec.g_fun = g_fun; + mpec.G_fun = G_fun; + mpec.H_fun = H_fun; + mpec.f = f; + mpec.g = g; + mpec.G = G; + mpec.H = H; + mpec.w0 = w0; + mpec.lbw = lbw; + mpec.ubw = ubw; + mpec.lbg = lbg; + mpec.ubg = ubg; + mpec.name = name; + mpecs = [mpecs, mpec]; % end end end diff --git a/benchmarks/nonlinear_mpec_benchmark/nonlinear_mpec_benchmark_dtable_loop.m b/benchmarks/nonlinear_mpec_benchmark/nonlinear_mpec_benchmark_dtable_loop.m index d40a769..4a75bc1 100644 --- a/benchmarks/nonlinear_mpec_benchmark/nonlinear_mpec_benchmark_dtable_loop.m +++ b/benchmarks/nonlinear_mpec_benchmark/nonlinear_mpec_benchmark_dtable_loop.m @@ -44,7 +44,6 @@ lpec_dstruct.cpu_time_lpec_phase_ii = {}; - % dstruct.solver_message = [;] dstruct.prob_num = []; dstruct.f = []; @@ -60,6 +59,10 @@ options = opts{ii}; j = 1; total_success = 0; + % log current problem + % fid = fopen('current_problem.txt','w'); + % fprintf(fid,'%s\n\n',name); + % fclose(fid); for jj = n_mpecs mpec = mpecs(jj); w = mpec.w; @@ -76,6 +79,12 @@ disp([ num2str(jj) '/' num2str(length(mpecs)) ': ' 'solving ' char(mpec.name) ' with solver ' char(name)]); fprintf('Problem info:name = %s, n_w = %d, n_g = %d, n_comp = %d\n',name, length(w),length(g),length(G)) + + % log current problem + fid = fopen('current_problem.txt','w'); + fprintf(fid,'%s\n',mpec.name); + fclose(fid); + mpec_struct = struct('x',w,'f',f,'g',g,'G',G,'H',H); solver_initalization = struct('x0', w0, 'lbx',lbw, 'ubx',ubw,'lbg',lbg,'ubg',ubg); % to add non-mpecsol solver you can add ifs here @@ -100,8 +109,10 @@ dstruct.n_lpec_total = [dstruct.n_lpec_total; stats.n_lpec_total]; dstruct.max_cpu_time_nlp = [dstruct.max_cpu_time_nlp; max([stats.iter.cpu_time_nlp_phase_i_iter(:);stats.iter.cpu_time_nlp_phase_ii_iter(:)])]; - dstruct.max_cpu_time_nlp_phase_i = [dstruct.max_cpu_time_nlp_phase_i; max(stats.iter.cpu_time_nlp_phase_i_iter)]; - dstruct.max_cpu_time_nlp_phase_ii = [dstruct.max_cpu_time_nlp_phase_ii; max(stats.iter.cpu_time_nlp_phase_ii_iter)]; + % dstruct.max_cpu_time_nlp_phase_i = [dstruct.max_cpu_time_nlp_phase_i; max(stats.iter.cpu_time_nlp_phase_i_iter)]; + % dstruct.max_cpu_time_nlp_phase_ii = [dstruct.max_cpu_time_nlp_phase_ii; max(stats.iter.cpu_time_nlp_phase_ii_iter)]; + dstruct.max_cpu_time_nlp_phase_i = [dstruct.max_cpu_time_nlp_phase_i; max([stats.iter.cpu_time_nlp_phase_i_iter;0])]; + dstruct.max_cpu_time_nlp_phase_ii = [dstruct.max_cpu_time_nlp_phase_ii; max([stats.iter.cpu_time_nlp_phase_ii_iter,0])]; dstruct.cpu_time = [dstruct.cpu_time; stats.cpu_time_total]; dstruct.cpu_time_phase_i = [dstruct.cpu_time_phase_i; stats.cpu_time_phase_i]; dstruct.cpu_time_phase_ii = [dstruct.cpu_time_phase_ii; stats.cpu_time_phase_ii]; @@ -110,7 +121,7 @@ dstruct.cpu_time_nlp_phase_i = [dstruct.cpu_time_nlp_phase_i; stats.cpu_time_nlp_phase_i]; dstruct.cpu_time_nlp_phase_ii = [dstruct.cpu_time_nlp_phase_ii; stats.cpu_time_nlp_phase_ii]; - dstruct.max_cpu_time_lpec = [dstruct.max_cpu_time_lpec; max([stats.iter.cpu_time_lpec_phase_i_iter';stats.iter.cpu_time_lpec_phase_ii_iter'])]; + dstruct.max_cpu_time_lpec = [dstruct.max_cpu_time_lpec; max([stats.iter.cpu_time_lpec_phase_i_iter';stats.iter.cpu_time_lpec_phase_ii_iter';0])]; dstruct.cpu_time_lpec = [dstruct.cpu_time_lpec; stats.cpu_time_lpec]; dstruct.cpu_time_lpec_phase_i = [dstruct.cpu_time_lpec_phase_i; stats.cpu_time_lpec_phase_i]; dstruct.cpu_time_lpec_phase_ii = [dstruct.cpu_time_lpec_phase_ii; stats.cpu_time_lpec_phase_ii]; diff --git a/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_large.m b/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_large.m index fdb28d3..ac171c9 100644 --- a/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_large.m +++ b/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_large.m @@ -4,53 +4,39 @@ %% file names filename = 'nonlinear_mpec_general2'; % name for figures and excel table results_name = ['results/' filename '_' datestr(datetime("today"))]; % name for matlab .dat with results + %% Generate test set +settings.casadi_variable_type = 'SX'; settings.nonlinear_eq_constraints = 1; settings.nonlinear_ineq_constraints = 1; -settings.s_nonlinear_eq = 0.05; -settings.s_nonlinear_ineq = 0.33; +settings.s_nonlinear_eq = 0.01; +settings.s_nonlinear_ineq = 1/4; settings.include_lifted_variables_in_objective = 0; settings.copy_ineq = 1; %have two copies of the inequality constrants -> violate licq if active -settings.s_ineq_copy = 0.25; -settings.copy_eq = 0; %have two copies of the inequality constrants -> violate licq if active +settings.s_ineq_copy = 1/4; +settings.copy_eq = 0; settings.s_eq_copy = 0.0; settings.inequality_constraints = 1; settings.inequality_constraints_coupling_terms = 1; % if 0, then in Ax+By >=f, B = 0; +% Trigonometric settings.objective_functions = {'Quadratic_psd','Quadratic_ind',... 'Fletcher','Himmelblau','McCormick',... - 'Powell','Trigonometric','Rosenbrock',... + 'Powell','Rosenbrock',... 'Raydan1','Raydan2',... - 'Diagonal3','Diagonal4','Diagonal5',... + 'Diagonal4','Diagonal5',... 'Extended_Trigiaonal','Three_Exponential_Terms',... 'Generalized_PSC1','Extended_PSC1',... 'Fletcvb3','Bdqrtic','Tridia',... 'EG2','Edensch','Indef',... 'Cube','Bdexp','Genhumps',... 'Arwhead', 'Quartc', 'Cosine', 'Sine',... - 'LUKVLE10', 'CURLY20', 'SCURLY30', 'FLETBV3M' + 'CURLY20', 'SCURLY30', 'FLETBV3M',... + 'NCVXQP6','DIXCHLNV','EDENSCH',... }; -settings.objective_functions = {'Quadratic_psd',... - 'Fletcher','Himmelblau','McCormick',... - 'Powell','Rosenbrock',... - 'Raydan1','Raydan2',... - 'Diagonal3','Diagonal4',... - 'Extended_Trigiaonal',... - 'Generalized_PSC1','Extended_PSC1',... - 'Fletcvb3','Bdqrtic','Tridia',... - 'EG2','Edensch','Indef',... - 'Cube','Bdexp','Genhumps',... - 'Arwhead', 'Quartc', 'Cosine'}; - - -settings.objective_functions = {'Quadratic_psd',... - 'Fletcher','Himmelblau','McCormick'}; - - - settings.rescale_factor = 1; settings.round_all_data = 1; settings.n_digits = 4; @@ -67,67 +53,65 @@ settings.s_density_M = 0.1; settings.variable_density = 1; -settings.range_s_density = [0.1 0.3]; +settings.range_s_density = [0.01 0.1]; settings.random_problem_sizes = 1; -settings.n_ineq_ub = 2; % n_ineq = n_ineq_ub*n_x % max relative number of ineq w.r.t x -settings.n_ineq_lb = 0.1; +settings.n_ineq_ub = 3; % n_ineq = n_ineq_ub*n_x % max relative number of ineq w.r.t x +settings.n_ineq_lb = 0.5; % Problem size -dimensions.N_rand_prob = 4; % number of problems per objective -dimensions.n_x_max = 100; -dimensions.n_x_min = 10; +dimensions.N_rand_prob = 3; % number of problems per objective +dimensions.n_x_max = 1600; +dimensions.n_x_min = 100; + + +% dimensions.N_rand_prob = 1; % number of problems per objective +% dimensions.n_x_max = 10; +% dimensions.n_x_min = 1; dimensions.n_fraction_of_x = 0.5; % n_y = round(n_x/n_fraction_of_x) % example for nonradnom problem sizes -dimensions.n_x_vec = [10,20,50,75,100,100]; -dimensions.n_y_vec = [10,10,25,40,50,70]; -dimensions.n_ineq_vec = [10,10,20,50,40,250]; +% dimensions.n_x_vec = [10,20,50,75,100,100]; +% dimensions.n_y_vec = [10,10,25,40,50,70]; +% dimensions.n_ineq_vec = [10,10,20,50,40,250]; mpecs = generate_nonlinear_mpec_problem_set(problem_set_name,settings,dimensions); length(mpecs) %% Solver settings - -solver_names = ["MPECopt-Reg-Gurobi", "MPECopt-Reg-HiGHS", "MPECopt-$\ell_1$-Gurobi", ... - "Reg" , "NLP", ... - "MINLP"]; - -solver_names = ["MPECopt-Reg-Gurobi", "MPECopt-Reg-Early", "MPECopt-$\ell_1$-Gurobi", ... - "Reg" , "NLP", ... +solver_names = ["MPECopt-Reg-Gurobi", "MPECopt-$\ell_1$-Gurobi", "MPECopt-Reg-Highs",... + "Reg", "NLP", "$\ell_1$-Penalty",... "MINLP"]; solver_functions = {@mpec_optimizer,@mpec_optimizer,@mpec_optimizer,... - @mpec_homotopy_solver,@mpec_homotopy_solver,... + @mpec_homotopy_solver,@mpec_homotopy_solver, @mpec_homotopy_solver,... @mpec_minlp_solver}; -default_opts1 = mpecopt.Options(); -default_opts1.solver_name = solver_names{1}; -default_opts1.settings_lpec.lpec_solver = "Highs_casadi"; -default_opts1.relax_and_project_homotopy_parameter_steering = "Direct"; -% default_opts1.initialization_strategy = "FeasibilityEll1General"; -% default_opts2 = mpecopt.Options(); -% default_opts2.solver_name = solver_names{2}; -% default_opts2.settings_lpec.lpec_solver = "Highs_casadi"; -% default_opts2.settings_lpec.stop_lpec_at_feasible = true; -% default_opts2.relax_and_project_homotopy_parameter_steering = "Direct"; -% default_opts2.rho_TR_phase_i_init = 1e-3; +opts1 = mpecopt.Options(); +opts1.solver_name = solver_names{1}; +opts1.settings_lpec.lpec_solver = "Gurobi"; +opts1.relax_and_project_homotopy_parameter_steering = "Direct"; +opts1.use_one_nlp_solver = true; +% opts1.initialization_strategy = "FeasibilityEll1General"; + -default_opts2 = mpecopt.Options(); -default_opts2.solver_name = solver_names{2}; -default_opts2.settings_lpec.lpec_solver = "Highs_casadi"; -default_opts2.relax_and_project_homotopy_parameter_steering = "Direct"; -default_opts2.settings_lpec.stop_lpec_at_feasible = true; -% default_opts2.rho_TR_phase_i_init = 1e-3; +opts2 = mpecopt.Options(); +opts2.solver_name = solver_names{2}; +opts2.settings_lpec.lpec_solver = "Gurobi"; +opts2.relax_and_project_homotopy_parameter_steering = "Ell_1"; +opts2.use_one_nlp_solver = true; +% opts2.settings_lpec.stop_lpec_at_feasible = true; +% opts2.rho_TR_phase_i_init = 1e-3; -default_opts3 = mpecopt.Options(); -default_opts3.solver_name = solver_names{3}; -default_opts3.settings_lpec.lpec_solver = "Gurobi"; -default_opts3.relax_and_project_homotopy_parameter_steering = "Ell_1"; +opts3 = mpecopt.Options(); +opts3.solver_name = solver_names{1}; +opts3.settings_lpec.lpec_solver = "Highs"; +opts3.relax_and_project_homotopy_parameter_steering = "Direct"; +opts3.use_one_nlp_solver = true; scholtes_opts1 = HomotopySolverOptions(); @@ -136,18 +120,24 @@ scholtes_opts2 = HomotopySolverOptions(); scholtes_opts2.homotopy_parameter_steering = 'Direct'; scholtes_opts2.max_iter = 1; -scholtes_opts2.settings_casadi_nlp.ipopt.bound_relax_factor = 1e-12; scholtes_opts2.sigma0 = 0; -minlp_opts = MINLPSolverOptions(); +scholtes_opts3 = HomotopySolverOptions(); scholtes_opts3.homotopy_parameter_steering = 'Ell_1'; -opts = {default_opts1, default_opts2, default_opts3, ... - scholtes_opts1, scholtes_opts2,... +minlp_opts = MINLPSolverOptions(); +minlp_opts.settings_casadi_nlp.bonmin.time_limit = 900; + + +opts = {opts1, opts2, opts3, ... + scholtes_opts1, scholtes_opts2, scholtes_opts3,... minlp_opts}; % list of options to pass to mpecsol (option structs) + + %% Create data struct -N_experiments = [1 2]; +N_experiments = [1 4 5 2 6 7]; + nonlinear_mpec_benchmark_dtable_loop; % this script runs the experimetns, creates a dtable %% Pick which results to plot diff --git a/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_lpec_histogram.m b/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_lpec_histogram.m index ab27700..94ce9c0 100644 --- a/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_lpec_histogram.m +++ b/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_lpec_histogram.m @@ -1,48 +1,39 @@ clear; clc; close all; problem_set_name = 'nonlinear_mpec2'; % (add large, degenerate, nonlinear, include lifted) - %% file names -filename = 'random_lpecs'; % name for figures and excel table +filename = 'random_lpecs2'; % name for figures and excel table results_name = ['results/' filename '_' datestr(datetime("today"))]; % name for matlab .dat with results %% Generate test set +settings.casadi_variable_type = 'SX'; settings.nonlinear_eq_constraints = 1; settings.nonlinear_ineq_constraints = 1; -settings.s_nonlinear_eq = 0.05; -settings.s_nonlinear_ineq = 1/3; +settings.s_nonlinear_eq = 0.01; +settings.s_nonlinear_ineq = 1/4; settings.include_lifted_variables_in_objective = 0; settings.copy_ineq = 1; %have two copies of the inequality constrants -> violate licq if active -settings.s_ineq_copy = 0.25; -settings.copy_eq = 0; %have two copies of the inequality constrants -> violate licq if active +settings.s_ineq_copy = 1/4; +settings.copy_eq = 0; settings.s_eq_copy = 0.0; settings.inequality_constraints = 1; settings.inequality_constraints_coupling_terms = 1; % if 0, then in Ax+By >=f, B = 0; +% Trigonometric settings.objective_functions = {'Quadratic_psd','Quadratic_ind',... - 'Fletcher','Himmelblau','McCormick',... - 'Powell','Trigonometric','Rosenbrock',... - 'Raydan1','Raydan2',... - 'Diagonal3','Diagonal4','Diagonal5',... - 'Extended_Trigiaonal','Three_Exponential_Terms',... - 'Generalized_PSC1','Extended_PSC1',... - 'Fletcvb3','Bdqrtic','Tridia',... - 'EG2','Edensch','Indef',... - 'Cube','Bdexp','Genhumps',... - 'Arwhead', 'Quartc', 'Cosine', 'Sine'}; - - -settings.objective_functions = {'Quadratic_psd',... 'Fletcher','Himmelblau','McCormick',... 'Powell','Rosenbrock',... 'Raydan1','Raydan2',... - 'Diagonal3','Diagonal4',... - 'Extended_Trigiaonal',... + 'Diagonal4','Diagonal5',... + 'Extended_Trigiaonal','Three_Exponential_Terms',... 'Generalized_PSC1','Extended_PSC1',... 'Fletcvb3','Bdqrtic','Tridia',... 'EG2','Edensch','Indef',... 'Cube','Bdexp','Genhumps',... - 'Arwhead', 'Quartc', 'Cosine'}; + 'Arwhead', 'Quartc', 'Cosine', 'Sine',... + 'CURLY20', 'SCURLY30', 'FLETBV3M',... + 'NCVXQP6','DIXCHLNV','EDENSCH',... + }; settings.rescale_factor = 1; @@ -61,24 +52,34 @@ settings.s_density_M = 0.1; settings.variable_density = 1; -settings.range_s_density = [0.1 0.3]; +settings.range_s_density = [0.01 0.1]; settings.random_problem_sizes = 1; settings.n_ineq_ub = 3; % n_ineq = n_ineq_ub*n_x % max relative number of ineq w.r.t x -settings.n_ineq_lb = 0.1; +settings.n_ineq_lb = 0.5; % Problem size -dimensions.N_rand_prob = 4; % number of problems per objective -dimensions.n_x_max = 400; +dimensions.N_rand_prob = 3; % number of problems per objective +dimensions.n_x_max = 425; dimensions.n_x_min = 100; + +% dimensions.N_rand_prob = 3; % number of problems per objective +% dimensions.n_x_max = 50; +% dimensions.n_x_min = 10; + +% Large +% dimensions.n_x_max = 1600; +% dimensions.n_x_min = 100; + + dimensions.n_fraction_of_x = 0.5; % n_y = round(n_x/n_fraction_of_x) % example for nonradnom problem sizes -dimensions.n_x_vec = [10,20,50,75,100,100]; -dimensions.n_y_vec = [10,10,25,40,50,70]; -dimensions.n_ineq_vec = [10,10,20,50,40,250]; +% dimensions.n_x_vec = [10,20,50,75,100,100]; +% dimensions.n_y_vec = [10,10,25,40,50,70]; +% dimensions.n_ineq_vec = [10,10,20,50,40,250]; mpecs = generate_nonlinear_mpec_problem_set(problem_set_name,settings,dimensions); length(mpecs) @@ -86,15 +87,16 @@ %% Solver settings %% Define list of solvers to use -solver_names = ["Gurobi", "Gurobi-early", "Highs", "Highs-early"]; +solver_names = ["Gurobi", "Gurobi-early-I", "Gurobi-early-I-II", "Highs", "Highs-early"]; -solver_functions = {@mpec_optimizer,@mpec_optimizer,@mpec_optimizer,@mpec_optimizer}; +solver_functions = {@mpec_optimizer,@mpec_optimizer,@mpec_optimizer,@mpec_optimizer,@mpec_optimizer}; opts1 = mpecopt.Options(); opts1.solver_name = solver_names{1}; opts1.settings_lpec.lpec_solver = "Gurobi"; opts1.relax_and_project_homotopy_parameter_steering = "Direct"; opts1.settings_lpec.stop_lpec_at_feasible = false; +opts1.use_one_nlp_solver = true; opts2 = mpecopt.Options(); @@ -102,25 +104,35 @@ opts2.settings_lpec.lpec_solver = "Gurobi"; opts2.relax_and_project_homotopy_parameter_steering = "Direct"; opts2.settings_lpec.stop_lpec_at_feasible = true; - +opts2.use_one_nlp_solver = true; opts3 = mpecopt.Options(); opts3.solver_name = solver_names{3}; -opts3.settings_lpec.lpec_solver = "Highs"; +opts3.settings_lpec.lpec_solver = "Gurobi"; opts3.relax_and_project_homotopy_parameter_steering = "Direct"; opts3.settings_lpec.stop_lpec_at_feasible = false; +opts3.settings_lpec.stop_lpec_at_descent = false; +opts3.use_one_nlp_solver = true; opts4 = mpecopt.Options(); opts4.solver_name = solver_names{4}; opts4.settings_lpec.lpec_solver = "Highs"; opts4.relax_and_project_homotopy_parameter_steering = "Direct"; -opts4.settings_lpec.stop_lpec_at_feasible = true; +opts4.settings_lpec.stop_lpec_at_feasible = false; +opts4.use_one_nlp_solver = true; + +opts5 = mpecopt.Options(); +opts5.solver_name = solver_names{5}; +opts5.settings_lpec.lpec_solver = "Highs"; +opts5.relax_and_project_homotopy_parameter_steering = "Direct"; +opts5.settings_lpec.stop_lpec_at_feasible = true; +opts5.use_one_nlp_solver = true; -opts = {opts1, opts2, opts3, opts4}; % list of options to pass to mpecsol (option structs) +opts = {opts1, opts2, opts3, opts4, opts5}; % list of options to pass to mpecsol (option structs) %% Create data struct -N_experiments = [1 2 3 4]; +N_experiments = [1:3]; nonlinear_mpec_benchmark_dtable_loop; % this script runs the experimetns, creates a dtable %% Pick which results to plot @@ -152,13 +164,13 @@ end %% Plot results -plot_settings.success_fail_statistics = 1; -plot_settings.n_biactive = 1; +plot_settings.success_fail_statistics = 0; +plot_settings.n_biactive = 0; plot_settings.lpecs_solved = 0; plot_settings.active_set_changes = 0; plot_settings.lpecs_cpu_time = 0; plot_settings.bar_timing_plots = 0; -plot_settings.nlps_solved = 1; +plot_settings.nlps_solved = 0; plot_settings.max_nlp_cpu_time = 0; plot_settings.max_nlp_cpu_time_phase_i = 0; plot_settings.max_nlp_cpu_time_phase_ii = 0; @@ -186,8 +198,8 @@ plot_settings.lpec_phases_cpu_time = 0; plot_settings.max_lpec_cpu_time = 0; -plot_settings.stationary_points = 1; -plot_settings.b_stationarity = 1; +plot_settings.stationary_points = 0; +plot_settings.b_stationarity = 0; plot_settings.b_stationarty_as_success_criterion = 0; plot_settings.plot_only_sucessful = 1; plot_settings.bar_comparisson_plots = 0; diff --git a/benchmarks/nonlinear_mpec_benchmark/sparsity.m b/benchmarks/nonlinear_mpec_benchmark/sparsity.m index b1f3142..501efea 100644 --- a/benchmarks/nonlinear_mpec_benchmark/sparsity.m +++ b/benchmarks/nonlinear_mpec_benchmark/sparsity.m @@ -1,72 +1,16 @@ import casadi.* -n_obj = 100; % number of variables +n_obj = 200; % number of variables w = SX.sym('w', n_obj, 1); % decision variables - -% case 'CHEBYQAD' - % % Parameters - % H = 1/(n_obj+1); - % H2 = H^2; - % KAPPA = 1.0; - % KAPPAH2 = KAPPA*H2; - % - % % Residuals (gradient-like) - % r = SX.zeros(n_obj,1); - % r(1) = w(1) - 0; % G(0) - % for i=1:n_obj-1 - % r(i+1) = w(i) - w(i+1); % G(i) - % end - % r = r - 2*H2; % L(i) - % r(n_obj) = r(n_obj) - (1 + 2*H2); % L(N) - % - % % Function - % f = 0.5 * r.' * r; - - -f = 0; -for i = 1:n_obj - % finite-difference like group (difference with next) - if i < n_obj - q = w(i+1) - w(i); - else - q = w(i); - end - % element contribution (scaled sin/cos) - f = f + 0.5 * q^2 + 100*sin(0.01*w(i)) + 1e8*cos(w(i)); -end - - + r = w(1:end-1).*w(2:end); % pairwise products + f = sum(r.^2) + sum(r.^4); % L2 + L4 contributions H = hessian(f, w); % symbolic Hessian H_fun = Function('H_fun', {w}, {H}); % evaluate at a dummy point -w0 = 1*ones(n_obj,1); +w0 = 2*ones(n_obj,1); H_val = full(H_fun(w0)); +max(H_val(:)) % plot sparsity pattern figure; spy(H_val); -%% -import casadi.* - -N = 10; % adjust N as needed -KAPPA = 1.0; -OBJSCALE = 1e8; - -w = SX.sym('w', N, 1); - -% Define individual contributions -S = 100.0*sin(0.01*w); -C = OBJSCALE*cos(w); - -% Define groups (HALFL2 structure) -f = 0.5*OBJSCALE*sum((diff([0; w])).^2); % finite-difference quadratic -f = f + sum(C) + sum(S); % add individual element contributions - -% Hessian -H_f = hessian(f, w); - -% Function to evaluate and plot -H_fun = Function('H_fun', {w}, {H_f}); -H_val = full(H_fun(zeros(N,1))); - -figure; spy(H_val); title('Hessian sparsity of FLETBV3M'); From 14147021da95c73a40d1b5c0b43a65491b1fc73c Mon Sep 17 00:00:00 2001 From: "armin.nurkanovic" Date: Mon, 8 Sep 2025 14:20:02 +0200 Subject: [PATCH 09/31] small polishing of solver settings, fixing some minlp breaks --- examples/cardinality_optimization.m | 15 +++++------- src/+mpecopt/Options.m | 4 +-- src/+mpecopt/Solver.m | 7 +++++- src/find_nonscalar.m | 38 ++++++++++++++++++++++++++--- src/lpec_solver.m | 2 +- src/mpec_minlp_solver.m | 25 ++++++++++++------- src/options/LPECSolverOptions.m | 3 ++- 7 files changed, 67 insertions(+), 27 deletions(-) diff --git a/examples/cardinality_optimization.m b/examples/cardinality_optimization.m index 2edc71c..274f232 100644 --- a/examples/cardinality_optimization.m +++ b/examples/cardinality_optimization.m @@ -9,8 +9,8 @@ import casadi.* %% Problem formulation -n = 25; % number of variabples -m = 10; +n = 150; % number of variabples +m = 100; A = rand(m,n); b = rand(m,1); c = 1-2*rand(n,1); @@ -51,18 +51,15 @@ % solver_settings.settings_lpec.lpec_solver = "Highs_casadi"; solver_settings.settings_lpec.lpec_solver = "Gurobi"; % solver_settings.rho_TR_phase_i_init = 1e-1; -% solver_settings.rho_TR_phase_ii_init = 1e-1; -% solver_settings.relax_and_project_homotopy_parameter_steering = "Direct"; -% solver_settings.initialization_strategy = "FeasibilityEllInfGeneral"; +solver_settings.rho_TR_phase_ii_init = 1e-5; +solver_settings.settings_lpec.stop_lpec_at_feasible = 1; +solver_settings.settings_lpec.stop_lpec_at_descent = 1; + solver_initalization = struct('x0', x0, 'lbx',lbx, 'ubx',ubx,'lbg',lbg, 'ubg',ubg); solver = mpecopt.Solver(mpec, solver_settings); [result_active_set,stats_active_set] = solver.solve(solver_initalization); -% -solver_initalization = struct('x0', x0, 'lbx',lbx, 'ubx',ubx,'lbg',lbg, 'ubg',ubg); -[result_active_set,stats_active_set] = mpec_optimizer(mpec, solver_initalization, solver_settings); -% solver = mpecopt.Solver(mpec, solver_settings); %% w_opt_active_set = full(result_active_set.x); diff --git a/src/+mpecopt/Options.m b/src/+mpecopt/Options.m index 0de7929..852a233 100644 --- a/src/+mpecopt/Options.m +++ b/src/+mpecopt/Options.m @@ -25,7 +25,7 @@ % --- BNLP/TNLP settings---- piece_nlp_strategy PieceNLPStartegy = "BNLP_integer" - use_one_nlp_solver (1,1) logical = true; % if true create only one NLP solver for phase i with reg, and phase ii for bnlp and tnlp check (but set sigma to inf) - saves lot of time for large problems; Option supportedo only for RelaxAndProject Phase I + use_one_nlp_solver (1,1) logical = false; % if true create only one NLP solver for phase i with reg, and phase ii for bnlp and tnlp check (but set sigma to inf) - saves lot of time for large problems; Option supportedo only for RelaxAndProject Phase I % Multiplier-based stationarity compute_tnlp_stationary_point (1,1) logical = true; % todo: reduce this and the one below settings to one @@ -48,7 +48,7 @@ relax_and_project_tighter_TR (1,1) logical = false; % if true take the maximum as max(max(x1,x2)), otherwise take it as rho_TR_phase_i relax_and_project_sigma0 (1,1) double {mustBeReal, mustBeNonnegative} = 1; relax_and_project_kappa (1,1) double {mustBeReal, mustBeNonnegative} = 0.1; - relax_and_project_comp_tol (1,1) double {mustBeReal, mustBeNonnegative} = 1e-10; + relax_and_project_comp_tol (1,1) double {mustBeReal, mustBeNonnegative} = 1e-9; relax_and_project_comps_aggregated (1,1) logical = false; relax_and_project_consider_all_comps_in_lpec (1,1) logical = false; relax_and_project_homotopy_parameter_steering HomotopySteering = 'Direct'; diff --git a/src/+mpecopt/Solver.m b/src/+mpecopt/Solver.m index 6ccea8a..2a96e19 100644 --- a/src/+mpecopt/Solver.m +++ b/src/+mpecopt/Solver.m @@ -1177,7 +1177,11 @@ function create_mpec_functions(obj) x = mpec.w; end f = mpec.f; - g = SX(mpec.g); + if strcmp(class(x),'casadi.SX') + g = SX(mpec.g); + else + g = MX(mpec.g); + end G = mpec.G; H = mpec.H; @@ -1293,6 +1297,7 @@ function create_mpec_functions(obj) % find index set if n_comp > 0 % TODO(@anton) do we actually need this here or can we calculate these "analytically" + % TODO(@anton) THIS does not work for MX -- need entierly different implementation! ind_x1 = []; ind_x2 = []; % HERE BE DRAGONS: This is using some casadi internals to deduplicate x1, x2, expressions. diff --git a/src/find_nonscalar.m b/src/find_nonscalar.m index 7e46e14..af5fa5c 100644 --- a/src/find_nonscalar.m +++ b/src/find_nonscalar.m @@ -3,19 +3,40 @@ % This function returns the indices of g which contain elements of the symbolic vector w, along with its % nonscalar indicies and the ind_map which are the indices in w which correspond to scalar elements of g. if ~exist('p') - p = SX([]); + if strcmp(class(w),'casadi.SX') + p = SX([]); + else + p = MX([]); + end end import casadi.* % Take jacobian of g with respect to w. ind_g_fun = Function('ind_G', {w,p}, {g.jacobian(w)}); - p_val = SX(ones(length(p),1)); + if strcmp(class(w),'casadi.SX') + p_val = SX(ones(length(p),1)); + else + p_val = MX(ones(length(p),1)); + end % HERE BE DRAGONS: % `ind_g_fun(w) == 1` creates a symbolic array where literal `1` are ones, other constants are zeros and % symbolics are nans when converted to a casadi.DM. % % We want to find rows which contain only exactly one nonstructural 0, 1, or nan. % We get this from using find on the sparsity patern of the DM. - sp = DM(ind_g_fun(w,p) == 1).sparsity; + if strcmp(class(w),'casadi.SX') + sp = DM(ind_g_fun(w,p) == 1).sparsity; + else + % Create a function from MX expression + % f = Function('f', {w, p}, {ind_g_fun(w, p) == 1}); + % % Evaluate with appropriate inputs (zeros work for sparsity detection) + % w_val = DM.zeros(w.size()); + % result = f(w_val, p_val); + % % Get sparsity + % sp = result.sparsity(); + % Get Jacobian sparsity (this works directly with MX) + mx_expr = ind_g_fun(w, p); + sp = mx_expr.sparsity(); + end sub = sp.find; [ind_g1, ind_g2] = ind2sub(size(ind_g_fun(w,p)),sub); % transpose because groupcounts expects column vector @@ -26,7 +47,16 @@ % HERE BE MORE DRAGONS: % We also need to check for constant offsets. In future we may want to capture the offset in lbw but for now just assume nonscalar - check = full(DM(g(ind_scalar)-w(ind_map))) == 0; + if strcmp(class(w),'casadi.SX') + check = full(DM(g(ind_scalar)-w(ind_map))) == 0; + else + diff_fun = Function('diff_check', {w}, {g(ind_scalar) - w(ind_map)}); + % You'll need numeric values here - either current values or zeros for structure check + w_val = DM.ones(w.size()); + % Evaluate and check + diff_result = diff_fun(w_val); + check = full(diff_result) == 0; + end ind_scalar = ind_scalar(check); ind_map = ind_map(check); diff --git a/src/lpec_solver.m b/src/lpec_solver.m index 67c1340..0c11da7 100644 --- a/src/lpec_solver.m +++ b/src/lpec_solver.m @@ -115,7 +115,7 @@ end if settings.stop_lpec_at_descent && ~settings.is_in_phase_i % terminate phase ii lpecs at a dscent direction - params.BestObjStop = -0.5*norm(f_lpec)*lpec.rho_TR; + params.BestObjStop = -0.05*norm(f_lpec)*lpec.rho_TR; end % params.ObjScale = -0.5; % https://www.gurobi.com/documentation/current/refman/objscale.html#parameter:ObjScale % params.ScaleFlag=0; % default -1, % https://www.gurobi.com/documentation/current/refman/scaleflag.html#parameter:ScaleFlag diff --git a/src/mpec_minlp_solver.m b/src/mpec_minlp_solver.m index 6604b4f..b6a40de 100644 --- a/src/mpec_minlp_solver.m +++ b/src/mpec_minlp_solver.m @@ -407,14 +407,21 @@ inf_pr_ii = full(h_std_fun(x_k,p0)); t_phase_ii_start = tic; t_nlp_start_ii = tic; -solution = solver('x0',x_k_minlp,'p',p0_minlp,'lbx',lbx_minlp,'ubx',ubx_minlp,'lbg',lbg_minlp,'ubg',ubg_minlp); +try + solution = solver('x0',x_k_minlp,'p',p0_minlp,'lbx',lbx_minlp,'ubx',ubx_minlp,'lbg',lbg_minlp,'ubg',ubg_minlp); + stats = solver.stats(); + x_k_minlp = full(solution.x); +catch + stats.return_status = "MINLP Solver failed." + warning('MINLP solution failed, returning initial guess.') +end + +x_k = x_k_minlp(1:n_primal); t_nlp_end_ii = toc(t_nlp_start_ii); cpu_time_phase_ii = toc(t_phase_ii_start); n_nlp_total = n_nlp_total + 1; cpu_time_nlp_iter = [cpu_time_nlp_iter,t_nlp_end_ii]; -stats = solver.stats(); -x_k_minlp = full(solution.x); -x_k = x_k_minlp(1:n_primal); + comp_res_ii = full(h_comp_constraints_fun(x_k,p0)); f_k = full(f_fun(x_k,p0)); X_outer = [X_outer, x_k]; @@ -428,12 +435,12 @@ solver_message = stats.return_status; end -if isequal(stats.return_status,'Maximum_Iterations_Exceeded') - success = false; - solver_message = 'Last NLP in homotopy reached maximum number of iterations.'; -end +% if isequal(stats.return_status,'Maximum_Iterations_Exceeded') +% success = false; +% solver_message = 'Last NLP in homotopy reached maximum number of iterations.'; +% end -if comp_res_ii > settings.comp_tol +if comp_res_ii > settings.comp_tol && ~isequal(stats.return_status,'SUCCESS') success = false; solver_message = 'Complementarity tolerance not satisfied.'; end diff --git a/src/options/LPECSolverOptions.m b/src/options/LPECSolverOptions.m index 66e8073..cfed2c9 100644 --- a/src/options/LPECSolverOptions.m +++ b/src/options/LPECSolverOptions.m @@ -12,7 +12,8 @@ rel_tol(1,1) double {mustBeReal, mustBeNonempty} = 1e-9; abs_tol(1,1) double {mustBeReal, mustBeNonempty} = 1e-9; solve_lpec_with_cutoff (1,1) logical = false; - stop_lpec_at_feasible (1,1) logical = false; + stop_lpec_at_feasible (1,1) logical = false; % Stop at lpec feasible point in ph i + stop_lpec_at_descent (1,1) logical = false; % Stop at descent direction in Phase II lepcs (sufficent for pogress) (supported only for gurobi atm) trust_region_on_slacks (1,1) logical = false; % Do the slack variables for the feasbility trasformation have a TR constraint? is_in_phase_i (1,1) logical = true; % tell lpec solver which phase it is homotopy_solver_settings From 55d4f2159353254bd6b6183ca0145e1351baafd1 Mon Sep 17 00:00:00 2001 From: "armin.nurkanovic" Date: Mon, 8 Sep 2025 14:51:31 +0200 Subject: [PATCH 10/31] fixing broken indexing --- .../macmpec/run_macmpec_experiments_general.m | 11 ++++++----- .../macmpec/solve_single_macmpec_problem.m | 11 ++++++----- benchmarks/nonlinear_mpec_benchmark/sparsity.m | 16 ---------------- src/lpec_solver.m | 4 ++-- 4 files changed, 14 insertions(+), 28 deletions(-) delete mode 100644 benchmarks/nonlinear_mpec_benchmark/sparsity.m diff --git a/benchmarks/macmpec/run_macmpec_experiments_general.m b/benchmarks/macmpec/run_macmpec_experiments_general.m index 1f72d2f..7151775 100644 --- a/benchmarks/macmpec/run_macmpec_experiments_general.m +++ b/benchmarks/macmpec/run_macmpec_experiments_general.m @@ -55,7 +55,7 @@ %% Define list of solvers to use -solver_names = ["MPECopt-Reg-Gurobi", "MPECopt-$\ell_1$-Gurobi", "MPECopt-Reg-Highs" +solver_names = ["MPECopt-Reg-Gurobi", "MPECopt-$\ell_1$-Gurobi", "MPECopt-Reg-Guroby-ET", ... "Reg", "NLP", ... "MINLP"]; @@ -82,10 +82,11 @@ opts3 = mpecopt.Options(); opts3.solver_name = solver_names{1}; -opts3.settings_lpec.lpec_solver = "Highs"; +opts3.settings_lpec.lpec_solver = "Gurobi"; opts3.relax_and_project_homotopy_parameter_steering = "Direct"; opts3.use_one_nlp_solver = false; - +opts3.settings_lpec.stop_lpec_at_feasible = true; +opts3.settings_lpec.stop_lpec_at_descent = true; scholtes_opts1 = HomotopySolverOptions(); scholtes_opts1.homotopy_parameter_steering = 'Direct'; @@ -105,8 +106,8 @@ %% Create data struct % N_experiments = [1, 3:6]; -N_experiments = [1:6]; -N_experiments = [1, 4, 6]; +N_experiments = [1:7]; +% N_experiments = [1, 4, 6]; mpec_benchmark_dtable_loop; % this script runs the experimetns, creates a dtable diff --git a/benchmarks/macmpec/solve_single_macmpec_problem.m b/benchmarks/macmpec/solve_single_macmpec_problem.m index 555c935..7d66bdb 100644 --- a/benchmarks/macmpec/solve_single_macmpec_problem.m +++ b/benchmarks/macmpec/solve_single_macmpec_problem.m @@ -15,7 +15,7 @@ % problame_name = 'nash1a'; % problame_name = 'nash1c'; % problame_name = 'tap-09'; -problame_name = ' design-cent-31'; +% problame_name = ' design-cent-31'; % problame_name = 'pack-rig2p-16'; @@ -94,9 +94,9 @@ %% Homotopy solver settings_homotopy = HomotopySolverOptions(); settings_homotopy.homotopy_parameter_steering = 'Direct'; -[result_homotopy,stats_homotopy] = mpec_homotopy_solver(mpec,solver_initalization,settings_homotopy); -f_opt_homotopy = full(result_homotopy.f); -w_opt_homotopy = full(result_homotopy.x); +% [result_homotopy,stats_homotopy] = mpec_homotopy_solver(mpec,solver_initalization,settings_homotopy); +% f_opt_homotopy = full(result_homotopy.f); +% w_opt_homotopy = full(result_homotopy.x); %% MINLP solver settings_minlp = MINLPSolverOptions(); @@ -104,7 +104,7 @@ settings_minlp.settings_casadi_nlp.bonmin.node_limit = 5; settings_minlp.settings_casadi_nlp.bonmin.solution_limit = 5; settings_minlp.settings_casadi_nlp.bonmin.max_consecutive_failures = 5; -[result_minlp,stats_minlp] = mpec_minlp_solver(mpec,solver_initalization,settings_minlp); +% [result_minlp,stats_minlp] = mpec_minlp_solver(mpec,solver_initalization,settings_minlp); % f_opt_minlp = full(result_minlp.f); % w_opt_minlp = full(result_minlp.x); @@ -114,6 +114,7 @@ solver_settings.settings_lpec.lpec_solver = "Gurobi"; % solver_settings.settings_casadi_nlp.ipopt.max_iter = 4000; solver_settings.settings_lpec.stop_lpec_at_feasible = true; +solver_settings.settings_lpec.stop_lpec_at_descent = true; % solver_settings.settings_casadi_nlp.ipopt.fixed_variable_treatment = 'relax_bounds'; % solver_settings.initialization_strategy = "FeasibilityEll1General"; % solver_settings.rho_TR_phase_i_init = 10; diff --git a/benchmarks/nonlinear_mpec_benchmark/sparsity.m b/benchmarks/nonlinear_mpec_benchmark/sparsity.m deleted file mode 100644 index 501efea..0000000 --- a/benchmarks/nonlinear_mpec_benchmark/sparsity.m +++ /dev/null @@ -1,16 +0,0 @@ -import casadi.* - -n_obj = 200; % number of variables -w = SX.sym('w', n_obj, 1); % decision variables - r = w(1:end-1).*w(2:end); % pairwise products - f = sum(r.^2) + sum(r.^4); % L2 + L4 contributions -H = hessian(f, w); % symbolic Hessian -H_fun = Function('H_fun', {w}, {H}); - -% evaluate at a dummy point -w0 = 2*ones(n_obj,1); -H_val = full(H_fun(w0)); -max(H_val(:)) - -% plot sparsity pattern -figure; spy(H_val); diff --git a/src/lpec_solver.m b/src/lpec_solver.m index 0c11da7..a7f9855 100644 --- a/src/lpec_solver.m +++ b/src/lpec_solver.m @@ -115,7 +115,7 @@ end if settings.stop_lpec_at_descent && ~settings.is_in_phase_i % terminate phase ii lpecs at a dscent direction - params.BestObjStop = -0.05*norm(f_lpec)*lpec.rho_TR; + params.BestObjStop = -0.01*norm(f_lpec)*lpec.rho_TR; end % params.ObjScale = -0.5; % https://www.gurobi.com/documentation/current/refman/objscale.html#parameter:ObjScale % params.ScaleFlag=0; % default -1, % https://www.gurobi.com/documentation/current/refman/scaleflag.html#parameter:ScaleFlag @@ -281,7 +281,7 @@ cpu_time_gurobi = nan; end - if (isequal(result_gurobi.status,'OPTIMAL') || isequal(result_gurobi.status,'NODE_LIMIT')) && isfield(result_gurobi,'x') + if ( isequal(result_gurobi.status,'OPTIMAL') || isequal(result_gurobi.status,'NODE_LIMIT')|| isequal(result_gurobi.status,'USER_OBJ_LIMIT') ) && isfield(result_gurobi,'x') results.d_lpec = result_gurobi.x(1:lpec.dims.n_primal); results.y_lpec = result_gurobi.x(end-lpec.dims.n_auxiliary+1:end); results.f_opt = result_gurobi.objval; From de576475dbd909bf9236b9cdd524308eb4cc14db Mon Sep 17 00:00:00 2001 From: "armin.nurkanovic" Date: Mon, 8 Sep 2025 14:55:46 +0200 Subject: [PATCH 11/31] add early termination to solvers to be tests --- .../macmpec/run_macmpec_experiments_general.m | 2 +- .../macmpec/solve_single_macmpec_problem.m | 2 ++ .../run_nonlinear_mpec_benchmark_large.m | 17 +++++++++++++---- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/benchmarks/macmpec/run_macmpec_experiments_general.m b/benchmarks/macmpec/run_macmpec_experiments_general.m index 7151775..77ab70c 100644 --- a/benchmarks/macmpec/run_macmpec_experiments_general.m +++ b/benchmarks/macmpec/run_macmpec_experiments_general.m @@ -81,7 +81,7 @@ opts3 = mpecopt.Options(); -opts3.solver_name = solver_names{1}; +opts3.solver_name = solver_names{3}; opts3.settings_lpec.lpec_solver = "Gurobi"; opts3.relax_and_project_homotopy_parameter_steering = "Direct"; opts3.use_one_nlp_solver = false; diff --git a/benchmarks/macmpec/solve_single_macmpec_problem.m b/benchmarks/macmpec/solve_single_macmpec_problem.m index 7d66bdb..f1a3012 100644 --- a/benchmarks/macmpec/solve_single_macmpec_problem.m +++ b/benchmarks/macmpec/solve_single_macmpec_problem.m @@ -123,7 +123,9 @@ tic solver = mpecopt.Solver(mpec, solver_settings); toc + [result_mpecopt,stats_mpecopt] = solver.solve(solver_initalization); +stats_mpecopt.cpu_time_total % [solution,stats] = mpec_optimizer(mpec, solver_initalization, solver_settings); w_opt_mpecopt = full(result_mpecopt.x); diff --git a/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_large.m b/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_large.m index ac171c9..d97be03 100644 --- a/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_large.m +++ b/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_large.m @@ -82,7 +82,7 @@ %% Solver settings -solver_names = ["MPECopt-Reg-Gurobi", "MPECopt-$\ell_1$-Gurobi", "MPECopt-Reg-Highs",... +solver_names = ["MPECopt-Reg-Gurobi", "MPECopt-$\ell_1$-Gurobi", "MPECopt-Reg-Gurobi-ET",... "Reg", "NLP", "$\ell_1$-Penalty",... "MINLP"]; @@ -107,11 +107,20 @@ % opts2.rho_TR_phase_i_init = 1e-3; +% opts3 = mpecopt.Options(); +% opts3.solver_name = solver_names{3}; +% opts3.settings_lpec.lpec_solver = "Highs"; +% opts3.relax_and_project_homotopy_parameter_steering = "Direct"; +% opts3.use_one_nlp_solver = true; +% opts3 = mpecopt.Options(); -opts3.solver_name = solver_names{1}; -opts3.settings_lpec.lpec_solver = "Highs"; +opts3.solver_name = solver_names{3}; +opts3.settings_lpec.lpec_solver = "Gurobi"; opts3.relax_and_project_homotopy_parameter_steering = "Direct"; opts3.use_one_nlp_solver = true; +opts3.settings_lpec.stop_lpec_at_feasible = true; +opts3.settings_lpec.stop_lpec_at_descent = true; + scholtes_opts1 = HomotopySolverOptions(); @@ -136,7 +145,7 @@ %% Create data struct -N_experiments = [1 4 5 2 6 7]; +N_experiments = [1 4 5 2 6 7 3]; nonlinear_mpec_benchmark_dtable_loop; % this script runs the experimetns, creates a dtable From bb11350a5a16eacb2b4acc10c89d306f22dfaf59 Mon Sep 17 00:00:00 2001 From: "armin.nurkanovic" Date: Mon, 8 Sep 2025 15:31:14 +0200 Subject: [PATCH 12/31] improved early termination, and addapation of it in experiments --- benchmarks/histogram_on_lpecs.m | 2 +- benchmarks/macmpec/run_macmpec_experiments_general.m | 1 + benchmarks/macmpec/solve_single_macmpec_problem.m | 4 +++- .../run_nonlinear_mpec_benchmark_large.m | 7 +++---- src/lpec_solver.m | 5 +++-- 5 files changed, 11 insertions(+), 8 deletions(-) diff --git a/benchmarks/histogram_on_lpecs.m b/benchmarks/histogram_on_lpecs.m index e10fa3f..dd05688 100644 --- a/benchmarks/histogram_on_lpecs.m +++ b/benchmarks/histogram_on_lpecs.m @@ -15,7 +15,7 @@ % solver_names = ["Gurobi", "Gurobi-early-I", "Gurobi-early-I-II", "Highs", "Highs-early"]; solver_names = unique(dtable.solver_name); -solver = solver_names{2}; +solver = solver_names{1}; idx = (dtable.solver_name == solver) & dtable.success == 1; diff --git a/benchmarks/macmpec/run_macmpec_experiments_general.m b/benchmarks/macmpec/run_macmpec_experiments_general.m index 77ab70c..792e327 100644 --- a/benchmarks/macmpec/run_macmpec_experiments_general.m +++ b/benchmarks/macmpec/run_macmpec_experiments_general.m @@ -87,6 +87,7 @@ opts3.use_one_nlp_solver = false; opts3.settings_lpec.stop_lpec_at_feasible = true; opts3.settings_lpec.stop_lpec_at_descent = true; +opts3.consider_all_complementarities_in_lpec = false; scholtes_opts1 = HomotopySolverOptions(); scholtes_opts1.homotopy_parameter_steering = 'Direct'; diff --git a/benchmarks/macmpec/solve_single_macmpec_problem.m b/benchmarks/macmpec/solve_single_macmpec_problem.m index f1a3012..21c0369 100644 --- a/benchmarks/macmpec/solve_single_macmpec_problem.m +++ b/benchmarks/macmpec/solve_single_macmpec_problem.m @@ -117,9 +117,11 @@ solver_settings.settings_lpec.stop_lpec_at_descent = true; % solver_settings.settings_casadi_nlp.ipopt.fixed_variable_treatment = 'relax_bounds'; % solver_settings.initialization_strategy = "FeasibilityEll1General"; -% solver_settings.rho_TR_phase_i_init = 10; +% solver_settings.rho_TR_phase_ii_init = 1e-4; +% solver_settings.consider_all_complementarities_in_lpec = false; % solver_settings.tol_active = 1e-6; solver_settings.use_one_nlp_solver = true; +solver_settings.compute_tnlp_stationary_point = false; tic solver = mpecopt.Solver(mpec, solver_settings); toc diff --git a/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_large.m b/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_large.m index d97be03..816d453 100644 --- a/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_large.m +++ b/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_large.m @@ -83,8 +83,7 @@ %% Solver settings solver_names = ["MPECopt-Reg-Gurobi", "MPECopt-$\ell_1$-Gurobi", "MPECopt-Reg-Gurobi-ET",... - "Reg", "NLP", "$\ell_1$-Penalty",... - "MINLP"]; + "Reg", "NLP", "$\ell_1$-Penalty", "MINLP"]; solver_functions = {@mpec_optimizer,@mpec_optimizer,@mpec_optimizer,... @mpec_homotopy_solver,@mpec_homotopy_solver, @mpec_homotopy_solver,... @@ -120,7 +119,7 @@ opts3.use_one_nlp_solver = true; opts3.settings_lpec.stop_lpec_at_feasible = true; opts3.settings_lpec.stop_lpec_at_descent = true; - +opts3.consider_all_complementarities_in_lpec = false; scholtes_opts1 = HomotopySolverOptions(); @@ -145,7 +144,7 @@ %% Create data struct -N_experiments = [1 4 5 2 6 7 3]; +N_experiments = [1 4 5 3 2 6 7]; nonlinear_mpec_benchmark_dtable_loop; % this script runs the experimetns, creates a dtable diff --git a/src/lpec_solver.m b/src/lpec_solver.m index a7f9855..319dc9a 100644 --- a/src/lpec_solver.m +++ b/src/lpec_solver.m @@ -111,7 +111,8 @@ params.OptimalityTol = 1e-9; if settings.stop_lpec_at_feasible && settings.is_in_phase_i % terminate phase i lpecs at a feasible point - params.MIPGap = 1; + % params.MIPGap = 1; + params.SolutionLimit = 1; end if settings.stop_lpec_at_descent && ~settings.is_in_phase_i % terminate phase ii lpecs at a dscent direction @@ -281,7 +282,7 @@ cpu_time_gurobi = nan; end - if ( isequal(result_gurobi.status,'OPTIMAL') || isequal(result_gurobi.status,'NODE_LIMIT')|| isequal(result_gurobi.status,'USER_OBJ_LIMIT') ) && isfield(result_gurobi,'x') + if ( isequal(result_gurobi.status,'OPTIMAL') || isequal(result_gurobi.status,'NODE_LIMIT')|| isequal(result_gurobi.status,'USER_OBJ_LIMIT') || isequal(result_gurobi.status,'SOLUTION_LIMIT')) && isfield(result_gurobi,'x') results.d_lpec = result_gurobi.x(1:lpec.dims.n_primal); results.y_lpec = result_gurobi.x(end-lpec.dims.n_auxiliary+1:end); results.f_opt = result_gurobi.objval; From 857e12d6d832870a6cef8d3fc14af52c49ad1ca0 Mon Sep 17 00:00:00 2001 From: "armin.nurkanovic" Date: Mon, 8 Sep 2025 15:35:19 +0200 Subject: [PATCH 13/31] less convesrtavie active set estimate for reduced lpecs --- src/+mpecopt/Solver.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/+mpecopt/Solver.m b/src/+mpecopt/Solver.m index 2a96e19..d6d3c07 100644 --- a/src/+mpecopt/Solver.m +++ b/src/+mpecopt/Solver.m @@ -1496,7 +1496,7 @@ function create_lpec_functions(obj) lpec.f = nabla_f_k; end t_lpec_preparation_iter = tic; - lpec = create_lpec_subproblem(x_k,p0,rho_TR_k_l,lpec_casadi,dims,opts,opts.tol_active); + lpec = create_lpec_subproblem(x_k,p0,rho_TR_k_l,lpec_casadi,dims,opts,opts.tol_active*10); % less conservative active set tol for reduced lpecs (tends to include more biactives) stats.iter.cpu_time_lpec_preparation_iter = [stats.iter.cpu_time_lpec_preparation_iter;toc(t_lpec_preparation_iter)]; % --------------------------- Inner (minor) itrations ------------------------------------------ From e4838c6658365f9fac059a03535bb5055ee720e9 Mon Sep 17 00:00:00 2001 From: "armin.nurkanovic" Date: Tue, 9 Sep 2025 11:28:37 +0200 Subject: [PATCH 14/31] support of MX in verticla from, skipping lifting for vertical form --- .../macmpec/solve_single_macmpec_problem.m | 2 +- .../current_problem.txt | 2 +- .../run_nonlinear_mpec_benchmark_large.m | 24 +- src/+mpecopt/Options.m | 1 + src/+mpecopt/Solver.m | 150 ++++++----- src/current_problem.txt | 1 + ...rmine_multipliers_based_stationary_point.m | 10 +- src/mpec_homotopy_solver.m | 248 ++++++++--------- src/mpec_minlp_solver.m | 249 +++++++++--------- src/options/HomotopySolverOptions.m | 2 + src/options/MINLPSolverOptions.m | 2 + 11 files changed, 377 insertions(+), 314 deletions(-) create mode 100644 src/current_problem.txt diff --git a/benchmarks/macmpec/solve_single_macmpec_problem.m b/benchmarks/macmpec/solve_single_macmpec_problem.m index 21c0369..8bab713 100644 --- a/benchmarks/macmpec/solve_single_macmpec_problem.m +++ b/benchmarks/macmpec/solve_single_macmpec_problem.m @@ -94,7 +94,7 @@ %% Homotopy solver settings_homotopy = HomotopySolverOptions(); settings_homotopy.homotopy_parameter_steering = 'Direct'; -% [result_homotopy,stats_homotopy] = mpec_homotopy_solver(mpec,solver_initalization,settings_homotopy); +[result_homotopy,stats_homotopy] = mpec_homotopy_solver(mpec,solver_initalization,settings_homotopy); % f_opt_homotopy = full(result_homotopy.f); % w_opt_homotopy = full(result_homotopy.x); diff --git a/benchmarks/nonlinear_mpec_benchmark/current_problem.txt b/benchmarks/nonlinear_mpec_benchmark/current_problem.txt index ddb8b13..89560ef 100644 --- a/benchmarks/nonlinear_mpec_benchmark/current_problem.txt +++ b/benchmarks/nonlinear_mpec_benchmark/current_problem.txt @@ -1 +1 @@ -nonlinear_mpec2_EDENSCH_Var1840_Comp736_Eq736_Ineq538 +nonlinear_mpec2_EDENSCH_Var10_Comp4_Eq4_Ineq5 diff --git a/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_large.m b/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_large.m index 816d453..ebad1b8 100644 --- a/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_large.m +++ b/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_large.m @@ -6,7 +6,7 @@ results_name = ['results/' filename '_' datestr(datetime("today"))]; % name for matlab .dat with results %% Generate test set -settings.casadi_variable_type = 'SX'; +settings.casadi_variable_type = 'MX'; settings.nonlinear_eq_constraints = 1; settings.nonlinear_ineq_constraints = 1; settings.s_nonlinear_eq = 0.01; @@ -37,6 +37,11 @@ }; +settings.objective_functions = {'FLETBV3M',... + 'NCVXQP6','DIXCHLNV','EDENSCH',... + }; + + settings.rescale_factor = 1; settings.round_all_data = 1; settings.n_digits = 4; @@ -66,9 +71,9 @@ dimensions.n_x_min = 100; -% dimensions.N_rand_prob = 1; % number of problems per objective -% dimensions.n_x_max = 10; -% dimensions.n_x_min = 1; +dimensions.N_rand_prob = 1; % number of problems per objective +dimensions.n_x_max = 10; +dimensions.n_x_min = 1; dimensions.n_fraction_of_x = 0.5; % n_y = round(n_x/n_fraction_of_x) @@ -94,6 +99,7 @@ opts1.settings_lpec.lpec_solver = "Gurobi"; opts1.relax_and_project_homotopy_parameter_steering = "Direct"; opts1.use_one_nlp_solver = true; +opts1.problem_in_vertical_from = true; % opts1.initialization_strategy = "FeasibilityEll1General"; @@ -102,6 +108,7 @@ opts2.settings_lpec.lpec_solver = "Gurobi"; opts2.relax_and_project_homotopy_parameter_steering = "Ell_1"; opts2.use_one_nlp_solver = true; +opts2.problem_in_vertical_from = true; % opts2.settings_lpec.stop_lpec_at_feasible = true; % opts2.rho_TR_phase_i_init = 1e-3; @@ -120,22 +127,29 @@ opts3.settings_lpec.stop_lpec_at_feasible = true; opts3.settings_lpec.stop_lpec_at_descent = true; opts3.consider_all_complementarities_in_lpec = false; +opts3.problem_in_vertical_from = true; scholtes_opts1 = HomotopySolverOptions(); scholtes_opts1.homotopy_parameter_steering = 'Direct'; +scholtes_opts1.problem_in_vertical_from = true; + scholtes_opts2 = HomotopySolverOptions(); scholtes_opts2.homotopy_parameter_steering = 'Direct'; scholtes_opts2.max_iter = 1; scholtes_opts2.sigma0 = 0; +scholtes_opts2.problem_in_vertical_from = true; + scholtes_opts3 = HomotopySolverOptions(); scholtes_opts3.homotopy_parameter_steering = 'Ell_1'; +scholtes_opts3.problem_in_vertical_from = true; + minlp_opts = MINLPSolverOptions(); minlp_opts.settings_casadi_nlp.bonmin.time_limit = 900; - +scholtes_opts3.problem_in_vertical_from = true; opts = {opts1, opts2, opts3, ... scholtes_opts1, scholtes_opts2, scholtes_opts3,... diff --git a/src/+mpecopt/Options.m b/src/+mpecopt/Options.m index 852a233..2963f80 100644 --- a/src/+mpecopt/Options.m +++ b/src/+mpecopt/Options.m @@ -7,6 +7,7 @@ casadi_symbolic_mode {mustBeMember(casadi_symbolic_mode,{'casadi.SX', 'casadi.MX'})} = 'casadi.SX'; comp_res_bilinear (1,1) logical = true; % If true: comp_res = max(x1.*x2), if false: comp_res = max(min(x1,x2)); the bilinear shrinks faster, e.g. x1 = 1e-3,x2 = 1e-3, bilinear = 1e-6, std 1e-3; lift_complementarities_full (1,1) logical = false; % If true brute force lifting x1-G = 0, if false, detect which components of G are already purely scalar. Affine expressions get lifted. + problem_in_vertical_from (1,1) logical = false; % If true, it is assumed that complementarity constraints are given as 0 \leq x_1 \perp x_2 \geq 0, and automatic formulation is skipeed. % --- Stopping criteria/tolernaces --- max_iter (1,1) double {mustBeInteger, mustBePositive} = 25; diff --git a/src/+mpecopt/Solver.m b/src/+mpecopt/Solver.m index d6d3c07..d3387ca 100644 --- a/src/+mpecopt/Solver.m +++ b/src/+mpecopt/Solver.m @@ -275,7 +275,7 @@ end if active_set_guess_exists && ~feasible_bnlp_found - + if opts.use_one_nlp_solver lbx_bnlp_k = solver_initialization_relaxed.lbx; ubx_bnlp_k = solver_initialization_relaxed.ubx; @@ -291,7 +291,7 @@ ubx_bnlp_k(dims.ind_x2(I_plus_0)) = 0; t_presolve_nlp_iter = tic; results_nlp = mpec_casadi.solver('x0',x_k,'p',solver_initialization.p0,'lbx',lbx_bnlp_k,'ubx',ubx_bnlp_k,'lbg',solver_initialization.lbg,'ubg',solver_initialization.ubg); - stats_nlp = mpec_casadi.solver.stats(); + stats_nlp = mpec_casadi.solver.stats(); end cpu_time_bnlp_ii = toc(t_presolve_nlp_iter); @@ -389,13 +389,13 @@ else s = MX.sym('s',n_g); %define slack variables for ell_1 norm of generla constraints; end - + n_slacks = n_g; else if strcmp(class(obj.mpec_casadi.x),'casadi.SX') s = SX.sym('s',1); %define slack variables for ell_inf norm of generla constraints; else - s = MX.sym('s',1); + s = MX.sym('s',1); end n_slacks = 1; end @@ -1205,48 +1205,55 @@ function create_mpec_functions(obj) H_copy = H_fun(x,p); % check if the comps are expressions or just subvectors of w. - if opts.lift_complementarities_full - % define lift vairables - if strcmp(class(x),'casadi.SX') - x1 = SX.sym('x1',n_comp); - else - x1 = MX.sym('x1',n_comp); - end - % update x and init guess - x = [x;x1]; - % lift - g = [g;x1-G]; + if opts.problem_in_vertical_from + x1 = G; + x2 = H; + n_lift_x1 = 0; + n_lift_x2 = 0; else - % lifting with only those that are not scaler - % define lift vairables - [ind_scalar,ind_nonscalar_x1, ind_map] = find_nonscalar(G,x,p); - n_lift_x1 = length(ind_nonscalar_x1); - if n_lift_x1 == 0 - % TODO(@anton) Figure out what this does. - try - x.jacobian(G_copy); - catch - n_lift_x1 = length(G_copy); - ind_nonscalar_x1 = 1:n_lift_x1; - ind_scalar = []; - end - end - if n_lift_x1 > 0 + if opts.lift_complementarities_full + % define lift vairables if strcmp(class(x),'casadi.SX') - x1_lift = SX.sym('x1_lift',n_lift_x1); + x1 = SX.sym('x1',n_comp); else - x1_lift = MX.sym('x1_lift',n_lift_x1); + x1 = MX.sym('x1',n_comp); end - % x1 = [x(ind_scalar);x1_lift]; % update x and init guess - x = [x;x1_lift]; + x = [x;x1]; % lift - g = [g;x1_lift-G(ind_nonscalar_x1)]; - - x1 = G_copy; - x1(ind_nonscalar_x1) = x1_lift; + g = [g;x1-G]; else - x1 = G; + % lifting with only those that are not scaler + % define lift vairables + [ind_scalar,ind_nonscalar_x1, ind_map] = find_nonscalar(G,x,p); + n_lift_x1 = length(ind_nonscalar_x1); + if n_lift_x1 == 0 + % TODO(@anton) Figure out what this does. + try + x.jacobian(G_copy); + catch + n_lift_x1 = length(G_copy); + ind_nonscalar_x1 = 1:n_lift_x1; + ind_scalar = []; + end + end + if n_lift_x1 > 0 + if strcmp(class(x),'casadi.SX') + x1_lift = SX.sym('x1_lift',n_lift_x1); + else + x1_lift = MX.sym('x1_lift',n_lift_x1); + end + % x1 = [x(ind_scalar);x1_lift]; + % update x and init guess + x = [x;x1_lift]; + % lift + g = [g;x1_lift-G(ind_nonscalar_x1)]; + + x1 = G_copy; + x1(ind_nonscalar_x1) = x1_lift; + else + x1 = G; + end end end @@ -1261,6 +1268,8 @@ function create_mpec_functions(obj) x = [x;x2]; % lift g = [g;x2-H]; + n_lift_x1 = 0; + n_lift_x2 = 0; else % lifting with only those that are not scaler [ind_scalar,ind_nonscalar_x2, ind_map] = find_nonscalar(H,x,p); @@ -1296,31 +1305,42 @@ function create_mpec_functions(obj) % find index set if n_comp > 0 - % TODO(@anton) do we actually need this here or can we calculate these "analytically" - % TODO(@anton) THIS does not work for MX -- need entierly different implementation! - ind_x1 = []; - ind_x2 = []; - % HERE BE DRAGONS: This is using some casadi internals to deduplicate x1, x2, expressions. - % Get element cell arrays. - x1_elem = x1.elements(); - x2_elem = x2.elements(); - % Get element hashes. - x1_hashes = cellfun(@(x) x.element_hash(), x1_elem); - x2_hashes = cellfun(@(x) x.element_hash(), x2_elem); - % Get indicies of unique elements by comparing the element hashes. - % WARNING: Technically these hashes may be nonunique but looking at how they are generated this should - % never practically be the case. If it happens though the symptoms should be obvious. - [~,x1_uniq,x1_uniq_backmap] = unique(x1_hashes, 'stable'); - [~,x2_uniq,x2_uniq_backmap] = unique(x2_hashes, 'stable'); - % Get indicies with jacobian trick - ind_x1_fun = Function('ind_1',{x},{x.jacobian(x1(x1_uniq))}); - ind_x2_fun = Function('ind_2',{x},{x.jacobian(x2(x2_uniq))}); - [ind_x1,~] = find(sparse(ind_x1_fun(zeros(size(x))))==1); - [ind_x2,~] = find(sparse(ind_x2_fun(zeros(size(x))))==1); - % Resize indices back into correct size by mapping back the nonunique elements - ind_x1 = ind_x1(x1_uniq_backmap); - ind_x2 = ind_x2(x2_uniq_backmap); + if opts.problem_in_vertical_from + % Efficient index extraction for vertical form + S1= which_depends(x,x1,1,true); + S2= which_depends(x,x2,1,true); + ind_x1 = find(S1); % gives column indices of x used in x1 + ind_x2 = find(S2); % gives column indices of x used in x2 + ind_nonscalar_x1 = []; + ind_nonscalar_x2 = []; + else + % TODO(@anton) do we actually need this here or can we calculate these "analytically" + % TODO(@anton) THIS does not work for MX -- need entierly different implementation! + ind_x1 = []; + ind_x2 = []; + % HERE BE DRAGONS: This is using some casadi internals to deduplicate x1, x2, expressions. + % Get element cell arrays. + x1_elem = x1.elements(); + x2_elem = x2.elements(); + % Get element hashes. + x1_hashes = cellfun(@(x) x.element_hash(), x1_elem); + x2_hashes = cellfun(@(x) x.element_hash(), x2_elem); + % Get indicies of unique elements by comparing the element hashes. + % WARNING: Technically these hashes may be nonunique but looking at how they are generated this should + % never practically be the case. If it happens though the symptoms should be obvious. + [~,x1_uniq,x1_uniq_backmap] = unique(x1_hashes, 'stable'); + [~,x2_uniq,x2_uniq_backmap] = unique(x2_hashes, 'stable'); + % Get indicies with jacobian trick + ind_x1_fun = Function('ind_1',{x},{x.jacobian(x1(x1_uniq))}); + ind_x2_fun = Function('ind_2',{x},{x.jacobian(x2(x2_uniq))}); + [ind_x1,~] = find(sparse(ind_x1_fun(zeros(size(x))))==1); + [ind_x2,~] = find(sparse(ind_x2_fun(zeros(size(x))))==1); + % Resize indices back into correct size by mapping back the nonunique elements + ind_x1 = ind_x1(x1_uniq_backmap); + ind_x2 = ind_x2(x2_uniq_backmap); + end opts.nlp_is_mpec = 1; % TODO: check is this still used? (its purpose: if not an mpec, just make single nlp call without mpec machinery); + else opts.nlp_is_mpec = 0; ind_x1 = []; @@ -1418,7 +1438,7 @@ function create_lpec_functions(obj) M = MX.sym('M', 1); y = MX.sym('y', dims.n_comp); % binary variablkes for comp. constraints end - + % Big M reformulation of complementarities A_lpec_sym = [-x1+M*y; -x2-M*y]; b_res = [x1;x2-M]; @@ -1496,7 +1516,7 @@ function create_lpec_functions(obj) lpec.f = nabla_f_k; end t_lpec_preparation_iter = tic; - lpec = create_lpec_subproblem(x_k,p0,rho_TR_k_l,lpec_casadi,dims,opts,opts.tol_active*10); % less conservative active set tol for reduced lpecs (tends to include more biactives) + lpec = create_lpec_subproblem(x_k,p0,rho_TR_k_l,lpec_casadi,dims,opts,opts.tol_active*10); % less conservative active set tol for reduced lpecs (tends to include more biactives) stats.iter.cpu_time_lpec_preparation_iter = [stats.iter.cpu_time_lpec_preparation_iter;toc(t_lpec_preparation_iter)]; % --------------------------- Inner (minor) itrations ------------------------------------------ diff --git a/src/current_problem.txt b/src/current_problem.txt new file mode 100644 index 0000000..f8d8812 --- /dev/null +++ b/src/current_problem.txt @@ -0,0 +1 @@ +nonlinear_mpec2_EDENSCH_Var80_Comp32_Eq32_Ineq34 diff --git a/src/determine_multipliers_based_stationary_point.m b/src/determine_multipliers_based_stationary_point.m index fd673c7..90faa82 100644 --- a/src/determine_multipliers_based_stationary_point.m +++ b/src/determine_multipliers_based_stationary_point.m @@ -24,11 +24,11 @@ eta_x1_biactive = eta_x1_biactive./max_mult; eta_x2_biactive = eta_x2_biactive./max_mult; -if max_mult > 1e3 - tol_M = 1e-3; -else - tol_M = settings.tol_B_stationarity*10; -end +% if max_mult > 1e3 +% tol_M = 1e-3; +% else +tol_M = settings.tol_B_stationarity*10; +% end % R diff --git a/src/mpec_homotopy_solver.m b/src/mpec_homotopy_solver.m index edb603d..2aec844 100644 --- a/src/mpec_homotopy_solver.m +++ b/src/mpec_homotopy_solver.m @@ -56,137 +56,149 @@ ind_x1 = []; ind_x2 = []; -% Lift complemetraties G -if settings.lift_complementarities_full - % full lifting with duplicatse; - if strcmp(class(x),'casadi.SX') - x1 = SX.sym('x1',n_comp); - else - x1 = MX.sym('x1',n_comp); - end - lbx = [lbx;0*ones(n_comp,1)]; - ubx = [ubx;inf*ones(n_comp,1)]; - x = [x;x1]; - x_k = [x_k;G_eval]; - - g = [g;x1-G]; - lbg = [lbg;0*ones(n_comp,1)]; - ubg = [ubg;0*ones(n_comp,1)]; - - ind_x1_fun = Function('ind_1',{x},{x.jacobian(x1)}); - [ind_x1,~] = find(sparse(ind_x1_fun(x_k)==1)); -elseif settings.lift_complementarities - % lifting with only those that are not scaler - [ind_scalar,ind_nonscalar, ind_map] = find_nonscalar(G,x); - n_lift_x1 = length(ind_nonscalar); - if n_lift_x1 == 0 - try x.jacobian(G_copy); - catch - n_lift_x1 = length(G_copy); - ind_nonscalar = 1:n_lift_x1; - ind_scalar = []; - end - end - if n_lift_x1 > 0 +if settings.problem_in_vertical_from + x1 = G; + x2 = H; + % Efficient index extraction for vertical form + S1= which_depends(x,x1,1,true); + S2= which_depends(x,x2,1,true); + ind_x1 = find(S1); % gives column indices of x used in x1 + ind_x2 = find(S2); % gives column indices of x used in x2 + ind_nonscalar_x1 = []; + ind_nonscalar_x2 = []; +else + % Lift complemetraties G + if settings.lift_complementarities_full + % full lifting with duplicatse; if strcmp(class(x),'casadi.SX') - x1_lift = SX.sym('x1_lift',n_lift_x1); + x1 = SX.sym('x1',n_comp); else - x1_lift = MX.sym('x1_lift',n_lift_x1); + x1 = MX.sym('x1',n_comp); end - lbx = [lbx;0*ones(n_lift_x1,1)]; - ubx = [ubx;inf*ones(n_lift_x1 ,1)]; - x = [x;x1_lift]; - x_k = [x_k;G_eval(ind_nonscalar)]; - % x1 = [x(ind_scalar);x1_lift]; - % ind_x1 = [ind_map;n_primal_non_lifted+1:n_primal_non_lifted+n_lift_x1]; - % lift - g = [g;x1_lift-G(ind_nonscalar)]; - lbg = [lbg;0*ones(n_lift_x1 ,1)]; - ubg = [ubg;0*ones(n_lift_x1 ,1)]; - - x1 = G_copy; - x1(ind_nonscalar) = x1_lift; - else - x1 = G; - % ind_x1 = ind_map; - end -else - x1 = G; % (an expression, a nontivial function of x) - if settings.update_comp_lower_bounds - g = [g;G]; - lbg = [lbg;0*ones(n_comp,1)]; - ubg = [ubg; inf*ones(n_comp,1)]; - end -end + lbx = [lbx;0*ones(n_comp,1)]; + ubx = [ubx;inf*ones(n_comp,1)]; + x = [x;x1]; + x_k = [x_k;G_eval]; -% Lift complemetraties H -if settings.lift_complementarities_full - % full lifting with duplicatse; - if strcmp(class(x),'casadi.SX') - x2 = SX.sym('x2',n_comp); + g = [g;x1-G]; + lbg = [lbg;0*ones(n_comp,1)]; + ubg = [ubg;0*ones(n_comp,1)]; + + ind_x1_fun = Function('ind_1',{x},{x.jacobian(x1)}); + [ind_x1,~] = find(sparse(ind_x1_fun(x_k)==1)); + elseif settings.lift_complementarities + % lifting with only those that are not scaler + [ind_scalar,ind_nonscalar, ind_map] = find_nonscalar(G,x); + n_lift_x1 = length(ind_nonscalar); + if n_lift_x1 == 0 + try x.jacobian(G_copy); + catch + n_lift_x1 = length(G_copy); + ind_nonscalar = 1:n_lift_x1; + ind_scalar = []; + end + end + if n_lift_x1 > 0 + if strcmp(class(x),'casadi.SX') + x1_lift = SX.sym('x1_lift',n_lift_x1); + else + x1_lift = MX.sym('x1_lift',n_lift_x1); + end + lbx = [lbx;0*ones(n_lift_x1,1)]; + ubx = [ubx;inf*ones(n_lift_x1 ,1)]; + x = [x;x1_lift]; + x_k = [x_k;G_eval(ind_nonscalar)]; + % x1 = [x(ind_scalar);x1_lift]; + % ind_x1 = [ind_map;n_primal_non_lifted+1:n_primal_non_lifted+n_lift_x1]; + % lift + g = [g;x1_lift-G(ind_nonscalar)]; + lbg = [lbg;0*ones(n_lift_x1 ,1)]; + ubg = [ubg;0*ones(n_lift_x1 ,1)]; + + x1 = G_copy; + x1(ind_nonscalar) = x1_lift; + else + x1 = G; + % ind_x1 = ind_map; + end else - x2 = MX.sym('x2',n_comp); - end - lbx = [lbx;0*ones(n_comp,1)]; - ubx = [ubx;inf*ones(n_comp,1)]; - x = [x;x2]; - x_k = [x_k;H_eval ]; - - g = [g;x2-H]; - lbg = [lbg;0*ones(n_comp,1)]; - ubg = [ubg;0*ones(n_comp,1)]; - - ind_x2_fun = Function('ind_2',{x},{x.jacobian(x2)}); - [ind_x2,~] = find(sparse(ind_x2_fun(x_k))==1); -elseif settings.lift_complementarities - % lifting with only those that are not scaler - [ind_scalar,ind_nonscalar, ind_map] = find_nonscalar(H,x); - n_lift_x2 = length(ind_nonscalar); - - if n_lift_x2 == 0 - try x.jacobian(H_copy); - catch - n_lift_x2 = length(H_copy); - ind_nonscalar = 1:n_lift_x2; - ind_scalar = []; + x1 = G; % (an expression, a nontivial function of x) + if settings.update_comp_lower_bounds + g = [g;G]; + lbg = [lbg;0*ones(n_comp,1)]; + ubg = [ubg; inf*ones(n_comp,1)]; end end - if n_lift_x2 > 0 + + % Lift complemetraties H + if settings.lift_complementarities_full + % full lifting with duplicatse; if strcmp(class(x),'casadi.SX') - x2_lift = SX.sym('x2_lift',n_lift_x2); + x2 = SX.sym('x2',n_comp); else - x2_lift = MX.sym('x2_lift',n_lift_x2); + x2 = MX.sym('x2',n_comp); end - lbx = [lbx;0*ones(n_lift_x2,1)]; - ubx = [ubx;inf*ones(n_lift_x2 ,1)]; - x = [x;x2_lift]; - x_k = [x_k;H_eval(ind_nonscalar)]; - % ind_x2 = [ind_map;n_primal_non_lifted+n_lift_x1+1:n_primal_non_lifted+n_lift_x1+n_lift_x2]; - % lift - g = [g;x2_lift-H(ind_nonscalar)]; - lbg = [lbg;0*ones(n_lift_x2 ,1)]; - ubg = [ubg;0*ones(n_lift_x2 ,1)]; - - x2 = H_copy; - x2(ind_nonscalar) = x2_lift; - else - x2 = H; - % ind_x2 = ind_map; - end -else - x2 = H; % (an expression, a nontivial function of x) - if settings.update_comp_lower_bounds - g = [g;H]; + lbx = [lbx;0*ones(n_comp,1)]; + ubx = [ubx;inf*ones(n_comp,1)]; + x = [x;x2]; + x_k = [x_k;H_eval]; + + g = [g;x2-H]; lbg = [lbg;0*ones(n_comp,1)]; - ubg = [ubg; inf*ones(n_comp,1)]; + ubg = [ubg;0*ones(n_comp,1)]; + + ind_x2_fun = Function('ind_2',{x},{x.jacobian(x2)}); + [ind_x2,~] = find(sparse(ind_x2_fun(x_k))==1); + elseif settings.lift_complementarities + % lifting with only those that are not scaler + [ind_scalar,ind_nonscalar, ind_map] = find_nonscalar(H,x); + n_lift_x2 = length(ind_nonscalar); + + if n_lift_x2 == 0 + try x.jacobian(H_copy); + catch + n_lift_x2 = length(H_copy); + ind_nonscalar = 1:n_lift_x2; + ind_scalar = []; + end + end + if n_lift_x2 > 0 + if strcmp(class(x),'casadi.SX') + x2_lift = SX.sym('x2_lift',n_lift_x2); + else + x2_lift = MX.sym('x2_lift',n_lift_x2); + end + lbx = [lbx;0*ones(n_lift_x2,1)]; + ubx = [ubx;inf*ones(n_lift_x2 ,1)]; + x = [x;x2_lift]; + x_k = [x_k;H_eval(ind_nonscalar)]; + % ind_x2 = [ind_map;n_primal_non_lifted+n_lift_x1+1:n_primal_non_lifted+n_lift_x1+n_lift_x2]; + % lift + g = [g;x2_lift-H(ind_nonscalar)]; + lbg = [lbg;0*ones(n_lift_x2 ,1)]; + ubg = [ubg;0*ones(n_lift_x2 ,1)]; + + x2 = H_copy; + x2(ind_nonscalar) = x2_lift; + else + x2 = H; + % ind_x2 = ind_map; + end + else + x2 = H; % (an expression, a nontivial function of x) + if settings.update_comp_lower_bounds + g = [g;H]; + lbg = [lbg;0*ones(n_comp,1)]; + ubg = [ubg; inf*ones(n_comp,1)]; + end end -end -if settings.lift_complementarities_full || settings.lift_complementarities - ind_x1_fun = Function('ind_1',{x},{x.jacobian(x1)}); - [ind_x1,~] = find(sparse(ind_x1_fun(x_k)==1)); - ind_x2_fun = Function('ind_2',{x},{x.jacobian(x2)}); - [ind_x2,~] = find(sparse(ind_x2_fun(x_k))==1); + if settings.lift_complementarities_full || settings.lift_complementarities + ind_x1_fun = Function('ind_1',{x},{x.jacobian(x1)}); + [ind_x1,~] = find(sparse(ind_x1_fun(x_k)==1)); + ind_x2_fun = Function('ind_2',{x},{x.jacobian(x2)}); + [ind_x2,~] = find(sparse(ind_x2_fun(x_k))==1); + end end % update lb on x1 and x2 if settings.update_comp_lower_bounds diff --git a/src/mpec_minlp_solver.m b/src/mpec_minlp_solver.m index b6a40de..0b92575 100644 --- a/src/mpec_minlp_solver.m +++ b/src/mpec_minlp_solver.m @@ -47,140 +47,151 @@ % 0 \leq G(x) \perp H(x) \geq 0 ind_x1 = []; ind_x2 = []; - -% Lift complemetraties G -if settings.lift_complementarities_full - % full lifting with duplicatse; - if strcmp(x_class,'casadi.SX') - x1 = SX.sym('x1', n_comp); - else - x1 = MX.sym('x1', n_comp); - end - - lbx = [lbx;0*ones(n_comp,1)]; - ubx = [ubx;inf*ones(n_comp,1)]; - x = [x;x1]; - x_k = [x_k;G_eval]; - - g = [g;x1-G]; - lbg = [lbg;0*ones(n_comp,1)]; - ubg = [ubg;0*ones(n_comp,1)]; - - ind_x1_fun = Function('ind_1',{x},{x.jacobian(x1)}); - [ind_x1,~] = find(sparse(ind_x1_fun(x_k)==1)); -elseif settings.lift_complementarities - % lifting with only those that are not scaler - [ind_scalar,ind_nonscalar, ind_map] = find_nonscalar(G,x); - n_lift_x1 = length(ind_nonscalar); - if n_lift_x1 == 0 - try x.jacobian(G_copy); - catch - n_lift_x1 = length(G_copy); - ind_nonscalar = 1:n_lift_x1; - ind_scalar = []; - end - end - if n_lift_x1 > 0 +if settings.problem_in_vertical_from + x1 = G; + x2 = H; + % Efficient index extraction for vertical form + S1= which_depends(x,x1,1,true); + S2= which_depends(x,x2,1,true); + ind_x1 = find(S1); % gives column indices of x used in x1 + ind_x2 = find(S2); % gives column indices of x used in x2 + ind_nonscalar_x1 = []; + ind_nonscalar_x2 = []; +else + % Lift complemetraties G + if settings.lift_complementarities_full + % full lifting with duplicatse; if strcmp(x_class,'casadi.SX') - x1_lift = SX.sym('x1_lift',n_lift_x1); + x1 = SX.sym('x1', n_comp); else - x1_lift = MX.sym('x1_lift',n_lift_x1); + x1 = MX.sym('x1', n_comp); end - lbx = [lbx;0*ones(n_lift_x1,1)]; - ubx = [ubx;inf*ones(n_lift_x1 ,1)]; - x = [x;x1_lift]; - x_k = [x_k;G_eval(ind_nonscalar)]; - % x1 = [x(ind_scalar);x1_lift]; - % ind_x1 = [ind_map;n_primal_non_lifted+1:n_primal_non_lifted+n_lift_x1]; - % lift - g = [g;x1_lift-G(ind_nonscalar)]; - lbg = [lbg;0*ones(n_lift_x1 ,1)]; - ubg = [ubg;0*ones(n_lift_x1 ,1)]; - - x1 = G_copy; - x1(ind_nonscalar) = x1_lift; - else - x1 = G; - % ind_x1 = ind_map; - end -else - x1 = G; % (an expression, a nontivial function of x) - if settings.update_comp_lower_bounds - g = [g;G]; + lbx = [lbx;0*ones(n_comp,1)]; + ubx = [ubx;inf*ones(n_comp,1)]; + x = [x;x1]; + x_k = [x_k;G_eval]; + + g = [g;x1-G]; lbg = [lbg;0*ones(n_comp,1)]; - ubg = [ubg; inf*ones(n_comp,1)]; - end -end + ubg = [ubg;0*ones(n_comp,1)]; + + ind_x1_fun = Function('ind_1',{x},{x.jacobian(x1)}); + [ind_x1,~] = find(sparse(ind_x1_fun(x_k)==1)); + elseif settings.lift_complementarities + % lifting with only those that are not scaler + [ind_scalar,ind_nonscalar, ind_map] = find_nonscalar(G,x); + n_lift_x1 = length(ind_nonscalar); + if n_lift_x1 == 0 + try x.jacobian(G_copy); + catch + n_lift_x1 = length(G_copy); + ind_nonscalar = 1:n_lift_x1; + ind_scalar = []; + end + end + if n_lift_x1 > 0 + if strcmp(x_class,'casadi.SX') + x1_lift = SX.sym('x1_lift',n_lift_x1); + else + x1_lift = MX.sym('x1_lift',n_lift_x1); + end -% Lift complemetraties H -if settings.lift_complementarities_full - % full lifting with duplicatse; - if strcmp(x_class,'casadi.SX') - x2 = SX.sym('x2', n_comp); + lbx = [lbx;0*ones(n_lift_x1,1)]; + ubx = [ubx;inf*ones(n_lift_x1 ,1)]; + x = [x;x1_lift]; + x_k = [x_k;G_eval(ind_nonscalar)]; + % x1 = [x(ind_scalar);x1_lift]; + % ind_x1 = [ind_map;n_primal_non_lifted+1:n_primal_non_lifted+n_lift_x1]; + % lift + g = [g;x1_lift-G(ind_nonscalar)]; + lbg = [lbg;0*ones(n_lift_x1 ,1)]; + ubg = [ubg;0*ones(n_lift_x1 ,1)]; + + x1 = G_copy; + x1(ind_nonscalar) = x1_lift; + else + x1 = G; + % ind_x1 = ind_map; + end else - x2 = MX.sym('x2', n_comp); - end - lbx = [lbx;0*ones(n_comp,1)]; - ubx = [ubx;inf*ones(n_comp,1)]; - x = [x;x2]; - x_k = [x_k;H_eval ]; - - g = [g;x2-H]; - lbg = [lbg;0*ones(n_comp,1)]; - ubg = [ubg;0*ones(n_comp,1)]; - - ind_x2_fun = Function('ind_2',{x},{x.jacobian(x2)}); - [ind_x2,~] = find(sparse(ind_x2_fun(x_k))==1); -elseif settings.lift_complementarities - % lifting with only those that are not scaler - [ind_scalar,ind_nonscalar, ind_map] = find_nonscalar(H,x); - n_lift_x2 = length(ind_nonscalar); - - if n_lift_x2 == 0 - try x.jacobian(H_copy); - catch - n_lift_x2 = length(H_copy); - ind_nonscalar = 1:n_lift_x2; - ind_scalar = []; + x1 = G; % (an expression, a nontivial function of x) + if settings.update_comp_lower_bounds + g = [g;G]; + lbg = [lbg;0*ones(n_comp,1)]; + ubg = [ubg; inf*ones(n_comp,1)]; end end - if n_lift_x2 > 0 + + % Lift complemetraties H + if settings.lift_complementarities_full + % full lifting with duplicatse; if strcmp(x_class,'casadi.SX') - x2_lift = SX.sym('x2_lift',n_lift_x2); + x2 = SX.sym('x2', n_comp); else - x2_lift = MX.sym('x2_lift',n_lift_x2); + x2 = MX.sym('x2', n_comp); end - lbx = [lbx;0*ones(n_lift_x2,1)]; - ubx = [ubx;inf*ones(n_lift_x2 ,1)]; - x = [x;x2_lift]; - x_k = [x_k;H_eval(ind_nonscalar)]; - % ind_x2 = [ind_map;n_primal_non_lifted+n_lift_x1+1:n_primal_non_lifted+n_lift_x1+n_lift_x2]; - % lift - g = [g;x2_lift-H(ind_nonscalar)]; - lbg = [lbg;0*ones(n_lift_x2 ,1)]; - ubg = [ubg;0*ones(n_lift_x2 ,1)]; - - x2 = H_copy; - x2(ind_nonscalar) = x2_lift; - else - x2 = H; - % ind_x2 = ind_map; - end -else - x2 = H; % (an expression, a nontivial function of x) - if settings.update_comp_lower_bounds - g = [g;H]; + lbx = [lbx;0*ones(n_comp,1)]; + ubx = [ubx;inf*ones(n_comp,1)]; + x = [x;x2]; + x_k = [x_k;H_eval ]; + + g = [g;x2-H]; lbg = [lbg;0*ones(n_comp,1)]; - ubg = [ubg; inf*ones(n_comp,1)]; + ubg = [ubg;0*ones(n_comp,1)]; + + ind_x2_fun = Function('ind_2',{x},{x.jacobian(x2)}); + [ind_x2,~] = find(sparse(ind_x2_fun(x_k))==1); + elseif settings.lift_complementarities + % lifting with only those that are not scaler + [ind_scalar,ind_nonscalar, ind_map] = find_nonscalar(H,x); + n_lift_x2 = length(ind_nonscalar); + + if n_lift_x2 == 0 + try x.jacobian(H_copy); + catch + n_lift_x2 = length(H_copy); + ind_nonscalar = 1:n_lift_x2; + ind_scalar = []; + end + end + if n_lift_x2 > 0 + if strcmp(x_class,'casadi.SX') + x2_lift = SX.sym('x2_lift',n_lift_x2); + else + x2_lift = MX.sym('x2_lift',n_lift_x2); + end + lbx = [lbx;0*ones(n_lift_x2,1)]; + ubx = [ubx;inf*ones(n_lift_x2 ,1)]; + x = [x;x2_lift]; + x_k = [x_k;H_eval(ind_nonscalar)]; + % ind_x2 = [ind_map;n_primal_non_lifted+n_lift_x1+1:n_primal_non_lifted+n_lift_x1+n_lift_x2]; + % lift + g = [g;x2_lift-H(ind_nonscalar)]; + lbg = [lbg;0*ones(n_lift_x2 ,1)]; + ubg = [ubg;0*ones(n_lift_x2 ,1)]; + + x2 = H_copy; + x2(ind_nonscalar) = x2_lift; + else + x2 = H; + % ind_x2 = ind_map; + end + else + x2 = H; % (an expression, a nontivial function of x) + if settings.update_comp_lower_bounds + g = [g;H]; + lbg = [lbg;0*ones(n_comp,1)]; + ubg = [ubg; inf*ones(n_comp,1)]; + end end -end -if settings.lift_complementarities_full || settings.lift_complementarities - ind_x1_fun = Function('ind_1',{x},{x.jacobian(x1)}); - [ind_x1,~] = find(sparse(ind_x1_fun(x_k)==1)); - ind_x2_fun = Function('ind_2',{x},{x.jacobian(x2)}); - [ind_x2,~] = find(sparse(ind_x2_fun(x_k))==1); + if settings.lift_complementarities_full || settings.lift_complementarities + ind_x1_fun = Function('ind_1',{x},{x.jacobian(x1)}); + [ind_x1,~] = find(sparse(ind_x1_fun(x_k)==1)); + ind_x2_fun = Function('ind_2',{x},{x.jacobian(x2)}); + [ind_x2,~] = find(sparse(ind_x2_fun(x_k))==1); + end end % update lb on x1 and x2 if settings.update_comp_lower_bounds diff --git a/src/options/HomotopySolverOptions.m b/src/options/HomotopySolverOptions.m index 5cea88f..2264aa3 100644 --- a/src/options/HomotopySolverOptions.m +++ b/src/options/HomotopySolverOptions.m @@ -5,6 +5,8 @@ solver_name {mustBeTextScalar} = 'mpec_homotopy'; casadi_symbolic_mode {mustBeMember(casadi_symbolic_mode,{'casadi.SX', 'casadi.MX'})} = 'casadi.SX'; comp_res_bilinear(1,1) logical = true; % if true comp_res = max(x1.*x2), if false, comp_res = max(min(x1,x2)); the bliniear shrinks faster, e.g. x1 = 1e-3,x2 = 1e-3, bilinear = 1e-6, std 1e-3; + problem_in_vertical_from (1,1) logical = false; % If true, it is assumed that complementarity constraints are given as 0 \leq x_1 \perp x_2 \geq 0, and automatic formulation is skipeed. + % Stopping criteria/tolernaces max_iter(1,1) double {mustBeInteger, mustBePositive} = 15; diff --git a/src/options/MINLPSolverOptions.m b/src/options/MINLPSolverOptions.m index c8f8c28..72d9e13 100644 --- a/src/options/MINLPSolverOptions.m +++ b/src/options/MINLPSolverOptions.m @@ -5,6 +5,8 @@ solver_name {mustBeTextScalar} = 'mpec_minlp'; casadi_symbolic_mode {mustBeMember(casadi_symbolic_mode,{'casadi.SX', 'casadi.MX'})} = 'casadi.SX'; comp_res_bilinear(1,1) logical = true; % if true comp_res = max(x1.*x2), if false, comp_res = max(min(x1,x2)); the bliniear shrinks faster, e.g. x1 = 1e-3,x2 = 1e-3, bilinear = 1e-6, std 1e-3; + problem_in_vertical_from (1,1) logical = false; % If true, it is assumed that complementarity constraints are given as 0 \leq x_1 \perp x_2 \geq 0, and automatic formulation is skipeed. + % Stopping criteria/tolernaces max_iter(1,1) double {mustBeInteger, mustBePositive} = 15; From 81e8508fa505ee4ac849441ac59b0c537ed8cc10 Mon Sep 17 00:00:00 2001 From: "armin.nurkanovic" Date: Tue, 9 Sep 2025 15:43:02 +0200 Subject: [PATCH 15/31] update nonlinear problem gen --- .../generate_nonlinear_mpec_problem_set.m | 2 +- .../solve_single_problem_nonlinear_mpec.m | 88 +++++++++++-------- 2 files changed, 50 insertions(+), 40 deletions(-) diff --git a/benchmarks/nonlinear_mpec_benchmark/generate_nonlinear_mpec_problem_set.m b/benchmarks/nonlinear_mpec_benchmark/generate_nonlinear_mpec_problem_set.m index 19c55f7..026c8af 100644 --- a/benchmarks/nonlinear_mpec_benchmark/generate_nonlinear_mpec_problem_set.m +++ b/benchmarks/nonlinear_mpec_benchmark/generate_nonlinear_mpec_problem_set.m @@ -123,7 +123,7 @@ % range for problem matrices if ~isfield(settings,'range_s_density') - range_s_density = [0.1 0.8]; + range_s_density = [0.01 0.1]; else range_s_density = settings.range_s_density; end diff --git a/benchmarks/nonlinear_mpec_benchmark/solve_single_problem_nonlinear_mpec.m b/benchmarks/nonlinear_mpec_benchmark/solve_single_problem_nonlinear_mpec.m index 187a0ad..c2e5fc6 100644 --- a/benchmarks/nonlinear_mpec_benchmark/solve_single_problem_nonlinear_mpec.m +++ b/benchmarks/nonlinear_mpec_benchmark/solve_single_problem_nonlinear_mpec.m @@ -1,31 +1,41 @@ clear; clc; close all; problem_set_name = 'nonlinear_mpec'; +%% Generate test set +settings.casadi_variable_type = 'SX'; settings.nonlinear_eq_constraints = 1; settings.nonlinear_ineq_constraints = 1; -settings.s_nonlinear_eq = 0.05; -settings.s_nonlinear_ineq = 0.05; +settings.s_nonlinear_eq = 0.01; +settings.s_nonlinear_ineq = 1/4; settings.include_lifted_variables_in_objective = 0; settings.copy_ineq = 1; %have two copies of the inequality constrants -> violate licq if active -settings.s_ineq_copy = 0.2; -settings.copy_eq = 0; %have two copies of the inequality constrants -> violate licq if active +settings.s_ineq_copy = 1/4; +settings.copy_eq = 0; settings.s_eq_copy = 0.0; settings.inequality_constraints = 1; settings.inequality_constraints_coupling_terms = 1; % if 0, then in Ax+By >=f, B = 0; +% Trigonometric settings.objective_functions = {'Quadratic_psd','Quadratic_ind',... 'Fletcher','Himmelblau','McCormick',... - 'Powell','Trigonometric','Rosenbrock',... + 'Powell','Rosenbrock',... 'Raydan1','Raydan2',... - 'Diagonal3','Diagonal4','Diagonal5',... + 'Diagonal4','Diagonal5',... 'Extended_Trigiaonal','Three_Exponential_Terms',... 'Generalized_PSC1','Extended_PSC1',... 'Fletcvb3','Bdqrtic','Tridia',... 'EG2','Edensch','Indef',... 'Cube','Bdexp','Genhumps',... - 'Arwhead', 'Quartc', 'Cosine', 'Sine'}; + 'Arwhead', 'Quartc', 'Cosine', 'Sine',... + 'CURLY20', 'SCURLY30', 'FLETBV3M',... + 'NCVXQP6','DIXCHLNV','EDENSCH',... + }; -settings.objective_functions = {'Himmelblau'}; + +settings.objective_functions = {'Edensch'}; + + +settings.objective_functions settings.rescale_factor = 1; settings.round_all_data = 1; @@ -43,25 +53,19 @@ settings.s_density_M = 0.1; settings.variable_density = 1; -settings.range_s_density = [0.1 0.3]; +settings.range_s_density = [0.01 0.05]; settings.random_problem_sizes = 1; settings.n_ineq_ub = 2; % n_ineq = n_ineq_ub*n_x % max relative number of ineq w.r.t x -settings.n_ineq_lb = 0.1; +settings.n_ineq_lb = 0.5; -% Problem size +settings.n_fraction_of_x = 0.5; dimensions.N_rand_prob = 1; % number of problems per objective -dimensions.n_x_max = 400; -dimensions.n_x_min = 400; - -dimensions.n_fraction_of_x = 1; % n_y = round(n_x/n_fraction_of_x) - -% example for nonradnom problem sizes -dimensions.n_x_vec = [10,20,50,75,100,100]; -dimensions.n_y_vec = [10,10,25,40,50,70]; -dimensions.n_ineq_vec = [10,10,20,50,40,250]; +dimensions.n_x_max = 1500; +dimensions.n_x_min = 100; +dimensions.n_fraction_of_x = 0.5; % n_y = round(n_x/n_fraction_of_x) mpecs = generate_nonlinear_mpec_problem_set(problem_set_name,settings,dimensions); length(mpecs) @@ -87,11 +91,11 @@ mpec_struct = struct('x',w,'f',f,'g',g,'G',G,'H',H); solver_initalization = struct('x0', w0, 'lbx',lbw, 'ubx',ubw,'lbg',lbg,'ubg',ubg); %% Homotopy solver -settings_homotopy = HomotopySolverOptions(); -settings_homotopy.homotopy_parameter_steering = "Direct"; -[result_homotopy,stats_homotopy] = mpec_homotopy_solver(mpec,solver_initalization,settings_homotopy); -f_opt_homotopy = full(result_homotopy.f); -w_opt_homotopy = full(result_homotopy.x); +% settings_homotopy = HomotopySolverOptions(); +% settings_homotopy.homotopy_parameter_steering = "Direct"; +% [result_homotopy,stats_homotopy] = mpec_homotopy_solver(mpec,solver_initalization,settings_homotopy); +% f_opt_homotopy = full(result_homotopy.f); +% w_opt_homotopy = full(result_homotopy.x); %% MINLP solver % settings_minlp = MINLPSolverOptions(); @@ -103,24 +107,30 @@ solver_settings = mpecopt.Options(); solver_settings.relax_and_project_homotopy_parameter_steering = "Direct"; solver_settings.settings_lpec.lpec_solver = 'Gurobi'; +solver_settings.use_one_nlp_solver = true; +solver_settings.problem_in_vertical_from = false; % solver_settings.rho_TR_phase_i_init = 10; +tic solver = mpecopt.Solver(mpec, solver_settings); +toc +%% [result_mpecopt,stats_mpecopt] = solver.solve(solver_initalization); -w_opt_mpecopt = full(result_mpecopt.x); -f_opt_mpecopt = full(result_mpecopt.f); +stats_mpecopt.cpu_time_total +% w_opt_mpecopt = full(result_mpecopt.x); +% f_opt_mpecopt = full(result_mpecopt.f); %% Results comparison -fprintf('\n-------------------------------------------------------------------------------\n'); -fprintf('Method \t\t Objective \t comp_res \t n_biactive \t CPU time (s)\t Success\t Stat. type\n') -fprintf('-------------------------------------------------------------------------------\n'); -fprintf('Reg \t %2.2e \t %2.2e \t\t %d \t\t\t %2.2f \t\t\t\t %d\t %s\n',f_opt_homotopy,stats_homotopy.comp_res,stats_homotopy.n_biactive,stats_homotopy.cpu_time_total,stats_homotopy.success,stats_homotopy.multiplier_based_stationarity) -% fprintf('MINLP \t\t %2.2e \t %2.2e \t\t %d \t\t\t %2.2f \t\t\t\t %d\t %s\n',f_opt_minlp,stats_minlp.comp_res,stats_minlp.n_biactive,stats_minlp.cpu_time_total,stats_minlp.success,stats_minlp.multiplier_based_stationarity) -fprintf('MPECopt \t %2.2e \t %2.2e \t\t %d \t\t\t %2.2f \t\t\t\t %d\t %s\n',f_opt_mpecopt,stats_mpecopt.comp_res,stats_mpecopt.n_biactive,stats_mpecopt.cpu_time_total,stats_mpecopt.success,stats_mpecopt.multiplier_based_stationarity) -fprintf('-------------------------------------------------------------------------------\n'); -fprintf('||w_reg - w_mpec|| = %2.2e \n',norm(w_opt_homotopy-w_opt_mpecopt)); -fprintf('||f_reg - f_mpec|| = %2.2e \n',norm(f_opt_homotopy-f_opt_mpecopt)); -% fprintf('||w_minlp - w_mpec|| = %2.2e \n',norm(w_opt_minlp-w_opt_mpecopt)); -fprintf('Solution: (%2.2f,%2.2f) \n',w_opt_mpecopt(1),w_opt_mpecopt(2)); - +% fprintf('\n-------------------------------------------------------------------------------\n'); +% fprintf('Method \t\t Objective \t comp_res \t n_biactive \t CPU time (s)\t Success\t Stat. type\n') +% fprintf('-------------------------------------------------------------------------------\n'); +% fprintf('Reg \t %2.2e \t %2.2e \t\t %d \t\t\t %2.2f \t\t\t\t %d\t %s\n',f_opt_homotopy,stats_homotopy.comp_res,stats_homotopy.n_biactive,stats_homotopy.cpu_time_total,stats_homotopy.success,stats_homotopy.multiplier_based_stationarity) +% % fprintf('MINLP \t\t %2.2e \t %2.2e \t\t %d \t\t\t %2.2f \t\t\t\t %d\t %s\n',f_opt_minlp,stats_minlp.comp_res,stats_minlp.n_biactive,stats_minlp.cpu_time_total,stats_minlp.success,stats_minlp.multiplier_based_stationarity) +% fprintf('MPECopt \t %2.2e \t %2.2e \t\t %d \t\t\t %2.2f \t\t\t\t %d\t %s\n',f_opt_mpecopt,stats_mpecopt.comp_res,stats_mpecopt.n_biactive,stats_mpecopt.cpu_time_total,stats_mpecopt.success,stats_mpecopt.multiplier_based_stationarity) +% fprintf('-------------------------------------------------------------------------------\n'); +% fprintf('||w_reg - w_mpec|| = %2.2e \n',norm(w_opt_homotopy-w_opt_mpecopt)); +% fprintf('||f_reg - f_mpec|| = %2.2e \n',norm(f_opt_homotopy-f_opt_mpecopt)); +% % fprintf('||w_minlp - w_mpec|| = %2.2e \n',norm(w_opt_minlp-w_opt_mpecopt)); +% fprintf('Solution: (%2.2f,%2.2f) \n',w_opt_mpecopt(1),w_opt_mpecopt(2)); +% From 436c03ff0ce3e39a32b7e7ffc4073d4fd206e9ff Mon Sep 17 00:00:00 2001 From: "armin.nurkanovic" Date: Tue, 9 Sep 2025 18:00:35 +0200 Subject: [PATCH 16/31] towards Large scale benchmarks --- .../current_problem.txt | 2 +- .../generate_nonlinear_mpec_problem_set.m | 45 ++++++++++++++- .../nonlinear_mpec_benchmark_dtable_loop.m | 3 + .../run_nonlinear_mpec_benchmark_large.m | 57 ++++++++++++------- .../solve_single_problem_nonlinear_mpec.m | 10 ++-- 5 files changed, 89 insertions(+), 28 deletions(-) diff --git a/benchmarks/nonlinear_mpec_benchmark/current_problem.txt b/benchmarks/nonlinear_mpec_benchmark/current_problem.txt index 89560ef..beca782 100644 --- a/benchmarks/nonlinear_mpec_benchmark/current_problem.txt +++ b/benchmarks/nonlinear_mpec_benchmark/current_problem.txt @@ -1 +1 @@ -nonlinear_mpec2_EDENSCH_Var10_Comp4_Eq4_Ineq5 +nonlinear_mpec2_DIXCHLNV_Var310_Comp124_Eq124_Ineq25 diff --git a/benchmarks/nonlinear_mpec_benchmark/generate_nonlinear_mpec_problem_set.m b/benchmarks/nonlinear_mpec_benchmark/generate_nonlinear_mpec_problem_set.m index 026c8af..508c11e 100644 --- a/benchmarks/nonlinear_mpec_benchmark/generate_nonlinear_mpec_problem_set.m +++ b/benchmarks/nonlinear_mpec_benchmark/generate_nonlinear_mpec_problem_set.m @@ -113,6 +113,9 @@ end fprintf('Largerst problem: n_var = %d, n_comp = %d \n',max(n_x_vec+2*n_y_vec), max(n_y_vec)) +n_comp_max = max(n_y_vec); +n_comp_min = 1000; % after this trashold addaptiv bound used + % range for feasible points range_x = [0,1]; range_y = [0,1]; @@ -127,6 +130,14 @@ else range_s_density = settings.range_s_density; end + + +if ~isfield(settings,'adaptive_density_bounds') + adaptive_density_bounds = 0; +else + adaptive_density_bounds = settings.adaptive_density_bounds; +end + range_A = [0,2]./rescale_factor; % uniform range_B = [0,2]./rescale_factor; % uniform % range_A = [-1,1]./rescale_factor; % uniform @@ -191,7 +202,36 @@ f = alpha_lin*f_lin; if settings.variable_density - s_density_A_B = range_s_density(1)+(range_s_density(2)-range_s_density(1)).*rand(1); + % Addaptive bounds on the densitiy, as for very larg problems less sparse problems can take hours to create an expression. + % graph + if adaptive_density_bounds + % for < 1000 keep fixed, for larger shrink linearly lower and upper bounds + % range_s_density_lb = max(0.0025, min(range_s_density(1), range_s_density(1) - (n_comp-n_comp_min)*(0.0075/(n_comp_max-n_comp_min)))); + % range_s_density_ub = max(0.005, min(range_s_density(2), range_s_density(2) - (n_comp-n_comp_min)*(0.045/(n_comp_max-n_comp_min)))); + if n_comp <= n_comp_min + % Below minimum: use original range_s_density bounds + range_s_density_lb = range_s_density(1); + range_s_density_ub = range_s_density(2); + + elseif n_comp >= n_comp_max + % At or above maximum: use target bounds + range_s_density_lb = 0.0025; + range_s_density_ub = 0.005; + else + % Between min and max: linear interpolation + % Calculate interpolation factor (0 at n_comp_min, 1 at n_comp_max) + alpha = (n_comp - n_comp_min) / (n_comp_max - n_comp_min); + + % Interpolate: shrink from original wide bounds to tighter target bounds + range_s_density_lb = range_s_density(1) + alpha * (0.0025 - range_s_density(1)); + range_s_density_ub = range_s_density(2) + alpha * (0.005 - range_s_density(2)); + end + s_density_A_B = range_s_density_lb +(range_s_density_ub-range_s_density_lb).*rand(1); + else + range_s_density_lb = range_s_density(1); + range_s_density_ub = range_s_density(2); + s_density_A_B = range_s_density_lb +(range_s_density_ub-range_s_density_lb).*rand(1); + end end switch objective_type @@ -497,7 +537,8 @@ r = round(1+(n_y-1)*rand(1)); % pick number between % s_density_M = (n_non_zero_E-n_y)/n_y^2; if settings.variable_density - s_density_M = range_s_density(1)+(range_s_density(2)-range_s_density(1)).*rand(1); + % s_density_M = range_s_density(1)+(range_s_density(2)-range_s_density(1)).*rand(1); + s_density_M = range_s_density_lb +(range_s_density_ub-range_s_density_lb).*rand(1); else s_density_M = settings.s_density_M; end diff --git a/benchmarks/nonlinear_mpec_benchmark/nonlinear_mpec_benchmark_dtable_loop.m b/benchmarks/nonlinear_mpec_benchmark/nonlinear_mpec_benchmark_dtable_loop.m index 4a75bc1..a126d4f 100644 --- a/benchmarks/nonlinear_mpec_benchmark/nonlinear_mpec_benchmark_dtable_loop.m +++ b/benchmarks/nonlinear_mpec_benchmark/nonlinear_mpec_benchmark_dtable_loop.m @@ -87,6 +87,7 @@ mpec_struct = struct('x',w,'f',f,'g',g,'G',G,'H',H); solver_initalization = struct('x0', w0, 'lbx',lbw, 'ubx',ubw,'lbg',lbg,'ubg',ubg); + fprintf(['Current problem solution started at: ' datestr(now) '.\n']) % to add non-mpecsol solver you can add ifs here if isequal(solver_functions{ii},@mpec_optimizer) solver = mpecopt.Solver(mpec_struct, options); @@ -94,6 +95,7 @@ else [result,stats] = solver_functions{ii}(mpec_struct,solver_initalization,options); end + fprintf(['Current problem solution ended at: ' datestr(now) '.\n']) % if stats.success~=1 % keyboard; @@ -152,6 +154,7 @@ dstruct.multiplier_based_stationarity = [dstruct.multiplier_based_stationarity; stats.multiplier_based_stationarity]; % j = j+1; total_success = total_success+stats.success; + fprintf(['success rate so far (%d of %d) or %2.2f precent \n\n'],total_success,jj,100*total_success/jj); jj = jj+1; diff --git a/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_large.m b/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_large.m index ebad1b8..b34d36c 100644 --- a/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_large.m +++ b/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_large.m @@ -6,7 +6,7 @@ results_name = ['results/' filename '_' datestr(datetime("today"))]; % name for matlab .dat with results %% Generate test set -settings.casadi_variable_type = 'MX'; +settings.casadi_variable_type = 'SX'; settings.nonlinear_eq_constraints = 1; settings.nonlinear_ineq_constraints = 1; settings.s_nonlinear_eq = 0.01; @@ -20,28 +20,38 @@ settings.inequality_constraints = 1; settings.inequality_constraints_coupling_terms = 1; % if 0, then in Ax+By >=f, B = 0; -% Trigonometric -settings.objective_functions = {'Quadratic_psd','Quadratic_ind',... - 'Fletcher','Himmelblau','McCormick',... +% settings.objective_functions = {'Quadratic_psd','Quadratic_ind',... +% 'Fletcher','Himmelblau','McCormick',... +% 'Powell','Rosenbrock',... +% 'Raydan1','Raydan2',... +% 'Diagonal4','Diagonal5',... +% 'Extended_Trigiaonal','Three_Exponential_Terms',... +% 'Generalized_PSC1','Extended_PSC1',... +% 'Fletcvb3','Bdqrtic','Tridia',... +% 'EG2','Edensch','Indef',... +% 'Cube','Bdexp','Genhumps',... +% 'Arwhead', 'Quartc', 'Cosine', 'Sine',... +% 'CURLY20', 'SCURLY30', 'FLETBV3M',... +% 'NCVXQP6','DIXCHLNV','EDENSCH',... +% }; + + +% reduced set +settings.objective_functions = {'Quadratic_psd',... + 'Fletcher','McCormick',... 'Powell','Rosenbrock',... 'Raydan1','Raydan2',... 'Diagonal4','Diagonal5',... 'Extended_Trigiaonal','Three_Exponential_Terms',... - 'Generalized_PSC1','Extended_PSC1',... + 'Generalized_PSC1',... 'Fletcvb3','Bdqrtic','Tridia',... 'EG2','Edensch','Indef',... 'Cube','Bdexp','Genhumps',... - 'Arwhead', 'Quartc', 'Cosine', 'Sine',... - 'CURLY20', 'SCURLY30', 'FLETBV3M',... - 'NCVXQP6','DIXCHLNV','EDENSCH',... + 'Arwhead', 'Quartc', 'Cosine',... + 'CURLY20',... + 'NCVXQP6','DIXCHLNV',... }; - -settings.objective_functions = {'FLETBV3M',... - 'NCVXQP6','DIXCHLNV','EDENSCH',... - }; - - settings.rescale_factor = 1; settings.round_all_data = 1; settings.n_digits = 4; @@ -57,11 +67,15 @@ settings.s_density_A_B = 0.05; % all same or change settings.s_density_M = 0.1; + +settings.adaptive_density_bounds = 1; % to account for very larg problems settings.variable_density = 1; -settings.range_s_density = [0.01 0.1]; +settings.range_s_density = [0.01 0.05]*3; settings.random_problem_sizes = 1; -settings.n_ineq_ub = 3; % n_ineq = n_ineq_ub*n_x % max relative number of ineq w.r.t x + + +settings.n_ineq_ub = 2; % n_ineq = n_ineq_ub*n_x % max relative number of ineq w.r.t x settings.n_ineq_lb = 0.5; % Problem size @@ -72,8 +86,8 @@ dimensions.N_rand_prob = 1; % number of problems per objective -dimensions.n_x_max = 10; -dimensions.n_x_min = 1; +dimensions.n_x_max = 50; +dimensions.n_x_min = 40; dimensions.n_fraction_of_x = 0.5; % n_y = round(n_x/n_fraction_of_x) @@ -82,9 +96,10 @@ % dimensions.n_y_vec = [10,10,25,40,50,70]; % dimensions.n_ineq_vec = [10,10,20,50,40,250]; +t_gen = tic; mpecs = generate_nonlinear_mpec_problem_set(problem_set_name,settings,dimensions); -length(mpecs) - +t_mpec_gen = toc(t_gen); +fprintf('Problem generation time : %2.2f \n',t_mpec_gen) %% Solver settings solver_names = ["MPECopt-Reg-Gurobi", "MPECopt-$\ell_1$-Gurobi", "MPECopt-Reg-Gurobi-ET",... @@ -159,7 +174,7 @@ %% Create data struct N_experiments = [1 4 5 3 2 6 7]; - +N_experiments = [1 ]; nonlinear_mpec_benchmark_dtable_loop; % this script runs the experimetns, creates a dtable %% Pick which results to plot diff --git a/benchmarks/nonlinear_mpec_benchmark/solve_single_problem_nonlinear_mpec.m b/benchmarks/nonlinear_mpec_benchmark/solve_single_problem_nonlinear_mpec.m index c2e5fc6..dd9f4b8 100644 --- a/benchmarks/nonlinear_mpec_benchmark/solve_single_problem_nonlinear_mpec.m +++ b/benchmarks/nonlinear_mpec_benchmark/solve_single_problem_nonlinear_mpec.m @@ -32,10 +32,10 @@ }; -settings.objective_functions = {'Edensch'}; +settings.objective_functions = {'EG2'}; + -settings.objective_functions settings.rescale_factor = 1; settings.round_all_data = 1; @@ -52,6 +52,8 @@ settings.s_density_A_B = 0.05; % all same or change settings.s_density_M = 0.1; + +settings.adaptive_density_bounds = 1; % to account for very larg problems settings.variable_density = 1; settings.range_s_density = [0.01 0.05]; settings.random_problem_sizes = 1; @@ -62,8 +64,8 @@ settings.n_fraction_of_x = 0.5; dimensions.N_rand_prob = 1; % number of problems per objective -dimensions.n_x_max = 1500; -dimensions.n_x_min = 100; +dimensions.n_x_max = 700; +dimensions.n_x_min = 700; dimensions.n_fraction_of_x = 0.5; % n_y = round(n_x/n_fraction_of_x) mpecs = generate_nonlinear_mpec_problem_set(problem_set_name,settings,dimensions); From 026553bb6c038366b31fde11ba5b0d9cc7fae9fc Mon Sep 17 00:00:00 2001 From: "armin.nurkanovic" Date: Wed, 10 Sep 2025 09:33:02 +0200 Subject: [PATCH 17/31] Towards a large scale benchmark that can be solved in a week --- .../current_problem.txt | 2 +- .../generate_nonlinear_mpec_problem_set.m | 13 ++++++-- .../run_nonlinear_mpec_benchmark_large.m | 30 ++++++++++--------- .../solve_single_problem_nonlinear_mpec.m | 9 +++--- 4 files changed, 31 insertions(+), 23 deletions(-) diff --git a/benchmarks/nonlinear_mpec_benchmark/current_problem.txt b/benchmarks/nonlinear_mpec_benchmark/current_problem.txt index beca782..4677ba9 100644 --- a/benchmarks/nonlinear_mpec_benchmark/current_problem.txt +++ b/benchmarks/nonlinear_mpec_benchmark/current_problem.txt @@ -1 +1 @@ -nonlinear_mpec2_DIXCHLNV_Var310_Comp124_Eq124_Ineq25 +nonlinear_mpec2_DIXCHLNV_Var225_Comp90_Eq90_Ineq26 diff --git a/benchmarks/nonlinear_mpec_benchmark/generate_nonlinear_mpec_problem_set.m b/benchmarks/nonlinear_mpec_benchmark/generate_nonlinear_mpec_problem_set.m index 508c11e..9150e6a 100644 --- a/benchmarks/nonlinear_mpec_benchmark/generate_nonlinear_mpec_problem_set.m +++ b/benchmarks/nonlinear_mpec_benchmark/generate_nonlinear_mpec_problem_set.m @@ -65,7 +65,13 @@ latexify_plot(); N_problems = N_objectives*N_rand_prob; n_x_max = max(N_problems+n_x_min,n_x_max); - n_x_vec = n_x_min+randperm(n_x_max-n_x_min,N_problems); % num of non comp vars + % n_x_vec = n_x_min+randperm(n_x_max-n_x_min,N_problems); % num of non comp vars + % Create a weighted distribution favoring smaller values + weights = linspace(5, 1, n_x_max-n_x_min+1); % Higher weights for smaller indices + weights = weights / sum(weights); % Normalize + % Generate biased random indices + biased_indices = randsample(1:(n_x_max-n_x_min+1), N_problems, true, weights); + n_x_vec = n_x_min + biased_indices - 1; n_y_vec = round(n_x_vec/n_fraction_of_x); % num of comp vars n_ineq_vec = round((settings.n_ineq_lb+(settings.n_ineq_ub-settings.n_ineq_lb)*(rand(1,N_problems)).*n_x_vec)); % n_ineq_vec = round((settings.n_ineq_lb+(settings.n_ineq_ub-settings.n_ineq_lb)*(rand(1,N_problems)).*n_x_vec))*3; @@ -544,17 +550,18 @@ end % s_density_M = settings.s_density_M; - + s_density_M = min(s_density_M, n_non_zero_E/(r*(n_y-r))); % given desnity but not more than 2000 nnz E = (range_E(2)-range_E(1))*sprand(r,n_y-r,s_density_M); E(E~=0) = range_E(1)+E(E~=0); + d1 = range_d1(1)+(range_d1(2)-range_d1(1)).*rand(r,1); d2 = range_d2(1)+(range_d2(2)-range_d2(1)).*rand(n_y-r,1); D1 = diag(d1); D2 = diag(d2); if settings.symmetric_psd_M_matrix M = [D1 E;... - E' D2]; + -E' D2]; [V,D] = eig(full(M)); M = V*abs(D)*V'; else diff --git a/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_large.m b/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_large.m index b34d36c..7dee59b 100644 --- a/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_large.m +++ b/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_large.m @@ -48,10 +48,15 @@ 'EG2','Edensch','Indef',... 'Cube','Bdexp','Genhumps',... 'Arwhead', 'Quartc', 'Cosine',... - 'CURLY20',... + 'CURLY20','SCURLY30',... 'NCVXQP6','DIXCHLNV',... }; + + +settings.objective_functions = {'NCVXQP6','DIXCHLNV',... + }; + settings.rescale_factor = 1; settings.round_all_data = 1; settings.n_digits = 4; @@ -70,21 +75,18 @@ settings.adaptive_density_bounds = 1; % to account for very larg problems settings.variable_density = 1; -settings.range_s_density = [0.01 0.05]*3; +settings.range_s_density = [0.01 0.05]; settings.random_problem_sizes = 1; - settings.n_ineq_ub = 2; % n_ineq = n_ineq_ub*n_x % max relative number of ineq w.r.t x settings.n_ineq_lb = 0.5; % Problem size - dimensions.N_rand_prob = 3; % number of problems per objective -dimensions.n_x_max = 1600; +dimensions.n_x_max = 1100; dimensions.n_x_min = 100; - dimensions.N_rand_prob = 1; % number of problems per objective dimensions.n_x_max = 50; dimensions.n_x_min = 40; @@ -115,6 +117,7 @@ opts1.relax_and_project_homotopy_parameter_steering = "Direct"; opts1.use_one_nlp_solver = true; opts1.problem_in_vertical_from = true; +opts1.settings_casadi_nlp.ipopt.max_wall_time = 900; % opts1.initialization_strategy = "FeasibilityEll1General"; @@ -124,6 +127,7 @@ opts2.relax_and_project_homotopy_parameter_steering = "Ell_1"; opts2.use_one_nlp_solver = true; opts2.problem_in_vertical_from = true; +opts2.settings_casadi_nlp.ipopt.max_wall_time = 900; % opts2.settings_lpec.stop_lpec_at_feasible = true; % opts2.rho_TR_phase_i_init = 1e-3; @@ -143,39 +147,37 @@ opts3.settings_lpec.stop_lpec_at_descent = true; opts3.consider_all_complementarities_in_lpec = false; opts3.problem_in_vertical_from = true; - +opts3.settings_casadi_nlp.ipopt.max_wall_time = 900; scholtes_opts1 = HomotopySolverOptions(); scholtes_opts1.homotopy_parameter_steering = 'Direct'; scholtes_opts1.problem_in_vertical_from = true; - +scholtes_opts1.settings_casadi_nlp.ipopt.max_wall_time = 900; scholtes_opts2 = HomotopySolverOptions(); scholtes_opts2.homotopy_parameter_steering = 'Direct'; scholtes_opts2.max_iter = 1; scholtes_opts2.sigma0 = 0; scholtes_opts2.problem_in_vertical_from = true; +scholtes_opts2.settings_casadi_nlp.ipopt.max_wall_time = 1800; scholtes_opts3 = HomotopySolverOptions(); scholtes_opts3.homotopy_parameter_steering = 'Ell_1'; scholtes_opts3.problem_in_vertical_from = true; +scholtes_opts3.settings_casadi_nlp.ipopt.max_wall_time = 900; minlp_opts = MINLPSolverOptions(); -minlp_opts.settings_casadi_nlp.bonmin.time_limit = 900; +minlp_opts.settings_casadi_nlp.bonmin.time_limit = 1800; scholtes_opts3.problem_in_vertical_from = true; opts = {opts1, opts2, opts3, ... scholtes_opts1, scholtes_opts2, scholtes_opts3,... minlp_opts}; % list of options to pass to mpecsol (option structs) - - %% Create data struct -N_experiments = [1 4 5 3 2 6 7]; -N_experiments = [1 ]; - +N_experiments = [3 4 5 1 2 6 7]; nonlinear_mpec_benchmark_dtable_loop; % this script runs the experimetns, creates a dtable %% Pick which results to plot dtable = dtable1; diff --git a/benchmarks/nonlinear_mpec_benchmark/solve_single_problem_nonlinear_mpec.m b/benchmarks/nonlinear_mpec_benchmark/solve_single_problem_nonlinear_mpec.m index dd9f4b8..978dfc0 100644 --- a/benchmarks/nonlinear_mpec_benchmark/solve_single_problem_nonlinear_mpec.m +++ b/benchmarks/nonlinear_mpec_benchmark/solve_single_problem_nonlinear_mpec.m @@ -35,8 +35,6 @@ settings.objective_functions = {'EG2'}; - - settings.rescale_factor = 1; settings.round_all_data = 1; settings.n_digits = 4; @@ -64,8 +62,8 @@ settings.n_fraction_of_x = 0.5; dimensions.N_rand_prob = 1; % number of problems per objective -dimensions.n_x_max = 700; -dimensions.n_x_min = 700; +dimensions.n_x_max = 1200; +dimensions.n_x_min = 1200; dimensions.n_fraction_of_x = 0.5; % n_y = round(n_x/n_fraction_of_x) mpecs = generate_nonlinear_mpec_problem_set(problem_set_name,settings,dimensions); @@ -110,7 +108,8 @@ solver_settings.relax_and_project_homotopy_parameter_steering = "Direct"; solver_settings.settings_lpec.lpec_solver = 'Gurobi'; solver_settings.use_one_nlp_solver = true; -solver_settings.problem_in_vertical_from = false; +solver_settings.problem_in_vertical_from = true; + % solver_settings.rho_TR_phase_i_init = 10; tic From 2638f83d4a33b0447882738bda8ecdd9440f654b Mon Sep 17 00:00:00 2001 From: "armin.nurkanovic" Date: Wed, 10 Sep 2025 13:27:52 +0200 Subject: [PATCH 18/31] better plots for lpec histograms --- benchmarks/histogram_on_lpecs.m | 44 +++++++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 10 deletions(-) diff --git a/benchmarks/histogram_on_lpecs.m b/benchmarks/histogram_on_lpecs.m index dd05688..f78f5e0 100644 --- a/benchmarks/histogram_on_lpecs.m +++ b/benchmarks/histogram_on_lpecs.m @@ -1,5 +1,6 @@ %% Evaluating LPEC MILP efficiency: close all; clc; +latexify_plot() % dtable; % lpec_dstruct; @@ -63,21 +64,33 @@ end end -% ------------------------ +%% +%------------------------ % Nodecount statistics (for each lpec call seperatalyover solver calls) % ------------------------ nc_i = cell2mat(process_cells(lpec_dstruct.nodecount_phase_i(idx))); nc_ii = cell2mat(process_cells(lpec_dstruct.nodecount_phase_ii(idx))); nc_tot = [nc_i, nc_ii]; +log_plot = true; figure; % --- Phase I --- subplot(1,2,1) -h1 = histogram(nc_i, 'BinMethod', 'integers'); +if log_plot + h1 = histogram(log2(nc_i), 'BinMethod', 'integers'); + min_pow = floor(min(log2(nc_i))); + max_pow = ceil(max(log2(nc_i))); + set(gca, 'XTick', min_pow:max_pow) + set(gca, 'XTickLabel', arrayfun(@(x) sprintf('$2^{%g}$', x), min_pow:max_pow, 'UniformOutput', false)) +else + h1 = histogram(nc_i, 'BinMethod', 'integers'); +end + +% Set ticks manually based on your data range title('Nodecount Phase I - each LPEC call'); xlabel('Nodes'); ylabel('Frequency'); grid on; - +xticks = get(gca, 'XTick'); % annotate counts for k = 1:numel(h1.BinEdges)-1 if h1.Values(k) > 0 @@ -86,21 +99,32 @@ text(x, y, num2str(y), 'HorizontalAlignment','center','VerticalAlignment','bottom'); end end - % adjust ylim ymax1 = max(h1.Values); ylim([0 ymax1 + max(2,ceil(0.2*ymax1))]) % --- Phase II --- subplot(1,2,2) -h2 = histogram(nc_ii, 'BinMethod', 'integers'); -title('Nodecount Phase II - each LPEC call'); -xlabel('Nodes'); ylabel('Frequency'); grid on; - -% enforce nicer axis if only one bar +if log_plot + h2 = histogram(log2(nc_ii), 'BinMethod', 'integers'); + % min_pow = floor(min(log2(nc_ii))); + % max_pow = ceil(max(log2(nc_ii))); + set(gca, 'XTick', min_pow:max_pow) + set(gca, 'XTickLabel', arrayfun(@(x) sprintf('$2^{%g}$', x), min_pow:max_pow, 'UniformOutput', false)) + % enforce nicer axis if only one bar + if numel(unique(nc_ii)) == 1 + xlim([-1 max_pow]); + end +else + h2 = histogram(nc_ii, 'BinMethod', 'integers'); + % enforce nicer axis if only one bar if numel(unique(nc_ii)) == 1 xlim([0 10]) end +end +title('Nodecount Phase II - each LPEC call'); +xlabel('Nodes'); ylabel('Frequency'); grid on; + % annotate counts for k = 1:numel(h2.BinEdges)-1 @@ -115,7 +139,7 @@ ymax2 = max(h2.Values); ylim([0 ymax2 + max(2,ceil(0.2*ymax2))]) - +%% % xticks(min(nc_ii):max(nc_ii)); % subplot(133) From 6b86d8905c05d77c9a82dfdab2e7eb3beb4e67e0 Mon Sep 17 00:00:00 2001 From: "armin.nurkanovic" Date: Wed, 10 Sep 2025 15:42:05 +0200 Subject: [PATCH 19/31] towards large scale benchamrks, less tnlp resolves for multiplier based stat detection --- .../macmpec/solve_single_macmpec_problem.m | 4 +- .../current_problem.txt | 2 +- .../generate_nonlinear_mpec_problem_set.m | 60 ++++++++++++++++--- .../nonlinear_mpec_benchmark_dtable_loop.m | 2 +- .../run_nonlinear_mpec_benchmark_large.m | 22 +++---- .../solve_single_problem_nonlinear_mpec.m | 23 ++++--- src/mpec_homotopy_solver.m | 52 +++++++++------- 7 files changed, 112 insertions(+), 53 deletions(-) diff --git a/benchmarks/macmpec/solve_single_macmpec_problem.m b/benchmarks/macmpec/solve_single_macmpec_problem.m index 8bab713..772d3ac 100644 --- a/benchmarks/macmpec/solve_single_macmpec_problem.m +++ b/benchmarks/macmpec/solve_single_macmpec_problem.m @@ -95,8 +95,8 @@ settings_homotopy = HomotopySolverOptions(); settings_homotopy.homotopy_parameter_steering = 'Direct'; [result_homotopy,stats_homotopy] = mpec_homotopy_solver(mpec,solver_initalization,settings_homotopy); -% f_opt_homotopy = full(result_homotopy.f); -% w_opt_homotopy = full(result_homotopy.x); +f_opt_homotopy = full(result_homotopy.f); +w_opt_homotopy = full(result_homotopy.x); %% MINLP solver settings_minlp = MINLPSolverOptions(); diff --git a/benchmarks/nonlinear_mpec_benchmark/current_problem.txt b/benchmarks/nonlinear_mpec_benchmark/current_problem.txt index 4677ba9..68b4237 100644 --- a/benchmarks/nonlinear_mpec_benchmark/current_problem.txt +++ b/benchmarks/nonlinear_mpec_benchmark/current_problem.txt @@ -1 +1 @@ -nonlinear_mpec2_DIXCHLNV_Var225_Comp90_Eq90_Ineq26 +nonlinear_mpec2_DIXCHLNV_Var100_Comp40_Eq40_Ineq3 diff --git a/benchmarks/nonlinear_mpec_benchmark/generate_nonlinear_mpec_problem_set.m b/benchmarks/nonlinear_mpec_benchmark/generate_nonlinear_mpec_problem_set.m index 9150e6a..f7cf120 100644 --- a/benchmarks/nonlinear_mpec_benchmark/generate_nonlinear_mpec_problem_set.m +++ b/benchmarks/nonlinear_mpec_benchmark/generate_nonlinear_mpec_problem_set.m @@ -43,6 +43,17 @@ unfold_struct(dimensions,'caller') +if ~isfield(settings,'nnz_bounded_by_dim') + nnz_bounded_by_dim = false; + +end + +if ~isfield(settings,'inv_cond_num') + inv_cond_num = 1e-4; + +end +nnz_factor = 1; + if ~isfield(settings,'casadi_variable_type') casadi_variable_type = 'SX'; % default; else @@ -251,7 +262,11 @@ % Quadratic % ... generate sparse Hessian matrix H (and make sure it it psd if needed) % Quadratic - H = full( sprandsym(n_obj, s_density_A_B) ); % ... full matrix for easier printing + if settings.nnz_bounded_by_dim + H = full( sprandsym(n_obj, n_obj/n_obj.^2,inv_cond_num) ); % ... full matrix for easier printing + else + H = full( sprandsym(n_obj, s_density_A_B) ); % ... full matrix for easier printing + end H = round( H , n_digits_data); % ... round matrix to nDigits min_eig = max( 0, -min( eig( H ) - eps_prec) ); % ... -epsPrec to ensure H is p.s.d. H = H + 2*round( min_eig*eye(n_obj), n_digits); % ... make trailing matrix p.s.d. @@ -259,7 +274,11 @@ f = 0.5*w(1:n_obj)'*H*w(1:n_obj)+grad_vec'*w(1:n_obj); % f = 0.5*v'*H*v+grad_vec'*w(1:n_obj); % +[c;d]'*v; case 'Quadratic_ind' - H = full( sprandsym(n_obj, s_density_A_B) ); + if settings.nnz_bounded_by_dim + H = full( sprandsym(n_obj, n_obj/n_obj.^2,inv_cond_num) ); % ... full matrix for easier printing + else + H = full( sprandsym(n_obj, s_density_A_B) ); % ... full matrix for easier printing + end H = round( H , n_digits_data); grad_vec = round( range_grad(1) + rand(n_obj,1) * (range_grad(2) - range_grad(1)) , n_digits ); f = 0.5*w(1:n_obj)'*H*w(1:n_obj)+grad_vec'*w(1:n_obj); @@ -267,7 +286,7 @@ % f = 0.5*v'*H*v+grad_vec'*v; % +[c;d]'*v; case 'Fletcher' for i = 1:n_obj-1 - f = f+100*(w(i+1)-w(i)+1-w(i)^2)^2; + f = f+(w(i+1)-w(i)+1-w(i)^2)^2; end case 'Himmelblau' for i = floor(n_obj/2) @@ -525,14 +544,24 @@ % end end + f = f*1e-3; % alliviate bad scaling + % Generate problem matrices if settings.inequality_constraints % Ax + By >= f - A = (range_A(2)-range_A(1))*sprand(n_ineq,n_x,s_density_A_B); + if settings.nnz_bounded_by_dim + A = (range_A(2)-range_A(1))*sprand(n_ineq,n_x,nnz_factor*n_ineq/(n_x*n_ineq),inv_cond_num); + else + A = (range_A(2)-range_A(1))*sprand(n_ineq,n_x,s_density_A_B); + end A(A~=0) = range_A(1)+A(A~=0); A = round(A,n_digits_data); if settings.inequality_constraints_coupling_terms - B = (range_B(2)-range_B(1))*sprand(n_ineq,n_y,s_density_A_B); + if settings.nnz_bounded_by_dim + B = (range_B(2)-range_B(1))*sprand(n_ineq,n_y,nnz_factor*n_ineq/(n_y*n_ineq),inv_cond_num); + else + B = (range_B(2)-range_B(1))*sprand(n_ineq,n_y,s_density_A_B); + end B(B~=0) = range_B(1)+B(B~=0); B = round(B,n_digits_data); else @@ -541,6 +570,9 @@ end r = round(1+(n_y-1)*rand(1)); % pick number between + if r == n_y + r = r-1; + end % s_density_M = (n_non_zero_E-n_y)/n_y^2; if settings.variable_density % s_density_M = range_s_density(1)+(range_s_density(2)-range_s_density(1)).*rand(1); @@ -551,7 +583,17 @@ % s_density_M = settings.s_density_M; s_density_M = min(s_density_M, n_non_zero_E/(r*(n_y-r))); % given desnity but not more than 2000 nnz - E = (range_E(2)-range_E(1))*sprand(r,n_y-r,s_density_M); + if settings.nnz_bounded_by_dim + % try + E = (range_E(2)-range_E(1))*sprand(r,n_y-r,n_non_zero_E/(r*(n_y-r)),inv_cond_num); + % catch + % keyboard; + % end + + else + E = (range_E(2)-range_E(1))*sprand(r,n_y-r,s_density_M); + end + E(E~=0) = range_E(1)+E(E~=0); @@ -575,7 +617,11 @@ % N = sprand(n_y,n_x,s_density); % r = a + (b-a).*rand(100,1); - N = (range_N(2)-range_N(1))*sprand(n_y,n_x,s_density_A_B); + if settings.nnz_bounded_by_dim + N = (range_N(2)-range_N(1))*sprand(n_y,n_x,nnz_factor*n_y/(n_x*n_y),inv_cond_num); + else + N = (range_N(2)-range_N(1))*sprand(n_y,n_x,s_density_A_B); + end N(N~=0) = range_N(1)+N(N~=0); N = round(N,n_digits_data); diff --git a/benchmarks/nonlinear_mpec_benchmark/nonlinear_mpec_benchmark_dtable_loop.m b/benchmarks/nonlinear_mpec_benchmark/nonlinear_mpec_benchmark_dtable_loop.m index a126d4f..97b9543 100644 --- a/benchmarks/nonlinear_mpec_benchmark/nonlinear_mpec_benchmark_dtable_loop.m +++ b/benchmarks/nonlinear_mpec_benchmark/nonlinear_mpec_benchmark_dtable_loop.m @@ -66,7 +66,7 @@ for jj = n_mpecs mpec = mpecs(jj); w = mpec.w; - f = mpec.f_fun(mpec.w); + f = mpec.f_fun(mpec.w); % alliviate bad scaling of random problems g = mpec.g_fun(mpec.w); G = mpec.G_fun(mpec.w); H = mpec.H_fun(mpec.w); diff --git a/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_large.m b/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_large.m index 7dee59b..b61fb88 100644 --- a/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_large.m +++ b/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_large.m @@ -53,10 +53,6 @@ }; - -settings.objective_functions = {'NCVXQP6','DIXCHLNV',... - }; - settings.rescale_factor = 1; settings.round_all_data = 1; settings.n_digits = 4; @@ -73,6 +69,9 @@ settings.s_density_M = 0.1; +settings.nnz_bounded_by_dim = 1; +settings.inv_cond_num = 1e-2; + settings.adaptive_density_bounds = 1; % to account for very larg problems settings.variable_density = 1; settings.range_s_density = [0.01 0.05]; @@ -84,12 +83,12 @@ % Problem size dimensions.N_rand_prob = 3; % number of problems per objective -dimensions.n_x_max = 1100; +dimensions.n_x_max = 1250; dimensions.n_x_min = 100; -dimensions.N_rand_prob = 1; % number of problems per objective -dimensions.n_x_max = 50; -dimensions.n_x_min = 40; +% dimensions.N_rand_prob = 1; % number of problems per objective +% dimensions.n_x_max = 50; +% dimensions.n_x_min = 20; dimensions.n_fraction_of_x = 0.5; % n_y = round(n_x/n_fraction_of_x) @@ -178,6 +177,7 @@ %% Create data struct N_experiments = [3 4 5 1 2 6 7]; +% N_experiments = [3 4]; nonlinear_mpec_benchmark_dtable_loop; % this script runs the experimetns, creates a dtable %% Pick which results to plot dtable = dtable1; @@ -214,7 +214,7 @@ plot_settings.active_set_changes = 0; plot_settings.lpecs_cpu_time = 0; plot_settings.bar_timing_plots = 0; -plot_settings.nlps_solved = 1; +plot_settings.nlps_solved = 0; plot_settings.max_nlp_cpu_time = 0; plot_settings.max_nlp_cpu_time_phase_i = 0; plot_settings.max_nlp_cpu_time_phase_ii = 0; @@ -242,8 +242,8 @@ plot_settings.lpec_phases_cpu_time = 0; plot_settings.max_lpec_cpu_time = 0; -plot_settings.stationary_points = 1; -plot_settings.b_stationarity = 1; +plot_settings.stationary_points = 0; +plot_settings.b_stationarity = 0; plot_settings.b_stationarty_as_success_criterion = 0; plot_settings.plot_only_sucessful = 1; plot_settings.bar_comparisson_plots = 0; diff --git a/benchmarks/nonlinear_mpec_benchmark/solve_single_problem_nonlinear_mpec.m b/benchmarks/nonlinear_mpec_benchmark/solve_single_problem_nonlinear_mpec.m index 978dfc0..ec91e1d 100644 --- a/benchmarks/nonlinear_mpec_benchmark/solve_single_problem_nonlinear_mpec.m +++ b/benchmarks/nonlinear_mpec_benchmark/solve_single_problem_nonlinear_mpec.m @@ -16,7 +16,7 @@ settings.inequality_constraints_coupling_terms = 1; % if 0, then in Ax+By >=f, B = 0; % Trigonometric -settings.objective_functions = {'Quadratic_psd','Quadratic_ind',... +settings.objective_functions_all = {'Quadratic_psd','Quadratic_ind',... 'Fletcher','Himmelblau','McCormick',... 'Powell','Rosenbrock',... 'Raydan1','Raydan2',... @@ -32,7 +32,7 @@ }; -settings.objective_functions = {'EG2'}; +settings.objective_functions = {settings.objective_functions_all{15}}; settings.rescale_factor = 1; @@ -51,6 +51,9 @@ settings.s_density_M = 0.1; +settings.nnz_bounded_by_dim = 1; +settings.inv_cond_num = 1e-2; + settings.adaptive_density_bounds = 1; % to account for very larg problems settings.variable_density = 1; settings.range_s_density = [0.01 0.05]; @@ -62,15 +65,17 @@ settings.n_fraction_of_x = 0.5; dimensions.N_rand_prob = 1; % number of problems per objective -dimensions.n_x_max = 1200; -dimensions.n_x_min = 1200; +dimensions.n_x_max = 1500; +dimensions.n_x_min = 1500; + + dimensions.n_fraction_of_x = 0.5; % n_y = round(n_x/n_fraction_of_x) mpecs = generate_nonlinear_mpec_problem_set(problem_set_name,settings,dimensions); length(mpecs) %% mpecopt solvers -ii_prob = 1; % 10 41 48 85 87 +ii_prob = 1; close all; mpec = mpecs(ii_prob); @@ -91,7 +96,7 @@ mpec_struct = struct('x',w,'f',f,'g',g,'G',G,'H',H); solver_initalization = struct('x0', w0, 'lbx',lbw, 'ubx',ubw,'lbg',lbg,'ubg',ubg); %% Homotopy solver -% settings_homotopy = HomotopySolverOptions(); +settings_homotopy = HomotopySolverOptions(); % settings_homotopy.homotopy_parameter_steering = "Direct"; % [result_homotopy,stats_homotopy] = mpec_homotopy_solver(mpec,solver_initalization,settings_homotopy); % f_opt_homotopy = full(result_homotopy.f); @@ -109,11 +114,13 @@ solver_settings.settings_lpec.lpec_solver = 'Gurobi'; solver_settings.use_one_nlp_solver = true; solver_settings.problem_in_vertical_from = true; - +solver_settings.settings_casadi_nlp.ipopt.print_level = 5; +% solver_settings.settings_casadi_nlp.ipopt.max_iter = 100; +% solver_settings.settings_casadi_nlp.jit = true; % solver_settings.rho_TR_phase_i_init = 10; tic -solver = mpecopt.Solver(mpec, solver_settings); +solver = mpecopt.Solver(mpec_struct, solver_settings); toc %% [result_mpecopt,stats_mpecopt] = solver.solve(solver_initalization); diff --git a/src/mpec_homotopy_solver.m b/src/mpec_homotopy_solver.m index 2aec844..c917a71 100644 --- a/src/mpec_homotopy_solver.m +++ b/src/mpec_homotopy_solver.m @@ -560,6 +560,7 @@ tol_active_default = settings.tol_active; tol_active = 1e-6; % tnlp % tol_active = 1e2; % tnlp +n_biactive_prev = 0; if settings.compute_tnlp_stationary_point && success && settings.lift_complementarities && ~settings.problem_is_lpec fprintf('----------------------------------- determining stationarity -----------------------------------------------\n') @@ -575,34 +576,39 @@ ubx(dims.ind_x2(active_set_estimate_k.I_plus_0)) = 0; ubx(dims.ind_x1(active_set_estimate_k.I_00)) = 0; ubx(dims.ind_x2(active_set_estimate_k.I_00)) = 0; - % n_biactive = sum(active_set_estimate_k.I_00) - - solution = solver('x0',x_k,'p',p0,'lbx',lbx,'ubx',ubx,'lbg',lbg,'ubg',ubg); - lambda_x_tnlp = full(solution.lam_x); - x_tnlp = full(solution.x); - f_tnlp = full(solution.f); - comp_res_tnlp = full(h_comp_constraints_fun(x_k,p0)); - inf_pr_tnlp = stats.iterations.inf_pr(end); - inf_du_tnlp = stats.iterations.inf_du(end); - settings.tol_active = tol_active; - if settings.verbose_solver - fprintf('%d \t %2.2e\t %2.2e\t %2.2e\t %2.2e\t %d \t\t %2.2e\t \t\t %s \n',ii,f_tnlp,comp_res_tnlp,inf_pr_tnlp,inf_du_tnlp,stats.iter_count,0,(stats.return_status)); - end - fprintf('\t\t ||x_tnlp - x_k|| = %2.2e, |f_tnlp-f_k| = %2.2e \n', norm(x_tnlp-x_k,inf),abs(f_tnlp-f_k)) - % Terminate TNLP loop - if norm(x_tnlp-x_k,inf) <= 1e-6 || abs(f_tnlp-f_k)/abs(f_k) <= 1e-3 || ii == N_TNLP - [multiplier_based_stationarity, ~] = determine_multipliers_based_stationary_point(x_tnlp,lambda_x_tnlp,dims,settings); - n_biactive = sum(active_set_estimate_k.I_00); - if ii~=N_TNLP - x_k = x_tnlp; + n_biactive_current = sum(active_set_estimate_k.I_00); + if n_biactive_current~= n_biactive_prev || ii == 1 + solution = solver('x0',x_k,'p',p0,'lbx',lbx,'ubx',ubx,'lbg',lbg,'ubg',ubg); + lambda_x_tnlp = full(solution.lam_x); + x_tnlp = full(solution.x); + f_tnlp = full(solution.f); + comp_res_tnlp = full(h_comp_constraints_fun(x_k,p0)); + inf_pr_tnlp = stats.iterations.inf_pr(end); + inf_du_tnlp = stats.iterations.inf_du(end); + settings.tol_active = tol_active; + if settings.verbose_solver + fprintf('%d \t %2.2e\t %2.2e\t %2.2e\t %2.2e\t %d \t\t %2.2e\t \t\t %s \n',ii,f_tnlp,comp_res_tnlp,inf_pr_tnlp,inf_du_tnlp,stats.iter_count,0,(stats.return_status)); + end + fprintf('\t\t ||x_tnlp - x_k|| = %2.2e, |f_tnlp-f_k| = %2.2e \n', norm(x_tnlp-x_k,inf),abs(f_tnlp-f_k)) + % Terminate TNLP loop + if norm(x_tnlp-x_k,inf) <= 1e-6 || abs(f_tnlp-f_k)/abs(f_k) <= 1e-3 || ii == N_TNLP + [multiplier_based_stationarity, ~] = determine_multipliers_based_stationary_point(x_tnlp,lambda_x_tnlp,dims,settings); + n_biactive = sum(active_set_estimate_k.I_00); + if ii~=N_TNLP + x_k = x_tnlp; + end + break; + else + % drop out greatest biactive + % tol_active = (1-1e-9)*max(max(abs(x_k(dims.ind_x1(active_set_estimate_k.I_00))),x_k(dims.ind_x2(active_set_estimate_k.I_00)))); + tol_active = 0.1*tol_active; + ii = ii+1; end - break; else - % drop out greatest biactive - % tol_active = (1-1e-9)*max(max(abs(x_k(dims.ind_x1(active_set_estimate_k.I_00))),x_k(dims.ind_x2(active_set_estimate_k.I_00)))); tol_active = 0.1*tol_active; ii = ii+1; end + n_biactive_prev = n_biactive_current; end % if ~strcmp(multiplier_based_stationarity,'X') % end From 1938c981cb0303d285493e863c7b875c41f552ad Mon Sep 17 00:00:00 2001 From: "armin.nurkanovic" Date: Thu, 11 Sep 2025 09:37:40 +0200 Subject: [PATCH 20/31] B stationarity needs optimality, check included explicitly --- src/+mpecopt/Solver.m | 9 +++++---- src/lpec_solver.m | 18 ++++++++++++++++-- src/mpec_homotopy_solver.m | 3 ++- src/options/LPECSolverOptions.m | 2 +- 4 files changed, 24 insertions(+), 8 deletions(-) diff --git a/src/+mpecopt/Solver.m b/src/+mpecopt/Solver.m index d3387ca..079390c 100644 --- a/src/+mpecopt/Solver.m +++ b/src/+mpecopt/Solver.m @@ -1575,13 +1575,14 @@ function create_lpec_functions(obj) bnlp_solved = true; % from phase 1 end % if (h_total_k <= opts.tol) && ((abs(f_lin_opt_k_l) <= opts.tol_B_stationarity || norm(nabla_f_k) <= opts.tol_B_stationarity)) % if objective zero (either if cost gradient zero, or solution leads to it) = then set step to zero => B stationarity - if bnlp_solved && ((abs(f_lin_opt_k_l) <= opts.tol_B_stationarity || norm(nabla_f_k) <= opts.tol_B_stationarity)) % if objective zero (either if cost gradient zero, or solution leads to it) = then set step to zero => B stationarity + if ((abs(f_lin_opt_k_l) <= opts.tol_B_stationarity || norm(nabla_f_k) <= opts.tol_B_stationarity)) % if objective zero (either if cost gradient zero, or solution leads to it) = then set step to zero => B stationarity if opts.reset_lpec_objective d_lpec_k_l = d_lpec_k_l*0; % if the current point is feasible, and the objective is zero, then d = 0 is also a solution of the lpec (occurs if a solution is not on the verties of the lp) f_lin_opt_k_l = 0; end end - if norm(d_lpec_k_l) <= opts.tol_B_stationarity + % check B-stat if BNLP and LPEC where solved sucessfully + if norm(d_lpec_k_l) <= opts.tol_B_stationarity && stats_lpec.optimal_solution_found && bnlp_solved % if abs(f_lin_opt_k_l) <= settings.tol_B_stationarity stats.stopping_criterion_fullfiled = true; % B-stationary point found, optimal solution found! stats.solver_message = 'B-stationary point found successfully.'; @@ -1839,7 +1840,7 @@ function create_lpec_functions(obj) end % ------------- max iteration but early terminaton tolorance achieved?--------------------- if ((stats.max_iterations_reached && stats.success == 0) || n_cycles == 3)&& opts.allow_early_termination - if (h_total_k <= opts.tol_B_stationarity_early_term) && ((abs(f_lin_opt_k_l) <= opts.tol_B_stationarity_early_term|| norm(nabla_f_k) <= opts.tol_B_stationarity_early_term)) % if objective zero (either if cost gradient zero, or solution leads to it) = then set step to zero => B stationarity + if stats_lpec.optimal_solution_found && (h_total_k <= opts.tol_B_stationarity_early_term) && ((abs(f_lin_opt_k_l) <= opts.tol_B_stationarity_early_term|| norm(nabla_f_k) <= opts.tol_B_stationarity_early_term)) % if objective zero (either if cost gradient zero, or solution leads to it) = then set step to zero => B stationarity % B-stationary point found, optimal solution found! if n_cycles == 3 stats.solver_message = 'Major loop was cycling due to bad problem scaling or too low tolerances. B-stationary point found at lower tolerance.'; @@ -1910,7 +1911,7 @@ function create_lpec_functions(obj) % [stats.multiplier_based_stationarity, ~] = determine_multipliers_based_stationary_point(x_k_multi,lambda_x_k,dims,opts); end end - settings.tol_active = tol_active_default; % reset + opts.tol_active = tol_active_default; % reset % Debug falure of stationary point computation diff --git a/src/lpec_solver.m b/src/lpec_solver.m index 319dc9a..ea6463c 100644 --- a/src/lpec_solver.m +++ b/src/lpec_solver.m @@ -33,6 +33,7 @@ stats.baritercount = 0; % nuber of barrier iters stats.nodecount = 0; % number of nodes in BnB stats.gap = inf; % integer gap +stats.optimal_solution_found = false; % B-stationarity can be checked only if the LPEC is solved to optimality, in other cases it is sufficent to have a feasible point. %% Prepare LPEC % add the boundso of the inaries @@ -282,7 +283,11 @@ cpu_time_gurobi = nan; end - if ( isequal(result_gurobi.status,'OPTIMAL') || isequal(result_gurobi.status,'NODE_LIMIT')|| isequal(result_gurobi.status,'USER_OBJ_LIMIT') || isequal(result_gurobi.status,'SOLUTION_LIMIT')) && isfield(result_gurobi,'x') + if isequal(result_gurobi.status,'OPTIMAL') + stats.optimal_solution_found = true; + end + + if (isequal(result_gurobi.status,'OPTIMAL') || isequal(result_gurobi.status,'NODE_LIMIT')|| isequal(result_gurobi.status,'USER_OBJ_LIMIT') || isequal(result_gurobi.status,'SOLUTION_LIMIT')) && isfield(result_gurobi,'x') results.d_lpec = result_gurobi.x(1:lpec.dims.n_primal); results.y_lpec = result_gurobi.x(end-lpec.dims.n_auxiliary+1:end); results.f_opt = result_gurobi.objval; @@ -323,6 +328,9 @@ else stats.solver_message = 'OPTIMAL'; end + if status == 1 + stats.optimal_solution_found = true; + end case {0,-2,-3,-9} results.d_lpec = lpec.d_lpec*nan; results.y_lpec = lpec.y_lpec*nan; @@ -355,8 +363,11 @@ results.y_lpec = round(x(lpec.vtype_num==1)); results.f_opt = full(r.cost); stats.lpec_solution_exists = true; - stats.success = highs_success; + + if strcmp(lpsol.stats.return_status, 'Optimal'); + stats.optimal_solution_found = true; + end else results.d_lpec = lpec.d_lpec*nan; results.y_lpec = lpec.y_lpec*nan; @@ -400,6 +411,8 @@ % results.y_lpec = lpec.x_lin(lpec.dims.ind_x1)+results.d_lpec(lpec.dims.ind_x1)>=lpec.x_lin(lpec.dims.ind_x2)+results.d_lpec(lpec.dims.ind_x2); results.f_opt = result_homotopy.f; stats.lpec_solution_exists = true; + stats.optimal_solution_found = true; + else results.d_lpec = result_homotopy.x; results.y_lpec = results.d_lpec(lpec.dims.ind_x1)>=results.d_lpec(lpec.dims.ind_x2); @@ -461,6 +474,7 @@ y_lpec = lpec.x_lin(lpec.dims.ind_x1)+d_lpec(lpec.dims.ind_x1)>lpec.x_lin(lpec.dims.ind_x2)+d_lpec(lpec.dims.ind_x2); lpec_solution_exists = true; solver_message = 'OPTIMAL'; + stats.optimal_solution_found = true; catch f_opt = nan; y_lpec = nan; diff --git a/src/mpec_homotopy_solver.m b/src/mpec_homotopy_solver.m index c917a71..39eaa14 100644 --- a/src/mpec_homotopy_solver.m +++ b/src/mpec_homotopy_solver.m @@ -558,7 +558,7 @@ tol_ell_inf = full(h_comp_constraints_tol_fun(x_k,p0)); % tol_active = max(2*settings.tol_active*1e3,2*tol_ell_inf); tol_active_default = settings.tol_active; -tol_active = 1e-6; % tnlp +tol_active = 1e-5; % tnlp % tol_active = 1e2; % tnlp n_biactive_prev = 0; if settings.compute_tnlp_stationary_point && success && settings.lift_complementarities && ~settings.problem_is_lpec @@ -590,6 +590,7 @@ fprintf('%d \t %2.2e\t %2.2e\t %2.2e\t %2.2e\t %d \t\t %2.2e\t \t\t %s \n',ii,f_tnlp,comp_res_tnlp,inf_pr_tnlp,inf_du_tnlp,stats.iter_count,0,(stats.return_status)); end fprintf('\t\t ||x_tnlp - x_k|| = %2.2e, |f_tnlp-f_k| = %2.2e \n', norm(x_tnlp-x_k,inf),abs(f_tnlp-f_k)) + fprintf('\t\t n_biactive = %d \n',n_biactive_current) % Terminate TNLP loop if norm(x_tnlp-x_k,inf) <= 1e-6 || abs(f_tnlp-f_k)/abs(f_k) <= 1e-3 || ii == N_TNLP [multiplier_based_stationarity, ~] = determine_multipliers_based_stationary_point(x_tnlp,lambda_x_tnlp,dims,settings); diff --git a/src/options/LPECSolverOptions.m b/src/options/LPECSolverOptions.m index cfed2c9..7f777a0 100644 --- a/src/options/LPECSolverOptions.m +++ b/src/options/LPECSolverOptions.m @@ -6,7 +6,7 @@ % MILP Solver for LPEC settings lpec_solver(1,1) LpecSolver = LpecSolver.Gurobi; % LpecSolver.Gurobi - max_nodes(1,1) double {mustBeInteger, mustBePositive} = 5e2; + max_nodes(1,1) double {mustBeInteger, mustBePositive} = 5e3; max_time(1,1) double {mustBeReal, mustBePositive} = 2e2; % ~3 min time out; cutoff(1,1) double {mustBeReal} = 10; rel_tol(1,1) double {mustBeReal, mustBeNonempty} = 1e-9; From 32821a3293e86a7f908867bf6843ccc443b20d6d Mon Sep 17 00:00:00 2001 From: "armin.nurkanovic" Date: Thu, 11 Sep 2025 09:38:08 +0200 Subject: [PATCH 21/31] support for very large sparse random mpecs --- .../generate_nonlinear_mpec_problem_set.m | 80 +++++++++++-------- .../solve_single_problem_nonlinear_mpec.m | 25 +++--- 2 files changed, 61 insertions(+), 44 deletions(-) diff --git a/benchmarks/nonlinear_mpec_benchmark/generate_nonlinear_mpec_problem_set.m b/benchmarks/nonlinear_mpec_benchmark/generate_nonlinear_mpec_problem_set.m index f7cf120..d4055b7 100644 --- a/benchmarks/nonlinear_mpec_benchmark/generate_nonlinear_mpec_problem_set.m +++ b/benchmarks/nonlinear_mpec_benchmark/generate_nonlinear_mpec_problem_set.m @@ -45,14 +45,21 @@ if ~isfield(settings,'nnz_bounded_by_dim') nnz_bounded_by_dim = false; - end +if ~isfield(settings,'n_comp_min') + n_comp_min = 500; % after this trashold addaptiv bound used +end + + if ~isfield(settings,'inv_cond_num') - inv_cond_num = 1e-4; - + inv_cond_num = 1e-1; end -nnz_factor = 1; + +if ~isfield(settings,'nnz_factor') + nnz_factor = 1; +end + if ~isfield(settings,'casadi_variable_type') casadi_variable_type = 'SX'; % default; @@ -131,7 +138,6 @@ fprintf('Largerst problem: n_var = %d, n_comp = %d \n',max(n_x_vec+2*n_y_vec), max(n_y_vec)) n_comp_max = max(n_y_vec); -n_comp_min = 1000; % after this trashold addaptiv bound used % range for feasible points range_x = [0,1]; @@ -229,7 +235,6 @@ % Below minimum: use original range_s_density bounds range_s_density_lb = range_s_density(1); range_s_density_ub = range_s_density(2); - elseif n_comp >= n_comp_max % At or above maximum: use target bounds range_s_density_lb = 0.0025; @@ -262,8 +267,12 @@ % Quadratic % ... generate sparse Hessian matrix H (and make sure it it psd if needed) % Quadratic - if settings.nnz_bounded_by_dim - H = full( sprandsym(n_obj, n_obj/n_obj.^2,inv_cond_num) ); % ... full matrix for easier printing + if nnz_bounded_by_dim + if adaptive_density_bounds + H = full( sprandsym(n_obj, s_density_A_B) ); % for smaller matrices use provided density bounds, for larger use sparse + else + H = full( sprandsym(n_obj, n_obj/n_obj.^2,inv_cond_num) ); % ... full matrix for easier printing + end else H = full( sprandsym(n_obj, s_density_A_B) ); % ... full matrix for easier printing end @@ -549,19 +558,25 @@ % Generate problem matrices if settings.inequality_constraints % Ax + By >= f + s_density_A = s_density_A_B; if settings.nnz_bounded_by_dim - A = (range_A(2)-range_A(1))*sprand(n_ineq,n_x,nnz_factor*n_ineq/(n_x*n_ineq),inv_cond_num); - else - A = (range_A(2)-range_A(1))*sprand(n_ineq,n_x,s_density_A_B); + if ~(n_comp <= n_comp_min && adaptive_density_bounds) + s_density_A = nnz_factor*n_ineq/(n_x*n_ineq); + end end + A = (range_A(2)-range_A(1))*sprand(n_ineq,n_x,s_density_A,inv_cond_num); + A(A~=0) = range_A(1)+A(A~=0); A = round(A,n_digits_data); if settings.inequality_constraints_coupling_terms - if settings.nnz_bounded_by_dim - B = (range_B(2)-range_B(1))*sprand(n_ineq,n_y,nnz_factor*n_ineq/(n_y*n_ineq),inv_cond_num); - else - B = (range_B(2)-range_B(1))*sprand(n_ineq,n_y,s_density_A_B); + s_density_B = s_density_A_B; + if settings.nnz_bounded_by_dim + if ~(n_comp <= n_comp_min && adaptive_density_bounds) + s_density_B = nnz_factor*n_ineq/(n_y*n_ineq); + end end + B = (range_B(2)-range_B(1))*sprand(n_ineq,n_y,s_density_B ,inv_cond_num); + B(B~=0) = range_B(1)+B(B~=0); B = round(B,n_digits_data); else @@ -573,30 +588,26 @@ if r == n_y r = r-1; end + + + % s_density_M = (n_non_zero_E-n_y)/n_y^2; if settings.variable_density % s_density_M = range_s_density(1)+(range_s_density(2)-range_s_density(1)).*rand(1); s_density_M = range_s_density_lb +(range_s_density_ub-range_s_density_lb).*rand(1); + s_density_M = min(s_density_M, n_non_zero_E/(r*(n_y-r))); % given desnity but not more than 2000 nnz + if settings.nnz_bounded_by_dim + if ~(n_comp <= n_comp_min && adaptive_density_bounds) + s_density_M = nnz_factor*min(r,n_non_zero_E)/(n_y*n_ineq); + end + end else s_density_M = settings.s_density_M; end - % s_density_M = settings.s_density_M; - - s_density_M = min(s_density_M, n_non_zero_E/(r*(n_y-r))); % given desnity but not more than 2000 nnz - if settings.nnz_bounded_by_dim - % try - E = (range_E(2)-range_E(1))*sprand(r,n_y-r,n_non_zero_E/(r*(n_y-r)),inv_cond_num); - % catch - % keyboard; - % end + E = (range_E(2)-range_E(1))*sprand(r,n_y-r,s_density_M,inv_cond_num); - else - E = (range_E(2)-range_E(1))*sprand(r,n_y-r,s_density_M); - end - E(E~=0) = range_E(1)+E(E~=0); - d1 = range_d1(1)+(range_d1(2)-range_d1(1)).*rand(r,1); d2 = range_d2(1)+(range_d2(2)-range_d2(1)).*rand(n_y-r,1); D1 = diag(d1); @@ -617,11 +628,14 @@ % N = sprand(n_y,n_x,s_density); % r = a + (b-a).*rand(100,1); - if settings.nnz_bounded_by_dim - N = (range_N(2)-range_N(1))*sprand(n_y,n_x,nnz_factor*n_y/(n_x*n_y),inv_cond_num); - else - N = (range_N(2)-range_N(1))*sprand(n_y,n_x,s_density_A_B); + s_density_N = s_density_A_B; + if settings.nnz_bounded_by_dim + if ~(n_comp <= n_comp_min && adaptive_density_bounds) + s_density_N = nnz_factor*n_y/(n_x*n_y); + end end + N = (range_N(2)-range_N(1))*sprand(n_y,n_x,s_density_N,inv_cond_num); + N(N~=0) = range_N(1)+N(N~=0); N = round(N,n_digits_data); diff --git a/benchmarks/nonlinear_mpec_benchmark/solve_single_problem_nonlinear_mpec.m b/benchmarks/nonlinear_mpec_benchmark/solve_single_problem_nonlinear_mpec.m index ec91e1d..208b72f 100644 --- a/benchmarks/nonlinear_mpec_benchmark/solve_single_problem_nonlinear_mpec.m +++ b/benchmarks/nonlinear_mpec_benchmark/solve_single_problem_nonlinear_mpec.m @@ -52,9 +52,10 @@ settings.nnz_bounded_by_dim = 1; -settings.inv_cond_num = 1e-2; +settings.inv_cond_num = 1e0; +settings.nnz_factor = 1; -settings.adaptive_density_bounds = 1; % to account for very larg problems +settings.adaptive_density_bounds = 0; % to account for very larg problems settings.variable_density = 1; settings.range_s_density = [0.01 0.05]; settings.random_problem_sizes = 1; @@ -97,8 +98,8 @@ solver_initalization = struct('x0', w0, 'lbx',lbw, 'ubx',ubw,'lbg',lbg,'ubg',ubg); %% Homotopy solver settings_homotopy = HomotopySolverOptions(); -% settings_homotopy.homotopy_parameter_steering = "Direct"; -% [result_homotopy,stats_homotopy] = mpec_homotopy_solver(mpec,solver_initalization,settings_homotopy); +settings_homotopy.homotopy_parameter_steering = "Direct"; +[result_homotopy,stats_homotopy] = mpec_homotopy_solver(mpec,solver_initalization,settings_homotopy); % f_opt_homotopy = full(result_homotopy.f); % w_opt_homotopy = full(result_homotopy.x); @@ -109,18 +110,20 @@ % w_opt_minlp = full(result_minlp.x); %% MPECopt solver -solver_settings = mpecopt.Options(); -solver_settings.relax_and_project_homotopy_parameter_steering = "Direct"; -solver_settings.settings_lpec.lpec_solver = 'Gurobi'; -solver_settings.use_one_nlp_solver = true; -solver_settings.problem_in_vertical_from = true; -solver_settings.settings_casadi_nlp.ipopt.print_level = 5; +opts = mpecopt.Options(); +opts.relax_and_project_homotopy_parameter_steering = "Direct"; +opts.settings_lpec.lpec_solver = 'Gurobi'; +opts.use_one_nlp_solver = true; +opts.problem_in_vertical_from = true; +opts.settings_casadi_nlp.ipopt.print_level = 5; +opts.rho_TR_phase_ii_init = 1e-1; + % solver_settings.settings_casadi_nlp.ipopt.max_iter = 100; % solver_settings.settings_casadi_nlp.jit = true; % solver_settings.rho_TR_phase_i_init = 10; tic -solver = mpecopt.Solver(mpec_struct, solver_settings); +solver = mpecopt.Solver(mpec_struct, opts); toc %% [result_mpecopt,stats_mpecopt] = solver.solve(solver_initalization); From a56b8e82a374e634b6b0b815fdc9d6e32ee7479a Mon Sep 17 00:00:00 2001 From: "armin.nurkanovic" Date: Fri, 12 Sep 2025 15:40:12 +0200 Subject: [PATCH 22/31] set up multiple experiments od diff seizeS --- .../cpu_experiments.txt | 0 .../current_problem.txt | 2 +- .../generate_nonlinear_mpec_problem_set.m | 155 +++++++++- .../nonlinear_mpec_benchmark_dtable_loop.m | 7 + .../run_diff_experiments.m | 21 ++ .../run_nonlinear_mpec_benchmark_large_1.m | 263 +++++++++++++++++ .../run_nonlinear_mpec_benchmark_large_2.m | 263 +++++++++++++++++ .../run_nonlinear_mpec_benchmark_medium.m | 271 ++++++++++++++++++ .../solve_single_problem_nonlinear_mpec.m | 9 +- .../nonlinear_mpec_benchmark/sparsity.m | 100 +++++++ 10 files changed, 1085 insertions(+), 6 deletions(-) create mode 100644 benchmarks/nonlinear_mpec_benchmark/cpu_experiments.txt create mode 100644 benchmarks/nonlinear_mpec_benchmark/run_diff_experiments.m create mode 100644 benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_large_1.m create mode 100644 benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_large_2.m create mode 100644 benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_medium.m create mode 100644 benchmarks/nonlinear_mpec_benchmark/sparsity.m diff --git a/benchmarks/nonlinear_mpec_benchmark/cpu_experiments.txt b/benchmarks/nonlinear_mpec_benchmark/cpu_experiments.txt new file mode 100644 index 0000000..e69de29 diff --git a/benchmarks/nonlinear_mpec_benchmark/current_problem.txt b/benchmarks/nonlinear_mpec_benchmark/current_problem.txt index 68b4237..af8c2c2 100644 --- a/benchmarks/nonlinear_mpec_benchmark/current_problem.txt +++ b/benchmarks/nonlinear_mpec_benchmark/current_problem.txt @@ -1 +1 @@ -nonlinear_mpec2_DIXCHLNV_Var100_Comp40_Eq40_Ineq3 +nonlinear_large1_Fletcher_Var60_Comp24_Eq24_Ineq11 diff --git a/benchmarks/nonlinear_mpec_benchmark/generate_nonlinear_mpec_problem_set.m b/benchmarks/nonlinear_mpec_benchmark/generate_nonlinear_mpec_problem_set.m index d4055b7..fa3a7b4 100644 --- a/benchmarks/nonlinear_mpec_benchmark/generate_nonlinear_mpec_problem_set.m +++ b/benchmarks/nonlinear_mpec_benchmark/generate_nonlinear_mpec_problem_set.m @@ -91,10 +91,12 @@ biased_indices = randsample(1:(n_x_max-n_x_min+1), N_problems, true, weights); n_x_vec = n_x_min + biased_indices - 1; n_y_vec = round(n_x_vec/n_fraction_of_x); % num of comp vars - n_ineq_vec = round((settings.n_ineq_lb+(settings.n_ineq_ub-settings.n_ineq_lb)*(rand(1,N_problems)).*n_x_vec)); + rand_ineq_scale = settings.n_ineq_lb+(settings.n_ineq_ub-settings.n_ineq_lb)*(rand(1,N_problems)); + n_ineq_vec = round(rand_ineq_scale.*n_x_vec); % n_ineq_vec = round((settings.n_ineq_lb+(settings.n_ineq_ub-settings.n_ineq_lb)*(rand(1,N_problems)).*n_x_vec))*3; N_per_problem = N_rand_prob; + if 1 figure n_var = n_x_vec+2*n_y_vec; % total num of vars, non comp, comp1 + comp2 + lift scatter(n_var,n_ineq_vec*(1+settings.s_ineq_copy)); @@ -106,6 +108,7 @@ ylabel('Number of constraints') legend({'Inequality constraints','Equality constraints', 'Comp. constraints'},'Location','northwest') axis equal + end % subplot(122) % scatter(n_var,n_y_vec); % hold on @@ -461,6 +464,142 @@ end f = f + q * (q * (q^2 - 20) - 0.1); end + + case 'SCURLY10' + scal = 12.0; + if n_obj > 10 + k = 10; + else + k = n_obj; + end + + % Compute scaling factors S(i) + S = zeros(n_obj, 1); + for i = 1:n_obj + rat = (i-1) / (n_obj-1); + S(i) = exp(rat * scal); + end + + % Compute objective function + for i = 1:min(n_obj-k, n_obj) + % Sum from j=i to min(i+k, n_obj) + gvar = 0; + for j = i:min(i+k, n_obj) + gvar = gvar + S(j) * w(j); + end + + % Apply P4 group function: gvar * (gvar * (gvar^2 - 20) - 0.1) + f = f + gvar * (gvar * (gvar^2 - 20) - 0.1); + end + + % Handle remaining groups for i > n_obj-k + for i = max(n_obj-k+1, 1):n_obj + gvar = 0; + for j = i:n_obj + gvar = gvar + S(j) * w(j); + end + + f = f + gvar * (gvar * (gvar^2 - 20) - 0.1); + end + + case 'SCURLY20' + if n_obj > 20 + k = 20; + else + k = n_obj; + end + scal = 12.0; + + % Compute scaling factors S(i) + S = zeros(n_obj, 1); + for i = 1:n_obj + rat = (i-1) / (n_obj-1); + S(i) = exp(rat * scal); + end + + % Compute objective function + for i = 1:min(n_obj-k, n_obj) + % Sum from j=i to min(i+k, n_obj) + gvar = 0; + for j = i:min(i+k, n_obj) + gvar = gvar + S(j) * w(j); + end + + % Apply P4 group function: gvar * (gvar * (gvar^2 - 20) - 0.1) + f = f + gvar * (gvar * (gvar^2 - 20) - 0.1); + end + + % Handle remaining groups for i > n_obj-k + for i = max(n_obj-k+1, 1):n_obj + gvar = 0; + for j = i:n_obj + gvar = gvar + S(j) * w(j); + end + + f = f + gvar * (gvar * (gvar^2 - 20) - 0.1); + end + + + case 'CURLY10' + if n_obj > 10 + k = 10; + else + k = n_obj; + end + + % Compute objective function + for i = 1:min(n_obj-k, n_obj) + % Sum from j=i to min(i+k, n_obj) + gvar = 0; + for j = i:min(i+k, n_obj) + gvar = gvar + w(j); + end + + % Apply P4 group function: gvar * (gvar * (gvar^2 - 20) - 0.1) + f = f + gvar * (gvar * (gvar^2 - 20) - 0.1); + end + + % Handle remaining groups for i > n_obj-k + for i = max(n_obj-k+1, 1):n_obj + gvar = 0; + for j = i:n_obj + gvar = gvar + w(j); + end + + f = f + gvar * (gvar * (gvar^2 - 20) - 0.1); + end + case 'FMINSURF' + p = round(sqrt(n_obj)); % assuming n_obj = p^2 + p_minus_1 = max(p - 1,1); + scale = 1 / (p_minus_1)^2; + param = 0.5 * (p_minus_1)^2; + + % Surface area terms (SQROOT groups) + for i = 1:p_minus_1 + for j = 1:p_minus_1 + % Convert 2D indices to 1D + idx_ij = (j-1)*p + i; + idx_i1j1 = j*p + (i+1); + idx_i1j = (j-1)*p + (i+1); + idx_ij1 = j*p + i; + + % a(i,j) = x(i,j) - x(i+1,j+1) + a_ij = w(idx_ij) - w(idx_i1j1); + % b(i,j) = x(i+1,j) - x(i,j+1) + b_ij = w(idx_i1j) - w(idx_ij1); + + alpha = 1 + param * (a_ij^2 + b_ij^2); + f = f + scale * sqrt(alpha); + end + end + + % Average height term (L2 group) + avg_height = 0; + for idx = 1:n_obj + avg_height = avg_height + w(idx); + end + f = f + (p^4) * scale * avg_height^2; + case 'FLETBV3M' f = 0; for i = 1:n_obj @@ -474,6 +613,20 @@ f = f + 0.5 * q^2 + 100*sin(0.01*w(i)) + 1e2*cos(w(i)); end + case 'SPARSQUR' + for i = 1:n_obj + % Calculate indices based on SIF modular arithmetic + j2 = mod(2*i - 1, n_obj) + 1; + j3 = mod(3*i - 1, n_obj) + 1; + j5 = mod(5*i - 1, n_obj) + 1; + j7 = mod(7*i - 1, n_obj) + 1; + j11 = mod(11*i - 1, n_obj) + 1; + + % Sum of squares with weight i + alpha = w(i) + w(j2) + w(j3) + w(j5) + w(j7) + w(j11); + f = f + 0.5 * i * alpha^2; + end + case 'NCVXQP6' % number of positive/negative rank-one terms diff --git a/benchmarks/nonlinear_mpec_benchmark/nonlinear_mpec_benchmark_dtable_loop.m b/benchmarks/nonlinear_mpec_benchmark/nonlinear_mpec_benchmark_dtable_loop.m index 97b9543..f7ff7d2 100644 --- a/benchmarks/nonlinear_mpec_benchmark/nonlinear_mpec_benchmark_dtable_loop.m +++ b/benchmarks/nonlinear_mpec_benchmark/nonlinear_mpec_benchmark_dtable_loop.m @@ -63,6 +63,7 @@ % fid = fopen('current_problem.txt','w'); % fprintf(fid,'%s\n\n',name); % fclose(fid); + t_current_exp = tic; for jj = n_mpecs mpec = mpecs(jj); w = mpec.w; @@ -163,6 +164,12 @@ dtable1 = struct2table(dstruct); save([results_name '_' num2str(ii)],"dtable1"); % pause(90); % cool down cpu pause + cpu_current_exp = toc(t_current_exp); + timing_str = sprintf(' - execution time: %.2f seconds (%.4f hours, %.6f days) \n', cpu_current_exp, cpu_current_exp/3600, cpu_current_exp/86400); + combined = strcat(name, timing_str); + fid = fopen('cpu_experiments.txt', 'a'); + fprintf(fid, '%s\n', combined); + fclose(fid); end %% Check results and plot dtable = struct2table(dstruct); diff --git a/benchmarks/nonlinear_mpec_benchmark/run_diff_experiments.m b/benchmarks/nonlinear_mpec_benchmark/run_diff_experiments.m new file mode 100644 index 0000000..c32fe2d --- /dev/null +++ b/benchmarks/nonlinear_mpec_benchmark/run_diff_experiments.m @@ -0,0 +1,21 @@ +%% run subseqeuntlly benchmarks of different size +try + run_nonlinear_mpec_benchmark_medium +catch + +end + +%% run subseqeuntlly benchmarks of different size + + +try + run_nonlinear_mpec_benchmark_large_1 +catch +end + + +%% run subseqeuntlly benchmarks of different size +try + run_nonlinear_mpec_benchmark_large_2 +catch +end \ No newline at end of file diff --git a/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_large_1.m b/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_large_1.m new file mode 100644 index 0000000..66ed8fa --- /dev/null +++ b/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_large_1.m @@ -0,0 +1,263 @@ +clear; clc; close all; +problem_set_name = 'nonlinear_large1'; % (add large, degenerate, nonlinear, include lifted) + +%% file names +filename = 'nonlinear_mpec_large1'; % name for figures and excel table +results_name = ['results/' filename '_' datestr(datetime("today"))]; % name for matlab .dat with results + +%% Generate test set +settings.casadi_variable_type = 'SX'; +settings.nonlinear_eq_constraints = 1; +settings.nonlinear_ineq_constraints = 1; +settings.s_nonlinear_eq = 0.01; +settings.s_nonlinear_ineq = 1/4; +settings.include_lifted_variables_in_objective = 0; +settings.copy_ineq = 1; %have two copies of the inequality constrants -> violate licq if active +settings.s_ineq_copy = 1/4; +settings.copy_eq = 0; +settings.s_eq_copy = 0.0; + +settings.inequality_constraints = 1; +settings.inequality_constraints_coupling_terms = 1; % if 0, then in Ax+By >=f, B = 0; + +% settings.objective_functions = {'Quadratic_psd','Quadratic_ind',... +% 'Fletcher','Himmelblau','McCormick',... +% 'Powell','Rosenbrock',... +% 'Raydan1','Raydan2',... +% 'Diagonal4','Diagonal5',... +% 'Extended_Trigiaonal','Three_Exponential_Terms',... +% 'Generalized_PSC1','Extended_PSC1',... +% 'Fletcvb3','Bdqrtic','Tridia',... +% 'EG2','Edensch','Indef',... +% 'Cube','Bdexp','Genhumps',... +% 'Arwhead', 'Quartc', 'Cosine', 'Sine',... +% 'CURLY20', 'SCURLY30', 'FLETBV3M',... +% 'NCVXQP6','DIXCHLNV','EDENSCH',... +% }; + + +% reduced set +settings.objective_functions = {'Fletcher','McCormick',... + 'Powell','Rosenbrock',... + 'Raydan1','Raydan2',... + 'Diagonal4','Diagonal5',... + 'Extended_Trigiaonal','Three_Exponential_Terms',... + 'Generalized_PSC1',... + 'Fletcvb3','Bdqrtic','Tridia',... + 'EG2','Edensch','Indef',... + 'Cube','Bdexp','Genhumps',... + 'Arwhead', 'Quartc', 'Cosine',... + 'NCVXQP6','DIXCHLNV',... + }; + +settings.rescale_factor = 1; +settings.round_all_data = 1; +settings.n_digits = 4; +settings.bounded_w = 1; +settings.use_normal_distributions_for_feasible_point = 1; +settings.alpha_lin = 10; % f + alpha_lin*[c;d]'*[x;y]' +settings.n_non_zero_E = 2000; +settings.symmetric_psd_M_matrix = 0; + +settings.s_density_A_B = 0.1; % all same or change +settings.s_density_M = 0.5; + +settings.s_density_A_B = 0.05; % all same or change +settings.s_density_M = 0.1; + + +settings.nnz_bounded_by_dim = 1; +settings.inv_cond_num = 1e0; +settings.nnz_factor = 1; +settings.n_comp_min = 400; + +settings.adaptive_density_bounds = 0; % to account for very larg problems +settings.variable_density = 1; +settings.range_s_density = [0.01 0.05]; +settings.random_problem_sizes = 1; + +settings.n_ineq_ub = 2; % n_ineq = n_ineq_ub*n_x % max relative number of ineq w.r.t x +settings.n_ineq_lb = 0.5; + +dimensions.n_fraction_of_x = 0.5; % n_y = round(n_x/n_fraction_of_x) + +% Problem size +dimensions.N_rand_prob = 3; % number of problems per objective +dimensions.n_x_max = 2100; +dimensions.n_x_min = 100; + + +t_gen = tic; +mpecs = generate_nonlinear_mpec_problem_set(problem_set_name,settings,dimensions); +t_mpec_gen = toc(t_gen); +fprintf('Problem generation time : %2.2f \n',t_mpec_gen) + +%% Solver settings +solver_names = ["MPECopt-Reg-Gurobi", "MPECopt-$\ell_1$-Gurobi", "MPECopt-Reg-Gurobi-ET",... + "Reg", "NLP", "$\ell_1$-Penalty", "MINLP"]; + +solver_functions = {@mpec_optimizer,@mpec_optimizer,@mpec_optimizer,... + @mpec_homotopy_solver,@mpec_homotopy_solver, @mpec_homotopy_solver,... + @mpec_minlp_solver}; + +opts1 = mpecopt.Options(); +opts1.solver_name = solver_names{1}; +opts1.settings_lpec.lpec_solver = "Gurobi"; +opts1.relax_and_project_homotopy_parameter_steering = "Direct"; +opts1.use_one_nlp_solver = true; +opts1.problem_in_vertical_from = true; +opts1.settings_casadi_nlp.ipopt.max_wall_time = 900; +% opts1.initialization_strategy = "FeasibilityEll1General"; + + +opts2 = mpecopt.Options(); +opts2.solver_name = solver_names{2}; +opts2.settings_lpec.lpec_solver = "Gurobi"; +opts2.relax_and_project_homotopy_parameter_steering = "Ell_1"; +opts2.use_one_nlp_solver = true; +opts2.problem_in_vertical_from = true; +opts2.settings_casadi_nlp.ipopt.max_wall_time = 900; +% opts2.settings_lpec.stop_lpec_at_feasible = true; +% opts2.rho_TR_phase_i_init = 1e-3; + + +% opts3 = mpecopt.Options(); +% opts3.solver_name = solver_names{3}; +% opts3.settings_lpec.lpec_solver = "Highs"; +% opts3.relax_and_project_homotopy_parameter_steering = "Direct"; +% opts3.use_one_nlp_solver = true; +% +opts3 = mpecopt.Options(); +opts3.solver_name = solver_names{3}; +opts3.settings_lpec.lpec_solver = "Gurobi"; +opts3.relax_and_project_homotopy_parameter_steering = "Direct"; +opts3.use_one_nlp_solver = true; +opts3.settings_lpec.stop_lpec_at_feasible = true; +opts3.settings_lpec.stop_lpec_at_descent = true; +opts3.consider_all_complementarities_in_lpec = false; +opts3.problem_in_vertical_from = true; +opts3.settings_casadi_nlp.ipopt.max_wall_time = 900; +% opts3.rho_TR_phase_i_init = 1e-2; + +scholtes_opts1 = HomotopySolverOptions(); +scholtes_opts1.homotopy_parameter_steering = 'Direct'; +scholtes_opts1.problem_in_vertical_from = true; +scholtes_opts1.settings_casadi_nlp.ipopt.max_wall_time = 900; + +scholtes_opts2 = HomotopySolverOptions(); +scholtes_opts2.homotopy_parameter_steering = 'Direct'; +scholtes_opts2.max_iter = 1; +scholtes_opts2.sigma0 = 0; +scholtes_opts2.problem_in_vertical_from = true; +scholtes_opts2.settings_casadi_nlp.ipopt.max_wall_time = 1800; + + +scholtes_opts3 = HomotopySolverOptions(); +scholtes_opts3.homotopy_parameter_steering = 'Ell_1'; +scholtes_opts3.problem_in_vertical_from = true; +scholtes_opts3.settings_casadi_nlp.ipopt.max_wall_time = 900; + + +minlp_opts = MINLPSolverOptions(); +minlp_opts.settings_casadi_nlp.bonmin.time_limit = 1800; +scholtes_opts3.problem_in_vertical_from = true; + +opts = {opts1, opts2, opts3, ... + scholtes_opts1, scholtes_opts2, scholtes_opts3,... + minlp_opts}; % list of options to pass to mpecsol (option structs) + +%% Create data struct +N_experiments = [4 3 5 1 2 6 7]; +% N_experiments = [3 1]; +nonlinear_mpec_benchmark_dtable_loop; % this script runs the experimetns, creates a dtable +%% Pick which results to plot +dtable = dtable1; +% Modift possibly which solvers are plotted +[~,temp_b] = unique(dtable.solver_name); +solver_name = dtable.solver_name(sort(temp_b)); +% solver_tab = table(solver_name); +N_plot = N_experiments; +% N_plot = [1,2]; + +dtable_plot = []; +for ii = N_plot + dtable_plot = [dtable_plot; dtable(dtable.solver_name == solver_names(ii),:)]; +end + +%% Export to excel +write_in_separate_sheets = true; +filename_excel = [filename '_details.xlsx']; +if write_in_separate_sheets + solver_tab = pivot(dtable_plot, Rows= "solver_name"); + for ii = 1:length(solver_tab.solver_name) + solver_name =solver_tab.solver_name{ii}; + dtable_ii = dtable_plot(dtable_plot.solver_name == solver_name,:); + writetable(dtable_ii,filename_excel,'Sheet',ii) + end +else + writetable(dtable_plot,filename_excel,'Sheet',1) +end + +%% Plot results +plot_settings.success_fail_statistics = 1; +plot_settings.n_biactive = 1; +plot_settings.lpecs_solved = 0; +plot_settings.active_set_changes = 0; +plot_settings.lpecs_cpu_time = 0; +plot_settings.bar_timing_plots = 0; +plot_settings.nlps_solved = 0; +plot_settings.max_nlp_cpu_time = 0; +plot_settings.max_nlp_cpu_time_phase_i = 0; +plot_settings.max_nlp_cpu_time_phase_ii = 0; +plot_settings.nlp_cpu_time = 0; % aggegated nlp times phase I and II +plot_settings.nlp_lpec_cpu_comparisson = 0; +plot_settings.objective = 0; +plot_settings.objective_rescaled = 1; +plot_settings.max_lpec_cpu_time = 0; + +plot_settings.relative = 1; +plot_settings.relative_phase_i = 0; +plot_settings.relative_phase_ii = 0; +plot_settings.relative_lpec = 0; + +plot_settings.absolute = 1; +plot_settings.absolute_phase_i = 0; +plot_settings.absolute_phase_ii = 0; +plot_settings.absolute_lpec = 0; + +plot_settings.solved_in_phase_i = 0; + +% split cpu time among phases and show in bar plot (just report for mpecopt +% guroi) +plot_settings.nlp_phases_cpu_time = 0; +plot_settings.lpec_phases_cpu_time = 0; + +plot_settings.max_lpec_cpu_time = 0; +plot_settings.stationary_points = 0; +plot_settings.b_stationarity = 0; +plot_settings.b_stationarty_as_success_criterion = 0; +plot_settings.plot_only_sucessful = 1; +plot_settings.bar_comparisson_plots = 0; + +plot_settings.save_plot = 1; + +close all; +plot_mpec_benchmark_result(dtable_plot,filename,plot_settings) +%% +dtable_ii = dtable_plot(dtable_plot.solver_name == solver_names{1},:); +dtable_jj = dtable_plot(dtable_plot.solver_name == solver_names{4},:); +hh = find(dtable_jj.b_stationarity == 0 & dtable_jj.success == 1); +% dtable_ii.f_lpec((hh)) +% dtable_jj.f((hh)) +% dtable_jj.f_lpec((hh)) +% abs((dtable_ii.f((hh))-dtable_jj.f((hh))))./abs(dtable_ii.f((hh)))>1e-3 + +%% +% figure +% semilogy(abs(dtable_ii.f_lpec)+1e-16,'LineWidth',1.5); +% hold on +% semilogy(abs(dtable_jj.f_lpec)+1e-16,'LineWidth',1.5); +% ylabel('$|\nabla f(x^*)^\top d|$') +% xlabel('Problem instance') +% grid on +% yline(1e-8,'LineWidth',2) \ No newline at end of file diff --git a/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_large_2.m b/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_large_2.m new file mode 100644 index 0000000..91fa7a3 --- /dev/null +++ b/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_large_2.m @@ -0,0 +1,263 @@ +clear; clc; close all; +problem_set_name = 'nonlinear_large2'; % (add large, degenerate, nonlinear, include lifted) + +%% file names +filename = 'nonlinear_mpec_large2'; % name for figures and excel table +results_name = ['results/' filename '_' datestr(datetime("today"))]; % name for matlab .dat with results + +%% Generate test set +settings.casadi_variable_type = 'SX'; +settings.nonlinear_eq_constraints = 1; +settings.nonlinear_ineq_constraints = 1; +settings.s_nonlinear_eq = 0.01; +settings.s_nonlinear_ineq = 1/4; +settings.include_lifted_variables_in_objective = 0; +settings.copy_ineq = 1; %have two copies of the inequality constrants -> violate licq if active +settings.s_ineq_copy = 1/4; +settings.copy_eq = 0; +settings.s_eq_copy = 0.0; + +settings.inequality_constraints = 1; +settings.inequality_constraints_coupling_terms = 1; % if 0, then in Ax+By >=f, B = 0; + +% settings.objective_functions = {'Quadratic_psd','Quadratic_ind',... +% 'Fletcher','Himmelblau','McCormick',... +% 'Powell','Rosenbrock',... +% 'Raydan1','Raydan2',... +% 'Diagonal4','Diagonal5',... +% 'Extended_Trigiaonal','Three_Exponential_Terms',... +% 'Generalized_PSC1','Extended_PSC1',... +% 'Fletcvb3','Bdqrtic','Tridia',... +% 'EG2','Edensch','Indef',... +% 'Cube','Bdexp','Genhumps',... +% 'Arwhead', 'Quartc', 'Cosine', 'Sine',... +% 'CURLY20', 'SCURLY30', 'FLETBV3M',... +% 'NCVXQP6','DIXCHLNV','EDENSCH',... +% }; + + +% reduced set +settings.objective_functions = {'Fletcher','McCormick',... + 'Powell','Rosenbrock',... + 'Raydan1','Raydan2',... + 'Diagonal4','Diagonal5',... + 'Extended_Trigiaonal','Three_Exponential_Terms',... + 'Generalized_PSC1',... + 'Fletcvb3','Bdqrtic','Tridia',... + 'EG2','Edensch','Indef',... + 'Cube','Bdexp','Genhumps',... + 'Arwhead', 'Quartc', 'Cosine',... + 'NCVXQP6','DIXCHLNV',... + }; + +settings.rescale_factor = 1; +settings.round_all_data = 1; +settings.n_digits = 4; +settings.bounded_w = 1; +settings.use_normal_distributions_for_feasible_point = 1; +settings.alpha_lin = 10; % f + alpha_lin*[c;d]'*[x;y]' +settings.n_non_zero_E = 2000; +settings.symmetric_psd_M_matrix = 0; + +settings.s_density_A_B = 0.1; % all same or change +settings.s_density_M = 0.5; + +settings.s_density_A_B = 0.05; % all same or change +settings.s_density_M = 0.1; + + +settings.nnz_bounded_by_dim = 1; +settings.inv_cond_num = 1e0; +settings.nnz_factor = 1; +settings.n_comp_min = 400; + +settings.adaptive_density_bounds = 0; % to account for very larg problems +settings.variable_density = 1; +settings.range_s_density = [0.01 0.05]; +settings.random_problem_sizes = 1; + +settings.n_ineq_ub = 2; % n_ineq = n_ineq_ub*n_x % max relative number of ineq w.r.t x +settings.n_ineq_lb = 0.5; + +dimensions.n_fraction_of_x = 0.5; % n_y = round(n_x/n_fraction_of_x) + +% Problem size +dimensions.N_rand_prob = 3; % number of problems per objective +dimensions.n_x_max = 3100; +dimensions.n_x_min = 100; + + +t_gen = tic; +mpecs = generate_nonlinear_mpec_problem_set(problem_set_name,settings,dimensions); +t_mpec_gen = toc(t_gen); +fprintf('Problem generation time : %2.2f \n',t_mpec_gen) + +%% Solver settings +solver_names = ["MPECopt-Reg-Gurobi", "MPECopt-$\ell_1$-Gurobi", "MPECopt-Reg-Gurobi-ET",... + "Reg", "NLP", "$\ell_1$-Penalty", "MINLP"]; + +solver_functions = {@mpec_optimizer,@mpec_optimizer,@mpec_optimizer,... + @mpec_homotopy_solver,@mpec_homotopy_solver, @mpec_homotopy_solver,... + @mpec_minlp_solver}; + +opts1 = mpecopt.Options(); +opts1.solver_name = solver_names{1}; +opts1.settings_lpec.lpec_solver = "Gurobi"; +opts1.relax_and_project_homotopy_parameter_steering = "Direct"; +opts1.use_one_nlp_solver = true; +opts1.problem_in_vertical_from = true; +opts1.settings_casadi_nlp.ipopt.max_wall_time = 900; +% opts1.initialization_strategy = "FeasibilityEll1General"; + + +opts2 = mpecopt.Options(); +opts2.solver_name = solver_names{2}; +opts2.settings_lpec.lpec_solver = "Gurobi"; +opts2.relax_and_project_homotopy_parameter_steering = "Ell_1"; +opts2.use_one_nlp_solver = true; +opts2.problem_in_vertical_from = true; +opts2.settings_casadi_nlp.ipopt.max_wall_time = 900; +% opts2.settings_lpec.stop_lpec_at_feasible = true; +% opts2.rho_TR_phase_i_init = 1e-3; + + +% opts3 = mpecopt.Options(); +% opts3.solver_name = solver_names{3}; +% opts3.settings_lpec.lpec_solver = "Highs"; +% opts3.relax_and_project_homotopy_parameter_steering = "Direct"; +% opts3.use_one_nlp_solver = true; +% +opts3 = mpecopt.Options(); +opts3.solver_name = solver_names{3}; +opts3.settings_lpec.lpec_solver = "Gurobi"; +opts3.relax_and_project_homotopy_parameter_steering = "Direct"; +opts3.use_one_nlp_solver = true; +opts3.settings_lpec.stop_lpec_at_feasible = true; +opts3.settings_lpec.stop_lpec_at_descent = true; +opts3.consider_all_complementarities_in_lpec = false; +opts3.problem_in_vertical_from = true; +opts3.settings_casadi_nlp.ipopt.max_wall_time = 900; +% opts3.rho_TR_phase_i_init = 1e-2; + +scholtes_opts1 = HomotopySolverOptions(); +scholtes_opts1.homotopy_parameter_steering = 'Direct'; +scholtes_opts1.problem_in_vertical_from = true; +scholtes_opts1.settings_casadi_nlp.ipopt.max_wall_time = 900; + +scholtes_opts2 = HomotopySolverOptions(); +scholtes_opts2.homotopy_parameter_steering = 'Direct'; +scholtes_opts2.max_iter = 1; +scholtes_opts2.sigma0 = 0; +scholtes_opts2.problem_in_vertical_from = true; +scholtes_opts2.settings_casadi_nlp.ipopt.max_wall_time = 1800; + + +scholtes_opts3 = HomotopySolverOptions(); +scholtes_opts3.homotopy_parameter_steering = 'Ell_1'; +scholtes_opts3.problem_in_vertical_from = true; +scholtes_opts3.settings_casadi_nlp.ipopt.max_wall_time = 900; + + +minlp_opts = MINLPSolverOptions(); +minlp_opts.settings_casadi_nlp.bonmin.time_limit = 1800; +scholtes_opts3.problem_in_vertical_from = true; + +opts = {opts1, opts2, opts3, ... + scholtes_opts1, scholtes_opts2, scholtes_opts3,... + minlp_opts}; % list of options to pass to mpecsol (option structs) + +%% Create data struct +N_experiments = [4 3 5 1 2 6 7]; +% N_experiments = [3 1]; +nonlinear_mpec_benchmark_dtable_loop; % this script runs the experimetns, creates a dtable +%% Pick which results to plot +dtable = dtable1; +% Modift possibly which solvers are plotted +[~,temp_b] = unique(dtable.solver_name); +solver_name = dtable.solver_name(sort(temp_b)); +% solver_tab = table(solver_name); +N_plot = N_experiments; +% N_plot = [1,2]; + +dtable_plot = []; +for ii = N_plot + dtable_plot = [dtable_plot; dtable(dtable.solver_name == solver_names(ii),:)]; +end + +%% Export to excel +write_in_separate_sheets = true; +filename_excel = [filename '_details.xlsx']; +if write_in_separate_sheets + solver_tab = pivot(dtable_plot, Rows= "solver_name"); + for ii = 1:length(solver_tab.solver_name) + solver_name =solver_tab.solver_name{ii}; + dtable_ii = dtable_plot(dtable_plot.solver_name == solver_name,:); + writetable(dtable_ii,filename_excel,'Sheet',ii) + end +else + writetable(dtable_plot,filename_excel,'Sheet',1) +end + +%% Plot results +plot_settings.success_fail_statistics = 1; +plot_settings.n_biactive = 1; +plot_settings.lpecs_solved = 0; +plot_settings.active_set_changes = 0; +plot_settings.lpecs_cpu_time = 0; +plot_settings.bar_timing_plots = 0; +plot_settings.nlps_solved = 0; +plot_settings.max_nlp_cpu_time = 0; +plot_settings.max_nlp_cpu_time_phase_i = 0; +plot_settings.max_nlp_cpu_time_phase_ii = 0; +plot_settings.nlp_cpu_time = 0; % aggegated nlp times phase I and II +plot_settings.nlp_lpec_cpu_comparisson = 0; +plot_settings.objective = 0; +plot_settings.objective_rescaled = 1; +plot_settings.max_lpec_cpu_time = 0; + +plot_settings.relative = 1; +plot_settings.relative_phase_i = 0; +plot_settings.relative_phase_ii = 0; +plot_settings.relative_lpec = 0; + +plot_settings.absolute = 1; +plot_settings.absolute_phase_i = 0; +plot_settings.absolute_phase_ii = 0; +plot_settings.absolute_lpec = 0; + +plot_settings.solved_in_phase_i = 0; + +% split cpu time among phases and show in bar plot (just report for mpecopt +% guroi) +plot_settings.nlp_phases_cpu_time = 0; +plot_settings.lpec_phases_cpu_time = 0; + +plot_settings.max_lpec_cpu_time = 0; +plot_settings.stationary_points = 0; +plot_settings.b_stationarity = 0; +plot_settings.b_stationarty_as_success_criterion = 0; +plot_settings.plot_only_sucessful = 1; +plot_settings.bar_comparisson_plots = 0; + +plot_settings.save_plot = 1; + +close all; +plot_mpec_benchmark_result(dtable_plot,filename,plot_settings) +%% +dtable_ii = dtable_plot(dtable_plot.solver_name == solver_names{1},:); +dtable_jj = dtable_plot(dtable_plot.solver_name == solver_names{4},:); +hh = find(dtable_jj.b_stationarity == 0 & dtable_jj.success == 1); +% dtable_ii.f_lpec((hh)) +% dtable_jj.f((hh)) +% dtable_jj.f_lpec((hh)) +% abs((dtable_ii.f((hh))-dtable_jj.f((hh))))./abs(dtable_ii.f((hh)))>1e-3 + +%% +% figure +% semilogy(abs(dtable_ii.f_lpec)+1e-16,'LineWidth',1.5); +% hold on +% semilogy(abs(dtable_jj.f_lpec)+1e-16,'LineWidth',1.5); +% ylabel('$|\nabla f(x^*)^\top d|$') +% xlabel('Problem instance') +% grid on +% yline(1e-8,'LineWidth',2) \ No newline at end of file diff --git a/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_medium.m b/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_medium.m new file mode 100644 index 0000000..8bcf041 --- /dev/null +++ b/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_medium.m @@ -0,0 +1,271 @@ +clear; clc; close all; +problem_set_name = 'nonlinear_med'; % (add large, degenerate, nonlinear, include lifted) + +%% file names +filename = 'nonlinear_mpec_med'; % name for figures and excel table +results_name = ['results/' filename '_' datestr(datetime("today"))]; % name for matlab .dat with results + +%% Generate test set +settings.casadi_variable_type = 'SX'; +settings.nonlinear_eq_constraints = 1; +settings.nonlinear_ineq_constraints = 1; +settings.s_nonlinear_eq = 0.01; +settings.s_nonlinear_ineq = 1/4; +settings.include_lifted_variables_in_objective = 0; +settings.copy_ineq = 1; %have two copies of the inequality constrants -> violate licq if active +settings.s_ineq_copy = 1/4; +settings.copy_eq = 0; +settings.s_eq_copy = 0.0; + +settings.inequality_constraints = 1; +settings.inequality_constraints_coupling_terms = 1; % if 0, then in Ax+By >=f, B = 0; + +% settings.objective_functions = {'Quadratic_psd','Quadratic_ind',... +% 'Fletcher','Himmelblau','McCormick',... +% 'Powell','Rosenbrock',... +% 'Raydan1','Raydan2',... +% 'Diagonal4','Diagonal5',... +% 'Extended_Trigiaonal','Three_Exponential_Terms',... +% 'Generalized_PSC1','Extended_PSC1',... +% 'Fletcvb3','Bdqrtic','Tridia',... +% 'EG2','Edensch','Indef',... +% 'Cube','Bdexp','Genhumps',... +% 'Arwhead', 'Quartc', 'Cosine', 'Sine',... +% 'CURLY20', 'SCURLY30', 'FLETBV3M',... +% 'NCVXQP6','DIXCHLNV','EDENSCH',... +% }; + + +% reduced set +settings.objective_functions = {'Fletcher','McCormick',... + 'Powell','Rosenbrock',... + 'Raydan1','Raydan2',... + 'Diagonal4','Diagonal5',... + 'Extended_Trigiaonal','Three_Exponential_Terms',... + 'Generalized_PSC1',... + 'Fletcvb3','Bdqrtic','Tridia',... + 'EG2','Edensch','Indef',... + 'Cube','Bdexp','Genhumps',... + 'Arwhead', 'Quartc', 'Cosine',... + 'NCVXQP6','DIXCHLNV',... + }; + +settings.objective_functions = {'Fletcher','McCormick',... + 'NCVXQP6','DIXCHLNV',... + }; + +settings.rescale_factor = 1; +settings.round_all_data = 1; +settings.n_digits = 4; +settings.bounded_w = 1; +settings.use_normal_distributions_for_feasible_point = 1; +settings.alpha_lin = 10; % f + alpha_lin*[c;d]'*[x;y]' +settings.n_non_zero_E = 2000; +settings.symmetric_psd_M_matrix = 0; + +settings.s_density_A_B = 0.1; % all same or change +settings.s_density_M = 0.5; + +settings.s_density_A_B = 0.05; % all same or change +settings.s_density_M = 0.1; + + +settings.nnz_bounded_by_dim = 1; +settings.inv_cond_num = 1e0; +settings.nnz_factor = 1; +settings.n_comp_min = 400; + +settings.adaptive_density_bounds = 0; % to account for very larg problems +settings.variable_density = 1; +settings.range_s_density = [0.01 0.05]; +settings.random_problem_sizes = 1; + +settings.n_ineq_ub = 2; % n_ineq = n_ineq_ub*n_x % max relative number of ineq w.r.t x +settings.n_ineq_lb = 0.5; + +dimensions.n_fraction_of_x = 0.5; % n_y = round(n_x/n_fraction_of_x) + +% Problem size +dimensions.N_rand_prob = 3; % number of problems per objective +dimensions.n_x_max = 1560; +dimensions.n_x_min = 100; + +dimensions.N_rand_prob = 1; % number of problems per objective +dimensions.n_x_max = 20; +dimensions.n_x_min = 10; + + +t_gen = tic; +mpecs = generate_nonlinear_mpec_problem_set(problem_set_name,settings,dimensions); +t_mpec_gen = toc(t_gen); +fprintf('Problem generation time : %2.2f \n',t_mpec_gen) + +%% Solver settings +solver_names = ["MPECopt-Reg-Gurobi", "MPECopt-$\ell_1$-Gurobi", "MPECopt-Reg-Gurobi-ET",... + "Reg", "NLP", "$\ell_1$-Penalty", "MINLP"]; + +solver_functions = {@mpec_optimizer,@mpec_optimizer,@mpec_optimizer,... + @mpec_homotopy_solver,@mpec_homotopy_solver, @mpec_homotopy_solver,... + @mpec_minlp_solver}; + +opts1 = mpecopt.Options(); +opts1.solver_name = solver_names{1}; +opts1.settings_lpec.lpec_solver = "Gurobi"; +opts1.relax_and_project_homotopy_parameter_steering = "Direct"; +opts1.use_one_nlp_solver = true; +opts1.problem_in_vertical_from = true; +opts1.settings_casadi_nlp.ipopt.max_wall_time = 900; +% opts1.initialization_strategy = "FeasibilityEll1General"; + + +opts2 = mpecopt.Options(); +opts2.solver_name = solver_names{2}; +opts2.settings_lpec.lpec_solver = "Gurobi"; +opts2.relax_and_project_homotopy_parameter_steering = "Ell_1"; +opts2.use_one_nlp_solver = true; +opts2.problem_in_vertical_from = true; +opts2.settings_casadi_nlp.ipopt.max_wall_time = 900; +% opts2.settings_lpec.stop_lpec_at_feasible = true; +% opts2.rho_TR_phase_i_init = 1e-3; + + +% opts3 = mpecopt.Options(); +% opts3.solver_name = solver_names{3}; +% opts3.settings_lpec.lpec_solver = "Highs"; +% opts3.relax_and_project_homotopy_parameter_steering = "Direct"; +% opts3.use_one_nlp_solver = true; +% +opts3 = mpecopt.Options(); +opts3.solver_name = solver_names{3}; +opts3.settings_lpec.lpec_solver = "Gurobi"; +opts3.relax_and_project_homotopy_parameter_steering = "Direct"; +opts3.use_one_nlp_solver = true; +opts3.settings_lpec.stop_lpec_at_feasible = true; +opts3.settings_lpec.stop_lpec_at_descent = true; +opts3.consider_all_complementarities_in_lpec = false; +opts3.problem_in_vertical_from = true; +opts3.settings_casadi_nlp.ipopt.max_wall_time = 900; +% opts3.rho_TR_phase_i_init = 1e-2; + +scholtes_opts1 = HomotopySolverOptions(); +scholtes_opts1.homotopy_parameter_steering = 'Direct'; +scholtes_opts1.problem_in_vertical_from = true; +scholtes_opts1.settings_casadi_nlp.ipopt.max_wall_time = 900; + +scholtes_opts2 = HomotopySolverOptions(); +scholtes_opts2.homotopy_parameter_steering = 'Direct'; +scholtes_opts2.max_iter = 1; +scholtes_opts2.sigma0 = 0; +scholtes_opts2.problem_in_vertical_from = true; +scholtes_opts2.settings_casadi_nlp.ipopt.max_wall_time = 1800; + + +scholtes_opts3 = HomotopySolverOptions(); +scholtes_opts3.homotopy_parameter_steering = 'Ell_1'; +scholtes_opts3.problem_in_vertical_from = true; +scholtes_opts3.settings_casadi_nlp.ipopt.max_wall_time = 900; + + +minlp_opts = MINLPSolverOptions(); +minlp_opts.settings_casadi_nlp.bonmin.time_limit = 1800; +scholtes_opts3.problem_in_vertical_from = true; + +opts = {opts1, opts2, opts3, ... + scholtes_opts1, scholtes_opts2, scholtes_opts3,... + minlp_opts}; % list of options to pass to mpecsol (option structs) + +%% Create data struct +N_experiments = [4 3 5 1 2 6 7]; +% N_experiments = [1]; +nonlinear_mpec_benchmark_dtable_loop; % this script runs the experimetns, creates a dtable +%% Pick which results to plot +dtable = dtable1; +% Modift possibly which solvers are plotted +[~,temp_b] = unique(dtable.solver_name); +solver_name = dtable.solver_name(sort(temp_b)); +% solver_tab = table(solver_name); +N_plot = N_experiments; +% N_plot = [1,2]; + +dtable_plot = []; +for ii = N_plot + dtable_plot = [dtable_plot; dtable(dtable.solver_name == solver_names(ii),:)]; +end + +%% Export to excel +write_in_separate_sheets = true; +filename_excel = [filename '_details.xlsx']; +if write_in_separate_sheets + solver_tab = pivot(dtable_plot, Rows= "solver_name"); + for ii = 1:length(solver_tab.solver_name) + solver_name =solver_tab.solver_name{ii}; + dtable_ii = dtable_plot(dtable_plot.solver_name == solver_name,:); + writetable(dtable_ii,filename_excel,'Sheet',ii) + end +else + writetable(dtable_plot,filename_excel,'Sheet',1) +end + +%% Plot results +plot_settings.success_fail_statistics = 1; +plot_settings.n_biactive = 1; +plot_settings.lpecs_solved = 0; +plot_settings.active_set_changes = 0; +plot_settings.lpecs_cpu_time = 0; +plot_settings.bar_timing_plots = 0; +plot_settings.nlps_solved = 0; +plot_settings.max_nlp_cpu_time = 0; +plot_settings.max_nlp_cpu_time_phase_i = 0; +plot_settings.max_nlp_cpu_time_phase_ii = 0; +plot_settings.nlp_cpu_time = 0; % aggegated nlp times phase I and II +plot_settings.nlp_lpec_cpu_comparisson = 0; +plot_settings.objective = 0; +plot_settings.objective_rescaled = 1; +plot_settings.max_lpec_cpu_time = 0; + +plot_settings.relative = 1; +plot_settings.relative_phase_i = 0; +plot_settings.relative_phase_ii = 0; +plot_settings.relative_lpec = 0; + +plot_settings.absolute = 1; +plot_settings.absolute_phase_i = 0; +plot_settings.absolute_phase_ii = 0; +plot_settings.absolute_lpec = 0; + +plot_settings.solved_in_phase_i = 0; + +% split cpu time among phases and show in bar plot (just report for mpecopt +% guroi) +plot_settings.nlp_phases_cpu_time = 0; +plot_settings.lpec_phases_cpu_time = 0; + +plot_settings.max_lpec_cpu_time = 0; +plot_settings.stationary_points = 0; +plot_settings.b_stationarity = 0; +plot_settings.b_stationarty_as_success_criterion = 0; +plot_settings.plot_only_sucessful = 1; +plot_settings.bar_comparisson_plots = 0; + +plot_settings.save_plot = 1; + +close all; +plot_mpec_benchmark_result(dtable_plot,filename,plot_settings) +%% +dtable_ii = dtable_plot(dtable_plot.solver_name == solver_names{1},:); +dtable_jj = dtable_plot(dtable_plot.solver_name == solver_names{4},:); +hh = find(dtable_jj.b_stationarity == 0 & dtable_jj.success == 1); +% dtable_ii.f_lpec((hh)) +% dtable_jj.f((hh)) +% dtable_jj.f_lpec((hh)) +% abs((dtable_ii.f((hh))-dtable_jj.f((hh))))./abs(dtable_ii.f((hh)))>1e-3 + +%% +% figure +% semilogy(abs(dtable_ii.f_lpec)+1e-16,'LineWidth',1.5); +% hold on +% semilogy(abs(dtable_jj.f_lpec)+1e-16,'LineWidth',1.5); +% ylabel('$|\nabla f(x^*)^\top d|$') +% xlabel('Problem instance') +% grid on +% yline(1e-8,'LineWidth',2) \ No newline at end of file diff --git a/benchmarks/nonlinear_mpec_benchmark/solve_single_problem_nonlinear_mpec.m b/benchmarks/nonlinear_mpec_benchmark/solve_single_problem_nonlinear_mpec.m index 208b72f..b8627be 100644 --- a/benchmarks/nonlinear_mpec_benchmark/solve_single_problem_nonlinear_mpec.m +++ b/benchmarks/nonlinear_mpec_benchmark/solve_single_problem_nonlinear_mpec.m @@ -34,6 +34,7 @@ settings.objective_functions = {settings.objective_functions_all{15}}; +settings.objective_functions = {'SCURLY30'}; settings.rescale_factor = 1; settings.round_all_data = 1; @@ -55,7 +56,7 @@ settings.inv_cond_num = 1e0; settings.nnz_factor = 1; -settings.adaptive_density_bounds = 0; % to account for very larg problems +settings.adaptive_density_bounds = 1; % to account for very larg problems settings.variable_density = 1; settings.range_s_density = [0.01 0.05]; settings.random_problem_sizes = 1; @@ -66,8 +67,8 @@ settings.n_fraction_of_x = 0.5; dimensions.N_rand_prob = 1; % number of problems per objective -dimensions.n_x_max = 1500; -dimensions.n_x_min = 1500; +dimensions.n_x_max = 2000; +dimensions.n_x_min = 2000; @@ -99,7 +100,7 @@ %% Homotopy solver settings_homotopy = HomotopySolverOptions(); settings_homotopy.homotopy_parameter_steering = "Direct"; -[result_homotopy,stats_homotopy] = mpec_homotopy_solver(mpec,solver_initalization,settings_homotopy); +% [result_homotopy,stats_homotopy] = mpec_homotopy_solver(mpec,solver_initalization,settings_homotopy); % f_opt_homotopy = full(result_homotopy.f); % w_opt_homotopy = full(result_homotopy.x); diff --git a/benchmarks/nonlinear_mpec_benchmark/sparsity.m b/benchmarks/nonlinear_mpec_benchmark/sparsity.m new file mode 100644 index 0000000..05cfaa1 --- /dev/null +++ b/benchmarks/nonlinear_mpec_benchmark/sparsity.m @@ -0,0 +1,100 @@ +import casadi.* + +% Define variables +clear; close all; +problem_set_name = 'nonlinear_mpec'; +%% Generate test set + + +settings.casadi_variable_type = 'SX'; +settings.nonlinear_eq_constraints = 1; +settings.nonlinear_ineq_constraints = 1; +settings.s_nonlinear_eq = 0.01; +settings.s_nonlinear_ineq = 1/4; +settings.include_lifted_variables_in_objective = 0; +settings.copy_ineq = 1; %have two copies of the inequality constrants -> violate licq if active +settings.s_ineq_copy = 1/4; +settings.copy_eq = 0; +settings.s_eq_copy = 0.0; + +settings.inequality_constraints = 1; +settings.inequality_constraints_coupling_terms = 1; % if 0, then in Ax+By >=f, B = 0; + +% Trigonometric +density_vec = []; +settings.objective_functions_all = {'Fletcher','McCormick',... + 'Powell','Rosenbrock',... + 'Raydan1','Raydan2',... + 'Diagonal4','Diagonal5',... + 'Extended_Trigiaonal','Three_Exponential_Terms',... + 'Generalized_PSC1',... + 'Fletcvb3','Bdqrtic','Tridia',... + 'EG2','Edensch','Indef',... + 'Cube','Bdexp','Genhumps',... + 'Arwhead', 'Quartc', 'Cosine',... + 'NCVXQP6','DIXCHLNV',... + }; + +for jj = 1:27 +settings.objective_functions = {settings.objective_functions_all{jj}}; + +% settings.objective_functions = {'SCURLY30'}; + + + +settings.rescale_factor = 1; +settings.round_all_data = 1; +settings.n_digits = 4; +settings.bounded_w = 1; +settings.use_normal_distributions_for_feasible_point = 1; +settings.alpha_lin = 10; % f + alpha_lin*[c;d]'*[x;y]' +settings.n_non_zero_E = 2000; +settings.symmetric_psd_M_matrix = 0; + +settings.s_density_A_B = 0.1; % all same or change +settings.s_density_M = 0.5; + +settings.s_density_A_B = 0.05; % all same or change +settings.s_density_M = 0.1; + + +settings.nnz_bounded_by_dim = 1; +settings.inv_cond_num = 1e0; +settings.nnz_factor = 1; + +settings.adaptive_density_bounds = 0; % to account for very larg problems +settings.variable_density = 1; +settings.range_s_density = [0.01 0.05]; +settings.random_problem_sizes = 1; + +settings.n_ineq_ub = 2; % n_ineq = n_ineq_ub*n_x % max relative number of ineq w.r.t x +settings.n_ineq_lb = 0.5; + +settings.n_fraction_of_x = 0.5; + +dimensions.N_rand_prob = 1; % number of problems per objective +dimensions.n_x_max = 300; +dimensions.n_x_min = 300; + + + +dimensions.n_fraction_of_x = 0.5; % n_y = round(n_x/n_fraction_of_x) +mpecs = generate_nonlinear_mpec_problem_set(problem_set_name,settings,dimensions); + + +w = mpecs.w; +f = mpecs.f; +% Compute Hessian +H = hessian(f, w); +H_func = casadi.Function('H', {w}, {H}); +n_obj = length(w); +% Evaluate at test point +w_test = 2.5 * rand(n_obj, 1); +H_val = full(H_func(w_test)); +density = nnz(H_val)/length(H_val(:)) +density_vec = [density_vec,density]; +% Plot sparsity pattern +% figure; +% spy(H_val); +% title('Hessian Sparsity Pattern'); +end \ No newline at end of file From 756e3ac6bb54c6c1986b1d5bb452fa3a78e0453e Mon Sep 17 00:00:00 2001 From: "armin.nurkanovic" Date: Mon, 15 Sep 2025 17:23:44 +0200 Subject: [PATCH 23/31] about to run new experiments --- .../generate_nonlinear_mpec_problem_set.m | 2 +- .../run_diff_experiments.m | 2 -- .../run_nonlinear_mpec_benchmark_large_1.m | 3 ++- .../run_nonlinear_mpec_benchmark_large_2.m | 6 ++++-- .../run_nonlinear_mpec_benchmark_medium.m | 14 +++++--------- 5 files changed, 12 insertions(+), 15 deletions(-) diff --git a/benchmarks/nonlinear_mpec_benchmark/generate_nonlinear_mpec_problem_set.m b/benchmarks/nonlinear_mpec_benchmark/generate_nonlinear_mpec_problem_set.m index fa3a7b4..4cd7509 100644 --- a/benchmarks/nonlinear_mpec_benchmark/generate_nonlinear_mpec_problem_set.m +++ b/benchmarks/nonlinear_mpec_benchmark/generate_nonlinear_mpec_problem_set.m @@ -175,7 +175,7 @@ range_E = [3,4]./rescale_factor; % uniform range_q = [-20,10]./rescale_factor; % uniform range_grad = [-10,10]; -range_ubw = [1e1,1e3]; +range_ubw = [1e1,0.5e3]; mpecs = []; n_eq_vec = []; diff --git a/benchmarks/nonlinear_mpec_benchmark/run_diff_experiments.m b/benchmarks/nonlinear_mpec_benchmark/run_diff_experiments.m index c32fe2d..dab83f7 100644 --- a/benchmarks/nonlinear_mpec_benchmark/run_diff_experiments.m +++ b/benchmarks/nonlinear_mpec_benchmark/run_diff_experiments.m @@ -6,8 +6,6 @@ end %% run subseqeuntlly benchmarks of different size - - try run_nonlinear_mpec_benchmark_large_1 catch diff --git a/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_large_1.m b/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_large_1.m index 66ed8fa..7a90bc0 100644 --- a/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_large_1.m +++ b/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_large_1.m @@ -167,7 +167,8 @@ minlp_opts}; % list of options to pass to mpecsol (option structs) %% Create data struct -N_experiments = [4 3 5 1 2 6 7]; +% N_experiments = [4 3 5 1 2 6 7]; +N_experiments = [4 3 1 2 6 5]; % N_experiments = [3 1]; nonlinear_mpec_benchmark_dtable_loop; % this script runs the experimetns, creates a dtable %% Pick which results to plot diff --git a/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_large_2.m b/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_large_2.m index 91fa7a3..2ad9868 100644 --- a/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_large_2.m +++ b/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_large_2.m @@ -167,8 +167,10 @@ minlp_opts}; % list of options to pass to mpecsol (option structs) %% Create data struct -N_experiments = [4 3 5 1 2 6 7]; -% N_experiments = [3 1]; +% N_experiments = [4 3 5 1 2 6 7]; +% % N_experiments = [3 1]; +N_experiments = [4 3 1 2 6 5]; + nonlinear_mpec_benchmark_dtable_loop; % this script runs the experimetns, creates a dtable %% Pick which results to plot dtable = dtable1; diff --git a/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_medium.m b/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_medium.m index 8bcf041..7617384 100644 --- a/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_medium.m +++ b/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_medium.m @@ -50,9 +50,9 @@ 'NCVXQP6','DIXCHLNV',... }; -settings.objective_functions = {'Fletcher','McCormick',... - 'NCVXQP6','DIXCHLNV',... - }; +% settings.objective_functions = {'Fletcher','McCormick',... +% 'NCVXQP6','DIXCHLNV',... +% }; settings.rescale_factor = 1; settings.round_all_data = 1; @@ -87,13 +87,9 @@ % Problem size dimensions.N_rand_prob = 3; % number of problems per objective -dimensions.n_x_max = 1560; +dimensions.n_x_max = 1000; dimensions.n_x_min = 100; -dimensions.N_rand_prob = 1; % number of problems per objective -dimensions.n_x_max = 20; -dimensions.n_x_min = 10; - t_gen = tic; mpecs = generate_nonlinear_mpec_problem_set(problem_set_name,settings,dimensions); @@ -176,7 +172,7 @@ %% Create data struct N_experiments = [4 3 5 1 2 6 7]; -% N_experiments = [1]; +N_experiments = [1 2 3 4 6]; nonlinear_mpec_benchmark_dtable_loop; % this script runs the experimetns, creates a dtable %% Pick which results to plot dtable = dtable1; From 2a5bbe98f59fc643f35be97dd81ee719b53ac9a2 Mon Sep 17 00:00:00 2001 From: "armin.nurkanovic" Date: Mon, 15 Sep 2025 17:39:37 +0200 Subject: [PATCH 24/31] add small test to batch --- .../cpu_experiments.txt | 2 + .../current_problem.txt | 2 +- .../run_diff_experiments.m | 5 + .../run_nonlinear_mpec_benchmark_medium.m | 2 +- .../run_nonlinear_mpec_benchmark_small.m | 267 ++++++++++++++++++ 5 files changed, 276 insertions(+), 2 deletions(-) create mode 100644 benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_small.m diff --git a/benchmarks/nonlinear_mpec_benchmark/cpu_experiments.txt b/benchmarks/nonlinear_mpec_benchmark/cpu_experiments.txt index e69de29..6212493 100644 --- a/benchmarks/nonlinear_mpec_benchmark/cpu_experiments.txt +++ b/benchmarks/nonlinear_mpec_benchmark/cpu_experiments.txt @@ -0,0 +1,2 @@ +MPECopt-Reg-Gurobi - execution time: 106.16 seconds (0.0295 hours, 0.001229 days) +Reg - execution time: 98.42 seconds (0.0273 hours, 0.001139 days) diff --git a/benchmarks/nonlinear_mpec_benchmark/current_problem.txt b/benchmarks/nonlinear_mpec_benchmark/current_problem.txt index af8c2c2..f451d97 100644 --- a/benchmarks/nonlinear_mpec_benchmark/current_problem.txt +++ b/benchmarks/nonlinear_mpec_benchmark/current_problem.txt @@ -1 +1 @@ -nonlinear_large1_Fletcher_Var60_Comp24_Eq24_Ineq11 +nonlinear_small_Fletcher_Var605_Comp242_Eq242_Ineq114 diff --git a/benchmarks/nonlinear_mpec_benchmark/run_diff_experiments.m b/benchmarks/nonlinear_mpec_benchmark/run_diff_experiments.m index dab83f7..baea247 100644 --- a/benchmarks/nonlinear_mpec_benchmark/run_diff_experiments.m +++ b/benchmarks/nonlinear_mpec_benchmark/run_diff_experiments.m @@ -1,4 +1,9 @@ %% run subseqeuntlly benchmarks of different size +try + run_nonlinear_mpec_benchmark_small +catch +end +%% try run_nonlinear_mpec_benchmark_medium catch diff --git a/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_medium.m b/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_medium.m index 7617384..d6a76d0 100644 --- a/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_medium.m +++ b/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_medium.m @@ -87,7 +87,7 @@ % Problem size dimensions.N_rand_prob = 3; % number of problems per objective -dimensions.n_x_max = 1000; +dimensions.n_x_max = 1050; dimensions.n_x_min = 100; diff --git a/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_small.m b/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_small.m new file mode 100644 index 0000000..cf935d0 --- /dev/null +++ b/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_small.m @@ -0,0 +1,267 @@ +clear; clc; close all; +problem_set_name = 'nonlinear_small'; % (add large, degenerate, nonlinear, include lifted) + +%% file names +filename = 'nonlinear_mpec_small'; % name for figures and excel table +results_name = ['results/' filename '_' datestr(datetime("today"))]; % name for matlab .dat with results + +%% Generate test set +settings.casadi_variable_type = 'SX'; +settings.nonlinear_eq_constraints = 1; +settings.nonlinear_ineq_constraints = 1; +settings.s_nonlinear_eq = 0.01; +settings.s_nonlinear_ineq = 1/4; +settings.include_lifted_variables_in_objective = 0; +settings.copy_ineq = 1; %have two copies of the inequality constrants -> violate licq if active +settings.s_ineq_copy = 1/4; +settings.copy_eq = 0; +settings.s_eq_copy = 0.0; + +settings.inequality_constraints = 1; +settings.inequality_constraints_coupling_terms = 1; % if 0, then in Ax+By >=f, B = 0; + +% settings.objective_functions = {'Quadratic_psd','Quadratic_ind',... +% 'Fletcher','Himmelblau','McCormick',... +% 'Powell','Rosenbrock',... +% 'Raydan1','Raydan2',... +% 'Diagonal4','Diagonal5',... +% 'Extended_Trigiaonal','Three_Exponential_Terms',... +% 'Generalized_PSC1','Extended_PSC1',... +% 'Fletcvb3','Bdqrtic','Tridia',... +% 'EG2','Edensch','Indef',... +% 'Cube','Bdexp','Genhumps',... +% 'Arwhead', 'Quartc', 'Cosine', 'Sine',... +% 'CURLY20', 'SCURLY30', 'FLETBV3M',... +% 'NCVXQP6','DIXCHLNV','EDENSCH',... +% }; + + +% reduced set +settings.objective_functions = {'Fletcher','McCormick',... + 'Powell','Rosenbrock',... + 'Raydan1','Raydan2',... + 'Diagonal4','Diagonal5',... + 'Extended_Trigiaonal','Three_Exponential_Terms',... + 'Generalized_PSC1',... + 'Fletcvb3','Bdqrtic','Tridia',... + 'EG2','Edensch','Indef',... + 'Cube','Bdexp','Genhumps',... + 'Arwhead', 'Quartc', 'Cosine',... + 'NCVXQP6','DIXCHLNV',... + }; + +% settings.objective_functions = {'Fletcher','McCormick',... +% 'NCVXQP6','DIXCHLNV',... +% }; + +settings.rescale_factor = 1; +settings.round_all_data = 1; +settings.n_digits = 4; +settings.bounded_w = 1; +settings.use_normal_distributions_for_feasible_point = 1; +settings.alpha_lin = 10; % f + alpha_lin*[c;d]'*[x;y]' +settings.n_non_zero_E = 2000; +settings.symmetric_psd_M_matrix = 0; + +settings.s_density_A_B = 0.1; % all same or change +settings.s_density_M = 0.5; + +settings.s_density_A_B = 0.05; % all same or change +settings.s_density_M = 0.1; + + +settings.nnz_bounded_by_dim = 1; +settings.inv_cond_num = 1e0; +settings.nnz_factor = 1; +settings.n_comp_min = 400; + +settings.adaptive_density_bounds = 0; % to account for very larg problems +settings.variable_density = 1; +settings.range_s_density = [0.01 0.05]; +settings.random_problem_sizes = 1; + +settings.n_ineq_ub = 2; % n_ineq = n_ineq_ub*n_x % max relative number of ineq w.r.t x +settings.n_ineq_lb = 0.5; + +dimensions.n_fraction_of_x = 0.5; % n_y = round(n_x/n_fraction_of_x) + +% Problem size +dimensions.N_rand_prob = 3; % number of problems per objective +dimensions.n_x_max = 200; +dimensions.n_x_min = 100; + + +t_gen = tic; +mpecs = generate_nonlinear_mpec_problem_set(problem_set_name,settings,dimensions); +t_mpec_gen = toc(t_gen); +fprintf('Problem generation time : %2.2f \n',t_mpec_gen) + +%% Solver settings +solver_names = ["MPECopt-Reg-Gurobi", "MPECopt-$\ell_1$-Gurobi", "MPECopt-Reg-Gurobi-ET",... + "Reg", "NLP", "$\ell_1$-Penalty", "MINLP"]; + +solver_functions = {@mpec_optimizer,@mpec_optimizer,@mpec_optimizer,... + @mpec_homotopy_solver,@mpec_homotopy_solver, @mpec_homotopy_solver,... + @mpec_minlp_solver}; + +opts1 = mpecopt.Options(); +opts1.solver_name = solver_names{1}; +opts1.settings_lpec.lpec_solver = "Gurobi"; +opts1.relax_and_project_homotopy_parameter_steering = "Direct"; +opts1.use_one_nlp_solver = true; +opts1.problem_in_vertical_from = true; +opts1.settings_casadi_nlp.ipopt.max_wall_time = 900; +% opts1.initialization_strategy = "FeasibilityEll1General"; + + +opts2 = mpecopt.Options(); +opts2.solver_name = solver_names{2}; +opts2.settings_lpec.lpec_solver = "Gurobi"; +opts2.relax_and_project_homotopy_parameter_steering = "Ell_1"; +opts2.use_one_nlp_solver = true; +opts2.problem_in_vertical_from = true; +opts2.settings_casadi_nlp.ipopt.max_wall_time = 900; +% opts2.settings_lpec.stop_lpec_at_feasible = true; +% opts2.rho_TR_phase_i_init = 1e-3; + + +% opts3 = mpecopt.Options(); +% opts3.solver_name = solver_names{3}; +% opts3.settings_lpec.lpec_solver = "Highs"; +% opts3.relax_and_project_homotopy_parameter_steering = "Direct"; +% opts3.use_one_nlp_solver = true; +% +opts3 = mpecopt.Options(); +opts3.solver_name = solver_names{3}; +opts3.settings_lpec.lpec_solver = "Gurobi"; +opts3.relax_and_project_homotopy_parameter_steering = "Direct"; +opts3.use_one_nlp_solver = true; +opts3.settings_lpec.stop_lpec_at_feasible = true; +opts3.settings_lpec.stop_lpec_at_descent = true; +opts3.consider_all_complementarities_in_lpec = false; +opts3.problem_in_vertical_from = true; +opts3.settings_casadi_nlp.ipopt.max_wall_time = 900; +% opts3.rho_TR_phase_i_init = 1e-2; + +scholtes_opts1 = HomotopySolverOptions(); +scholtes_opts1.homotopy_parameter_steering = 'Direct'; +scholtes_opts1.problem_in_vertical_from = true; +scholtes_opts1.settings_casadi_nlp.ipopt.max_wall_time = 900; + +scholtes_opts2 = HomotopySolverOptions(); +scholtes_opts2.homotopy_parameter_steering = 'Direct'; +scholtes_opts2.max_iter = 1; +scholtes_opts2.sigma0 = 0; +scholtes_opts2.problem_in_vertical_from = true; +scholtes_opts2.settings_casadi_nlp.ipopt.max_wall_time = 1800; + + +scholtes_opts3 = HomotopySolverOptions(); +scholtes_opts3.homotopy_parameter_steering = 'Ell_1'; +scholtes_opts3.problem_in_vertical_from = true; +scholtes_opts3.settings_casadi_nlp.ipopt.max_wall_time = 900; + + +minlp_opts = MINLPSolverOptions(); +minlp_opts.settings_casadi_nlp.bonmin.time_limit = 1800; +scholtes_opts3.problem_in_vertical_from = true; + +opts = {opts1, opts2, opts3, ... + scholtes_opts1, scholtes_opts2, scholtes_opts3,... + minlp_opts}; % list of options to pass to mpecsol (option structs) + +%% Create data struct +N_experiments = [4 3 5 1 2 6 7]; +N_experiments = [1 4 6]; +nonlinear_mpec_benchmark_dtable_loop; % this script runs the experimetns, creates a dtable +%% Pick which results to plot +dtable = dtable1; +% Modift possibly which solvers are plotted +[~,temp_b] = unique(dtable.solver_name); +solver_name = dtable.solver_name(sort(temp_b)); +% solver_tab = table(solver_name); +N_plot = N_experiments; +% N_plot = [1,2]; + +dtable_plot = []; +for ii = N_plot + dtable_plot = [dtable_plot; dtable(dtable.solver_name == solver_names(ii),:)]; +end + +%% Export to excel +write_in_separate_sheets = true; +filename_excel = [filename '_details.xlsx']; +if write_in_separate_sheets + solver_tab = pivot(dtable_plot, Rows= "solver_name"); + for ii = 1:length(solver_tab.solver_name) + solver_name =solver_tab.solver_name{ii}; + dtable_ii = dtable_plot(dtable_plot.solver_name == solver_name,:); + writetable(dtable_ii,filename_excel,'Sheet',ii) + end +else + writetable(dtable_plot,filename_excel,'Sheet',1) +end + +%% Plot results +plot_settings.success_fail_statistics = 1; +plot_settings.n_biactive = 1; +plot_settings.lpecs_solved = 0; +plot_settings.active_set_changes = 0; +plot_settings.lpecs_cpu_time = 0; +plot_settings.bar_timing_plots = 0; +plot_settings.nlps_solved = 0; +plot_settings.max_nlp_cpu_time = 0; +plot_settings.max_nlp_cpu_time_phase_i = 0; +plot_settings.max_nlp_cpu_time_phase_ii = 0; +plot_settings.nlp_cpu_time = 0; % aggegated nlp times phase I and II +plot_settings.nlp_lpec_cpu_comparisson = 0; +plot_settings.objective = 0; +plot_settings.objective_rescaled = 1; +plot_settings.max_lpec_cpu_time = 0; + +plot_settings.relative = 1; +plot_settings.relative_phase_i = 0; +plot_settings.relative_phase_ii = 0; +plot_settings.relative_lpec = 0; + +plot_settings.absolute = 1; +plot_settings.absolute_phase_i = 0; +plot_settings.absolute_phase_ii = 0; +plot_settings.absolute_lpec = 0; + +plot_settings.solved_in_phase_i = 0; + +% split cpu time among phases and show in bar plot (just report for mpecopt +% guroi) +plot_settings.nlp_phases_cpu_time = 0; +plot_settings.lpec_phases_cpu_time = 0; + +plot_settings.max_lpec_cpu_time = 0; +plot_settings.stationary_points = 0; +plot_settings.b_stationarity = 0; +plot_settings.b_stationarty_as_success_criterion = 0; +plot_settings.plot_only_sucessful = 1; +plot_settings.bar_comparisson_plots = 0; + +plot_settings.save_plot = 1; + +close all; +plot_mpec_benchmark_result(dtable_plot,filename,plot_settings) +%% +dtable_ii = dtable_plot(dtable_plot.solver_name == solver_names{1},:); +dtable_jj = dtable_plot(dtable_plot.solver_name == solver_names{4},:); +hh = find(dtable_jj.b_stationarity == 0 & dtable_jj.success == 1); +% dtable_ii.f_lpec((hh)) +% dtable_jj.f((hh)) +% dtable_jj.f_lpec((hh)) +% abs((dtable_ii.f((hh))-dtable_jj.f((hh))))./abs(dtable_ii.f((hh)))>1e-3 + +%% +% figure +% semilogy(abs(dtable_ii.f_lpec)+1e-16,'LineWidth',1.5); +% hold on +% semilogy(abs(dtable_jj.f_lpec)+1e-16,'LineWidth',1.5); +% ylabel('$|\nabla f(x^*)^\top d|$') +% xlabel('Problem instance') +% grid on +% yline(1e-8,'LineWidth',2) \ No newline at end of file From cc99c4261d21cebd2b6af673e06015944bfebfc7 Mon Sep 17 00:00:00 2001 From: "armin.nurkanovic" Date: Wed, 17 Sep 2025 15:20:14 +0200 Subject: [PATCH 25/31] fixing some of the plotting infrastructure, putting ell inf in largest benchmark --- benchmarks/histogram_on_lpecs.m | 169 +++++++++--------- benchmarks/macmpec/load_and_plot_results.m | 26 +-- .../macmpec/mpec_benchmark_dtable_loop.m | 1 + .../macmpec/run_macmpec_experiments_general.m | 19 +- .../macmpec/solve_single_macmpec_problem.m | 38 ++-- .../cpu_experiments.txt | 12 ++ .../current_problem.txt | 2 +- .../load_and_plot_results.m | 81 ++++----- .../nonlinear_mpec_benchmark_dtable_loop.m | 2 + .../run_nonlinear_mpec_benchmark_large_2.m | 34 +++- 10 files changed, 205 insertions(+), 179 deletions(-) diff --git a/benchmarks/histogram_on_lpecs.m b/benchmarks/histogram_on_lpecs.m index f78f5e0..bc079cb 100644 --- a/benchmarks/histogram_on_lpecs.m +++ b/benchmarks/histogram_on_lpecs.m @@ -4,11 +4,16 @@ % dtable; % lpec_dstruct; -S = load('macmpec_general_01-Sep-2025_lpec_details'); +S = load('macmpec_general_14-Sep-2025_lpec_details'); lpec_dstruct = S.lpec_dstruct; -S = load('macmpec_general_01-Sep-2025'); -dtable = S.dtable; +S = load('macmpec_general_14-Sep-2025'); +% +% S = load('nonlinear_mpec_med_15-Sep-2025_lpec_details'); +% lpec_dstruct = S.lpec_dstruct; +% S = load('nonlinear_mpec_med_15-Sep-2025'); + +dtable = S.dtable; plot_cpu = false; % Filter by solver and success @@ -16,9 +21,18 @@ % solver_names = ["Gurobi", "Gurobi-early-I", "Gurobi-early-I-II", "Highs", "Highs-early"]; solver_names = unique(dtable.solver_name); -solver = solver_names{1}; +solver = solver_names{3} idx = (dtable.solver_name == solver) & dtable.success == 1; +fields = fieldnames(lpec_dstruct); + +for i = 1:length(fields) + lpec_dstruct_filtered.(fields{i}) = lpec_dstruct.(fields{i})(idx); +end + +lpec_dstruct = lpec_dstruct_filtered; + + % ------------------------ % Helper functions @@ -68,8 +82,8 @@ %------------------------ % Nodecount statistics (for each lpec call seperatalyover solver calls) % ------------------------ -nc_i = cell2mat(process_cells(lpec_dstruct.nodecount_phase_i(idx))); -nc_ii = cell2mat(process_cells(lpec_dstruct.nodecount_phase_ii(idx))); +nc_i = cell2mat(process_cells(lpec_dstruct.nodecount_phase_i)); +nc_ii = cell2mat(process_cells(lpec_dstruct.nodecount_phase_ii)); nc_tot = [nc_i, nc_ii]; log_plot = true; @@ -77,14 +91,17 @@ % --- Phase I --- subplot(1,2,1) -if log_plot +if log_plot h1 = histogram(log2(nc_i), 'BinMethod', 'integers'); min_pow = floor(min(log2(nc_i))); - max_pow = ceil(max(log2(nc_i))); + max_pow = max(5,ceil(max(log2(nc_i)))); set(gca, 'XTick', min_pow:max_pow) set(gca, 'XTickLabel', arrayfun(@(x) sprintf('$2^{%g}$', x), min_pow:max_pow, 'UniformOutput', false)) + if numel(unique(nc_ii)) == 1 + xlim([-1 max_pow]); + end else - h1 = histogram(nc_i, 'BinMethod', 'integers'); + h1 = histogram(nc_i, 'BinMethod', 'integers'); end % Set ticks manually based on your data range @@ -102,10 +119,11 @@ % adjust ylim ymax1 = max(h1.Values); ylim([0 ymax1 + max(2,ceil(0.2*ymax1))]) +% xlim([0 100]) % --- Phase II --- subplot(1,2,2) -if log_plot +if log_plot h2 = histogram(log2(nc_ii), 'BinMethod', 'integers'); % min_pow = floor(min(log2(nc_ii))); % max_pow = ceil(max(log2(nc_ii))); @@ -113,14 +131,14 @@ set(gca, 'XTickLabel', arrayfun(@(x) sprintf('$2^{%g}$', x), min_pow:max_pow, 'UniformOutput', false)) % enforce nicer axis if only one bar if numel(unique(nc_ii)) == 1 - xlim([-1 max_pow]); + xlim([-1 max_pow]); end else h2 = histogram(nc_ii, 'BinMethod', 'integers'); % enforce nicer axis if only one bar -if numel(unique(nc_ii)) == 1 - xlim([0 10]) -end + if numel(unique(nc_ii)) == 1 + xlim([0 10]) + end end title('Nodecount Phase II - each LPEC call'); xlabel('Nodes'); ylabel('Frequency'); grid on; @@ -151,62 +169,69 @@ % ------------------------ % Nodecount statistics (cummulative over solver calls) % ------------------------ -nc_i = process_cells_cummulative(lpec_dstruct.nodecount_phase_i(idx)); -nc_ii = process_cells_cummulative(lpec_dstruct.nodecount_phase_ii(idx)); -nc_tot = nc_i + nc_ii; +nc_cummulative_i = process_cells_cummulative(lpec_dstruct.nodecount_phase_i); +nc_cummulative_ii = process_cells_cummulative(lpec_dstruct.nodecount_phase_ii); +nc_tot_cummulative = nc_cummulative_i + nc_cummulative_ii; if 0 % Histograms (nodecounts) figure; subplot(131) - histogram(nc_i, 'BinMethod', 'integers'); + histogram(nc_cummulative_i, 'BinMethod', 'integers'); title('Nodecount Phase I'); xlabel('Nodes'); ylabel('Frequency'); grid on; % xticks(min(nc_i):max(nc_i)); subplot(132) - histogram(nc_ii, 'BinMethod', 'integers'); + histogram(nc_cummulative_ii, 'BinMethod', 'integers'); title('Nodecount Phase II'); xlabel('Nodes'); ylabel('Frequency'); grid on; % xticks(min(nc_ii):max(nc_ii)); subplot(133) - histogram(nc_tot, 'BinMethod', 'integers'); + histogram(nc_tot_cummulative, 'BinMethod', 'integers'); title('Total Nodecount'); xlabel('Nodes'); ylabel('Frequency'); grid on; % xticks(min(nc_tot):max(nc_tot)); end -% Largest problem by total nodecount -[sort_val, sort_idx_rel] = sort(nc_tot); -max_idx_rel = sort_idx_rel(end-1); +%% Largest problem by total nodecount +[sort_val, sort_idx_rel] = sort(nc_tot_cummulative); +max_idx_rel = sort_idx_rel(end-2); % [~, max_idx_rel] = max(nc_tot); -max_idx_all = find(idx); % indices in original table -max_idx = max_idx_all(max_idx_rel); +% max_idx_all = find(idx); % indices in original table +% max_idx = max_idx_all(max_idx_rel); +max_idx = max_idx_rel; +% max_idx = 167; + +% [max_idx max_idx_rel] + +% pack-rig1-8.nl +% max_idx = 128; +% max_idx_rel = 128; + +% qpec-200-3 +% max_idx = 166; +% max_idx_rel = 157; + +% qpec-200-4 +% max_idx = 167; +% max_idx_rel = 158; + + + +% +% max_idx = 127; +% max_idx = 158; +% max_idx = 167; fprintf('Problem with largest total nodecount is %s with %d nodes.\n', ... - dtable.problem_name{max_idx}, nc_tot(max_idx_rel)); + dtable.problem_name{max_idx}, nc_tot_cummulative(max_idx)); % ------------------------ % CPU time statistics % ------------------------ -cpu_i = process_cpu(lpec_dstruct.cpu_time_lpec_phase_i(idx)); -cpu_ii = process_cpu(lpec_dstruct.cpu_time_lpec_phase_ii(idx)); +cpu_i = process_cpu(lpec_dstruct.cpu_time_lpec_phase_i); +cpu_ii = process_cpu(lpec_dstruct.cpu_time_lpec_phase_ii); cpu_tot = cpu_i + cpu_ii; -% Histograms (CPU times) -if plot_cpu - figure; - subplot(131) - histogram(cpu_i); - title('CPU Time Phase I'); xlabel('Time [s]'); ylabel('Frequency'); grid on; - - subplot(132) - histogram(cpu_ii); - title('CPU Time Phase II'); xlabel('Time [s]'); ylabel('Frequency'); grid on; - - subplot(133) - histogram(cpu_tot); - title('Total CPU Time'); xlabel('Time [s]'); ylabel('Frequency'); grid on; -end % ------------------------ -% max_idx = 14; %% Iteration plot for max problem (nodecounts) % ------------------------ vec_i = cell2mat(process_cells(lpec_dstruct.nodecount_phase_i(max_idx))); @@ -215,38 +240,7 @@ % vec_ii = convert_vec(vec_ii); x_i = 1:length(vec_i); x_ii = (length(vec_i)+1):(length(vec_i)+length(vec_ii)); -figure; -subplot(121) -plot(x_i, vec_i, '-o', 'LineWidth', 1.5, 'Color', [0 0.4470 0.7410], 'DisplayName','Phase I'); hold on; -plot(x_ii, vec_ii, '-s', 'LineWidth', 1.5, 'Color', [0.8500 0.3250 0.0980], 'DisplayName','Phase II'); -% Connect the two phases -if ~isempty(vec_i) && ~isempty(vec_ii) - plot([x_i(end), x_ii(1)], [vec_i(end), vec_ii(1)], '--', 'LineWidth', 1, 'Color', [0.5 0.5 0.5]); -end -ylim([0 (max([vec_i, vec_ii])+1)*1.1]) -xlabel('Iteration'); -ylabel('Nodecount'); -% title(sprintf('Nodecount per Iteration (%s)', dtable.problem_name{max_idx})); -legend('show'); grid on; -% ------------------------ -% Iteration plot for max problem (CPU times) -% ------------------------ -cpu_vec_i = lpec_dstruct.cpu_time_lpec_phase_i{max_idx}; -cpu_vec_ii = lpec_dstruct.cpu_time_lpec_phase_ii{max_idx}; -if isempty(cpu_vec_i), cpu_vec_i = 0; end -if isempty(cpu_vec_ii), cpu_vec_ii = 0; end -x_ci = 1:length(cpu_vec_i); -x_cii = (length(cpu_vec_i)+1):(length(cpu_vec_i)+length(cpu_vec_ii)); -subplot(122) -plot(x_ci, cpu_vec_i, '-o', 'LineWidth', 1.5, 'Color', [0 0.4470 0.7410], 'DisplayName','Phase I'); hold on; -plot(x_cii, cpu_vec_ii, '-s', 'LineWidth', 1.5, 'Color', [0.8500 0.3250 0.0980], 'DisplayName','Phase II'); -% Connect the two phases -if ~isempty(cpu_vec_i) && ~isempty(cpu_vec_ii) - plot([x_ci(end), x_cii(1)], [cpu_vec_i(end), cpu_vec_ii(1)], '--', 'LineWidth', 1, 'Color', [0.5 0.5 0.5]); -end -xlabel('Iteration'); -ylabel('CPU Time [s]'); -legend('show'); grid on; + %% %% Iteration plot for max problem (nodecounts) % ------------------------ @@ -262,7 +256,6 @@ % Create color array - blue for Phase I, orange for Phase II colors = [repmat([0 0.4470 0.7410], length(vec_i), 1); ... repmat([0.8500 0.3250 0.0980], length(vec_ii), 1)]; -%% figure; subplot(121) b = bar(x_combined, combined_vec, 'FaceColor', 'flat'); @@ -279,9 +272,7 @@ h2 = bar(NaN, NaN, 'FaceColor', [0.8500 0.3250 0.0980]); legend([h1, h2], {'Phase I', 'Phase II'}, 'Location', 'best'); -% ------------------------ -% Iteration plot for max problem (CPU times) -% ------------------------ + cpu_vec_i = lpec_dstruct.cpu_time_lpec_phase_i{max_idx}; cpu_vec_ii = lpec_dstruct.cpu_time_lpec_phase_ii{max_idx}; @@ -314,3 +305,21 @@ h4 = bar(NaN, NaN, 'FaceColor', [0.8500 0.3250 0.0980]); legend([h3, h4], {'Phase I', 'Phase II'}, 'Location', 'best'); + +%% + +% Histograms (CPU times) +if plot_cpu + figure; + subplot(131) + histogram(cpu_i); + title('CPU Time Phase I'); xlabel('Time [s]'); ylabel('Frequency'); grid on; + + subplot(132) + histogram(cpu_ii); + title('CPU Time Phase II'); xlabel('Time [s]'); ylabel('Frequency'); grid on; + + subplot(133) + histogram(cpu_tot); + title('Total CPU Time'); xlabel('Time [s]'); ylabel('Frequency'); grid on; +end \ No newline at end of file diff --git a/benchmarks/macmpec/load_and_plot_results.m b/benchmarks/macmpec/load_and_plot_results.m index 634ab8c..c1f7080 100644 --- a/benchmarks/macmpec/load_and_plot_results.m +++ b/benchmarks/macmpec/load_and_plot_results.m @@ -52,30 +52,16 @@ case 1 % S = load('macmpec_general_30-Oct-2024'); % S = load('macmpec_general_07-Nov-2024'); - S = load('macmpec_general_06-Sep-2025'); + S = load('macmpec_general_14-Sep-2025'); dtable = S.dtable; - % solver_names = ["MPECopt-Gurobi", "MPECopt-HiGHS",... - % "MPECopt-Simple", "Reg" , "Pen-$\ell_{\infty}$", "Pen-$\ell_{1}$"]; + % solver_names = unique(dtable.solver_name); - - % solver_names = ["MPECopt-Reg-Gurobi", "MPECopt-Reg-HiGHS",... - % "MPECopt-$\ell_1$-Gurobi", "Reg" , "Pen-$\ell_{\infty}$", "Pen-$\ell_{1}$"]; - - solver_names = ["MPECopt-Reg-Gurobi", "MPECopt-Reg-HiGHS", "MPECopt-$\ell_1$-Gurobi", ... - "Reg" , "NLP", ... + solver_names = ["MPECopt-Reg-Gurobi", "MPECopt-$\ell_1$-Gurobi", "MPECopt-Reg-Guroby-ET", ... + "Reg", "NLP", ... "MINLP"]; - solver_names = ["MINLP" - "MPECopt-$\ell_1$-Gurobi" - "MPECopt-Reg-Gurobi" - "NLP" - "Reg" - "MINLP" - ]; - - % solver_names = ["MPECopt-Gurobi", "MPECopt-Reg-HiGHS",... - % "MPECopt-$\ell_1$-Gurobi", "Reg" , "Pen-$\ell_{\infty}$", "Pen-$\ell_{1}$"]; + filename = 'macmpec'; N_plot = [1:6]; plot_settings.stationary_points = 1; @@ -84,7 +70,7 @@ plot_settings.solved_in_phase_i = 1; %% Who is not B stationary? dtable_ii = dtable(dtable.solver_name == solver_names{1},:); - dtable_jj = dtable(dtable.solver_name == solver_names{3},:); + dtable_jj = dtable(dtable.solver_name == solver_names{4},:); ind_not_B = find(dtable_jj.success == 1 & dtable_jj.b_stationarity == 0); % For Reg % diff in obj diff --git a/benchmarks/macmpec/mpec_benchmark_dtable_loop.m b/benchmarks/macmpec/mpec_benchmark_dtable_loop.m index d575248..134356a 100644 --- a/benchmarks/macmpec/mpec_benchmark_dtable_loop.m +++ b/benchmarks/macmpec/mpec_benchmark_dtable_loop.m @@ -169,6 +169,7 @@ % save intermediate reuslts dtable1 = struct2table(dstruct); save([results_name '_' num2str(ii)],"dtable1"); + save([results_name '_lpec_details_' num2str(ii)],"lpec_dstruct"); % pause(90); % cool down cpu pause end %% Check results and plot diff --git a/benchmarks/macmpec/run_macmpec_experiments_general.m b/benchmarks/macmpec/run_macmpec_experiments_general.m index 792e327..1f8c93e 100644 --- a/benchmarks/macmpec/run_macmpec_experiments_general.m +++ b/benchmarks/macmpec/run_macmpec_experiments_general.m @@ -55,12 +55,12 @@ %% Define list of solvers to use -solver_names = ["MPECopt-Reg-Gurobi", "MPECopt-$\ell_1$-Gurobi", "MPECopt-Reg-Guroby-ET", ... - "Reg", "NLP", ... +solver_names = ["MPECopt-Reg-Gurobi", "MPECopt-$\ell_{\infty}$-Gurobi", "MPECopt-Reg-Guroby-ET", ... + "Reg", "NLP", "$\ell_{\infty}$",... "MINLP"]; solver_functions = {@mpec_optimizer,@mpec_optimizer,@mpec_optimizer,... - @mpec_homotopy_solver,@mpec_homotopy_solver,... + @mpec_homotopy_solver,@mpec_homotopy_solver,@mpec_homotopy_solver, ... @mpec_minlp_solver}; opts1 = mpecopt.Options(); @@ -74,7 +74,7 @@ opts2 = mpecopt.Options(); opts2.solver_name = solver_names{2}; opts2.settings_lpec.lpec_solver = "Gurobi"; -opts2.relax_and_project_homotopy_parameter_steering = "Ell_1"; +opts2.relax_and_project_homotopy_parameter_steering = "Ell_inf"; opts2.use_one_nlp_solver = false; % opts2.settings_lpec.stop_lpec_at_feasible = true; % opts2.rho_TR_phase_i_init = 1e-3; @@ -87,7 +87,7 @@ opts3.use_one_nlp_solver = false; opts3.settings_lpec.stop_lpec_at_feasible = true; opts3.settings_lpec.stop_lpec_at_descent = true; -opts3.consider_all_complementarities_in_lpec = false; +opts3.consider_all_complementarities_in_lpec = true; scholtes_opts1 = HomotopySolverOptions(); scholtes_opts1.homotopy_parameter_steering = 'Direct'; @@ -97,18 +97,21 @@ scholtes_opts2.max_iter = 1; scholtes_opts2.sigma0 = 0; +scholtes_opts3 = HomotopySolverOptions(); +scholtes_opts3.homotopy_parameter_steering = 'Ell_inf'; + minlp_opts = MINLPSolverOptions(); minlp_opts.settings_casadi_nlp.bonmin.time_limit = 600; opts = {opts1, opts2, opts3, ... - scholtes_opts1, scholtes_opts2,... + scholtes_opts1, scholtes_opts2, scholtes_opts3,... minlp_opts}; % list of options to pass to mpecsol (option structs) %% Create data struct % N_experiments = [1, 3:6]; -N_experiments = [1:7]; -% N_experiments = [1, 4, 6]; +N_experiments = [1:6]; +N_experiments = [2, 6]; mpec_benchmark_dtable_loop; % this script runs the experimetns, creates a dtable diff --git a/benchmarks/macmpec/solve_single_macmpec_problem.m b/benchmarks/macmpec/solve_single_macmpec_problem.m index 772d3ac..1f26bea 100644 --- a/benchmarks/macmpec/solve_single_macmpec_problem.m +++ b/benchmarks/macmpec/solve_single_macmpec_problem.m @@ -6,13 +6,13 @@ all_problem_names = {macmpec_json.name}; -problame_name = 'qpec-200-3'; -% problame_name = 'ralph1'; -% problame_name = 'pack-rig-32'; +problame_name = 'qpec-200-4'; +% problame_name = 'pack-rig2-16'; +% % problame_name = 'pack-rig-32'; % problame_name = 'pack-rig-8'; -% problame_name = 'gnash15m.nl'; +% % problame_name = 'gnash15m.nl'; % problame_name = 'pack-rig2p-16.nl'; -% problame_name = 'nash1a'; +% % problame_name = 'nash1a'; % problame_name = 'nash1c'; % problame_name = 'tap-09'; % problame_name = ' design-cent-31'; @@ -109,21 +109,15 @@ % w_opt_minlp = full(result_minlp.x); %% MPECopt solver -solver_settings = mpecopt.Options(); -solver_settings.relax_and_project_homotopy_parameter_steering = "Direct"; -solver_settings.settings_lpec.lpec_solver = "Gurobi"; -% solver_settings.settings_casadi_nlp.ipopt.max_iter = 4000; -solver_settings.settings_lpec.stop_lpec_at_feasible = true; -solver_settings.settings_lpec.stop_lpec_at_descent = true; -% solver_settings.settings_casadi_nlp.ipopt.fixed_variable_treatment = 'relax_bounds'; -% solver_settings.initialization_strategy = "FeasibilityEll1General"; -% solver_settings.rho_TR_phase_ii_init = 1e-4; -% solver_settings.consider_all_complementarities_in_lpec = false; -% solver_settings.tol_active = 1e-6; -solver_settings.use_one_nlp_solver = true; -solver_settings.compute_tnlp_stationary_point = false; +opts = mpecopt.Options(); +opts.relax_and_project_homotopy_parameter_steering = "Direct"; +opts.settings_lpec.lpec_solver = "Gurobi"; +opts.settings_lpec.stop_lpec_at_feasible = 0; +solver_settings.settings_lpec.stop_lpec_at_descent = 0; +opts.use_one_nlp_solver = true; +opts.compute_tnlp_stationary_point = true; tic -solver = mpecopt.Solver(mpec, solver_settings); +solver = mpecopt.Solver(mpec, opts); toc [result_mpecopt,stats_mpecopt] = solver.solve(solver_initalization); @@ -135,8 +129,8 @@ stats_mpecopt.iter.nodecount_phase_i stats_mpecopt.iter.nodecount_phase_ii -stats_mpecopt.iter.cpu_time_lpec_phase_i_iter -stats_mpecopt.iter.cpu_time_lpec_phase_ii_iter +% stats_mpecopt.iter.cpu_time_lpec_phase_i_iter +% stats_mpecopt.iter.cpu_time_lpec_phase_ii_iter %% Results comparison fprintf('\n-------------------------------------------------------------------------------\n'); @@ -150,6 +144,6 @@ % fprintf('||w_minlp - w_mpec|| = %2.2e \n',norm(w_opt_minlp-w_opt_mpecopt)); -% +% % % stats_mpecopt.iter.itercount_phase_i % stats_mpecopt.iter.itercount_phase_ii \ No newline at end of file diff --git a/benchmarks/nonlinear_mpec_benchmark/cpu_experiments.txt b/benchmarks/nonlinear_mpec_benchmark/cpu_experiments.txt index 6212493..ac2a326 100644 --- a/benchmarks/nonlinear_mpec_benchmark/cpu_experiments.txt +++ b/benchmarks/nonlinear_mpec_benchmark/cpu_experiments.txt @@ -1,2 +1,14 @@ MPECopt-Reg-Gurobi - execution time: 106.16 seconds (0.0295 hours, 0.001229 days) Reg - execution time: 98.42 seconds (0.0273 hours, 0.001139 days) +Reg - execution time: 2.47 seconds (0.0007 hours, 0.000029 days) +MPECopt-Reg-Gurobi-ET - execution time: 2.16 seconds (0.0006 hours, 0.000025 days) +MPECopt-Reg-Gurobi - execution time: 2.02 seconds (0.0006 hours, 0.000023 days) +Reg - execution time: 1.68 seconds (0.0005 hours, 0.000019 days) +MPECopt-Reg-Gurobi-ET - execution time: 1.92 seconds (0.0005 hours, 0.000022 days) +MPECopt-Reg-Gurobi - execution time: 2.02 seconds (0.0006 hours, 0.000023 days) +Reg - execution time: 2.29 seconds (0.0006 hours, 0.000027 days) +MPECopt-Reg-Gurobi-ET - execution time: 2.64 seconds (0.0007 hours, 0.000031 days) +MPECopt-Reg-Gurobi - execution time: 2.58 seconds (0.0007 hours, 0.000030 days) +MPECopt-$\ell_{\infty}$-Gurobi - execution time: 1.64 seconds (0.0005 hours, 0.000019 days) +$\ell_\infty$-Penalty - execution time: 1.22 seconds (0.0003 hours, 0.000014 days) +NLP - execution time: 0.97 seconds (0.0003 hours, 0.000011 days) diff --git a/benchmarks/nonlinear_mpec_benchmark/current_problem.txt b/benchmarks/nonlinear_mpec_benchmark/current_problem.txt index f451d97..dd2c36f 100644 --- a/benchmarks/nonlinear_mpec_benchmark/current_problem.txt +++ b/benchmarks/nonlinear_mpec_benchmark/current_problem.txt @@ -1 +1 @@ -nonlinear_small_Fletcher_Var605_Comp242_Eq242_Ineq114 +nonlinear_large2_DIXCHLNV_Var70_Comp28_Eq28_Ineq14 diff --git a/benchmarks/nonlinear_mpec_benchmark/load_and_plot_results.m b/benchmarks/nonlinear_mpec_benchmark/load_and_plot_results.m index 1d85747..ef87dae 100644 --- a/benchmarks/nonlinear_mpec_benchmark/load_and_plot_results.m +++ b/benchmarks/nonlinear_mpec_benchmark/load_and_plot_results.m @@ -20,7 +20,7 @@ plot_settings.lpecs_cpu_time = 0; plot_settings.nlp_cpu_time = 0; % aggegated nlp times phase I and II -plot_settings.max_nlp_cpu_time = 0; +plot_settings.max_nlp_cpu_time = 1; plot_settings.max_lpec_cpu_time = 0; plot_settings.n_biactive_count = 0; @@ -36,14 +36,14 @@ plot_settings.solved_in_phase_i = 0; plot_settings.objective = 0; -plot_settings.objective_rescaled = 0; +plot_settings.objective_rescaled = 1; % split cpu time among phases and show in bar plot (just report for mpecopt % guroi) plot_settings.stationary_points = 0; plot_settings.b_stationarity = 0; plot_settings.b_stationarty_as_success_criterion = 0; -plot_settings.plot_only_sucessful = 1; +plot_settings.plot_only_sucessful = 0; plot_settings.save_plot = 1; @@ -51,52 +51,53 @@ results = 1; switch results case 1 - S = load('nonlinear_mpec_general_08-Nov-2024'); + S = load('nonlinear_mpec_large1_15-Sep-2025_5'); % S = load('nonlinear_mpec_general2_09-Nov-2024'); try dtable = S.dtable; catch dtable = S.dtable1; end - solver_names = ["MPECopt-Reg-Gurobi", "MPECopt-Reg-HiGHS",... - "MPECopt-$\ell_1$-Gurobi", "Reg" , "Pen-$\ell_{\infty}$", "Pen-$\ell_{1}$"]; + solver_names = ["MPECopt-Reg-Gurobi", "MPECopt-Reg-Gurobi-ET","Reg", "NLP", "$\ell_1$-Penalty"]; + filename = 'nonlinear_mpec'; - N_plot = [1:6]; - plot_settings.stationary_points = 1; - plot_settings.b_stationarity = 1; + N_plot = [1:5]; + plot_settings.stationary_points = 0; + plot_settings.b_stationarity = 0; plot_settings.n_biactive = 1; %% Who is not B stationary? - dtable_ii = dtable(dtable.solver_name == solver_names{1},:); - dtable_jj = dtable(dtable.solver_name == solver_names{4},:); - ind_not_B = find(dtable_jj.success == 1 & dtable_jj.b_stationarity == 0); - % For Reg - % diff in obj - delta_f = dtable_ii.f(ind_not_B)-dtable_jj.f(ind_not_B); - delta_f_relative_small = 100*abs(delta_f)./abs(dtable_ii.f(ind_not_B)+1e-16) - delta_f_relative_small_ind = abs(delta_f)./abs(dtable_ii.f(ind_not_B)+1e-16)<1e-3; - dtable_temp = dtable_jj; - dtable_temp.b_stationarity(ind_not_B(delta_f_relative_small_ind)) = 1; - dtable_temp.multiplier_based_stationarity(ind_not_B(delta_f_relative_small_ind)) = dtable_ii.multiplier_based_stationarity(ind_not_B(delta_f_relative_small_ind)); - dtable_temp.problem_name(ind_not_B(~delta_f_relative_small_ind)) - dtable_temp.prob_num(ind_not_B(~delta_f_relative_small_ind)) - dtable_temp.f_lpec(ind_not_B(~delta_f_relative_small_ind)) - dtable(dtable.solver_name == solver_names{4},:) = dtable_temp; - % For pen - dtable_ii = dtable(dtable.solver_name == solver_names{3},:); - dtable_jj = dtable(dtable.solver_name == solver_names{6},:); - ind_not_B = find(dtable_jj.success == 1 & dtable_jj.b_stationarity == 0); - % diff in obj - delta_f = dtable_ii.f(ind_not_B)-dtable_jj.f(ind_not_B); - delta_f_relative_small = 100*abs(delta_f)./abs(dtable_ii.f(ind_not_B)+1e-16) - delta_f_relative_small_ind = abs(dtable_ii.f(ind_not_B)-dtable_jj.f(ind_not_B))./abs(dtable_ii.f(ind_not_B)+1e-16)<1e-3; - dtable_temp = dtable_jj; - dtable_temp.b_stationarity(ind_not_B(delta_f_relative_small_ind)) = 1; - dtable_temp.multiplier_based_stationarity(ind_not_B(delta_f_relative_small_ind)) = dtable_ii.multiplier_based_stationarity(ind_not_B(delta_f_relative_small_ind)); - dtable_temp.problem_name(ind_not_B(~delta_f_relative_small_ind)) - dtable_temp.prob_num(ind_not_B(~delta_f_relative_small_ind)) - dtable_temp.f_lpec(ind_not_B(~delta_f_relative_small_ind)) - dtable(dtable.solver_name == solver_names{6},:) = dtable_temp; - + if 0 + dtable_ii = dtable(dtable.solver_name == solver_names{1},:); + dtable_jj = dtable(dtable.solver_name == solver_names{4},:); + ind_not_B = find(dtable_jj.success == 1 & dtable_jj.b_stationarity == 0); + % For Reg + % diff in obj + delta_f = dtable_ii.f(ind_not_B)-dtable_jj.f(ind_not_B); + delta_f_relative_small = 100*abs(delta_f)./abs(dtable_ii.f(ind_not_B)+1e-16) + delta_f_relative_small_ind = abs(delta_f)./abs(dtable_ii.f(ind_not_B)+1e-16)<1e-3; + dtable_temp = dtable_jj; + dtable_temp.b_stationarity(ind_not_B(delta_f_relative_small_ind)) = 1; + dtable_temp.multiplier_based_stationarity(ind_not_B(delta_f_relative_small_ind)) = dtable_ii.multiplier_based_stationarity(ind_not_B(delta_f_relative_small_ind)); + dtable_temp.problem_name(ind_not_B(~delta_f_relative_small_ind)) + dtable_temp.prob_num(ind_not_B(~delta_f_relative_small_ind)) + dtable_temp.f_lpec(ind_not_B(~delta_f_relative_small_ind)) + dtable(dtable.solver_name == solver_names{4},:) = dtable_temp; + % For pen + dtable_ii = dtable(dtable.solver_name == solver_names{3},:); + dtable_jj = dtable(dtable.solver_name == solver_names{6},:); + ind_not_B = find(dtable_jj.success == 1 & dtable_jj.b_stationarity == 0); + % diff in obj + delta_f = dtable_ii.f(ind_not_B)-dtable_jj.f(ind_not_B); + delta_f_relative_small = 100*abs(delta_f)./abs(dtable_ii.f(ind_not_B)+1e-16) + delta_f_relative_small_ind = abs(dtable_ii.f(ind_not_B)-dtable_jj.f(ind_not_B))./abs(dtable_ii.f(ind_not_B)+1e-16)<1e-3; + dtable_temp = dtable_jj; + dtable_temp.b_stationarity(ind_not_B(delta_f_relative_small_ind)) = 1; + dtable_temp.multiplier_based_stationarity(ind_not_B(delta_f_relative_small_ind)) = dtable_ii.multiplier_based_stationarity(ind_not_B(delta_f_relative_small_ind)); + dtable_temp.problem_name(ind_not_B(~delta_f_relative_small_ind)) + dtable_temp.prob_num(ind_not_B(~delta_f_relative_small_ind)) + dtable_temp.f_lpec(ind_not_B(~delta_f_relative_small_ind)) + dtable(dtable.solver_name == solver_names{6},:) = dtable_temp; + end case 2 S = load('macmpec_phase_i_29-Oct-2024'); diff --git a/benchmarks/nonlinear_mpec_benchmark/nonlinear_mpec_benchmark_dtable_loop.m b/benchmarks/nonlinear_mpec_benchmark/nonlinear_mpec_benchmark_dtable_loop.m index f7ff7d2..043006b 100644 --- a/benchmarks/nonlinear_mpec_benchmark/nonlinear_mpec_benchmark_dtable_loop.m +++ b/benchmarks/nonlinear_mpec_benchmark/nonlinear_mpec_benchmark_dtable_loop.m @@ -163,6 +163,8 @@ % save intermediate reuslts dtable1 = struct2table(dstruct); save([results_name '_' num2str(ii)],"dtable1"); + save([results_name '_lpec_details_' num2str(ii)],"lpec_dstruct"); + % pause(90); % cool down cpu pause cpu_current_exp = toc(t_current_exp); timing_str = sprintf(' - execution time: %.2f seconds (%.4f hours, %.6f days) \n', cpu_current_exp, cpu_current_exp/3600, cpu_current_exp/86400); diff --git a/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_large_2.m b/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_large_2.m index 2ad9868..bbb8363 100644 --- a/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_large_2.m +++ b/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_large_2.m @@ -50,6 +50,7 @@ 'NCVXQP6','DIXCHLNV',... }; + settings.rescale_factor = 1; settings.round_all_data = 1; settings.n_digits = 4; @@ -93,11 +94,11 @@ fprintf('Problem generation time : %2.2f \n',t_mpec_gen) %% Solver settings -solver_names = ["MPECopt-Reg-Gurobi", "MPECopt-$\ell_1$-Gurobi", "MPECopt-Reg-Gurobi-ET",... - "Reg", "NLP", "$\ell_1$-Penalty", "MINLP"]; +solver_names = ["MPECopt-Reg-Gurobi", "MPECopt-$\ell_1$-Gurobi", "MPECopt-Reg-Gurobi-ET", "MPECopt-$\ell_{\infty}$-Gurobi",... + "Reg", "NLP", "$\ell_1$-Penalty","$\ell_\infty$-Penalty", "MINLP"]; -solver_functions = {@mpec_optimizer,@mpec_optimizer,@mpec_optimizer,... - @mpec_homotopy_solver,@mpec_homotopy_solver, @mpec_homotopy_solver,... +solver_functions = {@mpec_optimizer,@mpec_optimizer,@mpec_optimizer,@mpec_optimizer,... + @mpec_homotopy_solver,@mpec_homotopy_solver, @mpec_homotopy_solver,@mpec_homotopy_solver,... @mpec_minlp_solver}; opts1 = mpecopt.Options(); @@ -139,6 +140,17 @@ opts3.settings_casadi_nlp.ipopt.max_wall_time = 900; % opts3.rho_TR_phase_i_init = 1e-2; +opts4 = mpecopt.Options(); +opts4.solver_name = solver_names{4}; +opts4.settings_lpec.lpec_solver = "Gurobi"; +opts4.relax_and_project_homotopy_parameter_steering = "Ell_inf"; +opts4.use_one_nlp_solver = false; +opts4.problem_in_vertical_from = false; +opts4.settings_casadi_nlp.ipopt.max_wall_time = 900; +% opts2.settings_lpec.stop_lpec_at_feasible = true; +% opts2.rho_TR_phase_i_init = 1e-3; + + scholtes_opts1 = HomotopySolverOptions(); scholtes_opts1.homotopy_parameter_steering = 'Direct'; scholtes_opts1.problem_in_vertical_from = true; @@ -149,7 +161,7 @@ scholtes_opts2.max_iter = 1; scholtes_opts2.sigma0 = 0; scholtes_opts2.problem_in_vertical_from = true; -scholtes_opts2.settings_casadi_nlp.ipopt.max_wall_time = 1800; +scholtes_opts2.settings_casadi_nlp.ipopt.max_wall_time = 2700; scholtes_opts3 = HomotopySolverOptions(); @@ -158,18 +170,24 @@ scholtes_opts3.settings_casadi_nlp.ipopt.max_wall_time = 900; +scholtes_opts4 = HomotopySolverOptions(); +scholtes_opts4.homotopy_parameter_steering = 'Ell_inf'; +scholtes_opts4.problem_in_vertical_from = false; +scholtes_opts4.settings_casadi_nlp.ipopt.max_wall_time = 900; + + minlp_opts = MINLPSolverOptions(); minlp_opts.settings_casadi_nlp.bonmin.time_limit = 1800; scholtes_opts3.problem_in_vertical_from = true; -opts = {opts1, opts2, opts3, ... - scholtes_opts1, scholtes_opts2, scholtes_opts3,... +opts = {opts1, opts2, opts3, opts4,... + scholtes_opts1, scholtes_opts2, scholtes_opts3, scholtes_opts4,... minlp_opts}; % list of options to pass to mpecsol (option structs) %% Create data struct % N_experiments = [4 3 5 1 2 6 7]; % % N_experiments = [3 1]; -N_experiments = [4 3 1 2 6 5]; +N_experiments = [5 3 1 4 8 6]; nonlinear_mpec_benchmark_dtable_loop; % this script runs the experimetns, creates a dtable %% Pick which results to plot From 645d37f567bdee0c2bcd62554b69a44d4f7d536a Mon Sep 17 00:00:00 2001 From: "armin.nurkanovic" Date: Wed, 17 Sep 2025 17:50:25 +0200 Subject: [PATCH 26/31] more dense problems among the large ones --- benchmarks/histogram_on_lpecs.m | 4 +-- .../run_nonlinear_mpec_benchmark_large_2.m | 4 +-- .../solve_single_problem_nonlinear_mpec.m | 25 ++++++++++--------- src/+mpecopt/Options.m | 2 +- src/lpec_solver.m | 2 +- src/mpec_homotopy_solver.m | 2 +- 6 files changed, 20 insertions(+), 19 deletions(-) diff --git a/benchmarks/histogram_on_lpecs.m b/benchmarks/histogram_on_lpecs.m index bc079cb..c81994c 100644 --- a/benchmarks/histogram_on_lpecs.m +++ b/benchmarks/histogram_on_lpecs.m @@ -4,9 +4,9 @@ % dtable; % lpec_dstruct; -S = load('macmpec_general_14-Sep-2025_lpec_details'); +S = load('random_lpecs2_07-Sep-2025_lpec_details'); lpec_dstruct = S.lpec_dstruct; -S = load('macmpec_general_14-Sep-2025'); +S = load('random_lpecs2_07-Sep-2025'); % % S = load('nonlinear_mpec_med_15-Sep-2025_lpec_details'); % lpec_dstruct = S.lpec_dstruct; diff --git a/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_large_2.m b/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_large_2.m index bbb8363..6eb4c4a 100644 --- a/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_large_2.m +++ b/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_large_2.m @@ -70,9 +70,9 @@ settings.nnz_bounded_by_dim = 1; settings.inv_cond_num = 1e0; settings.nnz_factor = 1; -settings.n_comp_min = 400; +settings.n_comp_min = 500; -settings.adaptive_density_bounds = 0; % to account for very larg problems +settings.adaptive_density_bounds = 1; % to account for very larg problems settings.variable_density = 1; settings.range_s_density = [0.01 0.05]; settings.random_problem_sizes = 1; diff --git a/benchmarks/nonlinear_mpec_benchmark/solve_single_problem_nonlinear_mpec.m b/benchmarks/nonlinear_mpec_benchmark/solve_single_problem_nonlinear_mpec.m index b8627be..360d678 100644 --- a/benchmarks/nonlinear_mpec_benchmark/solve_single_problem_nonlinear_mpec.m +++ b/benchmarks/nonlinear_mpec_benchmark/solve_single_problem_nonlinear_mpec.m @@ -34,11 +34,11 @@ settings.objective_functions = {settings.objective_functions_all{15}}; -settings.objective_functions = {'SCURLY30'}; +settings.objective_functions = {'Quadratic_psd'}; settings.rescale_factor = 1; settings.round_all_data = 1; -settings.n_digits = 4; +settings.n_digits = 6; settings.bounded_w = 1; settings.use_normal_distributions_for_feasible_point = 1; settings.alpha_lin = 10; % f + alpha_lin*[c;d]'*[x;y]' @@ -54,23 +54,22 @@ settings.nnz_bounded_by_dim = 1; settings.inv_cond_num = 1e0; -settings.nnz_factor = 1; +settings.nnz_factor = 1.05; -settings.adaptive_density_bounds = 1; % to account for very larg problems +settings.adaptive_density_bounds = 0; % to account for very larg problems settings.variable_density = 1; -settings.range_s_density = [0.01 0.05]; +settings.range_s_density = [0.01 0.1]; settings.random_problem_sizes = 1; -settings.n_ineq_ub = 2; % n_ineq = n_ineq_ub*n_x % max relative number of ineq w.r.t x +settings.n_ineq_ub = 3; % n_ineq = n_ineq_ub*n_x % max relative number of ineq w.r.t x settings.n_ineq_lb = 0.5; settings.n_fraction_of_x = 0.5; dimensions.N_rand_prob = 1; % number of problems per objective -dimensions.n_x_max = 2000; -dimensions.n_x_min = 2000; - - +% settings.n_comp_min = 250; +dimensions.n_x_max = 260; +dimensions.n_x_min = 260; dimensions.n_fraction_of_x = 0.5; % n_y = round(n_x/n_fraction_of_x) mpecs = generate_nonlinear_mpec_problem_set(problem_set_name,settings,dimensions); @@ -116,8 +115,8 @@ opts.settings_lpec.lpec_solver = 'Gurobi'; opts.use_one_nlp_solver = true; opts.problem_in_vertical_from = true; -opts.settings_casadi_nlp.ipopt.print_level = 5; -opts.rho_TR_phase_ii_init = 1e-1; +opts.settings_casadi_nlp.ipopt.print_level = 0; +% opts.rho_TR_phase_ii_init = 1e-1; % solver_settings.settings_casadi_nlp.ipopt.max_iter = 100; % solver_settings.settings_casadi_nlp.jit = true; @@ -129,6 +128,8 @@ %% [result_mpecopt,stats_mpecopt] = solver.solve(solver_initalization); stats_mpecopt.cpu_time_total +stats_mpecopt.iter.nodecount_phase_i +stats_mpecopt.iter.nodecount_phase_ii % w_opt_mpecopt = full(result_mpecopt.x); % f_opt_mpecopt = full(result_mpecopt.f); diff --git a/src/+mpecopt/Options.m b/src/+mpecopt/Options.m index 2963f80..f132c77 100644 --- a/src/+mpecopt/Options.m +++ b/src/+mpecopt/Options.m @@ -16,7 +16,7 @@ tol (1,1) double {mustBeReal, mustBeNonnegative} = 1e-8; tol_B_stationarity (1,1) double {mustBeReal, mustBeNonnegative} = 1e-8; tol_feasibility (1,1) double {mustBeReal, mustBeNonnegative} = 1e-9; - tol_B_stationarity_early_term (1,1) double {mustBeReal, mustBeNonnegative} = 1e-7; + tol_B_stationarity_early_term (1,1) double {mustBeReal, mustBeNonnegative} = 5e-8; tol_active (1,1) double {mustBeReal, mustBeNonnegative} = 1e-9; % below this treshold a constaint is considered to be active rescale_large_objective_gradients (1,1) logical = true; diff --git a/src/lpec_solver.m b/src/lpec_solver.m index ea6463c..5749e79 100644 --- a/src/lpec_solver.m +++ b/src/lpec_solver.m @@ -117,7 +117,7 @@ end if settings.stop_lpec_at_descent && ~settings.is_in_phase_i % terminate phase ii lpecs at a dscent direction - params.BestObjStop = -0.01*norm(f_lpec)*lpec.rho_TR; + params.BestObjStop = -0.1*norm(f_lpec)*lpec.rho_TR; end % params.ObjScale = -0.5; % https://www.gurobi.com/documentation/current/refman/objscale.html#parameter:ObjScale % params.ScaleFlag=0; % default -1, % https://www.gurobi.com/documentation/current/refman/scaleflag.html#parameter:ScaleFlag diff --git a/src/mpec_homotopy_solver.m b/src/mpec_homotopy_solver.m index 39eaa14..86c856f 100644 --- a/src/mpec_homotopy_solver.m +++ b/src/mpec_homotopy_solver.m @@ -628,7 +628,7 @@ % end %% Check B stationarity % ----------------------------------- B STAT ------------------------------------- -f_lpec = 1; +f_lpec = 1e3; rho_TR = 1e-3; if ~settings.problem_is_lpec && success From 83613210a8d45d212e0de7d7fd2f94f8f5037dbd Mon Sep 17 00:00:00 2001 From: "armin.nurkanovic" Date: Thu, 18 Sep 2025 14:47:58 +0200 Subject: [PATCH 27/31] improve conditioning number in large experiments --- .../cpu_experiments.txt | 2 + .../current_problem.txt | 2 +- .../generate_nonlinear_mpec_problem_set.m | 42 +++++++++++++++---- ..._nonlinear_mpec_benchmark_lpec_histogram.m | 4 +- .../solve_single_problem_nonlinear_mpec.m | 25 +++++++---- src/lpec_solver.m | 1 + 6 files changed, 59 insertions(+), 17 deletions(-) diff --git a/benchmarks/nonlinear_mpec_benchmark/cpu_experiments.txt b/benchmarks/nonlinear_mpec_benchmark/cpu_experiments.txt index ac2a326..0e3733d 100644 --- a/benchmarks/nonlinear_mpec_benchmark/cpu_experiments.txt +++ b/benchmarks/nonlinear_mpec_benchmark/cpu_experiments.txt @@ -12,3 +12,5 @@ MPECopt-Reg-Gurobi - execution time: 2.58 seconds (0.0007 hours, 0.000030 days) MPECopt-$\ell_{\infty}$-Gurobi - execution time: 1.64 seconds (0.0005 hours, 0.000019 days) $\ell_\infty$-Penalty - execution time: 1.22 seconds (0.0003 hours, 0.000014 days) NLP - execution time: 0.97 seconds (0.0003 hours, 0.000011 days) +Gurobi - execution time: 18.53 seconds (0.0051 hours, 0.000214 days) +Gurobi-early-I - execution time: 16.31 seconds (0.0045 hours, 0.000189 days) diff --git a/benchmarks/nonlinear_mpec_benchmark/current_problem.txt b/benchmarks/nonlinear_mpec_benchmark/current_problem.txt index dd2c36f..536bd60 100644 --- a/benchmarks/nonlinear_mpec_benchmark/current_problem.txt +++ b/benchmarks/nonlinear_mpec_benchmark/current_problem.txt @@ -1 +1 @@ -nonlinear_large2_DIXCHLNV_Var70_Comp28_Eq28_Ineq14 +nonlinear_mpec2_McCormick_Var1725_Comp690_Eq690_Ineq1030 diff --git a/benchmarks/nonlinear_mpec_benchmark/generate_nonlinear_mpec_problem_set.m b/benchmarks/nonlinear_mpec_benchmark/generate_nonlinear_mpec_problem_set.m index 4cd7509..8ea89ec 100644 --- a/benchmarks/nonlinear_mpec_benchmark/generate_nonlinear_mpec_problem_set.m +++ b/benchmarks/nonlinear_mpec_benchmark/generate_nonlinear_mpec_problem_set.m @@ -53,7 +53,8 @@ if ~isfield(settings,'inv_cond_num') - inv_cond_num = 1e-1; + % inv_cond_num = 1e-1; + inv_cond_num = []; end if ~isfield(settings,'nnz_factor') @@ -274,7 +275,11 @@ if adaptive_density_bounds H = full( sprandsym(n_obj, s_density_A_B) ); % for smaller matrices use provided density bounds, for larger use sparse else - H = full( sprandsym(n_obj, n_obj/n_obj.^2,inv_cond_num) ); % ... full matrix for easier printing + if isempty(inv_cond_num) + H = full( sprandsym(n_obj, n_obj/n_obj.^2) ); % ... full matrix for easier printing + else + H = full( sprandsym(n_obj, n_obj/n_obj.^2,inv_cond_num) ); % ... full matrix for easier printing + end end else H = full( sprandsym(n_obj, s_density_A_B) ); % ... full matrix for easier printing @@ -287,7 +292,11 @@ % f = 0.5*v'*H*v+grad_vec'*w(1:n_obj); % +[c;d]'*v; case 'Quadratic_ind' if settings.nnz_bounded_by_dim - H = full( sprandsym(n_obj, n_obj/n_obj.^2,inv_cond_num) ); % ... full matrix for easier printing + if isempty(inv_cond_num) + H = full( sprandsym(n_obj, n_obj/n_obj.^2) ); % ... full matrix for easier printing + else + H = full( sprandsym(n_obj, n_obj/n_obj.^2,inv_cond_num) ); % ... full matrix for easier printing + end else H = full( sprandsym(n_obj, s_density_A_B) ); % ... full matrix for easier printing end @@ -717,8 +726,12 @@ s_density_A = nnz_factor*n_ineq/(n_x*n_ineq); end end - A = (range_A(2)-range_A(1))*sprand(n_ineq,n_x,s_density_A,inv_cond_num); - + if isempty(inv_cond_num) + A = (range_A(2)-range_A(1))*sprand(n_ineq,n_x,s_density_A); + else + A = (range_A(2)-range_A(1))*sprand(n_ineq,n_x,s_density_A,inv_cond_num); + end + A(A~=0) = range_A(1)+A(A~=0); A = round(A,n_digits_data); if settings.inequality_constraints_coupling_terms @@ -728,7 +741,11 @@ s_density_B = nnz_factor*n_ineq/(n_y*n_ineq); end end - B = (range_B(2)-range_B(1))*sprand(n_ineq,n_y,s_density_B ,inv_cond_num); + if isempty(inv_cond_num) + B = (range_B(2)-range_B(1))*sprand(n_ineq,n_y,s_density_B); + else + B = (range_B(2)-range_B(1))*sprand(n_ineq,n_y,s_density_B ,inv_cond_num); + end B(B~=0) = range_B(1)+B(B~=0); B = round(B,n_digits_data); @@ -756,8 +773,13 @@ end else s_density_M = settings.s_density_M; + + end + if isempty(inv_cond_num) + E = (range_E(2)-range_E(1))*sprand(r,n_y-r,s_density_M); + else + E = (range_E(2)-range_E(1))*sprand(r,n_y-r,s_density_M,inv_cond_num); end - E = (range_E(2)-range_E(1))*sprand(r,n_y-r,s_density_M,inv_cond_num); E(E~=0) = range_E(1)+E(E~=0); @@ -787,7 +809,11 @@ s_density_N = nnz_factor*n_y/(n_x*n_y); end end - N = (range_N(2)-range_N(1))*sprand(n_y,n_x,s_density_N,inv_cond_num); + if isempty(inv_cond_num) + N = (range_N(2)-range_N(1))*sprand(n_y,n_x,s_density_N); + else + N = (range_N(2)-range_N(1))*sprand(n_y,n_x,s_density_N,inv_cond_num); + end N(N~=0) = range_N(1)+N(N~=0); N = round(N,n_digits_data); diff --git a/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_lpec_histogram.m b/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_lpec_histogram.m index 94ce9c0..ed2580d 100644 --- a/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_lpec_histogram.m +++ b/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_lpec_histogram.m @@ -51,6 +51,7 @@ settings.s_density_A_B = 0.05; % all same or change settings.s_density_M = 0.1; +settings.nnz_bounded_by_dim = 0; settings.variable_density = 1; settings.range_s_density = [0.01 0.1]; settings.random_problem_sizes = 1; @@ -58,6 +59,7 @@ settings.n_ineq_ub = 3; % n_ineq = n_ineq_ub*n_x % max relative number of ineq w.r.t x settings.n_ineq_lb = 0.5; +settings.inv_cond_num = []; % Problem size dimensions.N_rand_prob = 3; % number of problems per objective @@ -65,7 +67,7 @@ dimensions.n_x_min = 100; -% dimensions.N_rand_prob = 3; % number of problems per objective +% dimensions.N_rand_prob = 1; % number of problems per objective % dimensions.n_x_max = 50; % dimensions.n_x_min = 10; diff --git a/benchmarks/nonlinear_mpec_benchmark/solve_single_problem_nonlinear_mpec.m b/benchmarks/nonlinear_mpec_benchmark/solve_single_problem_nonlinear_mpec.m index 360d678..d8f108e 100644 --- a/benchmarks/nonlinear_mpec_benchmark/solve_single_problem_nonlinear_mpec.m +++ b/benchmarks/nonlinear_mpec_benchmark/solve_single_problem_nonlinear_mpec.m @@ -32,9 +32,9 @@ }; -settings.objective_functions = {settings.objective_functions_all{15}}; +settings.objective_functions = {settings.objective_functions_all{1}}; -settings.objective_functions = {'Quadratic_psd'}; +% settings.objective_functions = {'Himmelblau'}; settings.rescale_factor = 1; settings.round_all_data = 1; @@ -53,23 +53,31 @@ settings.nnz_bounded_by_dim = 1; -settings.inv_cond_num = 1e0; -settings.nnz_factor = 1.05; +settings.inv_cond_num = []; +settings.nnz_factor = 3.00; + +% settings.inv_cond_num = 1e0; +% settings.nnz_factor = 1.00; +%% settings.adaptive_density_bounds = 0; % to account for very larg problems settings.variable_density = 1; settings.range_s_density = [0.01 0.1]; settings.random_problem_sizes = 1; -settings.n_ineq_ub = 3; % n_ineq = n_ineq_ub*n_x % max relative number of ineq w.r.t x +settings.n_ineq_ub = 2; % n_ineq = n_ineq_ub*n_x % max relative number of ineq w.r.t x settings.n_ineq_lb = 0.5; settings.n_fraction_of_x = 0.5; dimensions.N_rand_prob = 1; % number of problems per objective % settings.n_comp_min = 250; -dimensions.n_x_max = 260; -dimensions.n_x_min = 260; +dimensions.n_x_max = 2000; +dimensions.n_x_min = 2000; + +% dimensions.n_x_max = 100; +% dimensions.n_x_min = 100; + dimensions.n_fraction_of_x = 0.5; % n_y = round(n_x/n_fraction_of_x) mpecs = generate_nonlinear_mpec_problem_set(problem_set_name,settings,dimensions); @@ -117,6 +125,9 @@ opts.problem_in_vertical_from = true; opts.settings_casadi_nlp.ipopt.print_level = 0; % opts.rho_TR_phase_ii_init = 1e-1; +opts.settings_lpec.stop_lpec_at_feasible = true; +opts.settings_lpec.stop_lpec_at_descent = true; + % solver_settings.settings_casadi_nlp.ipopt.max_iter = 100; % solver_settings.settings_casadi_nlp.jit = true; diff --git a/src/lpec_solver.m b/src/lpec_solver.m index 5749e79..57c6d7c 100644 --- a/src/lpec_solver.m +++ b/src/lpec_solver.m @@ -118,6 +118,7 @@ if settings.stop_lpec_at_descent && ~settings.is_in_phase_i % terminate phase ii lpecs at a dscent direction params.BestObjStop = -0.1*norm(f_lpec)*lpec.rho_TR; + % params.BestObjStop = -0.1*lpec.rho_TR; end % params.ObjScale = -0.5; % https://www.gurobi.com/documentation/current/refman/objscale.html#parameter:ObjScale % params.ScaleFlag=0; % default -1, % https://www.gurobi.com/documentation/current/refman/scaleflag.html#parameter:ScaleFlag From 59dedd86fe2545d5ed312044dbccd3d9a90461d7 Mon Sep 17 00:00:00 2001 From: "armin.nurkanovic" Date: Thu, 18 Sep 2025 17:51:23 +0200 Subject: [PATCH 28/31] large benchmark with dense problems --- .../macmpec/run_macmpec_experiments_general.m | 18 ++++----- .../cpu_experiments.txt | 3 ++ .../generate_nonlinear_mpec_problem_set.m | 19 +++++++++- .../run_diff_experiments.m | 37 +++++++++++-------- .../run_nonlinear_mpec_benchmark_large_2.m | 12 +++--- .../solve_single_problem_nonlinear_mpec.m | 23 ++++++------ 6 files changed, 70 insertions(+), 42 deletions(-) diff --git a/benchmarks/macmpec/run_macmpec_experiments_general.m b/benchmarks/macmpec/run_macmpec_experiments_general.m index 1f8c93e..7402a7a 100644 --- a/benchmarks/macmpec/run_macmpec_experiments_general.m +++ b/benchmarks/macmpec/run_macmpec_experiments_general.m @@ -44,14 +44,14 @@ mpecs = [mpecs,mpec]; end -N_interesting = []; -for ii=1:length(macmpec_json) - if mpecs(ii).n_w <= 100 - N_interesting = [N_interesting; ii]; - end -end +% N_interesting = []; +% for ii=1:length(macmpec_json) +% if mpecs(ii).n_w <= 100 +% N_interesting = [N_interesting; ii]; +% end +% end -mpecs = mpecs(N_interesting); +% mpecs = mpecs(N_interesting); %% Define list of solvers to use @@ -110,8 +110,8 @@ %% Create data struct % N_experiments = [1, 3:6]; -N_experiments = [1:6]; -N_experiments = [2, 6]; +N_experiments = [1:7]; +% N_experiments = [2, 6]; mpec_benchmark_dtable_loop; % this script runs the experimetns, creates a dtable diff --git a/benchmarks/nonlinear_mpec_benchmark/cpu_experiments.txt b/benchmarks/nonlinear_mpec_benchmark/cpu_experiments.txt index 0e3733d..43b1e2e 100644 --- a/benchmarks/nonlinear_mpec_benchmark/cpu_experiments.txt +++ b/benchmarks/nonlinear_mpec_benchmark/cpu_experiments.txt @@ -14,3 +14,6 @@ $\ell_\infty$-Penalty - execution time: 1.22 seconds (0.0003 hours, 0.000014 day NLP - execution time: 0.97 seconds (0.0003 hours, 0.000011 days) Gurobi - execution time: 18.53 seconds (0.0051 hours, 0.000214 days) Gurobi-early-I - execution time: 16.31 seconds (0.0045 hours, 0.000189 days) +Gurobi - execution time: 0.10 seconds (0.0000 hours, 0.000001 days) +Gurobi-early-I - execution time: 0.02 seconds (0.0000 hours, 0.000000 days) +Gurobi-early-I-II - execution time: 0.03 seconds (0.0000 hours, 0.000000 days) diff --git a/benchmarks/nonlinear_mpec_benchmark/generate_nonlinear_mpec_problem_set.m b/benchmarks/nonlinear_mpec_benchmark/generate_nonlinear_mpec_problem_set.m index 8ea89ec..58d20aa 100644 --- a/benchmarks/nonlinear_mpec_benchmark/generate_nonlinear_mpec_problem_set.m +++ b/benchmarks/nonlinear_mpec_benchmark/generate_nonlinear_mpec_problem_set.m @@ -86,7 +86,7 @@ n_x_max = max(N_problems+n_x_min,n_x_max); % n_x_vec = n_x_min+randperm(n_x_max-n_x_min,N_problems); % num of non comp vars % Create a weighted distribution favoring smaller values - weights = linspace(5, 1, n_x_max-n_x_min+1); % Higher weights for smaller indices + weights = linspace(10, 1, n_x_max-n_x_min+1); % Higher weights for smaller indices weights = weights / sum(weights); % Normalize % Generate biased random indices biased_indices = randsample(1:(n_x_max-n_x_min+1), N_problems, true, weights); @@ -141,6 +141,8 @@ end fprintf('Largerst problem: n_var = %d, n_comp = %d \n',max(n_x_vec+2*n_y_vec), max(n_y_vec)) +fprintf('Dense problems (with n_comp < n_comp_min) : %d \n',sum(n_y_vec<=n_comp_min)) + n_comp_max = max(n_y_vec); % range for feasible points @@ -202,6 +204,18 @@ % store dimensions: n_var_vec = [n_var_vec, n]; n_comp_vec = [n_comp_vec, n_comp]; + + % Addaptivity: + if settings.nnz_bounded_by_dim && settings.adaptive_density_bounds + if n_y > settings.n_comp_min + inv_cond_num = 1e0; + else + inv_cond_num = settings.inv_cond_num; + end + end + + + % Define symbolic variables: if isequal(casadi_variable_type,'SX') x = SX.sym('x', n_x); @@ -726,11 +740,13 @@ s_density_A = nnz_factor*n_ineq/(n_x*n_ineq); end end + if isempty(inv_cond_num) A = (range_A(2)-range_A(1))*sprand(n_ineq,n_x,s_density_A); else A = (range_A(2)-range_A(1))*sprand(n_ineq,n_x,s_density_A,inv_cond_num); end + A(A~=0) = range_A(1)+A(A~=0); A = round(A,n_digits_data); @@ -741,6 +757,7 @@ s_density_B = nnz_factor*n_ineq/(n_y*n_ineq); end end + if isempty(inv_cond_num) B = (range_B(2)-range_B(1))*sprand(n_ineq,n_y,s_density_B); else diff --git a/benchmarks/nonlinear_mpec_benchmark/run_diff_experiments.m b/benchmarks/nonlinear_mpec_benchmark/run_diff_experiments.m index baea247..02e6d08 100644 --- a/benchmarks/nonlinear_mpec_benchmark/run_diff_experiments.m +++ b/benchmarks/nonlinear_mpec_benchmark/run_diff_experiments.m @@ -1,24 +1,31 @@ -%% run subseqeuntlly benchmarks of different size -try - run_nonlinear_mpec_benchmark_small -catch -end -%% -try - run_nonlinear_mpec_benchmark_medium -catch +% %% run subseqeuntlly benchmarks of different size +% try +% run_nonlinear_mpec_benchmark_small +% catch +% end +% %% +% try +% run_nonlinear_mpec_benchmark_medium +% catch +% +% end +% +% %% run subseqeuntlly benchmarks of different size +% try +% run_nonlinear_mpec_benchmark_large_1 +% catch +% end -end %% run subseqeuntlly benchmarks of different size try - run_nonlinear_mpec_benchmark_large_1 + run_nonlinear_mpec_benchmark_large_2 catch end - -%% run subseqeuntlly benchmarks of different size -try - run_nonlinear_mpec_benchmark_large_2 +%% +try + cd ..\macmpec\ + run_macmpec_experiments_general catch end \ No newline at end of file diff --git a/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_large_2.m b/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_large_2.m index 6eb4c4a..1c993e5 100644 --- a/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_large_2.m +++ b/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_large_2.m @@ -66,15 +66,15 @@ settings.s_density_A_B = 0.05; % all same or change settings.s_density_M = 0.1; - +settings.adaptive_density_bounds = 1; % to account for very larg problems settings.nnz_bounded_by_dim = 1; settings.inv_cond_num = 1e0; -settings.nnz_factor = 1; -settings.n_comp_min = 500; +settings.nnz_factor = 1.00; +settings.n_comp_min = 1000; + -settings.adaptive_density_bounds = 1; % to account for very larg problems settings.variable_density = 1; -settings.range_s_density = [0.01 0.05]; +settings.range_s_density = [0.01 0.1]; settings.random_problem_sizes = 1; settings.n_ineq_ub = 2; % n_ineq = n_ineq_ub*n_x % max relative number of ineq w.r.t x @@ -84,7 +84,7 @@ % Problem size dimensions.N_rand_prob = 3; % number of problems per objective -dimensions.n_x_max = 3100; +dimensions.n_x_max = 2100; dimensions.n_x_min = 100; diff --git a/benchmarks/nonlinear_mpec_benchmark/solve_single_problem_nonlinear_mpec.m b/benchmarks/nonlinear_mpec_benchmark/solve_single_problem_nonlinear_mpec.m index d8f108e..5d530e5 100644 --- a/benchmarks/nonlinear_mpec_benchmark/solve_single_problem_nonlinear_mpec.m +++ b/benchmarks/nonlinear_mpec_benchmark/solve_single_problem_nonlinear_mpec.m @@ -32,7 +32,7 @@ }; -settings.objective_functions = {settings.objective_functions_all{1}}; +settings.objective_functions = {settings.objective_functions_all{24}}; % settings.objective_functions = {'Himmelblau'}; @@ -54,15 +54,16 @@ settings.nnz_bounded_by_dim = 1; settings.inv_cond_num = []; -settings.nnz_factor = 3.00; +settings.nnz_factor = 1.02; +settings.n_comp_min = 1401; % settings.inv_cond_num = 1e0; % settings.nnz_factor = 1.00; %% -settings.adaptive_density_bounds = 0; % to account for very larg problems +settings.adaptive_density_bounds = 1; % to account for very larg problems settings.variable_density = 1; -settings.range_s_density = [0.01 0.1]; +settings.range_s_density = [0.01 0.05]; settings.random_problem_sizes = 1; settings.n_ineq_ub = 2; % n_ineq = n_ineq_ub*n_x % max relative number of ineq w.r.t x @@ -71,12 +72,12 @@ settings.n_fraction_of_x = 0.5; dimensions.N_rand_prob = 1; % number of problems per objective -% settings.n_comp_min = 250; -dimensions.n_x_max = 2000; -dimensions.n_x_min = 2000; -% dimensions.n_x_max = 100; -% dimensions.n_x_min = 100; +dimensions.n_x_max = 1000; +dimensions.n_x_min = 1000; + +dimensions.n_x_max = 100; +dimensions.n_x_min = 100; dimensions.n_fraction_of_x = 0.5; % n_y = round(n_x/n_fraction_of_x) @@ -125,8 +126,8 @@ opts.problem_in_vertical_from = true; opts.settings_casadi_nlp.ipopt.print_level = 0; % opts.rho_TR_phase_ii_init = 1e-1; -opts.settings_lpec.stop_lpec_at_feasible = true; -opts.settings_lpec.stop_lpec_at_descent = true; +% opts.settings_lpec.stop_lpec_at_feasible = true; +% opts.settings_lpec.stop_lpec_at_descent = true; % solver_settings.settings_casadi_nlp.ipopt.max_iter = 100; From 92a4c0bdf9074e88676a56dbd0078b343ae65153 Mon Sep 17 00:00:00 2001 From: "armin.nurkanovic" Date: Tue, 23 Sep 2025 15:27:22 +0200 Subject: [PATCH 29/31] improv plottin scripts for benchmark results --- benchmarks/histogram_on_lpecs.m | 63 +++- benchmarks/macmpec/load_and_plot_results.m | 158 +++++----- .../generate_nonlinear_mpec_problem_set.m | 105 +++++-- .../load_and_plot_results.m | 175 +++++------ .../run_nonlinear_mpec_benchmark_large_3.m | 283 ++++++++++++++++++ .../solve_single_problem_nonlinear_mpec.m | 12 +- benchmarks/plot_mpec_benchmark_result.m | 7 +- 7 files changed, 577 insertions(+), 226 deletions(-) create mode 100644 benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_large_3.m diff --git a/benchmarks/histogram_on_lpecs.m b/benchmarks/histogram_on_lpecs.m index c81994c..f81e446 100644 --- a/benchmarks/histogram_on_lpecs.m +++ b/benchmarks/histogram_on_lpecs.m @@ -3,25 +3,53 @@ latexify_plot() % dtable; % lpec_dstruct; +plot_cpu = false; + +% Large benchmark +if 1 + S = load('nonlinear_mpec_large2_18-Sep-2025_lpec_details'); + lpec_dstruct = S.lpec_dstruct; + S = load('nonlinear_mpec_large2_18-Sep-2025'); + dtable = S.dtable; + solver_names = unique(dtable.solver_name); + solver = solver_names{3}; + filename = 'nonlinear_optimal'; + solver = solver_names{4}; + filename = 'nonlinear_early'; + + +else + S = load('macmpec_general_14-Sep-2025_lpec_details'); + lpec_dstruct = S.lpec_dstruct; + S = load('macmpec_general_14-Sep-2025'); + dtable = S.dtable; + solver_names = unique(dtable.solver_name); + solver = solver_names{3}; + filename = 'macmpec_optimal'; + solver = solver_names{4} + filename = 'macmpec_early'; + + % S = load('macmpec_general_22-Sep-2025_lpec_details'); + % lpec_dstruct = S.lpec_dstruct; + % S = load('macmpec_general_22-Sep-2025'); + % dtable = S.dtable; + + % solver_names = unique(dtable.solver_name); + % solver = solver_names{4}; + % solver = solver_names{5}; -S = load('random_lpecs2_07-Sep-2025_lpec_details'); -lpec_dstruct = S.lpec_dstruct; -S = load('random_lpecs2_07-Sep-2025'); +end % % S = load('nonlinear_mpec_med_15-Sep-2025_lpec_details'); % lpec_dstruct = S.lpec_dstruct; % S = load('nonlinear_mpec_med_15-Sep-2025'); -dtable = S.dtable; - -plot_cpu = false; -% Filter by solver and success +% dtable = S.dtable; % solver_names = ["Gurobi", "Gurobi-early-I", "Gurobi-early-I-II", "Highs", "Highs-early"]; -solver_names = unique(dtable.solver_name); -solver = solver_names{3} + idx = (dtable.solver_name == solver) & dtable.success == 1; fields = fieldnames(lpec_dstruct); @@ -94,10 +122,10 @@ if log_plot h1 = histogram(log2(nc_i), 'BinMethod', 'integers'); min_pow = floor(min(log2(nc_i))); - max_pow = max(5,ceil(max(log2(nc_i)))); + max_pow = max(8,ceil(max(log2(nc_i)))); set(gca, 'XTick', min_pow:max_pow) set(gca, 'XTickLabel', arrayfun(@(x) sprintf('$2^{%g}$', x), min_pow:max_pow, 'UniformOutput', false)) - if numel(unique(nc_ii)) == 1 + if numel(unique(nc_i)) == 1 xlim([-1 max_pow]); end else @@ -105,8 +133,9 @@ end % Set ticks manually based on your data range -title('Nodecount Phase I - each LPEC call'); +title('Nodecount Phase I'); xlabel('Nodes'); ylabel('Frequency'); grid on; +set(gca,'FontSize',13) xticks = get(gca, 'XTick'); % annotate counts for k = 1:numel(h1.BinEdges)-1 @@ -121,6 +150,7 @@ ylim([0 ymax1 + max(2,ceil(0.2*ymax1))]) % xlim([0 100]) +% exportgraphics(gca, [filename '_phase_i.pdf']); % --- Phase II --- subplot(1,2,2) if log_plot @@ -140,8 +170,11 @@ xlim([0 10]) end end -title('Nodecount Phase II - each LPEC call'); -xlabel('Nodes'); ylabel('Frequency'); grid on; +title('Nodecount Phase II'); +xlabel('Nodes'); +% ylabel('Frequency'); +grid on; +set(gca,'FontSize',13) % annotate counts @@ -156,6 +189,8 @@ % adjust ylim ymax2 = max(h2.Values); ylim([0 ymax2 + max(2,ceil(0.2*ymax2))]) +% exportgraphics(gca, [filename '_phase_i.pdf']); +exportgraphics(gcf, [filename '.pdf']); %% % xticks(min(nc_ii):max(nc_ii)); diff --git a/benchmarks/macmpec/load_and_plot_results.m b/benchmarks/macmpec/load_and_plot_results.m index c1f7080..5d37b26 100644 --- a/benchmarks/macmpec/load_and_plot_results.m +++ b/benchmarks/macmpec/load_and_plot_results.m @@ -10,11 +10,11 @@ plot_settings.absolute_phase_ii = 0; plot_settings.absolute_lpec = 0; -plot_settings.success_fail_statistics = 1; +plot_settings.success_fail_statistics = 0; plot_settings.nlp_lpec_cpu_comparisson = 0; -plot_settings.lpecs_solved = 1; -plot_settings.nlps_solved = 1; +plot_settings.lpecs_solved = 0; +plot_settings.nlps_solved = 0; plot_settings.lpecs_cpu_time = 0; plot_settings.nlp_cpu_time = 0; % aggegated nlp times phase I and II @@ -22,7 +22,7 @@ plot_settings.max_nlp_cpu_time = 0; plot_settings.max_lpec_cpu_time = 0; -plot_settings.n_biactive_count = 0; +plot_settings.n_biactive_count = 1; plot_settings.n_biactive = 0; plot_settings.active_set_changes = 0; plot_settings.bar_timing_plots = 0; @@ -35,7 +35,7 @@ plot_settings.solved_in_phase_i = 0; plot_settings.objective = 0; -plot_settings.objective_rescaled = 0; +plot_settings.objective_rescaled = 1; % split cpu time among phases and show in bar plot (just report for mpecopt % guroi) @@ -52,54 +52,81 @@ case 1 % S = load('macmpec_general_30-Oct-2024'); % S = load('macmpec_general_07-Nov-2024'); - S = load('macmpec_general_14-Sep-2025'); + % S = load('macmpec_general_14-Sep-2025'); + S = load('macmpec_general_22-Sep-2025'); dtable = S.dtable; - % solver_names = unique(dtable.solver_name); + solver_names = unique(dtable.solver_name); + N_plot = [4 3 1 7 6 2]; - solver_names = ["MPECopt-Reg-Gurobi", "MPECopt-$\ell_1$-Gurobi", "MPECopt-Reg-Guroby-ET", ... - "Reg", "NLP", ... - "MINLP"]; + % solver_names = ["MPECopt-Reg-Gurobi", "MPECopt-$\ell_1$-Gurobi", "MPECopt-Reg-Guroby-ET", ... + % "Reg", "NLP", ... + % "MINLP"]; - filename = 'macmpec'; - N_plot = [1:6]; - plot_settings.stationary_points = 1; - plot_settings.b_stationarity = 1; - plot_settings.n_biactive = 1; - plot_settings.solved_in_phase_i = 1; - %% Who is not B stationary? - dtable_ii = dtable(dtable.solver_name == solver_names{1},:); - dtable_jj = dtable(dtable.solver_name == solver_names{4},:); - ind_not_B = find(dtable_jj.success == 1 & dtable_jj.b_stationarity == 0); - % For Reg - % diff in obj - delta_f = dtable_ii.f(ind_not_B)-dtable_jj.f(ind_not_B); - delta_f_relative_small = abs(delta_f)./abs(dtable_ii.f(ind_not_B)+1e-16)<1e-3; - dtable_temp = dtable_jj; - dtable_temp.b_stationarity(ind_not_B(delta_f_relative_small)) = dtable_ii.b_stationarity(ind_not_B(delta_f_relative_small)); - dtable_temp.multiplier_based_stationarity(ind_not_B(delta_f_relative_small)) = dtable_ii.multiplier_based_stationarity(ind_not_B(delta_f_relative_small)); - dtable_temp.problem_name(ind_not_B(~delta_f_relative_small)) - dtable_temp.multiplier_based_stationarity(ind_not_B(~delta_f_relative_small)) - dtable_temp.f_lpec(ind_not_B(~delta_f_relative_small)) - dtable(dtable.solver_name == solver_names{4},:) = dtable_temp; - % For pen - dtable_ii = dtable(dtable.solver_name == solver_names{3},:); - dtable_jj = dtable(dtable.solver_name == solver_names{6},:); - ind_not_B = find(dtable_jj.success == 1 & dtable_jj.b_stationarity == 0); - % diff in obj - delta_f = dtable_ii.f(ind_not_B)-dtable_jj.f(ind_not_B); - delta_f_relative_small = abs(dtable_ii.f(ind_not_B)-dtable_jj.f(ind_not_B))./abs(dtable_ii.f(ind_not_B)+1e-16)<1e-3; - dtable_temp = dtable_jj; - dtable_temp.b_stationarity(ind_not_B(delta_f_relative_small)) = dtable_ii.b_stationarity(ind_not_B(delta_f_relative_small)); - dtable_temp.multiplier_based_stationarity(ind_not_B(delta_f_relative_small)) = dtable_ii.multiplier_based_stationarity(ind_not_B(delta_f_relative_small)); - dtable_temp.problem_name(ind_not_B(~delta_f_relative_small)) - dtable_temp.f_lpec(ind_not_B(~delta_f_relative_small)) - dtable(dtable.solver_name == solver_names{6},:) = dtable_temp; - %% Solved by gurobi and not by highs - dtable_ii = dtable(dtable.solver_name == solver_names{1},:); - dtable_jj = dtable(dtable.solver_name == solver_names{2},:); - % dtable_jj.problem_name(dtable_ii.success == 1 & dtable_jj.success == 0) + % N_plot = [1:6]; + % plot_settings.stationary_points = 1; + % plot_settings.b_stationarity = 1; + % plot_settings.n_biactive = 1; + % plot_settings.solved_in_phase_i = 1; + %% Post process to verify success and B-stationarity + if 1 + ind_ref = 1; + ind_fix = 4; + dtable_ii = dtable(dtable.solver_name == solver_names{ind_ref},:); % Reference solution + dtable_jj = dtable(dtable.solver_name == solver_names{ind_fix},:); % To be chechked + ind_not_success = find(dtable_ii.success == 1 & dtable_jj.success == 0); + ind_not_success = find(dtable_jj.success == 0); + % For Reg + % diff in obj + delta_f = dtable_ii.f(ind_not_success)-dtable_jj.f(ind_not_success); + delta_f_relative_small = 100*abs(delta_f)./abs(dtable_ii.f(ind_not_success)+1e-16); + delta_f_relative_small_ind = abs(delta_f)./abs(dtable_ii.f(ind_not_success)+1e-16)<1e-2; + dtable_temp = dtable_jj; + dtable_temp.success(ind_not_success(delta_f_relative_small_ind)) = dtable_ii.success(ind_not_success(delta_f_relative_small_ind)); + dtable_temp.b_stationarity(ind_not_success(delta_f_relative_small_ind)) = dtable_ii.b_stationarity(ind_not_success(delta_f_relative_small_ind)); + dtable_temp.multiplier_based_stationarity(ind_not_success(delta_f_relative_small_ind)) = dtable_ii.multiplier_based_stationarity(ind_not_success(delta_f_relative_small_ind)); + dtable_temp.problem_name(ind_not_success(~delta_f_relative_small_ind)) + dtable_temp.prob_num(ind_not_success(~delta_f_relative_small_ind)) + dtable_temp.f_lpec(ind_not_success(~delta_f_relative_small_ind)) + dtable_jj = dtable_temp; + dtable(dtable.solver_name == solver_names{ind_fix},:) = dtable_temp; + + % still not successful + ind_not_success = find(dtable_ii.success == 1 & dtable_jj.success == 0); + dtable_jj(ind_not_success,:) + + % For pen + ind_ref = 3; + ind_fix = 1; + dtable_ii = dtable(dtable.solver_name == solver_names{ind_ref},:); % Reference solution + dtable_jj = dtable(dtable.solver_name == solver_names{ind_fix},:); % To be chechked + ind_not_success = find(dtable_ii.success == 1 & dtable_jj.success == 0); + % diff in obj + delta_f = dtable_ii.f(ind_not_success)-dtable_jj.f(ind_not_success); + delta_f_relative_small = 100*abs(delta_f)./abs(dtable_ii.f(ind_not_success)+1e-16); + delta_f_relative_small_ind = abs(dtable_ii.f(ind_not_success)-dtable_jj.f(ind_not_success))./abs(dtable_ii.f(ind_not_success)+1e-16)<1e-2; + dtable_temp = dtable_jj; + dtable_temp.success(ind_not_success(delta_f_relative_small_ind)) = dtable_ii.success(ind_not_success(delta_f_relative_small_ind)); + dtable_temp.b_stationarity(ind_not_success(delta_f_relative_small_ind)) = dtable_ii.b_stationarity(ind_not_success(delta_f_relative_small_ind)); + dtable_temp.multiplier_based_stationarity(ind_not_success(delta_f_relative_small_ind)) = dtable_ii.multiplier_based_stationarity(ind_not_success(delta_f_relative_small_ind)); + dtable_temp.problem_name(ind_not_success(~delta_f_relative_small_ind)) + dtable_temp.prob_num(ind_not_success(~delta_f_relative_small_ind)) + dtable_temp.f_lpec(ind_not_success(~delta_f_relative_small_ind)) + dtable_jj = dtable_temp; + dtable(dtable.solver_name == solver_names{ind_fix},:) = dtable_temp; + + % still not successful + ind_not_success = find(dtable_ii.success == 1 & dtable_jj.success == 0); + dtable_jj(ind_not_success,:) + + % number of feasible but not b stat in minlp + ind_minlp = 2; + dtable_ii = dtable(dtable.solver_name == solver_names{ind_minlp},:); + sum(dtable_ii.success == 0 & dtable_ii.problem_infeasible == 0) + + end + case 2 @@ -120,41 +147,6 @@ max(dtable_reg_lpec.n_nlp_total(dtable_reg_lpec.success ==1)) - case 3 - % todo: compine with highs and gurobi from general; - S = load('macmpec_lpec_29-Oct-2024'); - % S = load('macmpec_lpec_27-Oct-2024'); - % S = load('macmpec_lpec_07-Nov-2024_3'); - % S = load('macmpec_lpec_08-Nov-2024_3'); - - try - dtable = S.dtable1; - catch - dtable = S.dtable; - end - solver_names = ["Gurobi-MILP", "HiGHS-MILP",... - "Reg-MPEC","$\ell_{1}$-MPEC",... - "$\ell_{\infty}$-MPEC"]; - N_plot = 1:5; - filename = 'macmpec_lpec'; - % N_plot = [3,5]; - plot_settings.relative= 0; - plot_settings.absolute = 0; - plot_settings.relative_lpec = 1; - plot_settings.absolute_lpec = 1; - plot_settings.nlp_lpec_cpu_comparisson = 1; - plot_settings.max_lpec_cpu_time = 1; - case 4 - S = load('debug5_30-Oct-2024.mat'); - dtable = S.dtable; - filename = 'macmpec'; - - solver_names = ["MPECopt-Gurobi-def", "gurobi-not-tigher", "gurobi-phI-1e-2",... - "gurobi not tight 1e-2", "highs-1e-3","highs-1e-3-dont try","reg-no recovery","reg-default",... - 'Reg1e-9','Reg1e-10']; - - N_plot = [1,2]; - end % Modift possibly which solvers are plotted diff --git a/benchmarks/nonlinear_mpec_benchmark/generate_nonlinear_mpec_problem_set.m b/benchmarks/nonlinear_mpec_benchmark/generate_nonlinear_mpec_problem_set.m index 58d20aa..e1a1ae4 100644 --- a/benchmarks/nonlinear_mpec_benchmark/generate_nonlinear_mpec_problem_set.m +++ b/benchmarks/nonlinear_mpec_benchmark/generate_nonlinear_mpec_problem_set.m @@ -61,12 +61,17 @@ nnz_factor = 1; end +if ~isfield(settings,'sparsity_as_hessian') + % if bound by nnz, but use hessian sparsiy instead; + sparsity_as_hessian = 0; +end + if ~isfield(settings,'casadi_variable_type') casadi_variable_type = 'SX'; % default; else if ~ismember(settings.casadi_variable_type,{'SX','MX'}) - error('casadi_variable_type must be = ''SX'' or ''MX'' ') + error('casadi_variable_type must be = ''SX'' or ''MX'' ') end end @@ -98,17 +103,17 @@ N_per_problem = N_rand_prob; if 1 - figure - n_var = n_x_vec+2*n_y_vec; % total num of vars, non comp, comp1 + comp2 + lift - scatter(n_var,n_ineq_vec*(1+settings.s_ineq_copy)); - hold on - scatter(n_var,n_x_vec); - hold on - scatter(n_var,n_y_vec); - xlabel('$n+2m$ - number of variables') - ylabel('Number of constraints') - legend({'Inequality constraints','Equality constraints', 'Comp. constraints'},'Location','northwest') - axis equal + figure + n_var = n_x_vec+2*n_y_vec; % total num of vars, non comp, comp1 + comp2 + lift + scatter(n_var,n_ineq_vec*(1+settings.s_ineq_copy)); + hold on + scatter(n_var,n_x_vec); + hold on + scatter(n_var,n_y_vec); + xlabel('$n+2m$ - number of variables') + ylabel('Number of constraints') + legend({'Inequality constraints','Equality constraints', 'Comp. constraints'},'Location','northwest') + axis equal end % subplot(122) % scatter(n_var,n_y_vec); @@ -207,13 +212,19 @@ % Addaptivity: if settings.nnz_bounded_by_dim && settings.adaptive_density_bounds + if n_y > settings.n_comp_min - inv_cond_num = 1e0; + if sparsity_as_hessian + inv_cond_num = settings.inv_cond_num; + else + inv_cond_num = 1e0; + + end else inv_cond_num = settings.inv_cond_num; end end - + % Define symbolic variables: @@ -285,7 +296,7 @@ % Quadratic % ... generate sparse Hessian matrix H (and make sure it it psd if needed) % Quadratic - if nnz_bounded_by_dim + if nnz_bounded_by_dim if adaptive_density_bounds H = full( sprandsym(n_obj, s_density_A_B) ); % for smaller matrices use provided density bounds, for larger use sparse else @@ -296,7 +307,7 @@ end end else - H = full( sprandsym(n_obj, s_density_A_B) ); % ... full matrix for easier printing + H = full( sprandsym(n_obj, s_density_A_B) ); % ... full matrix for easier printing end H = round( H , n_digits_data); % ... round matrix to nDigits min_eig = max( 0, -min( eig( H ) - eps_prec) ); % ... -epsPrec to ensure H is p.s.d. @@ -312,7 +323,7 @@ H = full( sprandsym(n_obj, n_obj/n_obj.^2,inv_cond_num) ); % ... full matrix for easier printing end else - H = full( sprandsym(n_obj, s_density_A_B) ); % ... full matrix for easier printing + H = full( sprandsym(n_obj, s_density_A_B) ); % ... full matrix for easier printing end H = round( H , n_digits_data); grad_vec = round( range_grad(1) + rand(n_obj,1) * (range_grad(2) - range_grad(1)) , n_digits ); @@ -564,7 +575,7 @@ case 'CURLY10' - if n_obj > 10 + if n_obj > 10 k = 10; else k = n_obj; @@ -731,23 +742,44 @@ f = f*1e-3; % alliviate bad scaling + % compute hessian sparsity + if sparsity_as_hessian + H = hessian(f,v); % Hessian + % H_fun = Function('Hess',{v},{H}); + % S = full(H_fun(rand(size(v)))); + S = sparsity(H); % sparsity pattern + nnz_entries = nnz(S); % number of nonzeros + total_entries = numel(S); % total number of entries + s_density_H = nnz_factor*nnz_entries/total_entries; + end + + % Generate problem matrices if settings.inequality_constraints % Ax + By >= f s_density_A = s_density_A_B; - if settings.nnz_bounded_by_dim + if settings.nnz_bounded_by_dim if ~(n_comp <= n_comp_min && adaptive_density_bounds) s_density_A = nnz_factor*n_ineq/(n_x*n_ineq); end end - + + + if sparsity_as_hessian + if (n_comp > n_comp_min && adaptive_density_bounds) + s_density_A = s_density_H; + elseif ~adaptive_density_bounds + s_density_A = s_density_H; + end + end + if isempty(inv_cond_num) A = (range_A(2)-range_A(1))*sprand(n_ineq,n_x,s_density_A); else A = (range_A(2)-range_A(1))*sprand(n_ineq,n_x,s_density_A,inv_cond_num); end - - + + A(A~=0) = range_A(1)+A(A~=0); A = round(A,n_digits_data); if settings.inequality_constraints_coupling_terms @@ -757,7 +789,15 @@ s_density_B = nnz_factor*n_ineq/(n_y*n_ineq); end end - + + if sparsity_as_hessian + if (n_comp > n_comp_min && adaptive_density_bounds) + s_density_B = s_density_H; + elseif ~adaptive_density_bounds + s_density_B = s_density_H; + end + end + if isempty(inv_cond_num) B = (range_B(2)-range_B(1))*sprand(n_ineq,n_y,s_density_B); else @@ -776,8 +816,6 @@ r = r-1; end - - % s_density_M = (n_non_zero_E-n_y)/n_y^2; if settings.variable_density % s_density_M = range_s_density(1)+(range_s_density(2)-range_s_density(1)).*rand(1); @@ -787,11 +825,16 @@ if ~(n_comp <= n_comp_min && adaptive_density_bounds) s_density_M = nnz_factor*min(r,n_non_zero_E)/(n_y*n_ineq); end + if sparsity_as_hessian && (n_comp > n_comp_min && adaptive_density_bounds) + s_density_M = max(s_density_H,nnz_factor*(n_non_zero_E)/(n_y*n_ineq)); + elseif sparsity_as_hessian + s_density_M = s_density_H; + end end else s_density_M = settings.s_density_M; - end + if isempty(inv_cond_num) E = (range_E(2)-range_E(1))*sprand(r,n_y-r,s_density_M); else @@ -821,11 +864,21 @@ % N = sprand(n_y,n_x,s_density); % r = a + (b-a).*rand(100,1); s_density_N = s_density_A_B; + + if settings.nnz_bounded_by_dim if ~(n_comp <= n_comp_min && adaptive_density_bounds) s_density_N = nnz_factor*n_y/(n_x*n_y); end end + + if sparsity_as_hessian + if (n_comp > n_comp_min && adaptive_density_bounds) + s_density_N = s_density_H; + elseif ~adaptive_density_bounds + s_density_N = s_density_H; + end + end if isempty(inv_cond_num) N = (range_N(2)-range_N(1))*sprand(n_y,n_x,s_density_N); else diff --git a/benchmarks/nonlinear_mpec_benchmark/load_and_plot_results.m b/benchmarks/nonlinear_mpec_benchmark/load_and_plot_results.m index ef87dae..efd8608 100644 --- a/benchmarks/nonlinear_mpec_benchmark/load_and_plot_results.m +++ b/benchmarks/nonlinear_mpec_benchmark/load_and_plot_results.m @@ -10,21 +10,21 @@ plot_settings.absolute_phase_ii = 0; plot_settings.absolute_lpec = 0; -plot_settings.success_fail_statistics = 1; +plot_settings.success_fail_statistics = 0; plot_settings.nlp_lpec_cpu_comparisson = 0; -plot_settings.lpecs_solved = 1; +plot_settings.lpecs_solved = 0; plot_settings.nlps_solved = 1; plot_settings.lpecs_cpu_time = 0; -plot_settings.nlp_cpu_time = 0; % aggegated nlp times phase I and II +plot_settings.nlp_cpu_time = 1; % aggegated nlp times phase I and II -plot_settings.max_nlp_cpu_time = 1; +plot_settings.max_nlp_cpu_time = 0; plot_settings.max_lpec_cpu_time = 0; plot_settings.n_biactive_count = 0; -plot_settings.n_biactive = 1; +plot_settings.n_biactive = 0; plot_settings.active_set_changes = 0; plot_settings.bar_timing_plots = 0; plot_settings.bar_comparisson_plots = 0; @@ -47,100 +47,81 @@ plot_settings.save_plot = 1; -%% -results = 1; -switch results - case 1 - S = load('nonlinear_mpec_large1_15-Sep-2025_5'); - % S = load('nonlinear_mpec_general2_09-Nov-2024'); - try - dtable = S.dtable; - catch - dtable = S.dtable1; - end - solver_names = ["MPECopt-Reg-Gurobi", "MPECopt-Reg-Gurobi-ET","Reg", "NLP", "$\ell_1$-Penalty"]; - - filename = 'nonlinear_mpec'; - N_plot = [1:5]; - plot_settings.stationary_points = 0; - plot_settings.b_stationarity = 0; - plot_settings.n_biactive = 1; - %% Who is not B stationary? - if 0 - dtable_ii = dtable(dtable.solver_name == solver_names{1},:); - dtable_jj = dtable(dtable.solver_name == solver_names{4},:); - ind_not_B = find(dtable_jj.success == 1 & dtable_jj.b_stationarity == 0); - % For Reg - % diff in obj - delta_f = dtable_ii.f(ind_not_B)-dtable_jj.f(ind_not_B); - delta_f_relative_small = 100*abs(delta_f)./abs(dtable_ii.f(ind_not_B)+1e-16) - delta_f_relative_small_ind = abs(delta_f)./abs(dtable_ii.f(ind_not_B)+1e-16)<1e-3; - dtable_temp = dtable_jj; - dtable_temp.b_stationarity(ind_not_B(delta_f_relative_small_ind)) = 1; - dtable_temp.multiplier_based_stationarity(ind_not_B(delta_f_relative_small_ind)) = dtable_ii.multiplier_based_stationarity(ind_not_B(delta_f_relative_small_ind)); - dtable_temp.problem_name(ind_not_B(~delta_f_relative_small_ind)) - dtable_temp.prob_num(ind_not_B(~delta_f_relative_small_ind)) - dtable_temp.f_lpec(ind_not_B(~delta_f_relative_small_ind)) - dtable(dtable.solver_name == solver_names{4},:) = dtable_temp; - % For pen - dtable_ii = dtable(dtable.solver_name == solver_names{3},:); - dtable_jj = dtable(dtable.solver_name == solver_names{6},:); - ind_not_B = find(dtable_jj.success == 1 & dtable_jj.b_stationarity == 0); - % diff in obj - delta_f = dtable_ii.f(ind_not_B)-dtable_jj.f(ind_not_B); - delta_f_relative_small = 100*abs(delta_f)./abs(dtable_ii.f(ind_not_B)+1e-16) - delta_f_relative_small_ind = abs(dtable_ii.f(ind_not_B)-dtable_jj.f(ind_not_B))./abs(dtable_ii.f(ind_not_B)+1e-16)<1e-3; - dtable_temp = dtable_jj; - dtable_temp.b_stationarity(ind_not_B(delta_f_relative_small_ind)) = 1; - dtable_temp.multiplier_based_stationarity(ind_not_B(delta_f_relative_small_ind)) = dtable_ii.multiplier_based_stationarity(ind_not_B(delta_f_relative_small_ind)); - dtable_temp.problem_name(ind_not_B(~delta_f_relative_small_ind)) - dtable_temp.prob_num(ind_not_B(~delta_f_relative_small_ind)) - dtable_temp.f_lpec(ind_not_B(~delta_f_relative_small_ind)) - dtable(dtable.solver_name == solver_names{6},:) = dtable_temp; - end - case 2 - - S = load('macmpec_phase_i_29-Oct-2024'); - dtable = S.dtable; - solver_names = ["Reg-LPEC", "Reg-Simple",... - "Pen-$\ell_{\infty}$-LPEC","Pen-$\ell_{1}$-LPEC",... - "Feasibility-$\ell_1$", "Feasibility-$\ell_{\infty}$"]; - filename = 'macmpec_phase_i'; - N_plot = [1,2,3,5,6]; - plot_settings.relative_phase_i = 1; - plot_settings.absolute_phase_i = 1; - - case 3 - % todo: compine with highs and gurobi from general; - S = load('macmpec_lpec_08-Nov-2024_3'); - try - dtable = S.dtable; - catch - dtable = S.dtable1; - end - solver_names = ["Gurobi-MILP", "HiGHS-MILP",... - "Reg-MPEC","$\ell_{1}$-MPEC",... - "$\ell_{\infty}$-MPEC"]; - N_plot = 1:4; - % N_plot = [3,5]; - plot_settings.relative= 0; - plot_settings.absolute = 0; - plot_settings.relative_lpec = 1; - plot_settings.absolute_lpec = 1; - plot_settings.nlp_lpec_cpu_comparisson = 1; - plot_settings.max_lpec_cpu_time = 1; - case 4 - S = load('debug5_30-Oct-2024.mat'); - dtable = S.dtable; - filename = 'macmpec'; - - solver_names = ["MPECopt-Gurobi-def", "gurobi-not-tigher", "gurobi-phI-1e-2",... - "gurobi not tight 1e-2", "highs-1e-3","highs-1e-3-dont try","reg-no recovery","reg-default",... - 'Reg1e-9','Reg1e-10']; - - N_plot = [1,2]; +S = load('nonlinear_mpec_large2_18-Sep-2025'); +% S = load('nonlinear_mpec_general2_09-Nov-2024'); +try + dtable = S.dtable; +catch + dtable = S.dtable1; end +solver_names = ["MPECopt-Reg-Gurobi", "MPECopt-$\ell_1$-Gurobi", "MPECopt-Reg-Gurobi-ET", "MPECopt-$\ell_{\infty}$-Gurobi",... + "Reg", "NLP", "$\ell_1$-Penalty","$\ell_\infty$-Penalty", "MINLP"]; +filename = 'nonlinear_mpec'; +N_plot = [3 4 5 6 8]; +solver_names = unique(dtable.solver_name); +N_plot = [3 2 6 1 5]; +% N_plot = [1:6]; +% N_plot = [5 3 1 4 8 6]; + +plot_settings.stationary_points = 0; +plot_settings.b_stationarity = 0; +plot_settings.n_biactive = 1; +%% Post process to verify success and B-stationarity +if 1 + ind_ref = 3; + ind_fix = 6; + dtable_ii = dtable(dtable.solver_name == solver_names{ind_ref },:); % Reference solution + dtable_jj = dtable(dtable.solver_name == solver_names{ind_fix},:); % To be chechked + ind_not_success = find(dtable_ii.success == 1 & dtable_jj.success == 0); + % For Reg + % diff in obj + delta_f = dtable_ii.f(ind_not_success)-dtable_jj.f(ind_not_success); + delta_f_relative_small = 100*abs(delta_f)./abs(dtable_ii.f(ind_not_success)+1e-16) + delta_f_relative_small_ind = abs(delta_f)./abs(dtable_ii.f(ind_not_success)+1e-16)<1e-2; + dtable_temp = dtable_jj; + dtable_temp.success(ind_not_success(delta_f_relative_small_ind)) = dtable_ii.success(ind_not_success(delta_f_relative_small_ind)); + dtable_temp.b_stationarity(ind_not_success(delta_f_relative_small_ind)) = dtable_ii.b_stationarity(ind_not_success(delta_f_relative_small_ind)); + dtable_temp.multiplier_based_stationarity(ind_not_success(delta_f_relative_small_ind)) = dtable_ii.multiplier_based_stationarity(ind_not_success(delta_f_relative_small_ind)); + dtable_temp.problem_name(ind_not_success(~delta_f_relative_small_ind)) + dtable_temp.prob_num(ind_not_success(~delta_f_relative_small_ind)) + dtable_temp.f_lpec(ind_not_success(~delta_f_relative_small_ind)) + dtable_jj = dtable_temp; + dtable(dtable.solver_name == solver_names{ind_fix},:) = dtable_temp; + + % still not successful + ind_not_success = find(dtable_ii.success == 1 & dtable_jj.success == 0); + dtable_jj(ind_not_success,:) + + + % For pen + ind_ref = 2; + ind_fix = 1; + dtable_ii = dtable(dtable.solver_name == solver_names{ind_ref},:); % Reference solution + dtable_jj = dtable(dtable.solver_name == solver_names{ind_fix},:); % To be chechked + ind_not_success = find(dtable_ii.success == 1 & dtable_jj.success == 0); + ind_not_success = find(dtable_jj.success == 0); + % diff in obj + delta_f = dtable_ii.f(ind_not_success)-dtable_jj.f(ind_not_success); + delta_f_relative_small = 100*abs(delta_f)./abs(dtable_ii.f(ind_not_success)+1e-16); + delta_f_relative_small_ind = abs(dtable_ii.f(ind_not_success)-dtable_jj.f(ind_not_success))./abs(dtable_ii.f(ind_not_success)+1e-16)<1e-2; + dtable_temp = dtable_jj; + dtable_temp.success(ind_not_success(delta_f_relative_small_ind)) = dtable_ii.success(ind_not_success(delta_f_relative_small_ind)); + dtable_temp.b_stationarity(ind_not_success(delta_f_relative_small_ind)) = dtable_ii.b_stationarity(ind_not_success(delta_f_relative_small_ind)); + dtable_temp.multiplier_based_stationarity(ind_not_success(delta_f_relative_small_ind)) = dtable_ii.multiplier_based_stationarity(ind_not_success(delta_f_relative_small_ind)); + dtable_temp.problem_name(ind_not_success(~delta_f_relative_small_ind)) + dtable_temp.prob_num(ind_not_success(~delta_f_relative_small_ind)) + dtable_temp.f_lpec(ind_not_success(~delta_f_relative_small_ind)) + dtable_jj = dtable_temp; + dtable(dtable.solver_name == solver_names{ind_fix},:) = dtable_temp; + + % still not successful + ind_not_success = find(dtable_ii.success == 1 & dtable_jj.success == 0); + dtable_jj(ind_not_success,:) + +end + +%% % Modift possibly which solvers are plotted [~,temp_b] = unique(dtable.solver_name); diff --git a/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_large_3.m b/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_large_3.m new file mode 100644 index 0000000..045d1de --- /dev/null +++ b/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_large_3.m @@ -0,0 +1,283 @@ +clear; clc; close all; +problem_set_name = 'nonlinear_large2'; % (add large, degenerate, nonlinear, include lifted) + +%% file names +filename = 'nonlinear_mpec_large2'; % name for figures and excel table +results_name = ['results/' filename '_' datestr(datetime("today"))]; % name for matlab .dat with results + +%% Generate test set +settings.casadi_variable_type = 'SX'; +settings.nonlinear_eq_constraints = 1; +settings.nonlinear_ineq_constraints = 1; +settings.s_nonlinear_eq = 0.01; +settings.s_nonlinear_ineq = 1/4; +settings.include_lifted_variables_in_objective = 0; +settings.copy_ineq = 1; %have two copies of the inequality constrants -> violate licq if active +settings.s_ineq_copy = 1/4; +settings.copy_eq = 0; +settings.s_eq_copy = 0.0; + +settings.inequality_constraints = 1; +settings.inequality_constraints_coupling_terms = 1; % if 0, then in Ax+By >=f, B = 0; + +% settings.objective_functions = {'Quadratic_psd','Quadratic_ind',... +% 'Fletcher','Himmelblau','McCormick',... +% 'Powell','Rosenbrock',... +% 'Raydan1','Raydan2',... +% 'Diagonal4','Diagonal5',... +% 'Extended_Trigiaonal','Three_Exponential_Terms',... +% 'Generalized_PSC1','Extended_PSC1',... +% 'Fletcvb3','Bdqrtic','Tridia',... +% 'EG2','Edensch','Indef',... +% 'Cube','Bdexp','Genhumps',... +% 'Arwhead', 'Quartc', 'Cosine', 'Sine',... +% 'CURLY20', 'SCURLY30', 'FLETBV3M',... +% 'NCVXQP6','DIXCHLNV','EDENSCH',... +% }; + + +% reduced set +settings.objective_functions = {'Fletcher','McCormick',... + 'Powell','Rosenbrock',... + 'Raydan1','Raydan2',... + 'Diagonal4','Diagonal5',... + 'Extended_Trigiaonal','Three_Exponential_Terms',... + 'Generalized_PSC1',... + 'Fletcvb3','Bdqrtic','Tridia',... + 'EG2','Edensch','Indef',... + 'Cube','Bdexp','Genhumps',... + 'Arwhead', 'Quartc', 'Cosine',... + 'NCVXQP6','DIXCHLNV',... + }; + + +settings.rescale_factor = 1; +settings.round_all_data = 1; +settings.n_digits = 4; +settings.bounded_w = 1; +settings.use_normal_distributions_for_feasible_point = 1; +settings.alpha_lin = 10; % f + alpha_lin*[c;d]'*[x;y]' +settings.n_non_zero_E = 2000; +settings.symmetric_psd_M_matrix = 0; + +settings.s_density_A_B = 0.1; % all same or change +settings.s_density_M = 0.5; + +settings.s_density_A_B = 0.05; % all same or change +settings.s_density_M = 0.1; + +settings.adaptive_density_bounds = 1; % to account for very larg problems +settings.nnz_bounded_by_dim = 1; +settings.inv_cond_num = []; +settings.nnz_factor = 1.00; +settings.n_comp_min = 1000; + + +settings.variable_density = 1; +settings.range_s_density = [0.01 0.1]; +settings.random_problem_sizes = 1; + +settings.n_ineq_ub = 2; % n_ineq = n_ineq_ub*n_x % max relative number of ineq w.r.t x +settings.n_ineq_lb = 0.5; + +dimensions.n_fraction_of_x = 0.5; % n_y = round(n_x/n_fraction_of_x) + +% Problem size +dimensions.N_rand_prob = 3; % number of problems per objective +dimensions.n_x_max = 2100; +dimensions.n_x_min = 100; + + +t_gen = tic; +mpecs = generate_nonlinear_mpec_problem_set(problem_set_name,settings,dimensions); +t_mpec_gen = toc(t_gen); +fprintf('Problem generation time : %2.2f \n',t_mpec_gen) + +%% Solver settings +solver_names = ["MPECopt-Reg-Gurobi", "MPECopt-$\ell_1$-Gurobi", "MPECopt-Reg-Gurobi-ET", "MPECopt-$\ell_{\infty}$-Gurobi",... + "Reg", "NLP", "$\ell_1$-Penalty","$\ell_\infty$-Penalty", "MINLP"]; + +solver_functions = {@mpec_optimizer,@mpec_optimizer,@mpec_optimizer,@mpec_optimizer,... + @mpec_homotopy_solver,@mpec_homotopy_solver, @mpec_homotopy_solver,@mpec_homotopy_solver,... + @mpec_minlp_solver}; + +opts1 = mpecopt.Options(); +opts1.solver_name = solver_names{1}; +opts1.settings_lpec.lpec_solver = "Gurobi"; +opts1.relax_and_project_homotopy_parameter_steering = "Direct"; +opts1.use_one_nlp_solver = true; +opts1.problem_in_vertical_from = true; +opts1.settings_casadi_nlp.ipopt.max_wall_time = 900; +% opts1.initialization_strategy = "FeasibilityEll1General"; + + +opts2 = mpecopt.Options(); +opts2.solver_name = solver_names{2}; +opts2.settings_lpec.lpec_solver = "Gurobi"; +opts2.relax_and_project_homotopy_parameter_steering = "Ell_1"; +opts2.use_one_nlp_solver = true; +opts2.problem_in_vertical_from = true; +opts2.settings_casadi_nlp.ipopt.max_wall_time = 900; +% opts2.settings_lpec.stop_lpec_at_feasible = true; +% opts2.rho_TR_phase_i_init = 1e-3; + + +% opts3 = mpecopt.Options(); +% opts3.solver_name = solver_names{3}; +% opts3.settings_lpec.lpec_solver = "Highs"; +% opts3.relax_and_project_homotopy_parameter_steering = "Direct"; +% opts3.use_one_nlp_solver = true; +% +opts3 = mpecopt.Options(); +opts3.solver_name = solver_names{3}; +opts3.settings_lpec.lpec_solver = "Gurobi"; +opts3.relax_and_project_homotopy_parameter_steering = "Direct"; +opts3.use_one_nlp_solver = true; +opts3.settings_lpec.stop_lpec_at_feasible = true; +opts3.settings_lpec.stop_lpec_at_descent = true; +opts3.consider_all_complementarities_in_lpec = false; +opts3.problem_in_vertical_from = true; +opts3.settings_casadi_nlp.ipopt.max_wall_time = 900; +% opts3.rho_TR_phase_i_init = 1e-2; + +opts4 = mpecopt.Options(); +opts4.solver_name = solver_names{4}; +opts4.settings_lpec.lpec_solver = "Gurobi"; +opts4.relax_and_project_homotopy_parameter_steering = "Ell_inf"; +opts4.use_one_nlp_solver = false; +opts4.problem_in_vertical_from = false; +opts4.settings_casadi_nlp.ipopt.max_wall_time = 900; +% opts2.settings_lpec.stop_lpec_at_feasible = true; +% opts2.rho_TR_phase_i_init = 1e-3; + + +scholtes_opts1 = HomotopySolverOptions(); +scholtes_opts1.homotopy_parameter_steering = 'Direct'; +scholtes_opts1.problem_in_vertical_from = true; +scholtes_opts1.settings_casadi_nlp.ipopt.max_wall_time = 900; + +scholtes_opts2 = HomotopySolverOptions(); +scholtes_opts2.homotopy_parameter_steering = 'Direct'; +scholtes_opts2.max_iter = 1; +scholtes_opts2.sigma0 = 0; +scholtes_opts2.problem_in_vertical_from = true; +scholtes_opts2.settings_casadi_nlp.ipopt.max_wall_time = 2700; + + +scholtes_opts3 = HomotopySolverOptions(); +scholtes_opts3.homotopy_parameter_steering = 'Ell_1'; +scholtes_opts3.problem_in_vertical_from = true; +scholtes_opts3.settings_casadi_nlp.ipopt.max_wall_time = 900; + + +scholtes_opts4 = HomotopySolverOptions(); +scholtes_opts4.homotopy_parameter_steering = 'Ell_inf'; +scholtes_opts4.problem_in_vertical_from = false; +scholtes_opts4.settings_casadi_nlp.ipopt.max_wall_time = 900; + + +minlp_opts = MINLPSolverOptions(); +minlp_opts.settings_casadi_nlp.bonmin.time_limit = 1800; +scholtes_opts3.problem_in_vertical_from = true; + +opts = {opts1, opts2, opts3, opts4,... + scholtes_opts1, scholtes_opts2, scholtes_opts3, scholtes_opts4,... + minlp_opts}; % list of options to pass to mpecsol (option structs) + +%% Create data struct +% N_experiments = [4 3 5 1 2 6 7]; +% % N_experiments = [3 1]; +N_experiments = [5 3 1 4 8 6]; + +nonlinear_mpec_benchmark_dtable_loop; % this script runs the experimetns, creates a dtable +%% Pick which results to plot +dtable = dtable1; +% Modift possibly which solvers are plotted +[~,temp_b] = unique(dtable.solver_name); +solver_name = dtable.solver_name(sort(temp_b)); +% solver_tab = table(solver_name); +N_plot = N_experiments; +% N_plot = [1,2]; + +dtable_plot = []; +for ii = N_plot + dtable_plot = [dtable_plot; dtable(dtable.solver_name == solver_names(ii),:)]; +end + +%% Export to excel +write_in_separate_sheets = true; +filename_excel = [filename '_details.xlsx']; +if write_in_separate_sheets + solver_tab = pivot(dtable_plot, Rows= "solver_name"); + for ii = 1:length(solver_tab.solver_name) + solver_name =solver_tab.solver_name{ii}; + dtable_ii = dtable_plot(dtable_plot.solver_name == solver_name,:); + writetable(dtable_ii,filename_excel,'Sheet',ii) + end +else + writetable(dtable_plot,filename_excel,'Sheet',1) +end + +%% Plot results +plot_settings.success_fail_statistics = 1; +plot_settings.n_biactive = 1; +plot_settings.lpecs_solved = 0; +plot_settings.active_set_changes = 0; +plot_settings.lpecs_cpu_time = 0; +plot_settings.bar_timing_plots = 0; +plot_settings.nlps_solved = 0; +plot_settings.max_nlp_cpu_time = 0; +plot_settings.max_nlp_cpu_time_phase_i = 0; +plot_settings.max_nlp_cpu_time_phase_ii = 0; +plot_settings.nlp_cpu_time = 0; % aggegated nlp times phase I and II +plot_settings.nlp_lpec_cpu_comparisson = 0; +plot_settings.objective = 0; +plot_settings.objective_rescaled = 1; +plot_settings.max_lpec_cpu_time = 0; + +plot_settings.relative = 1; +plot_settings.relative_phase_i = 0; +plot_settings.relative_phase_ii = 0; +plot_settings.relative_lpec = 0; + +plot_settings.absolute = 1; +plot_settings.absolute_phase_i = 0; +plot_settings.absolute_phase_ii = 0; +plot_settings.absolute_lpec = 0; + +plot_settings.solved_in_phase_i = 0; + +% split cpu time among phases and show in bar plot (just report for mpecopt +% guroi) +plot_settings.nlp_phases_cpu_time = 0; +plot_settings.lpec_phases_cpu_time = 0; + +plot_settings.max_lpec_cpu_time = 0; +plot_settings.stationary_points = 0; +plot_settings.b_stationarity = 0; +plot_settings.b_stationarty_as_success_criterion = 0; +plot_settings.plot_only_sucessful = 1; +plot_settings.bar_comparisson_plots = 0; + +plot_settings.save_plot = 1; + +close all; +plot_mpec_benchmark_result(dtable_plot,filename,plot_settings) +%% +dtable_ii = dtable_plot(dtable_plot.solver_name == solver_names{1},:); +dtable_jj = dtable_plot(dtable_plot.solver_name == solver_names{4},:); +hh = find(dtable_jj.b_stationarity == 0 & dtable_jj.success == 1); +% dtable_ii.f_lpec((hh)) +% dtable_jj.f((hh)) +% dtable_jj.f_lpec((hh)) +% abs((dtable_ii.f((hh))-dtable_jj.f((hh))))./abs(dtable_ii.f((hh)))>1e-3 + +%% +% figure +% semilogy(abs(dtable_ii.f_lpec)+1e-16,'LineWidth',1.5); +% hold on +% semilogy(abs(dtable_jj.f_lpec)+1e-16,'LineWidth',1.5); +% ylabel('$|\nabla f(x^*)^\top d|$') +% xlabel('Problem instance') +% grid on +% yline(1e-8,'LineWidth',2) \ No newline at end of file diff --git a/benchmarks/nonlinear_mpec_benchmark/solve_single_problem_nonlinear_mpec.m b/benchmarks/nonlinear_mpec_benchmark/solve_single_problem_nonlinear_mpec.m index 5d530e5..94e4038 100644 --- a/benchmarks/nonlinear_mpec_benchmark/solve_single_problem_nonlinear_mpec.m +++ b/benchmarks/nonlinear_mpec_benchmark/solve_single_problem_nonlinear_mpec.m @@ -32,7 +32,7 @@ }; -settings.objective_functions = {settings.objective_functions_all{24}}; +settings.objective_functions = {settings.objective_functions_all{14}}; % settings.objective_functions = {'Himmelblau'}; @@ -44,6 +44,7 @@ settings.alpha_lin = 10; % f + alpha_lin*[c;d]'*[x;y]' settings.n_non_zero_E = 2000; settings.symmetric_psd_M_matrix = 0; +settings.sparsity_as_hessian = 1; settings.s_density_A_B = 0.1; % all same or change settings.s_density_M = 0.5; @@ -61,7 +62,7 @@ % settings.nnz_factor = 1.00; %% -settings.adaptive_density_bounds = 1; % to account for very larg problems +settings.adaptive_density_bounds = 0; % to account for very larg problems settings.variable_density = 1; settings.range_s_density = [0.01 0.05]; settings.random_problem_sizes = 1; @@ -73,12 +74,15 @@ dimensions.N_rand_prob = 1; % number of problems per objective + dimensions.n_x_max = 1000; dimensions.n_x_min = 1000; -dimensions.n_x_max = 100; -dimensions.n_x_min = 100; +dimensions.n_x_max = 700; +dimensions.n_x_min = 700; +settings.sparsity_as_hessian = 1; +settings.nnz_factor = 3; dimensions.n_fraction_of_x = 0.5; % n_y = round(n_x/n_fraction_of_x) mpecs = generate_nonlinear_mpec_problem_set(problem_set_name,settings,dimensions); diff --git a/benchmarks/plot_mpec_benchmark_result.m b/benchmarks/plot_mpec_benchmark_result.m index eb6e255..4b3a603 100644 --- a/benchmarks/plot_mpec_benchmark_result.m +++ b/benchmarks/plot_mpec_benchmark_result.m @@ -157,7 +157,8 @@ ylim([0, 1.0]); ax.LineStyleCyclingMethod = line_style_cycling_method; hold off; - legend('Location', 'southeast') + % legend('Location', 'southeast') + legend('Location', 'east') % grid on; if settings.save_plot exportgraphics(gca, [filename '_absolute.pdf']); @@ -190,6 +191,7 @@ ax.LineStyleCyclingMethod = line_style_cycling_method; hold off; legend('Location', 'southeast') + % grid on; if settings.save_plot exportgraphics(gca, [filename '_absolute_phase_i.pdf']); @@ -294,7 +296,8 @@ ax.LineStyleCyclingMethod = line_style_cycling_method; hold off; % grid on; - legend('Location', 'southeast'); + % legend('Location', 'southeast'); + legend('Location', 'east'); if settings.save_plot exportgraphics(gca, [filename '_relative.pdf']); end From 9999b0e4db2b546c099fadf8d9343c93b3f84615 Mon Sep 17 00:00:00 2001 From: "armin.nurkanovic" Date: Wed, 24 Sep 2025 11:01:55 +0200 Subject: [PATCH 30/31] adress comments: remove txt, and open source in defaults settings --- .gitignore | 1 + .../cpu_experiments.txt | 19 ------------------- .../current_problem.txt | 1 - src/+mpecopt/Options.m | 4 ++-- 4 files changed, 3 insertions(+), 22 deletions(-) delete mode 100644 benchmarks/nonlinear_mpec_benchmark/cpu_experiments.txt delete mode 100644 benchmarks/nonlinear_mpec_benchmark/current_problem.txt diff --git a/.gitignore b/.gitignore index d7b73d4..3edd9b0 100644 --- a/.gitignore +++ b/.gitignore @@ -28,6 +28,7 @@ qt_temp* *.glo *.acn *.acr +*.txt *.alg *.glg *.gls diff --git a/benchmarks/nonlinear_mpec_benchmark/cpu_experiments.txt b/benchmarks/nonlinear_mpec_benchmark/cpu_experiments.txt deleted file mode 100644 index 43b1e2e..0000000 --- a/benchmarks/nonlinear_mpec_benchmark/cpu_experiments.txt +++ /dev/null @@ -1,19 +0,0 @@ -MPECopt-Reg-Gurobi - execution time: 106.16 seconds (0.0295 hours, 0.001229 days) -Reg - execution time: 98.42 seconds (0.0273 hours, 0.001139 days) -Reg - execution time: 2.47 seconds (0.0007 hours, 0.000029 days) -MPECopt-Reg-Gurobi-ET - execution time: 2.16 seconds (0.0006 hours, 0.000025 days) -MPECopt-Reg-Gurobi - execution time: 2.02 seconds (0.0006 hours, 0.000023 days) -Reg - execution time: 1.68 seconds (0.0005 hours, 0.000019 days) -MPECopt-Reg-Gurobi-ET - execution time: 1.92 seconds (0.0005 hours, 0.000022 days) -MPECopt-Reg-Gurobi - execution time: 2.02 seconds (0.0006 hours, 0.000023 days) -Reg - execution time: 2.29 seconds (0.0006 hours, 0.000027 days) -MPECopt-Reg-Gurobi-ET - execution time: 2.64 seconds (0.0007 hours, 0.000031 days) -MPECopt-Reg-Gurobi - execution time: 2.58 seconds (0.0007 hours, 0.000030 days) -MPECopt-$\ell_{\infty}$-Gurobi - execution time: 1.64 seconds (0.0005 hours, 0.000019 days) -$\ell_\infty$-Penalty - execution time: 1.22 seconds (0.0003 hours, 0.000014 days) -NLP - execution time: 0.97 seconds (0.0003 hours, 0.000011 days) -Gurobi - execution time: 18.53 seconds (0.0051 hours, 0.000214 days) -Gurobi-early-I - execution time: 16.31 seconds (0.0045 hours, 0.000189 days) -Gurobi - execution time: 0.10 seconds (0.0000 hours, 0.000001 days) -Gurobi-early-I - execution time: 0.02 seconds (0.0000 hours, 0.000000 days) -Gurobi-early-I-II - execution time: 0.03 seconds (0.0000 hours, 0.000000 days) diff --git a/benchmarks/nonlinear_mpec_benchmark/current_problem.txt b/benchmarks/nonlinear_mpec_benchmark/current_problem.txt deleted file mode 100644 index 536bd60..0000000 --- a/benchmarks/nonlinear_mpec_benchmark/current_problem.txt +++ /dev/null @@ -1 +0,0 @@ -nonlinear_mpec2_McCormick_Var1725_Comp690_Eq690_Ineq1030 diff --git a/src/+mpecopt/Options.m b/src/+mpecopt/Options.m index f132c77..9ca40ce 100644 --- a/src/+mpecopt/Options.m +++ b/src/+mpecopt/Options.m @@ -119,7 +119,7 @@ function obj = Options() obj.settings_lpec = LPECSolverOptions(); % obj.settings_lpec.solver_name = 'lpec_solver'; - % obj.settings_lpec.lpec_solver= LpecSolver.Gurobi; + obj.settings_lpec.lpec_solver= LpecSolver.Highs; % obj.settings_lpec.max_nodes = 2e3; obj.settings_lpec.stop_lpec_at_feasible = false; % Stop at a feasible point in Phase I lpecs (sufficent for pogress) obj.settings_lpec.stop_lpec_at_descent = false; % Stop at descent direction in Phase II lepcs (sufficent for pogress) (supported only for gurobi atm) @@ -149,7 +149,7 @@ % obj.settings_casadi_nlp.ipopt.warm_start_bound_push = 1e-6; % obj.settings_casadi_nlp.ipopt.warm_start_bound_frac = 1e-6; % obj.settings_casadi_nlp.ipopt.warm_start_entire_iterate = 'yes'; - obj.settings_casadi_nlp.ipopt.linear_solver = 'ma27'; % mumps, ma27, ma57 + obj.settings_casadi_nlp.ipopt.linear_solver = 'mumps'; % mumps, ma27, ma57 % obj.settings_casadi_nlp.ipopt.fixed_variable_treatment = 'relax_bounds'; % make_parameter make_constraint relax_bounds obj.settings_casadi_nlp.detect_simple_bounds = true; obj.settings_casadi_nlp.ipopt.fixed_variable_treatment = 'make_parameter'; % make_parameter make_constraint relax_bounds From 62d2d3ecf8f8690bd85dc10b7b0fce3dbf7438bf Mon Sep 17 00:00:00 2001 From: "armin.nurkanovic" Date: Wed, 24 Sep 2025 11:04:46 +0200 Subject: [PATCH 31/31] getting started is open src --- examples/getting_started.m | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/getting_started.m b/examples/getting_started.m index e0d06fb..e56ebb6 100644 --- a/examples/getting_started.m +++ b/examples/getting_started.m @@ -12,7 +12,7 @@ % parameter p = SX.sym('p'); % objective -f = x1+x2-p*x3; +f = x1^2+x2^2-p*x3; % inital guess x0 = ones(3,1); @@ -47,10 +47,10 @@ % Solver settings solver_settings = mpecopt.Options(); % change some settings -solver_settings.settings_lpec.lpec_solver = 'Gurobi' ; % 'Gurobi'; for best perfomance; 'Highs_casadi' - via CasADi conic -solver_settings.settings_casadi_nlp.ipopt.linear_solver = 'ma27'; % 'ma27' for better perfomance +solver_settings.settings_lpec.lpec_solver = 'Highs' ; % 'Gurobi'; for best perfomance; 'Highs_casadi' - via CasADi conic +solver_settings.settings_casadi_nlp.ipopt.linear_solver = 'mumps'; % 'ma27' for better perfomance solver_settings.rho_TR_phase_i_init = 1e1; -solver_settings.rho_TR_phase_ii_init = 1e-2; +solver_settings.rho_TR_phase_ii_init = 1e-6; % Call solver