Skip to content

Commit b1890e9

Browse files
committed
git subrepo pull mp-opt-model
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"
1 parent e3c4baf commit b1890e9

File tree

10 files changed

+99
-49
lines changed

10 files changed

+99
-49
lines changed

mp-opt-model/.gitrepo

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
[subrepo]
77
remote = [email protected]:MATPOWER/mp-opt-model
88
branch = master
9-
commit = 9c9ff65abf88021e1f8fabeacad617b609433b32
10-
parent = 2f766a4ab4ff55a563b811be068f2f397f0f0862
9+
commit = 1dd6e8e8aa9d96e216c1de6b9f827b04b309b6b6
10+
parent = e3c4baf74fa20f2d65ee69faaff849afc9689295
1111
method = merge
1212
cmdver = 0.4.9

mp-opt-model/CHANGES.md

+4
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ Change history for MP-Opt-Model
55
since 4.2
66
---------
77

8+
#### 10/29/24
9+
- Add `relax_integer` option for `opt_model.solve()`. Set to true to easily
10+
solve the LP or QP relaxation of a mixed integer LP or QP.
11+
812
#### 8/17/24
913
- Add `mp.set_manager_opt_model` base class to handle common `opt_model`
1014
functionality, such as handling solutions, for the individual field

mp-opt-model/docs/src/MP-Opt-Model-manual/MP-Opt-Model-manual.tex

+2-1
Original file line numberDiff line numberDiff line change
@@ -2444,6 +2444,7 @@ \subsection{Solving the Model}
24442444
\end{tabular} \\
24452445
\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}
24462446
property of the field type objects \\
2447+
\code{~~relax\_integer} & 0 & relax integer constraints, if true \\
24472448
\code{~~x0} & \emph{empty} & optional initial value of $x$, overrides value stored in model, \emph{(ignored by some solvers)} \\
24482449
\midrule
24492450
\multicolumn{3}{l}{\emph{Additional Options for Specific Problem Types}} \\
@@ -4669,7 +4670,7 @@ \subsection{Version 4.3 -- released ??? ?, 202?}
46694670

46704671
\subsubsection*{New Features}
46714672
\begin{itemize}
4672-
\item
4673+
\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.
46734674
\item New classes:
46744675
\begin{itemize}
46754676
\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.

mp-opt-model/lib/+mp/set_manager_opt_model.m

+6-8
Original file line numberDiff line numberDiff line change
@@ -316,15 +316,13 @@
316316
end
317317
nargs = length(args);
318318

319-
if nargs < 2
320-
idx = [];
321-
if nargs < 1
322-
name = [];
323-
else
324-
name = args{1};
319+
idx = [];
320+
name = [];
321+
if nargs >= 1
322+
name = args{1};
323+
if nargs >= 2
324+
idx = args{2};
325325
end
326-
else
327-
idx = args{2};
328326
end
329327

330328
if obj.N

mp-opt-model/lib/@opt_model/display_soln.m

+9-6
Original file line numberDiff line numberDiff line change
@@ -60,12 +60,15 @@
6060
end
6161
nargs = length(args);
6262

63-
if nargs < 3
64-
idx = [];
65-
if nargs < 2
66-
name = [];
67-
if nargs < 1
68-
set_type = 'all';
63+
set_type = 'all';
64+
name = [];
65+
idx = [];
66+
if nargs >= 1
67+
set_type = args{1};
68+
if nargs >= 2
69+
name = args{2};
70+
if nargs >= 3
71+
idx = args{3};
6972
end
7073
end
7174
end

mp-opt-model/lib/@opt_model/solve.m

+24-17
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@
8787
% value and primal variable relative match required to avoid
8888
% mis-match warning message if mixed integer price computation
8989
% stage is not skipped
90+
% relax_integer (0) - relax integer constraints, if true
9091
% skip_prices (0) - flag that specifies whether or not to skip the
9192
% price computation stage for mixed integer problems, in which
9293
% the problem is re-solved for only the continuous variables,
@@ -135,8 +136,6 @@
135136
%% call appropriate solver
136137
pt = om.problem_type();
137138
switch pt
138-
case 'MINLP' %% MINLP - mixed integer non-linear program
139-
error('opt_model.solve: not yet implemented for ''MINLP'' problems.')
140139
case 'LEQ' %% LEQ - linear equations
141140
if isfield(opt, 'leq_opt')
142141
if isfield(opt.leq_opt, 'solver')
@@ -202,26 +201,34 @@
202201
case 'PNE' %% PNE - parameterized nonlinear equation
203202
[x, f, eflag, output, lambda] = pnes_master(fcn, x0, opt);
204203
end
205-
case 'NLP' %% NLP - nonlinear program
206-
%% optimization vars, bounds, types
207-
[x0, xmin, xmax] = om.params_var();
208-
if isfield(opt, 'x0')
209-
x0 = opt.x0;
204+
case {'MINLP', 'NLP'}
205+
mixed_integer = strcmp(pt(1:2), 'MI') && ...
206+
(~isfield(opt, 'relax_integer') || ~opt.relax_integer);
207+
if mixed_integer %% MINLP - mixed integer non-linear program
208+
error('opt_model.solve: not yet implemented for ''MINLP'' problems.')
209+
else %% NLP - nonlinear program
210+
%% optimization vars, bounds, types
211+
[x0, xmin, xmax] = om.params_var();
212+
if isfield(opt, 'x0')
213+
x0 = opt.x0;
214+
end
215+
216+
%% run solver
217+
[A, l, u] = om.params_lin_constraint();
218+
f_fcn = @(x)nlp_costfcn(om, x);
219+
gh_fcn = @(x)nlp_consfcn(om, x);
220+
hess_fcn = @(x, lambda, cost_mult)nlp_hessfcn(om, x, lambda, cost_mult);
221+
[x, f, eflag, output, lambda] = ...
222+
nlps_master(f_fcn, x0, A, l, u, xmin, xmax, gh_fcn, hess_fcn, opt);
210223
end
211-
212-
%% run solver
213-
[A, l, u] = om.params_lin_constraint();
214-
f_fcn = @(x)nlp_costfcn(om, x);
215-
gh_fcn = @(x)nlp_consfcn(om, x);
216-
hess_fcn = @(x, lambda, cost_mult)nlp_hessfcn(om, x, lambda, cost_mult);
217-
[x, f, eflag, output, lambda] = ...
218-
nlps_master(f_fcn, x0, A, l, u, xmin, xmax, gh_fcn, hess_fcn, opt);
219224
otherwise
220225
%% get parameters
221226
[HH, CC, C0] = om.params_quad_cost();
222227
[A, l, u] = om.params_lin_constraint();
228+
mixed_integer = strcmp(pt(1:2), 'MI') && ...
229+
(~isfield(opt, 'relax_integer') || ~opt.relax_integer);
223230

224-
if strcmp(pt(1:2), 'MI') %% MILP, MIQP - mixed integer linear/quadratic program
231+
if mixed_integer %% MILP, MIQP - mixed integer linear/quadratic program
225232
%% optimization vars, bounds, types
226233
[x0, xmin, xmax, vtype] = om.params_var();
227234
if isfield(opt, 'x0')
@@ -231,7 +238,7 @@
231238
%% run solver
232239
[x, f, eflag, output, lambda] = ...
233240
miqps_master(HH, CC, A, l, u, xmin, xmax, x0, vtype, opt);
234-
else %% LP, QP - linear/quadratic program
241+
else %% LP, QP - linear/quadratic program
235242
%% optimization vars, bounds, types
236243
[x0, xmin, xmax] = om.params_var();
237244
if isfield(opt, 'x0')

mp-opt-model/lib/miqps_master.m

+11-7
Original file line numberDiff line numberDiff line change
@@ -192,14 +192,18 @@
192192
alg = 'CPLEX';
193193
elseif have_feature('mosek') %% if not, then MOSEK, if available
194194
alg = 'MOSEK';
195-
elseif isempty(H) || ~any(any(H)) %% if not, and linear objective
196-
if have_feature('intlinprog') %% then Optimization Tbx, if available
197-
alg = 'OT';
198-
elseif have_feature('glpk') %% if not, and then GLPK, if available
199-
alg = 'GLPK';
200-
end
201195
else
202-
error('miqps_master: no solvers available - requires CPLEX, Gurobi, MOSEK, INTLINPROG or GLPK');
196+
if isempty(H) || ~any(any(H)) %% if not, and linear objective
197+
if have_feature('intlinprog') %% then Optimization Tbx, if available
198+
alg = 'OT';
199+
elseif have_feature('glpk') %% if not, and then GLPK, if available
200+
alg = 'GLPK';
201+
else
202+
error('miqps_master: no solvers available - requires CPLEX, Gurobi, MOSEK, INTLINPROG or GLPK');
203+
end
204+
else %% if not, and quadratic objective
205+
error('miqps_master: no solvers available - requires CPLEX, Gurobi, or MOSEK');
206+
end
203207
end
204208
end
205209

mp-opt-model/lib/mpomver.m

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
v = struct( 'Name', 'MP-Opt-Model', ...
2727
'Version', '4.3-dev', ...
2828
'Release', '', ...
29-
'Date', '30-Aug-2024' );
29+
'Date', '29-Oct-2024' );
3030
if nargout > 0
3131
if nargin > 0
3232
rv = v;

mp-opt-model/lib/t/t_miqps_master.m

+5-2
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ function t_miqps_master(quiet)
1515

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

272275
t_end;
273276

274-
function TorF = have_miqp_solver()
277+
function TorF = have_milp_solver()
275278
TorF = have_feature('cplex') || have_feature('glpk') || ...
276279
have_feature('gurobi') || have_feature('intlinprog') || ...
277280
have_feature('mosek');

mp-opt-model/lib/t/t_om_solve_miqps.m

+35-5
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,14 @@ function t_om_solve_miqps(quiet)
1515

1616
algs = {'DEFAULT', 'CPLEX', 'MOSEK', 'GUROBI', 'GLPK', 'OT'};
1717
names = {'DEFAULT', 'CPLEX', 'MOSEK', 'Gurobi', 'glpk', 'intlin/lin/quadprog'};
18-
check = {@have_miqp_solver, 'cplex', 'mosek', 'gurobi', 'glpk', 'intlinprog'};
18+
check = {@have_milp_solver, 'cplex', 'mosek', 'gurobi', 'glpk', 'intlinprog'};
1919
does_qp = [0 1 1 1 0 0];
2020
if have_feature('gurobi') || have_feature('cplex') || have_feature('mosek')
2121
does_qp(1) = 1;
2222
end
2323

24-
n = 17;
25-
nmiqp = 10;
24+
n = 33;
25+
nmiqp = 20;
2626
nmiqp_soln = 30;
2727
diff_tool = 'bbdiff';
2828
show_diff_on_fail = false;
@@ -87,6 +87,8 @@ function t_om_solve_miqps(quiet)
8787
end
8888
opt.mosek_opt = mosek_options([], mpopt);
8989
end
90+
opt_r = opt;
91+
opt_r.relax_integer = 1;
9092

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

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

129142
if does_qp(k)
130143
t = sprintf('%s - 4-d MIQP : ', names{k});
131144
%% from cplexmiqpex.m, CPLEX_Studio_Academic124/cplex/examples/src/matlab/cplexmiqpex.m
145+
%% Note: This is a lame example; the integer relaxed problem already
146+
%% has an integer feasible solution, so this is actually just
147+
%% a simple QP. -RDZ 10/29/24
132148
H = sparse([ 33 6 0 0;
133149
6 22 11.5 0;
134150
0 11.5 11 0;
@@ -157,6 +173,15 @@ function t_om_solve_miqps(quiet)
157173
t_is(lam.mu_u, [0; 272; 0], 6, [t 'lam.mu_u']);
158174
t_is(lam.lower, [0; 0; 349.5; 4350], 5, [t 'lam.lower']);
159175
t_is(lam.upper, [0; 0; 0; 0], 7, [t 'lam.upper']);
176+
t = sprintf('%s - 4-d MIQP (integer relaxed) : ', names{k});
177+
[x, f, s, out, lam] = om.solve(opt_r);
178+
t_is(s, 1, 12, [t 'success']);
179+
t_is(x, [7; 7; 0; 2], 7, [t 'x']);
180+
t_is(f, 1618.5, 4, [t 'f']);
181+
t_is(lam.mu_l, [466; 0; 0], 6, [t 'lam.mu_l']);
182+
t_is(lam.mu_u, [0; 272; 0], 6, [t 'lam.mu_u']);
183+
t_is(lam.lower, [0; 0; 349.5; 4350], 5, [t 'lam.lower']);
184+
t_is(lam.upper, [0; 0; 0; 0], 7, [t 'lam.upper']);
160185

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

186-
if have_miqp_solver()
216+
if have_milp_solver()
187217
t = 'om.soln.';
188218
c = [-2; -3];
189219
A = sparse([195 273; 4 40]);
@@ -276,7 +306,7 @@ function t_om_solve_miqps(quiet)
276306

277307
t_end;
278308

279-
function TorF = have_miqp_solver()
309+
function TorF = have_milp_solver()
280310
TorF = have_feature('cplex') || have_feature('glpk') || ...
281311
have_feature('gurobi') || have_feature('intlinprog') || ...
282312
have_feature('mosek');

0 commit comments

Comments
 (0)