Skip to content

Commit

Permalink
git subrepo pull mp-opt-model
Browse files Browse the repository at this point in the history
subrepo:
  subdir:   "mp-opt-model"
  merged:   "1dd6e8e8"
upstream:
  origin:   "[email protected]:MATPOWER/mp-opt-model"
  branch:   "master"
  commit:   "1dd6e8e8"
git-subrepo:
  version:  "0.4.9"
  origin:   "https://github.com/ingydotnet/git-subrepo"
  commit:   "b00d41b"
  • Loading branch information
rdzman committed Feb 12, 2025
1 parent e3c4baf commit b1890e9
Show file tree
Hide file tree
Showing 10 changed files with 99 additions and 49 deletions.
4 changes: 2 additions & 2 deletions mp-opt-model/.gitrepo
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
[subrepo]
remote = [email protected]:MATPOWER/mp-opt-model
branch = master
commit = 9c9ff65abf88021e1f8fabeacad617b609433b32
parent = 2f766a4ab4ff55a563b811be068f2f397f0f0862
commit = 1dd6e8e8aa9d96e216c1de6b9f827b04b309b6b6
parent = e3c4baf74fa20f2d65ee69faaff849afc9689295
method = merge
cmdver = 0.4.9
4 changes: 4 additions & 0 deletions mp-opt-model/CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ Change history for MP-Opt-Model
since 4.2
---------

#### 10/29/24
- Add `relax_integer` option for `opt_model.solve()`. Set to true to easily
solve the LP or QP relaxation of a mixed integer LP or QP.

#### 8/17/24
- Add `mp.set_manager_opt_model` base class to handle common `opt_model`
functionality, such as handling solutions, for the individual field
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2444,6 +2444,7 @@ \subsection{Solving the Model}
\end{tabular} \\
\code{~~parse\_soln} & 0 & flag that specifies whether or not to call the \code{parse\_soln} method and place the return values in the \code{soln}
property of the field type objects \\
\code{~~relax\_integer} & 0 & relax integer constraints, if true \\
\code{~~x0} & \emph{empty} & optional initial value of $x$, overrides value stored in model, \emph{(ignored by some solvers)} \\
\midrule
\multicolumn{3}{l}{\emph{Additional Options for Specific Problem Types}} \\
Expand Down Expand Up @@ -4669,7 +4670,7 @@ \subsection{Version 4.3 -- released ??? ?, 202?}

\subsubsection*{New Features}
\begin{itemize}
\item
\item New \code{relax\_integer} option for \code{opt\_model.solve()}. Set to true to easily solve the LP or QP relaxation of a mixed integer LP or QP.
\item New classes:
\begin{itemize}
\item \code{mp.set\_manager} encapsulates \code{mp\_idx\_manager} functionality into an individual field object representing a specific set type, rather than in the container class.
Expand Down
14 changes: 6 additions & 8 deletions mp-opt-model/lib/+mp/set_manager_opt_model.m
Original file line number Diff line number Diff line change
Expand Up @@ -316,15 +316,13 @@
end
nargs = length(args);

if nargs < 2
idx = [];
if nargs < 1
name = [];
else
name = args{1};
idx = [];
name = [];
if nargs >= 1
name = args{1};
if nargs >= 2
idx = args{2};
end
else
idx = args{2};
end

if obj.N
Expand Down
15 changes: 9 additions & 6 deletions mp-opt-model/lib/@opt_model/display_soln.m
Original file line number Diff line number Diff line change
Expand Up @@ -60,12 +60,15 @@
end
nargs = length(args);

if nargs < 3
idx = [];
if nargs < 2
name = [];
if nargs < 1
set_type = 'all';
set_type = 'all';
name = [];
idx = [];
if nargs >= 1
set_type = args{1};
if nargs >= 2
name = args{2};
if nargs >= 3
idx = args{3};
end
end
end
Expand Down
41 changes: 24 additions & 17 deletions mp-opt-model/lib/@opt_model/solve.m
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@
% value and primal variable relative match required to avoid
% mis-match warning message if mixed integer price computation
% stage is not skipped
% relax_integer (0) - relax integer constraints, if true
% skip_prices (0) - flag that specifies whether or not to skip the
% price computation stage for mixed integer problems, in which
% the problem is re-solved for only the continuous variables,
Expand Down Expand Up @@ -135,8 +136,6 @@
%% call appropriate solver
pt = om.problem_type();
switch pt
case 'MINLP' %% MINLP - mixed integer non-linear program
error('opt_model.solve: not yet implemented for ''MINLP'' problems.')
case 'LEQ' %% LEQ - linear equations
if isfield(opt, 'leq_opt')
if isfield(opt.leq_opt, 'solver')
Expand Down Expand Up @@ -202,26 +201,34 @@
case 'PNE' %% PNE - parameterized nonlinear equation
[x, f, eflag, output, lambda] = pnes_master(fcn, x0, opt);
end
case 'NLP' %% NLP - nonlinear program
%% optimization vars, bounds, types
[x0, xmin, xmax] = om.params_var();
if isfield(opt, 'x0')
x0 = opt.x0;
case {'MINLP', 'NLP'}
mixed_integer = strcmp(pt(1:2), 'MI') && ...
(~isfield(opt, 'relax_integer') || ~opt.relax_integer);
if mixed_integer %% MINLP - mixed integer non-linear program
error('opt_model.solve: not yet implemented for ''MINLP'' problems.')
else %% NLP - nonlinear program
%% optimization vars, bounds, types
[x0, xmin, xmax] = om.params_var();
if isfield(opt, 'x0')
x0 = opt.x0;
end

%% run solver
[A, l, u] = om.params_lin_constraint();
f_fcn = @(x)nlp_costfcn(om, x);
gh_fcn = @(x)nlp_consfcn(om, x);
hess_fcn = @(x, lambda, cost_mult)nlp_hessfcn(om, x, lambda, cost_mult);
[x, f, eflag, output, lambda] = ...
nlps_master(f_fcn, x0, A, l, u, xmin, xmax, gh_fcn, hess_fcn, opt);
end

%% run solver
[A, l, u] = om.params_lin_constraint();
f_fcn = @(x)nlp_costfcn(om, x);
gh_fcn = @(x)nlp_consfcn(om, x);
hess_fcn = @(x, lambda, cost_mult)nlp_hessfcn(om, x, lambda, cost_mult);
[x, f, eflag, output, lambda] = ...
nlps_master(f_fcn, x0, A, l, u, xmin, xmax, gh_fcn, hess_fcn, opt);
otherwise
%% get parameters
[HH, CC, C0] = om.params_quad_cost();
[A, l, u] = om.params_lin_constraint();
mixed_integer = strcmp(pt(1:2), 'MI') && ...
(~isfield(opt, 'relax_integer') || ~opt.relax_integer);

if strcmp(pt(1:2), 'MI') %% MILP, MIQP - mixed integer linear/quadratic program
if mixed_integer %% MILP, MIQP - mixed integer linear/quadratic program
%% optimization vars, bounds, types
[x0, xmin, xmax, vtype] = om.params_var();
if isfield(opt, 'x0')
Expand All @@ -231,7 +238,7 @@
%% run solver
[x, f, eflag, output, lambda] = ...
miqps_master(HH, CC, A, l, u, xmin, xmax, x0, vtype, opt);
else %% LP, QP - linear/quadratic program
else %% LP, QP - linear/quadratic program
%% optimization vars, bounds, types
[x0, xmin, xmax] = om.params_var();
if isfield(opt, 'x0')
Expand Down
18 changes: 11 additions & 7 deletions mp-opt-model/lib/miqps_master.m
Original file line number Diff line number Diff line change
Expand Up @@ -192,14 +192,18 @@
alg = 'CPLEX';
elseif have_feature('mosek') %% if not, then MOSEK, if available
alg = 'MOSEK';
elseif isempty(H) || ~any(any(H)) %% if not, and linear objective
if have_feature('intlinprog') %% then Optimization Tbx, if available
alg = 'OT';
elseif have_feature('glpk') %% if not, and then GLPK, if available
alg = 'GLPK';
end
else
error('miqps_master: no solvers available - requires CPLEX, Gurobi, MOSEK, INTLINPROG or GLPK');
if isempty(H) || ~any(any(H)) %% if not, and linear objective
if have_feature('intlinprog') %% then Optimization Tbx, if available
alg = 'OT';
elseif have_feature('glpk') %% if not, and then GLPK, if available
alg = 'GLPK';
else
error('miqps_master: no solvers available - requires CPLEX, Gurobi, MOSEK, INTLINPROG or GLPK');
end
else %% if not, and quadratic objective
error('miqps_master: no solvers available - requires CPLEX, Gurobi, or MOSEK');
end
end
end

Expand Down
2 changes: 1 addition & 1 deletion mp-opt-model/lib/mpomver.m
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
v = struct( 'Name', 'MP-Opt-Model', ...
'Version', '4.3-dev', ...
'Release', '', ...
'Date', '30-Aug-2024' );
'Date', '29-Oct-2024' );
if nargout > 0
if nargin > 0
rv = v;
Expand Down
7 changes: 5 additions & 2 deletions mp-opt-model/lib/t/t_miqps_master.m
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ function t_miqps_master(quiet)

algs = {'DEFAULT', 'CPLEX', 'MOSEK', 'GUROBI', 'GLPK', 'OT'};
names = {'DEFAULT', 'CPLEX', 'MOSEK', 'Gurobi', 'glpk', 'intlin/lin/quadprog'};
check = {@have_miqp_solver, 'cplex', 'mosek', 'gurobi', 'glpk', 'intlinprog'};
check = {@have_milp_solver, 'cplex', 'mosek', 'gurobi', 'glpk', 'intlinprog'};
does_qp = [0 1 1 1 0 0];
if have_feature('gurobi') || have_feature('cplex') || have_feature('mosek')
does_qp(1) = 1;
Expand Down Expand Up @@ -213,6 +213,9 @@ function t_miqps_master(quiet)
if does_qp(k)
t = sprintf('%s - 4-d MIQP : ', names{k});
%% from cplexmiqpex.m, CPLEX_Studio_Academic124/cplex/examples/src/matlab/cplexmiqpex.m
%% Note: This is a lame example; the integer relaxed problem already
%% has an integer feasible solution, so this is actually just
%% a simple QP. -RDZ 10/29/24
H = sparse([ 33 6 0 0;
6 22 11.5 0;
0 11.5 11 0;
Expand Down Expand Up @@ -271,7 +274,7 @@ function t_miqps_master(quiet)

t_end;

function TorF = have_miqp_solver()
function TorF = have_milp_solver()
TorF = have_feature('cplex') || have_feature('glpk') || ...
have_feature('gurobi') || have_feature('intlinprog') || ...
have_feature('mosek');
Expand Down
40 changes: 35 additions & 5 deletions mp-opt-model/lib/t/t_om_solve_miqps.m
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@ function t_om_solve_miqps(quiet)

algs = {'DEFAULT', 'CPLEX', 'MOSEK', 'GUROBI', 'GLPK', 'OT'};
names = {'DEFAULT', 'CPLEX', 'MOSEK', 'Gurobi', 'glpk', 'intlin/lin/quadprog'};
check = {@have_miqp_solver, 'cplex', 'mosek', 'gurobi', 'glpk', 'intlinprog'};
check = {@have_milp_solver, 'cplex', 'mosek', 'gurobi', 'glpk', 'intlinprog'};
does_qp = [0 1 1 1 0 0];
if have_feature('gurobi') || have_feature('cplex') || have_feature('mosek')
does_qp(1) = 1;
end

n = 17;
nmiqp = 10;
n = 33;
nmiqp = 20;
nmiqp_soln = 30;
diff_tool = 'bbdiff';
show_diff_on_fail = false;
Expand Down Expand Up @@ -87,6 +87,8 @@ function t_om_solve_miqps(quiet)
end
opt.mosek_opt = mosek_options([], mpopt);
end
opt_r = opt;
opt_r.relax_integer = 1;

% opt.verbose = 3;
t = sprintf('%s - 2-d ILP : ', names{k});
Expand All @@ -105,6 +107,11 @@ function t_om_solve_miqps(quiet)
t_is(x, [4; 2], 12, [t 'x']);
t_is(f, -14, 12, [t 'f']);
t_ok(~om.has_parsed_soln(), [t 'has_parsed_soln() is false']);
t = sprintf('%s - 2-d ILP (integer relaxed) : ', names{k});
[x, f, s, out, lam] = om.solve(opt_r);
t_is(s, 1, 12, [t 'success']);
t_is(x, [2.441860465; 3.255813953], 8, [t 'x']);
t_is(f, -14.651162791, 8, [t 'f']);

t = sprintf('%s - 6-d ILP : ', names{k});
%% from https://doi.org/10.1109/TASE.2020.2998048
Expand All @@ -125,10 +132,19 @@ function t_om_solve_miqps(quiet)
norm(x - [0; 0; 3; 1; 0; 2], Inf) < 1e-12 || ...
norm(x - [0; 0; 3; 0; 2; 1], Inf) < 1e-12, [t 'x']);
t_is(f, 16, 12, [t 'f']);
t = sprintf('%s - 6-d ILP (integer relaxed) : ', names{k});
[x, f, s, out, lam] = om.solve(opt_r);
t_is(s, 1, 12, [t 'success']);
t_ok(norm(x - [0; 0; 2.7; 0; 0; 2.5], Inf) < 1e-12 || ...
norm(x - [0; 0; 3.0; 0; 0; 2.2], Inf) < 1e-12, [t 'x']);
t_is(f, 15.6, 12, [t 'f']);

if does_qp(k)
t = sprintf('%s - 4-d MIQP : ', names{k});
%% from cplexmiqpex.m, CPLEX_Studio_Academic124/cplex/examples/src/matlab/cplexmiqpex.m
%% Note: This is a lame example; the integer relaxed problem already
%% has an integer feasible solution, so this is actually just
%% a simple QP. -RDZ 10/29/24
H = sparse([ 33 6 0 0;
6 22 11.5 0;
0 11.5 11 0;
Expand Down Expand Up @@ -157,6 +173,15 @@ function t_om_solve_miqps(quiet)
t_is(lam.mu_u, [0; 272; 0], 6, [t 'lam.mu_u']);
t_is(lam.lower, [0; 0; 349.5; 4350], 5, [t 'lam.lower']);
t_is(lam.upper, [0; 0; 0; 0], 7, [t 'lam.upper']);
t = sprintf('%s - 4-d MIQP (integer relaxed) : ', names{k});
[x, f, s, out, lam] = om.solve(opt_r);
t_is(s, 1, 12, [t 'success']);
t_is(x, [7; 7; 0; 2], 7, [t 'x']);
t_is(f, 1618.5, 4, [t 'f']);
t_is(lam.mu_l, [466; 0; 0], 6, [t 'lam.mu_l']);
t_is(lam.mu_u, [0; 272; 0], 6, [t 'lam.mu_u']);
t_is(lam.lower, [0; 0; 349.5; 4350], 5, [t 'lam.lower']);
t_is(lam.upper, [0; 0; 0; 0], 7, [t 'lam.upper']);

t = sprintf('%s - 6-d IQP : ', names{k});
%% from Bragin, et. al. https://doi.org/10.1007/s10957-014-0561-3
Expand All @@ -176,14 +201,19 @@ function t_om_solve_miqps(quiet)
norm(x - [17; 0; 16; 0; 17; 0], Inf) < 1e-7 || ...
norm(x - [17; 0; 17; 0; 16; 0], Inf) < 1e-7, [t 'x']);
t_is(f, 417, 6, [t 'f']);
t = sprintf('%s - 6-d IQP (integer relaxed) : ', names{k});
[x, f, s, out, lam] = om.solve(opt_r);
t_is(s, 1, 12, [t 'success']);
t_is(x, [50;0;50;0;50;0]/3, 8, [t 'x']);
t_is(f, 1250/3, 6, [t 'f']);
else
t_skip(nmiqp, sprintf('%s does not handle MIQP problems', names{k}));
end
% opt.verbose = 0;
end
end

if have_miqp_solver()
if have_milp_solver()
t = 'om.soln.';
c = [-2; -3];
A = sparse([195 273; 4 40]);
Expand Down Expand Up @@ -276,7 +306,7 @@ function t_om_solve_miqps(quiet)

t_end;

function TorF = have_miqp_solver()
function TorF = have_milp_solver()
TorF = have_feature('cplex') || have_feature('glpk') || ...
have_feature('gurobi') || have_feature('intlinprog') || ...
have_feature('mosek');
Expand Down

0 comments on commit b1890e9

Please sign in to comment.