Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 103 additions & 0 deletions .github/workflows/test_build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
name: mpecopt Test suite

on:
pull_request:

jobs:
# 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.
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 mpecopt
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
Optimization_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: '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
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/
4 changes: 3 additions & 1 deletion src/Mpecopt.m
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 3 additions & 3 deletions src/mpecopt_phase_ii.m
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);
end
[stats.multiplier_based_stationarity, ~] = determine_multipliers_based_stationary_point(x_k_multi,lambda_x_k,dims,settings);
[stats.multiplier_based_stationarity, ~] = determine_multipliers_based_stationary_point(x_k_multi,lambda_x_k,dims,settings);
end

% Debug falure of stationary point computation
Expand Down
52 changes: 52 additions & 0 deletions test/TestPhaseOneOpts.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
classdef TestPhaseOneOpts < matlab.unittest.TestCase
properties (TestParameter)
% Only test the
initialization_strategy = {'RelaxAndProject', 'FeasibilityEll1General', 'FeasibilityEllInfGeneral', 'TakeInitialGuessActiveSet', 'TakeProvidedActiveSet'};
end

methods (Test, ParameterCombination = 'exhaustive')
function test_initialization_strategy(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');
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.initialization_strategy = initialization_strategy;
%opts.verbose_solver = false;
%opts.verbose_summary = false;

solver = Mpecopt(mpec, opts);
[sol,stats] = solver.solve(solver_initalization);
end
end
end
52 changes: 52 additions & 0 deletions test/TestPhaseTwoOpts.m
Original file line number Diff line number Diff line change
@@ -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