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/histogram_on_lpecs.m b/benchmarks/histogram_on_lpecs.m new file mode 100644 index 0000000..f81e446 --- /dev/null +++ b/benchmarks/histogram_on_lpecs.m @@ -0,0 +1,360 @@ +%% Evaluating LPEC MILP efficiency: +close all; clc; +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}; + +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; + +% solver_names = ["Gurobi", "Gurobi-early-I", "Gurobi-early-I-II", "Highs", "Highs-early"]; + + +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 +% ------------------------ + +% 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)); +nc_ii = cell2mat(process_cells(lpec_dstruct.nodecount_phase_ii)); +nc_tot = [nc_i, nc_ii]; + +log_plot = true; +figure; + +% --- Phase I --- +subplot(1,2,1) +if log_plot + h1 = histogram(log2(nc_i), 'BinMethod', 'integers'); + min_pow = floor(min(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_i)) == 1 + xlim([-1 max_pow]); + end +else + h1 = histogram(nc_i, 'BinMethod', 'integers'); +end + +% Set ticks manually based on your data range +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 + 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))]) +% xlim([0 100]) + +% exportgraphics(gca, [filename '_phase_i.pdf']); +% --- Phase II --- +subplot(1,2,2) +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'); +xlabel('Nodes'); +% ylabel('Frequency'); +grid on; +set(gca,'FontSize',13) + + +% 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))]) +% exportgraphics(gca, [filename '_phase_i.pdf']); +exportgraphics(gcf, [filename '.pdf']); + +%% +% 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_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_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_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_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_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 = 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_cummulative(max_idx)); + +% ------------------------ +% CPU time statistics +% ------------------------ +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; + + +% ------------------------ +%% 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)); + +%% +%% 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); + +% 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'); + + +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 + +% 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'); + + +%% + +% 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 7315d18..5d37b26 100644 --- a/benchmarks/macmpec/load_and_plot_results.m +++ b/benchmarks/macmpec/load_and_plot_results.m @@ -10,12 +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 @@ -23,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; @@ -36,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) @@ -51,57 +50,83 @@ 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_14-Sep-2025'); + S = load('macmpec_general_22-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); + 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-Reg-HiGHS",... - "MPECopt-$\ell_1$-Gurobi", "Reg" , "Pen-$\ell_{\infty}$", "Pen-$\ell_{1}$"]; - % 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; - 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 @@ -122,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/macmpec/mpec_benchmark_dtable_loop.m b/benchmarks/macmpec/mpec_benchmark_dtable_loop.m index 3130c41..134356a 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)]; @@ -93,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]; @@ -103,11 +124,29 @@ 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]; - + + % 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]; @@ -130,8 +169,11 @@ % 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 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 40a675c..7402a7a 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_general'; % name for figures and excel table %% Load macmpec macmpec_json = dir('macMPEC/*.json'); @@ -21,8 +21,9 @@ 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_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 @@ -38,49 +39,82 @@ 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 <= 100 +% 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-$\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}; - -default_opts1 = mpecopt.Options(); -default_opts1.solver_name = solver_names{1}; - -default_opts2 = mpecopt.Options(); -default_opts2.solver_name = solver_names{2}; -default_opts2.settings_lpec.lpec_solver = "Highs"; -default_opts2.rho_TR_phase_i_init = 1e-3; - -default_opts3 = mpecopt.Options(); -default_opts3.solver_name = solver_names{3}; -default_opts3.relax_and_project_homotopy_parameter_steering = "Ell_1"; -% default_opts3.bnlp_projection_strategy = "Simple"; + @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 = false; +% 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_inf"; +opts2.use_one_nlp_solver = false; +% 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 = "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; +opts3.consider_all_complementarities_in_lpec = true; 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; scholtes_opts3 = HomotopySolverOptions(); -scholtes_opts3.homotopy_parameter_steering = 'Ell_1'; +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_opts3,... + 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]; +N_experiments = [1:7]; +% N_experiments = [2, 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 @@ -112,14 +146,14 @@ %% 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; 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 +161,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,7 +179,7 @@ plot_settings.max_lpec_cpu_time = 0; plot_settings.stationary_points = 1; -plot_settings.b_stationarity = 0; +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; 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..33f55cd --- /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_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'); +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:2]; +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 1abaf90..1f26bea 100644 --- a/benchmarks/macmpec/solve_single_macmpec_problem.m +++ b/benchmarks/macmpec/solve_single_macmpec_problem.m @@ -6,12 +6,35 @@ all_problem_names = {macmpec_json.name}; -problame_name = 'qpec-200-2'; -% 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 = 'pack-rig2p-16.nl'; +% % problame_name = 'nash1a'; +% problame_name = 'nash1c'; +% problame_name = 'tap-09'; +% problame_name = ' design-cent-31'; + +% problame_name = 'pack-rig2p-16'; + +% problame_name = 'pack-rig2p-8'; + + % problame_name = 'pack-rig1p-32'; % lot of lpec itters + + +% 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 = 17; fname = fullfile(macmpec_json(ii_prob).folder, macmpec_json(ii_prob).name); @@ -26,6 +49,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; @@ -33,6 +57,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 +90,60 @@ 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(); +settings_homotopy.homotopy_parameter_steering = 'Direct'; [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'; - -[results,stats] = mpec_optimizer(mpec, solver_initalization, solver_settings); -w_opt_mpecopt = full(results.x); -f_opt_mpecopt = full(results.f); - -%% 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 +w_opt_homotopy = full(result_homotopy.x); + +%% MINLP solver +settings_minlp = MINLPSolverOptions(); +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 +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, opts); +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); +f_opt_mpecopt = full(result_mpecopt.f); + +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'); +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)); +% % +% 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 2d50775..e1a1ae4 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. @@ -38,6 +42,39 @@ unfold_struct(settings,'caller') unfold_struct(dimensions,'caller') + +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-1; + inv_cond_num = []; +end + +if ~isfield(settings,'nnz_factor') + 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'' ') + end +end + eps_prec = 10^(-settings.n_digits); if settings.round_all_data @@ -52,37 +89,55 @@ 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_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 = 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(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); + n_x_vec = n_x_min + biased_indices - 1; + n_y_vec = round(n_x_vec/n_fraction_of_x); % num of comp vars + 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; - figure - n_var = n_x_vec+2*n_y_vec; - 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 -% 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; + 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 + end + % 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); @@ -90,6 +145,11 @@ 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)) +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 range_x = [0,1]; range_y = [0,1]; @@ -100,10 +160,18 @@ % 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 + + +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 @@ -115,7 +183,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 = []; @@ -128,375 +196,806 @@ 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: - 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; + % 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]; + + % Addaptivity: + if settings.nnz_bounded_by_dim && settings.adaptive_density_bounds + + if n_y > settings.n_comp_min + if sparsity_as_hessian + inv_cond_num = settings.inv_cond_num; + else + inv_cond_num = 1e0; + + end else - n_obj = n_non_lifted; + inv_cond_num = settings.inv_cond_num; end + 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; + % 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); + 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 + % 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); - if settings.variable_density - s_density_A_B = range_s_density(1)+(range_s_density(2)-range_s_density(1)).*rand(1); + % 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 - - 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 + + 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 + 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 + 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 - 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; + 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. + 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' + if settings.nnz_bounded_by_dim + 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 + 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+(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 '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 - 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); + % 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 - case 'Tridia' - f = f+(w(1)-1)^2; - for i = 2:n_obj - f = f+ i*(2*w(i)-w(i-1))^2; + f = f + q * (q * (q^2 - 20) - 0.1); + end + case 'SCURLY30' + if n_obj > 30 + K = 30; + else + K = n_obj; + end + + % 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 - case 'EG2' - for i = 1:n_obj-1 - f = f+ sin(w(i)+w(i)^2-1)+0.5*sin(w(i)^2); + 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 - 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; + + % 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 - case 'Indef' - f = f+sum(w); - for i = 2:n_obj-1 - f = f+ 0.5*cos(2*w(i)-w(end)-w(1)); + + 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 - case 'Cube' - f = f+(w(1)-1)^2; - for i = 2:n_obj - f = f+ 100*(w(i)-w(i-1)^3)^2; + + % 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 - case 'Bdexp' - for i = n_obj-2 - f = f+(w(i)-w(i+1))*exp(-w(i+2)*(w(i)+w(i+1))); + + 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 - 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); + + % 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 - case 'Arwhead' - for i = n_obj-1 - f = f + (-4*w(i)+3)+(w(i)^2+w(end)^2)^2; + + 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 - 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 + + % 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 + % finite-difference like group (difference with next) + if i < n_obj + q = w(i+1) - w(i); + else + q = w(i); end - case 'Sine' - for i = n_obj-1 - f = f + sin(-0.5*w(i+1)+w(i)^2); + % element contribution (scaled sin/cos) + 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 + nplus = floor(0.75 * n_obj); + + % 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; + + alpha = w(i) + w(j) + w(k); + + if i <= nplus + f = f + 0.5 * (alpha^2); % positive term + else + f = f - 0.5 * (alpha^2); % negative term 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 + 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 + + + % 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 + + 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 ~(n_comp <= n_comp_min && adaptive_density_bounds) + s_density_A = nnz_factor*n_ineq/(n_x*n_ineq); + 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); + + 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 - 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); + if isempty(inv_cond_num) + A = (range_A(2)-range_A(1))*sprand(n_ineq,n_x,s_density_A); else - s_density_M = settings.s_density_M; + A = (range_A(2)-range_A(1))*sprand(n_ineq,n_x,s_density_A,inv_cond_num); 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); + A(A~=0) = range_A(1)+A(A~=0); + A = round(A,n_digits_data); + if settings.inequality_constraints_coupling_terms + 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 - 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; + 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 - 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; + 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); + else + B = zeros(n_ineq,n_y); 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 + + 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); + 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 + 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 + E = (range_E(2)-range_E(1))*sprand(r,n_y-r,s_density_M,inv_cond_num); + 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); + 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); + 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_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)]; + 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 + 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); + + 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 - ubw = inf*ones(n,1); + 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/load_and_plot_results.m b/benchmarks/nonlinear_mpec_benchmark/load_and_plot_results.m index 1d85747..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 = 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; @@ -36,111 +36,93 @@ 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; -%% -results = 1; -switch results - case 1 - S = load('nonlinear_mpec_general_08-Nov-2024'); - % 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}$"]; - filename = 'nonlinear_mpec'; - N_plot = [1:6]; - plot_settings.stationary_points = 1; - plot_settings.b_stationarity = 1; - 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; - - 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); solver_name = dtable.solver_name(sort(temp_b)); 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..043006b 100644 --- a/benchmarks/nonlinear_mpec_benchmark/nonlinear_mpec_benchmark_dtable_loop.m +++ b/benchmarks/nonlinear_mpec_benchmark/nonlinear_mpec_benchmark_dtable_loop.m @@ -31,6 +31,19 @@ dstruct.problem_infeasible = []; dstruct.f_lpec = []; +%% Lpec details +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 = []; @@ -46,10 +59,15 @@ 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); + t_current_exp = tic; 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); @@ -62,10 +80,27 @@ 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); + fprintf(['Current problem solution started at: ' datestr(now) '.\n']) % 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 + fprintf(['Current problem solution ended at: ' datestr(now) '.\n']) + + % if stats.success~=1 + % keyboard; + % end dstruct.solver_name = [dstruct.solver_name; string(name)]; dstruct.problem_name = [dstruct.problem_name; string(mpec_name)]; @@ -77,8 +112,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]; @@ -87,11 +124,22 @@ 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]; - + + % 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; + 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]; @@ -107,6 +155,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; @@ -114,8 +163,17 @@ % 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); + 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); save(results_name,"dtable"); +save([results_name '_lpec_details'],"lpec_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..02e6d08 --- /dev/null +++ b/benchmarks/nonlinear_mpec_benchmark/run_diff_experiments.m @@ -0,0 +1,31 @@ +% %% 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 + + +%% run subseqeuntlly benchmarks of different size +try + run_nonlinear_mpec_benchmark_large_2 +catch +end + +%% +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.m b/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_large.m new file mode 100644 index 0000000..b61fb88 --- /dev/null +++ b/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_large.m @@ -0,0 +1,272 @@ +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.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 = {'Quadratic_psd',... + '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',... + 'CURLY20','SCURLY30',... + '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 = 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]; +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 = 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 = 20; + +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]; + +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; + +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 = [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; +% 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_1.m b/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_large_1.m new file mode 100644 index 0000000..7a90bc0 --- /dev/null +++ b/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_large_1.m @@ -0,0 +1,264 @@ +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 = [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 +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..1c993e5 --- /dev/null +++ b/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_large_2.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 = 1e0; +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/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/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..ed2580d --- /dev/null +++ b/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_lpec_histogram.m @@ -0,0 +1,230 @@ +clear; clc; close all; +problem_set_name = 'nonlinear_mpec2'; % (add large, degenerate, nonlinear, include lifted) + +%% file names +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.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 +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',... + }; + + +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 = 0; +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_lb = 0.5; + +settings.inv_cond_num = []; +% Problem size + +dimensions.N_rand_prob = 3; % number of problems per objective +dimensions.n_x_max = 425; +dimensions.n_x_min = 100; + + +% dimensions.N_rand_prob = 1; % 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]; + +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-I", "Gurobi-early-I-II", "Highs", "Highs-early"]; + +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(); +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; +opts2.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.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 = 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, opts5}; % list of options to pass to mpecsol (option structs) + +%% Create data struct +N_experiments = [1:3]; + +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 = 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 = 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..d6a76d0 --- /dev/null +++ b/benchmarks/nonlinear_mpec_benchmark/run_nonlinear_mpec_benchmark_medium.m @@ -0,0 +1,267 @@ +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 = 1050; +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 2 3 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 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 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..94e4038 100644 --- a/benchmarks/nonlinear_mpec_benchmark/solve_single_problem_nonlinear_mpec.m +++ b/benchmarks/nonlinear_mpec_benchmark/solve_single_problem_nonlinear_mpec.m @@ -1,85 +1,95 @@ 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.5; -settings.s_nonlinear_ineq = 0.5; -settings.include_lifted_variables_in_objective = 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 = 0.5; -settings.copy_eq = 0; %have two copies of the inequality constrants -> violate licq if active -settings.s_eq_copy = 0.25; +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',... +% Trigonometric +settings.objective_functions_all = {'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 = {settings.objective_functions_all{14}}; + +% settings.objective_functions = {'Himmelblau'}; settings.rescale_factor = 1; -settings.round_all_data = 0; +settings.round_all_data = 1; 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]' 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; -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.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_lb = 0.1; -dimensions.N_rand_prob = 5; % number of problems per objective -dimensions.n_x_max = 100; -dimensions.n_x_min = 10; +settings.nnz_bounded_by_dim = 1; +settings.inv_cond_num = []; +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.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 -dimensions.N_rand_prob = 4; % number of problems per objective -dimensions.n_x_max = 50; -dimensions.n_x_min = 10; +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_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_x_max = 1000; +dimensions.n_x_min = 1000; -dimensions.n_fraction_of_x = 1; % n_y = round(n_x/n_fraction_of_x) +dimensions.n_x_max = 700; +dimensions.n_x_min = 700; -% 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]; +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); length(mpecs) -% pack-rig-32.nl % scholtes %% mpecopt solvers - % 2 - - -ii_prob = 85; % 10 41 48 85 87 +ii_prob = 1; close all; mpec = mpecs(ii_prob); @@ -95,55 +105,61 @@ 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; -[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) -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); +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(); +% [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 +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 = 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; +% solver_settings.rho_TR_phase_i_init = 10; + +tic +solver = mpecopt.Solver(mpec_struct, opts); +toc +%% +[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); + +%% 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/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 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 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/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..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,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_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-4; +solver_settings.rho_TR_phase_ii_init = 1e-6; + % 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..9ca40ce 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; @@ -15,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; @@ -24,7 +25,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 = 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 @@ -47,7 +49,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'; @@ -117,9 +119,10 @@ 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; + 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; @@ -132,7 +135,7 @@ 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.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; @@ -145,8 +148,10 @@ % 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 = '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 % obj.opts_casadi_nlp.snopt = struct(); diff --git a/src/+mpecopt/Solver.m b/src/+mpecopt/Solver.m index e2dd49e..079390c 100644 --- a/src/+mpecopt/Solver.m +++ b/src/+mpecopt/Solver.m @@ -22,7 +22,6 @@ 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; @@ -49,8 +48,8 @@ lpec_casadi = obj.lpec_casadi; dims = obj.dims; stats = obj.stats; - - + + %% Ininitialization k = 1; x_k = solver_initialization.x0; @@ -75,11 +74,24 @@ 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.gap_phase_i = []; + stats.iter.gap_phase_ii = []; + 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; @@ -103,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 ------------------------ @@ -125,319 +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.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; - % 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_feasibility - % 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.'; - 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); - 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); + x_k = solution_feas.x(1+n_slacks:end); 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)]; + 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 + else + 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 - 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 + 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; @@ -448,107 +548,55 @@ 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 --- - 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; @@ -563,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; @@ -591,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; @@ -604,20 +652,27 @@ % ------------------------------- 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; phase_ii = true; + opts.settings_lpec.is_in_phase_i = false; if opts.verbose_solver 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 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 @@ -653,6 +708,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 @@ -667,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 @@ -746,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 @@ -774,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 @@ -807,8 +864,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? @@ -818,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); @@ -873,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 @@ -886,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}); @@ -910,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; @@ -919,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; @@ -957,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}; @@ -981,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}); @@ -1005,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{:}); @@ -1015,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}; @@ -1027,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) @@ -1072,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 = []; @@ -1103,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]'; @@ -1119,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; @@ -1135,7 +1197,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; @@ -1143,50 +1205,71 @@ 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 - x1 = SX.sym('x1',n_comp); - % 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 = []; + 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 - end - if n_lift_x1 > 0 - x1_lift = SX.sym('x1_lift',n_lift_x1); - % 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 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 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); @@ -1202,7 +1285,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]; @@ -1218,30 +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" - 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 = []; @@ -1260,7 +1359,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 @@ -1295,7 +1394,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; @@ -1304,13 +1403,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; @@ -1329,8 +1431,13 @@ 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]; @@ -1360,7 +1467,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; @@ -1389,7 +1496,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 @@ -1409,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); + 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 ------------------------------------------ @@ -1422,12 +1529,20 @@ 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 - [results_lpec,stats_lpec] = lpec_solver(lpec,opts.settings_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 + 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 n_lpec_k = n_lpec_k + 1; stats.n_lpec_total = stats.n_lpec_total + 1; % extract LPEC results @@ -1435,7 +1550,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)); @@ -1449,17 +1564,28 @@ 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 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 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 - % if abs(f_lin_opt_k_l) <= settings.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 sucessfully.'; + stats.solver_message = 'B-stationary point found successfully.'; stats.success = true; stats.b_stationarity = true; resolve_nlp = false; @@ -1471,7 +1597,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; @@ -1492,11 +1618,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)); @@ -1513,9 +1647,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'}) @@ -1583,7 +1717,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)); @@ -1618,9 +1752,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; @@ -1630,7 +1764,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 @@ -1706,8 +1840,8 @@ 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 - % B-stationary point found, optimal solution found! + 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.'; else @@ -1724,28 +1858,61 @@ 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; + % 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); + 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 + opts.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) @@ -1789,46 +1956,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 ----------------------------------- @@ -1841,36 +2015,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 @@ -1886,31 +2063,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/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 ff7f6c3..90faa82 100644 --- a/src/determine_multipliers_based_stationary_point.m +++ b/src/determine_multipliers_based_stationary_point.m @@ -12,11 +12,32 @@ 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-12); +% max_mult = 1; + +% 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 +46,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 +111,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/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 0ebaba5..57c6d7c 100644 --- a/src/lpec_solver.m +++ b/src/lpec_solver.m @@ -1,42 +1,39 @@ 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 +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 @@ -113,9 +110,15 @@ params.IntFeasTol = 1e-9; params.TimeLimit = settings.max_time; params.OptimalityTol = 1e-9; - if settings.stop_lpec_at_feasible - params.SolutionLimit = 1; + if settings.stop_lpec_at_feasible && settings.is_in_phase_i + % terminate phase i lpecs at a feasible point % 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 + 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 @@ -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'; @@ -160,7 +167,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 +180,23 @@ 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; + 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; + % 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" @@ -237,9 +259,15 @@ 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; + 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, catch model; % keyboard; @@ -249,37 +277,42 @@ % 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; + result_gurobi.mipgap = nan; + 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') + 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; 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; + % keyboard; result_gurobi = []; end switch status @@ -289,18 +322,23 @@ 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 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; 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 @@ -311,34 +349,40 @@ 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 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; - 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; results.f_opt = nan; - stats.lpec_solution_exists = false; end stats.nodecount = lpsol.stats.n_call_solver; + 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'} % reg @@ -368,6 +412,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); @@ -377,7 +423,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); @@ -429,6 +475,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/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 50005af..86c856f 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]; @@ -52,121 +56,149 @@ ind_x1 = []; ind_x2 = []; -% Lift complemetraties G -if settings.lift_complementarities_full - % full lifting with duplicatse; - x1 = SX.sym('x1',n_comp); - 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 - x1_lift = SX.sym('x1_lift',n_lift_x1); - 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 +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 - 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 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]; -% Lift complemetraties H -if settings.lift_complementarities_full - % full lifting with duplicatse; - x2 = SX.sym('x2',n_comp); - 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 = []; + 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 - end - if n_lift_x2 > 0 - x2_lift = SX.sym('x2_lift',n_lift_x2); - 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; + 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 -else - x2 = H; % (an expression, a nontivial function of x) - if settings.update_comp_lower_bounds - g = [g;H]; + + % Lift complemetraties H + if settings.lift_complementarities_full + % full lifting with duplicatse; + 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]; + 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 @@ -290,9 +322,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 +392,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]; @@ -513,10 +553,14 @@ x_k_before_tnlp = x_k; %% % ----------------------------------- MULTIPLIER BASED STAT ------------------------------------- -N_TNLP = 10; % 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 = max(2*settings.tol_active*1e3,2*tol_ell_inf); +tol_active_default = settings.tol_active; +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 fprintf('----------------------------------- determining stationarity -----------------------------------------------\n') @@ -525,48 +569,66 @@ 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; 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)) - 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)) + 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); + 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 tol_active = 0.1*tol_active; ii = ii+1; end + n_biactive_prev = n_biactive_current; 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 %% Check B stationarity % ----------------------------------- B STAT ------------------------------------- -f_lpec = 1; +f_lpec = 1e3; rho_TR = 1e-3; if ~settings.problem_is_lpec && success @@ -616,7 +678,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); @@ -717,7 +779,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 new file mode 100644 index 0000000..0b92575 --- /dev/null +++ b/src/mpec_minlp_solver.m @@ -0,0 +1,730 @@ +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 = []; +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 = 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 +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; +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]; + +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 && ~isequal(stats.return_status,'SUCCESS') + 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 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 + + + +%% 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; + +% 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; +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/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 fbaff66..2264aa3 100644 --- a/src/options/HomotopySolverOptions.m +++ b/src/options/HomotopySolverOptions.m @@ -5,11 +5,13 @@ 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; 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; @@ -63,6 +65,11 @@ 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.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/LPECSolverOptions.m b/src/options/LPECSolverOptions.m index 7f28697..7f777a0 100644 --- a/src/options/LPECSolverOptions.m +++ b/src/options/LPECSolverOptions.m @@ -5,15 +5,17 @@ solver_name {mustBeTextScalar} = 'lpec_solver'; % MILP Solver for LPEC settings - lpec_solver(1,1) LpecSolver = LpecSolver.Highs; % LpecSolver.Gurobi - max_nodes(1,1) double {mustBeInteger, mustBePositive} = 5e2; + lpec_solver(1,1) LpecSolver = LpecSolver.Gurobi; % LpecSolver.Gurobi + 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; 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 % settings for custom nlp based lpec methods (Scholtes, Ell1, Ell_inf) @@ -23,6 +25,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 new file mode 100644 index 0000000..72d9e13 --- /dev/null +++ b/src/options/MINLPSolverOptions.m @@ -0,0 +1,96 @@ +% 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; + 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; + 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() + 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; + 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 + + 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(); + % 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);