From 4548a4e7a2f07199ec7c39f87a8c92da68752171 Mon Sep 17 00:00:00 2001 From: Anton Edvinovich Pozharskiy Date: Thu, 27 Feb 2025 10:40:58 +0100 Subject: [PATCH 1/8] initial github action port from nosnoc --- .github/workflows/test_build.yml | 102 +++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 .github/workflows/test_build.yml diff --git a/.github/workflows/test_build.yml b/.github/workflows/test_build.yml new file mode 100644 index 0000000..96e9242 --- /dev/null +++ b/.github/workflows/test_build.yml @@ -0,0 +1,102 @@ +name: NOSNOC Test suite + +on: + pull_request: + +jobs: + # Main job that runs all the tests for NOSNOC in matlab. + test_nosnoc: + strategy: + fail-fast: false # We always want to see where the failures are. + matrix: + matlab_version: ["R2021b", "R2022a", "R2022b", "R2023a", "R2023b", "R2024a"] + casadi: + - version: "3.5.5" + file: "casadi-linux-matlabR2014b-v3.5.5.tar.gz" + extract: "tar -zxvf" + - version: "3.6.7" + file: "casadi-3.6.7-linux64-matlab2018b.zip" + extract: "unzip" + runs-on: ubuntu-22.04 + + steps: + # Checkout repo + - name: Checkout NOSNOC + uses: actions/checkout@v4 + with: + submodules: 'recursive' + # Install matlab to run tests. + - name: Install MATLAB + uses: matlab-actions/setup-matlab@v2.3 + with: + # NOTE: no longer earliest version to support more property validation functions + release: ${{ matrix.matlab_version }} + products: > + Parallel_Computing_Toolbox + cache: true + # Download relevant matlab release artifacts for the casadi release we are using. + # TODO: Definitely cache this. + - name: Download CasADi release + uses: robinraju/release-downloader@v1.11 + with: + repository: "casadi/casadi" + tag: ${{ matrix.casadi.version }} + fileName: ${{ matrix.casadi.file }} + + # untar the casadi 3.5.5 release and then add it to the matlab path. + - name: Install CasADi + shell: bash + run: | + mkdir casadi + cd casadi + ${{ matrix.casadi.extract}} $GITHUB_WORKSPACE/${{ matrix.casadi.file }} + echo "MATLABPATH=$MATLABPATH:$GITHUB_WORKSPACE/casadi" >> $GITHUB_ENV + + # Run matlab tests from the test directory and dump the results in junit format. + - name: Install mpecopt + uses: matlab-actions/run-command@v2.1 + with: + command: addpath(genpath('src'));savepath; + + # Run matlab tests from the test directory and dump the results in junit format. + - name: Run mpecopt tests + uses: matlab-actions/run-tests@v2.1 + with: + source-folder: src + select-by-folder: test + test-results-junit: "test-results/matlab-results-${{ matrix.matlab_version }}-${{ matrix.casadi.version }}.xml" + code-coverage-cobertura: "test-results/coverage-${{ matrix.matlab_version }}-${{ matrix.casadi.version }}.xml" + strict: true # Non-explicitly suppressed warnings should fail. + use-parallel: true + + # Run ReportGenerator to generage human readable coverage (only for latest versions of matlab and casadi for now) + - name: Setup .NET Core # Required to execute ReportGenerator + if: ${{ matrix.matlab_version == 'R2024a' && matrix.casadi.version == '3.6.7' }} + uses: actions/setup-dotnet@v4 + with: + dotnet-version: 8.x + dotnet-quality: 'ga' + - name: ReportGenerator + if: ${{ matrix.matlab_version == 'R2024a' && matrix.casadi.version == '3.6.7' }} + uses: danielpalme/ReportGenerator-GitHub-Action@5.3.11 + with: + reports: "test-results/coverage-${{ matrix.matlab_version }}-${{ matrix.casadi.version }}.xml" + targetdir: 'test-results' + reporttypes: 'HtmlInline;MarkdownSummaryGithub' + verbosity: 'Info' # The verbosity level of the log messages. Values: Verbose, Info, Warning, Error, Off + title: 'nosnoc coverage' + - name: Comment PR with coverage + if: ${{ matrix.matlab_version == 'R2024a' && matrix.casadi.version == '3.6.7' }} + uses: thollander/actions-comment-pull-request@v3 + with: + github-token: ${{ secrets.GH_TOKEN }} + file-path: "test-results/SummaryGithub.md" + comment-tag: coverage + + # Upload raw coverage and test results. + - name: Upload test results + if: always() + uses: actions/upload-artifact@v4 + with: + name: "test-results-${{ matrix.matlab_version }}-${{ matrix.casadi.version }}" + path: test-results/ \ No newline at end of file From 8b446f073c4a356fb5c496a7d44cb3eb19c302ef Mon Sep 17 00:00:00 2001 From: Anton Edvinovich Pozharskiy Date: Thu, 27 Feb 2025 11:06:41 +0100 Subject: [PATCH 2/8] phase one testing --- test/TestPhaseOneOpts.m | 47 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 test/TestPhaseOneOpts.m diff --git a/test/TestPhaseOneOpts.m b/test/TestPhaseOneOpts.m new file mode 100644 index 0000000..e98d90e --- /dev/null +++ b/test/TestPhaseOneOpts.m @@ -0,0 +1,47 @@ +classdef TestPhaseOneOpts < matlab.unittest.TestCase + properties (TestParameter) + % Only test the + initialization_strategy = {'RelaxAndProject', 'FeasibilityEll1General', 'FeasibilityEllInfGeneral', 'AllBiactive', 'TakeInitialGuessActiveSet'}; + end + + methods (Test, ParameterCombination = 'exhaustive') + function test_pds_integrator(tc,initialization_strategy) + x1 = SX.sym('x1'); + x2 = SX.sym('x2'); + x3 = SX.sym('x3'); + x4 = SX.sym('x4'); + x5 = SX.sym('x5'); + x6 = SX.sym('x6'); + x7 = SX.sym('x7'); + x8 = SX.sym('x8'); + + w = [x1;x2;x3;x4;x5;x6;x7;x8]; + + p = SX.sym('p'); + f = (x1-5)^2+(2*x2+1)^2; + g = [2*(x2-1)-1.5*x1+x3-0.5*x4+x5 + 3*x1-x2-3-x6;... + -x1+0.5*x2+4-x7;... + -x1-x2+7-x8]; + G = [x6;x7;x8]; + H = [x3;x4;x5]; + x0 = zeros(8,1); + lbw = zeros(8,1); + ubw = inf*ones(8,1); + + lbg = zeros(4,1); + ubg = zeros(4,1); + + mpec = struct('x', w, 'f', f, 'g', g,'p',p,'G',G ,'H',H); + solver_initalization = struct('x0', x0, 'lbx',lbw, 'ubx',ubw,'lbg',lbg, 'ubg',ubg,'p0',1); + + opts = HomotopySolverOptions(); + opts.initialization_strategy = initialization_strategy; + + solver = Mpecopt(mpec, solver_settings); + [sol_active_set,stats_active_set] = solver.solve(solver_initalization); + + tc.verify() + end + end +end From d1b21feee670a6755f54eae27dba7ce8cf16899c Mon Sep 17 00:00:00 2001 From: Anton Edvinovich Pozharskiy Date: Thu, 27 Feb 2025 11:22:14 +0100 Subject: [PATCH 3/8] phase one smoketest --- src/Mpecopt.m | 4 +++- test/TestPhaseOneOpts.m | 17 +++++++++-------- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/Mpecopt.m b/src/Mpecopt.m index d9ba40c..9271a9c 100644 --- a/src/Mpecopt.m +++ b/src/Mpecopt.m @@ -117,7 +117,9 @@ 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)); - print_iter_stats('I',0,f_opt_ii,h_std_ii,h_comp_ii,'/',0,'Initial guess',0,0,0,1) + if opts.verbose_solver + print_iter_stats('I',0,f_opt_ii,h_std_ii,h_comp_ii,'/',0,'Initial guess',0,0,0,1) + end t_phase_i_start = tic; switch opts.initialization_strategy diff --git a/test/TestPhaseOneOpts.m b/test/TestPhaseOneOpts.m index e98d90e..2245668 100644 --- a/test/TestPhaseOneOpts.m +++ b/test/TestPhaseOneOpts.m @@ -1,11 +1,12 @@ classdef TestPhaseOneOpts < matlab.unittest.TestCase properties (TestParameter) % Only test the - initialization_strategy = {'RelaxAndProject', 'FeasibilityEll1General', 'FeasibilityEllInfGeneral', 'AllBiactive', 'TakeInitialGuessActiveSet'}; + initialization_strategy = {'RelaxAndProject', 'FeasibilityEll1General', 'FeasibilityEllInfGeneral', 'TakeInitialGuessActiveSet', 'TakeProvidedActiveSet'}; end methods (Test, ParameterCombination = 'exhaustive') function test_pds_integrator(tc,initialization_strategy) + import casadi.* x1 = SX.sym('x1'); x2 = SX.sym('x2'); x3 = SX.sym('x3'); @@ -25,7 +26,7 @@ function test_pds_integrator(tc,initialization_strategy) -x1-x2+7-x8]; G = [x6;x7;x8]; H = [x3;x4;x5]; - x0 = zeros(8,1); + x0 = [0;0;1;0;0;0;1;1]; lbw = zeros(8,1); ubw = inf*ones(8,1); @@ -33,15 +34,15 @@ function test_pds_integrator(tc,initialization_strategy) ubg = zeros(4,1); mpec = struct('x', w, 'f', f, 'g', g,'p',p,'G',G ,'H',H); - solver_initalization = struct('x0', x0, 'lbx',lbw, 'ubx',ubw,'lbg',lbg, 'ubg',ubg,'p0',1); + solver_initalization = struct('x0', x0, 'lbx',lbw, 'ubx',ubw,'lbg',lbg, 'ubg',ubg,'p0',1, 'y0', [0;1;1]); - opts = HomotopySolverOptions(); + opts = MPECOptimizerOptions(); opts.initialization_strategy = initialization_strategy; + opts.verbose_solver = false; + opts.verbose_summary = false; - solver = Mpecopt(mpec, solver_settings); - [sol_active_set,stats_active_set] = solver.solve(solver_initalization); - - tc.verify() + solver = Mpecopt(mpec, opts); + [sol,stats] = solver.solve(solver_initalization); end end end From b0892c79d7d62ea312da076a424a126e4a599d93 Mon Sep 17 00:00:00 2001 From: Anton Edvinovich Pozharskiy Date: Thu, 27 Feb 2025 11:34:12 +0100 Subject: [PATCH 4/8] add optimization toolbox for optimoptions --- .github/workflows/test_build.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test_build.yml b/.github/workflows/test_build.yml index 96e9242..8e4c3ea 100644 --- a/.github/workflows/test_build.yml +++ b/.github/workflows/test_build.yml @@ -1,11 +1,11 @@ -name: NOSNOC Test suite +name: mpecopt Test suite on: pull_request: jobs: # Main job that runs all the tests for NOSNOC in matlab. - test_nosnoc: + test_mpecopt: strategy: fail-fast: false # We always want to see where the failures are. matrix: @@ -33,6 +33,7 @@ jobs: release: ${{ matrix.matlab_version }} products: > Parallel_Computing_Toolbox + Optimization_Toolbox cache: true # Download relevant matlab release artifacts for the casadi release we are using. # TODO: Definitely cache this. From b0608ef1111c7f5b200c284564582518dbb3966e Mon Sep 17 00:00:00 2001 From: Anton Edvinovich Pozharskiy Date: Thu, 27 Feb 2025 11:42:47 +0100 Subject: [PATCH 5/8] suppress infeasible x0 --- test/TestPhaseOneOpts.m | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/test/TestPhaseOneOpts.m b/test/TestPhaseOneOpts.m index 2245668..16b8803 100644 --- a/test/TestPhaseOneOpts.m +++ b/test/TestPhaseOneOpts.m @@ -7,6 +7,10 @@ methods (Test, ParameterCombination = 'exhaustive') function test_pds_integrator(tc,initialization_strategy) import casadi.* + import matlab.unittest.fixtures.SuppressedWarningsFixture + tc.applyFixture( ... + SuppressedWarningsFixture("optim:intlinprog:IgnoreX0")) + x1 = SX.sym('x1'); x2 = SX.sym('x2'); x3 = SX.sym('x3'); @@ -38,8 +42,8 @@ function test_pds_integrator(tc,initialization_strategy) opts = MPECOptimizerOptions(); opts.initialization_strategy = initialization_strategy; - opts.verbose_solver = false; - opts.verbose_summary = false; + %opts.verbose_solver = false; + %opts.verbose_summary = false; solver = Mpecopt(mpec, opts); [sol,stats] = solver.solve(solver_initalization); From f89ed392a32873538b0c0b5f1a5fd712c019e250 Mon Sep 17 00:00:00 2001 From: Anton Edvinovich Pozharskiy Date: Thu, 27 Feb 2025 11:58:58 +0100 Subject: [PATCH 6/8] phase two opts and fix bug for TNLP --- src/mpecopt_phase_ii.m | 2 +- test/TestPhaseOneOpts.m | 2 +- test/TestPhaseTwoOpts.m | 52 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 54 insertions(+), 2 deletions(-) create mode 100644 test/TestPhaseTwoOpts.m diff --git a/src/mpecopt_phase_ii.m b/src/mpecopt_phase_ii.m index 7855055..2323dc3 100644 --- a/src/mpecopt_phase_ii.m +++ b/src/mpecopt_phase_ii.m @@ -379,8 +379,8 @@ 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,settings); end - [stats.multiplier_based_stationarity, ~] = determine_multipliers_based_stationary_point(x_k_multi,lambda_x_k,dims,settings); end % Debug falure of stationary point computation diff --git a/test/TestPhaseOneOpts.m b/test/TestPhaseOneOpts.m index 16b8803..7324584 100644 --- a/test/TestPhaseOneOpts.m +++ b/test/TestPhaseOneOpts.m @@ -5,7 +5,7 @@ end methods (Test, ParameterCombination = 'exhaustive') - function test_pds_integrator(tc,initialization_strategy) + function test_initialization_strategy(tc,initialization_strategy) import casadi.* import matlab.unittest.fixtures.SuppressedWarningsFixture tc.applyFixture( ... diff --git a/test/TestPhaseTwoOpts.m b/test/TestPhaseTwoOpts.m new file mode 100644 index 0000000..60239aa --- /dev/null +++ b/test/TestPhaseTwoOpts.m @@ -0,0 +1,52 @@ +classdef TestPhaseTwoOpts < matlab.unittest.TestCase + properties (TestParameter) + % Only test the + piece_nlp_strategy = {'TNLP', 'BNLP_integer', 'BNLP_Gradient1', 'BNLP_Gradient2'}; + end + + methods (Test, ParameterCombination = 'exhaustive') + function test_initialization_strategy(tc,piece_nlp_strategy) + import casadi.* + import matlab.unittest.fixtures.SuppressedWarningsFixture + tc.applyFixture( ... + SuppressedWarningsFixture("optim:intlinprog:IgnoreX0")) + + x1 = SX.sym('x1'); + x2 = SX.sym('x2'); + x3 = SX.sym('x3'); + x4 = SX.sym('x4'); + x5 = SX.sym('x5'); + x6 = SX.sym('x6'); + x7 = SX.sym('x7'); + x8 = SX.sym('x8'); + + w = [x1;x2;x3;x4;x5;x6;x7;x8]; + + p = SX.sym('p'); + f = (x1-5)^2+(2*x2+1)^2; + g = [2*(x2-1)-1.5*x1+x3-0.5*x4+x5 + 3*x1-x2-3-x6;... + -x1+0.5*x2+4-x7;... + -x1-x2+7-x8]; + G = [x6;x7;x8]; + H = [x3;x4;x5]; + x0 = [0;0;1;0;0;0;1;1]; + lbw = zeros(8,1); + ubw = inf*ones(8,1); + + lbg = zeros(4,1); + ubg = zeros(4,1); + + mpec = struct('x', w, 'f', f, 'g', g,'p',p,'G',G ,'H',H); + solver_initalization = struct('x0', x0, 'lbx',lbw, 'ubx',ubw,'lbg',lbg, 'ubg',ubg,'p0',1, 'y0', [0;1;1]); + + opts = MPECOptimizerOptions(); + opts.piece_nlp_strategy = piece_nlp_strategy; + %opts.verbose_solver = false; + %opts.verbose_summary = false; + + solver = Mpecopt(mpec, opts); + [sol,stats] = solver.solve(solver_initalization); + end + end +end From 99c4dca5e03427734e965e5b189e36716660a4ed Mon Sep 17 00:00:00 2001 From: "armin.nurkanovic" Date: Thu, 27 Feb 2025 14:06:46 +0100 Subject: [PATCH 7/8] fix stationarity check for tnlp --- src/mpecopt_phase_ii.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mpecopt_phase_ii.m b/src/mpecopt_phase_ii.m index 2323dc3..251c294 100644 --- a/src/mpecopt_phase_ii.m +++ b/src/mpecopt_phase_ii.m @@ -361,7 +361,7 @@ % --------------- compute multiplier-based stationary points -------------- multiplier_based_stationarity_debug = stats.multiplier_based_stationarity; if (stats.success || k==settings.max_iter) && settings.compute_tnlp_stationary_point && phase_ii - if ~strcmp(settings.piece_nlp_strategy,'TNLP') + % if ~strcmp(settings.piece_nlp_strategy,'TNLP') % resolve TNLP for correct multipliers lbx_bnlp_k = lbx; ubx_bnlp_k = ubx; % reset bounds of bnlp. lbg_tnlp_k = lbg; ubg_tnlp_k = ubg; @@ -374,13 +374,13 @@ 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; settings.piece_nlp_strategy = initial_strategy; + % end t_nlp_start = tic; results_nlp = 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,settings); - end end % Debug falure of stationary point computation From 92af49ff8cf3d91947ad32fab128808a70b78cf1 Mon Sep 17 00:00:00 2001 From: Anton Edvinovich Pozharskiy Date: Thu, 27 Feb 2025 15:13:41 +0100 Subject: [PATCH 8/8] nosnoc->mpecopt --- .github/workflows/test_build.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test_build.yml b/.github/workflows/test_build.yml index 8e4c3ea..0d3b2c7 100644 --- a/.github/workflows/test_build.yml +++ b/.github/workflows/test_build.yml @@ -4,7 +4,7 @@ on: pull_request: jobs: - # Main job that runs all the tests for NOSNOC in matlab. + # Main job that runs all the tests for mpecopt in matlab. test_mpecopt: strategy: fail-fast: false # We always want to see where the failures are. @@ -21,7 +21,7 @@ jobs: steps: # Checkout repo - - name: Checkout NOSNOC + - name: Checkout mpecopt uses: actions/checkout@v4 with: submodules: 'recursive' @@ -85,7 +85,7 @@ jobs: targetdir: 'test-results' reporttypes: 'HtmlInline;MarkdownSummaryGithub' verbosity: 'Info' # The verbosity level of the log messages. Values: Verbose, Info, Warning, Error, Off - title: 'nosnoc coverage' + title: 'mpecopt coverage' - name: Comment PR with coverage if: ${{ matrix.matlab_version == 'R2024a' && matrix.casadi.version == '3.6.7' }} uses: thollander/actions-comment-pull-request@v3