Skip to content

Commit dfdf3de

Browse files
committed
Add fix_integer and relax_integer options to miqps_master()
And move the implementations from mp.opt_model.solve() to miqps_master().
1 parent 9631de6 commit dfdf3de

7 files changed

Lines changed: 84 additions & 24 deletions

File tree

CHANGES.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ Change history for MP-Opt-Model
55
since version 5.0
66
-----------------
77

8+
#### 10/28/25
9+
- Add `fix_integer` and `relax_integer` options to `miqps_master()` and move
10+
the implementations from `mp.opt_model.solve()` to `miqps_master()`.
11+
812
#### 10/21/25
913
- Add `fix_integer` option for `mp.opt_model.solve()`. Set to true to fix
1014
any integer variables to their initial values, as specified in ``x0`` or

docs/src/MP-Opt-Model-manual/MP-Opt-Model-manual.tex

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6718,7 +6718,8 @@ \subsection{Version 5.1 -- released ??? ?, 202?}
67186718

67196719
\subsubsection*{New Features}
67206720
\begin{itemize}
6721-
\item New \code{fix\_integer} option for \code{mp.opt\_model.solve()}. Set to true to fix any integer variables to their initial values, as specified in \code{x0} or \code{opt.x0}.
6721+
\item New \code{fix\_integer} option for \code{miqps\_master()} and \code{mp.opt\_model.solve()}. Set to true to fix any integer variables to their initial values, as specified in \code{x0} or \code{opt.x0}.
6722+
\item Add \code{relax\_integer} option to \code{miqps\_master()} and move the implementation from \code{mp.opt\_model.solve()}.
67226723
\item New functions:
67236724
\begin{itemize}
67246725
\item \code{foobar()} does whizbang.

lib/+mp/opt_model.m

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -659,16 +659,16 @@
659659
% nleqs_newton
660660
% - ``osqp_opt`` - options struct for OSQP
661661
% - ``quadprog_opt`` - options struct for :func:`quadprog`
662+
% - ``fix_integer`` (0) - fix integer variables at value in
663+
% ``x0``, if true
664+
% - ``relax_integer`` (0) - relax integer constraints, if true
662665
% - ``parse_soln`` (0) - flag that specifies whether or not
663666
% to call the parse_soln() method and place the return values
664667
% in ``mm.soln``.
665668
% - ``price_stage_warn_tol`` (1e-7) - tolerance on
666669
% objective fcn value and primal variable relative match
667670
% required to avoid mismatch warning message if mixed
668671
% integer price computation stage is not skipped
669-
% - ``relax_integer`` (0) - relax integer constraints, if true
670-
% - ``fix_integer`` (0) - fix integer variables at value in
671-
% ``x0``, if true
672672
% - ``skip_prices`` (0) - flag that specifies whether or not
673673
% to skip the price computation stage for mixed integer
674674
% problems, in which the problem is re-solved for only the
@@ -1126,13 +1126,13 @@ function display(mm, varargin)
11261126
x0 = opt.x0;
11271127
end
11281128

1129-
if isfield(opt, 'fix_integer') && opt.fix_integer
1130-
%% fix integer variables
1131-
j = find(vtype == 'B' | vtype == 'I')';
1132-
xmin(j) = x0(j);
1133-
xmax(j) = x0(j);
1134-
mixed_integer = false;
1135-
end
1129+
% if isfield(opt, 'fix_integer') && opt.fix_integer
1130+
% %% fix integer variables
1131+
% j = find(vtype == 'B' | vtype == 'I')';
1132+
% xmin(j) = x0(j);
1133+
% xmax(j) = x0(j);
1134+
% mixed_integer = false;
1135+
% end
11361136
else
11371137
%% optimization vars, bounds, types
11381138
[x0, xmin, xmax] = mm.var.params();

lib/@opt_model/solve.m

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -84,14 +84,14 @@
8484
% newton_opt - options struct for Newton method, NLEQS_NEWTON
8585
% osqp_opt - options struct for OSQP
8686
% quadprog_opt - options struct for QUADPROG
87+
% fix_integer (0) - fix integer variables at value in x0, if true
88+
% relax_integer (0) - relax integer constraints, if true
8789
% parse_soln (0) - flag that specifies whether or not to call
8890
% the PARSE_SOLN method and place the return values in OM.soln.
8991
% price_stage_warn_tol (1e-7) - tolerance on the objective fcn
9092
% value and primal variable relative match required to avoid
9193
% mis-match warning message if mixed integer price computation
9294
% stage is not skipped
93-
% relax_integer (0) - relax integer constraints, if true
94-
% fix_integer (0) - fix integer variables at value in x0, if true
9595
% skip_prices (0) - flag that specifies whether or not to skip the
9696
% price computation stage for mixed integer problems, in which
9797
% the problem is re-solved for only the continuous variables,
@@ -296,13 +296,13 @@
296296
x0 = opt.x0;
297297
end
298298

299-
if isfield(opt, 'fix_integer') && opt.fix_integer
300-
%% fix integer variables
301-
j = find(vtype == 'B' | vtype == 'I')';
302-
xmin(j) = x0(j);
303-
xmax(j) = x0(j);
304-
mixed_integer = false;
305-
end
299+
% if isfield(opt, 'fix_integer') && opt.fix_integer
300+
% %% fix integer variables
301+
% j = (vtype == 'B' | vtype == 'I')';
302+
% xmin(j) = x0(j);
303+
% xmax(j) = x0(j);
304+
% mixed_integer = false;
305+
% end
306306
else
307307
%% optimization vars, bounds, types
308308
[x0, xmin, xmax] = om.var.params();

lib/miqps_master.m

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@
5252
% 0 = no progress output
5353
% 1 = some progress output
5454
% 2 = verbose progress output
55+
% fix_integer (0) - fix integer variables at value in x0, if true
56+
% relax_integer (0) - relax integer constraints, if true
5557
% skip_prices (0) - flag that specifies whether or not to
5658
% skip the price computation stage, in which the problem
5759
% is re-solved for only the continuous variables, with all
@@ -109,7 +111,7 @@
109111
% [x, f, exitflag, output] = miqps_master(...)
110112
% [x, f, exitflag, output, lambda] = miqps_master(...)
111113
%
112-
% Example: (problem from %% from MOSEK 6.0 Guided Tour, section 7.13.1
114+
% Example: (problem from MOSEK 6.0 Guided Tour, section 7.13.1
113115
% https://docs.mosek.com/6.0/toolbox/node009.html)
114116
% c = [-2; -3];
115117
% A = sparse([195 273; 4 40]);
@@ -236,6 +238,35 @@
236238
end
237239
end
238240

241+
%% handle relax_integer and fix_integer options
242+
if ~isempty(vtype) && (isfield(opt, 'relax_integer') && opt.relax_integer || ...
243+
isfield(opt, 'fix_integer') && opt.fix_integer)
244+
nx = length(x0);
245+
if length(vtype) == 1 %% expand if necessary
246+
vtype = char(vtype * ones(1, nx));
247+
end
248+
j = (vtype == 'B' | vtype == 'I')';
249+
if isfield(opt, 'fix_integer') && opt.fix_integer
250+
%% fix integer variables
251+
if ~isempty(j)
252+
%% expand if necessary
253+
if length(xmin) == 1
254+
xmin = xmin * ones(nx, 1);
255+
elseif isempty(xmin)
256+
xmin = -Inf(nx, 1);
257+
end
258+
if length(xmax) == 1
259+
xmax = xmax * ones(nx, 1);
260+
elseif isempty(xmax)
261+
xmax = Inf(nx, 1);
262+
end
263+
xmin(j) = x0(j);
264+
xmax(j) = x0(j);
265+
end
266+
end
267+
vtype(j) = 'C';
268+
end
269+
239270
%%----- call the appropriate solver -----
240271
switch alg
241272
case 'CPLEX'

lib/mpomver.m

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
v = struct( 'Name', 'MP-Opt-Model', ...
2727
'Version', '5.1-dev', ...
2828
'Release', '', ...
29-
'Date', '21-Oct-2025' );
29+
'Date', '28-Oct-2025' );
3030
if nargout > 0
3131
if nargin > 0
3232
rv = v;

lib/t/t_miqps_master.m

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ function t_miqps_master(quiet)
2626
does_miqp(1) = 1;
2727
end
2828

29-
n = 62;
29+
n = 83;
3030
nqp = 28;
3131
nmiqp = 11;
3232
t_begin(n*length(algs), quiet);
@@ -94,6 +94,10 @@ function t_miqps_master(quiet)
9494
end
9595
opt.mosek_opt = mosek_options([], mpopt);
9696
end
97+
opt_r = opt;
98+
opt_r.relax_integer = 1;
99+
opt_f = opt;
100+
opt_f.fix_integer = 1;
97101

98102
t = sprintf('%s - 3-d LP : ', names{k});
99103
%% based on example from 'doc linprog'
@@ -205,6 +209,13 @@ function t_miqps_master(quiet)
205209
t_is(x, [4; 2], 12, [t 'x']);
206210
t_is(f, -14, 12, [t 'f']);
207211

212+
t = sprintf('%s - 2-d ILP (integer relaxed) : ', names{k});
213+
p.opt = opt_r;
214+
[x, f, s, out, lam] = miqps_master(p);
215+
t_is(s, 1, 12, [t 'success']);
216+
t_is(x, [2.441860465; 3.255813953], 8, [t 'x']);
217+
t_is(f, -14.651162791, 8, [t 'f']);
218+
208219
t = sprintf('%s - 6-d ILP : ', names{k});
209220
%% from https://doi.org/10.1109/TASE.2020.2998048
210221
c = [1; 2; 3; 1; 2; 3];
@@ -287,9 +298,22 @@ function t_miqps_master(quiet)
287298
t_is(s, 1, 12, [t 'exitflag']);
288299
t_is(x, ex(:, Scenario), 12, [t 'x']);
289300
t_is(f, ef(Scenario, 1), 12, [t 'f']);
301+
302+
t = sprintf('%s - 14-d MILP Scenario %d (integer relaxed) : ', names{k}, Scenario);
303+
[x, f, s, out, lam] = miqps_master(HH, cc, A, l, u, xl, xu, x0, vt, opt_r);
304+
t_is(s, 1, 12, [t 'exitflag']);
305+
t_is(x, ex(:, 4), 12, [t 'x']);
306+
t_is(f, ef(Scenario, 2), 12, [t 'f']);
307+
308+
t = sprintf('%s - 14-d MILP Scenario %d (integer fixed) : ', names{k}, Scenario);
309+
x0 = ones(size(x));
310+
[x, f, s, out, lam] = miqps_master(HH, cc, A, l, u, xl, xu, x0, vt, opt_f);
311+
t_is(s, 1, 12, [t 'exitflag']);
312+
t_is(x, ex(:, 3), 12, [t 'x']);
313+
t_is(f, ef(Scenario, 3), 12, [t 'f']);
290314
end
291315
else
292-
t_skip(9, sprintf('%s : mp.opt_model not available', names{k}));
316+
t_skip(27, sprintf('%s : mp.opt_model not available', names{k}));
293317
end
294318

295319

0 commit comments

Comments
 (0)