Skip to content

Commit 6653114

Browse files
committed
Critical bugfix for LP dualization failing when there are nontrivial variable bounds (such as when default_non_negative=True)
This resolves Issue #169
1 parent f4b4b3a commit 6653114

File tree

1 file changed

+23
-27
lines changed

1 file changed

+23
-27
lines changed

inflation/lp/lp_utils.py

Lines changed: 23 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,17 @@ def solveLP_sparse(objective: coo_array = blank_coo_array,
210210
# Since the value of infinity is ignored, define it for symbolic purposes
211211
inf = 0.0
212212

213+
nof_primal_variables = inequalities.shape[1]
214+
if default_non_negative and solve_dual:
215+
merely_lbbounded = np.setdiff1d(np.arange(nof_primal_variables), known_vars.col)
216+
lower_bounds = coo_array((lower_bounds.toarray().ravel()[merely_lbbounded], (
217+
[0] * len(merely_lbbounded),
218+
merely_lbbounded
219+
)), shape=(1, nof_primal_variables))
220+
221+
222+
223+
213224
if relax_known_vars or relax_inequalities:
214225
default_non_negative = False
215226

@@ -297,36 +308,18 @@ def solveLP_sparse(objective: coo_array = blank_coo_array,
297308
if objective.shape[-1] == 0:
298309
objective = coo_array((1, nof_primal_variables), dtype=np.int8)
299310

300-
nof_lb = lower_bounds.nnz
301-
nof_ub = upper_bounds.nnz
302-
303311
if verbose > 0:
304312
print(f"Size of constraint matrix: {constraints.shape}")
305313

306314
if solve_dual:
307315
if verbose > 1:
308316
print("Proceeding with dual initialization...")
309317

310-
nof_primal_nontriv_bounds = nof_lb + nof_ub
311318
nof_dual_constraints = nof_primal_variables
312-
nof_dual_variables = nof_primal_constraints + \
313-
nof_primal_nontriv_bounds
314-
315-
# Add variable bounds as inequality constraints to matrix
316-
if nof_primal_nontriv_bounds > 0:
317-
lb_mat = expand_sparse_vec(lower_bounds,
318-
conversion_style="eq")
319-
ub_mat = expand_sparse_vec(upper_bounds,
320-
conversion_style="eq")
321-
ub_mat.data[:] = -ub_mat.data # just a list of ones
322-
matrix = vstack((constraints, lb_mat, ub_mat),
323-
format='csr')
324-
b_extra = np.concatenate(
325-
(lower_bounds.data, -np.asarray(upper_bounds.data)))
326-
objective_vector = np.concatenate((b, b_extra))
327-
else:
328-
matrix = constraints.tocsr(copy=False)
329-
objective_vector = b
319+
nof_dual_variables = nof_primal_constraints
320+
321+
matrix = constraints.tocsr(copy=False)
322+
objective_vector = b
330323

331324
if verbose > 1:
332325
print("Sparse matrix reformat complete...")
@@ -338,11 +331,14 @@ def solveLP_sparse(objective: coo_array = blank_coo_array,
338331
else:
339332
blc = buc = objective.toarray().ravel()
340333

341-
# Set constraint bounds corresponding to primal variable bounds
342-
if default_non_negative:
343-
bkc = np.broadcast_to(mosek.boundkey.lo, nof_dual_constraints)
344-
else:
345-
bkc = np.broadcast_to(mosek.boundkey.fx, nof_dual_constraints)
334+
bkc = np.array(np.broadcast_to(mosek.boundkey.fx, nof_dual_constraints), copy=True)
335+
# Variable bounds are reflected in the dual on the objective!
336+
bkc[lower_bounds.col] = mosek.boundkey.up
337+
buc[lower_bounds.col] = lower_bounds.data
338+
bkc[upper_bounds.col] = mosek.boundkey.lo
339+
blc[upper_bounds.col] = upper_bounds.data
340+
bkc[np.intersect1d(upper_bounds.col, upper_bounds.col)] = mosek.boundkey.ra
341+
346342

347343
# Set bound keys and values for variables
348344
# Non-positivity for y corresponding to inequalities

0 commit comments

Comments
 (0)