Secure your code as it's written. Use Snyk Code to scan source code in minutes - no build needed - and fix issues immediately.
def bound_and_branch(self, leaf):
"""
Analize result from leaf solution and bound
"""
# Update total number of OSQP ADMM iteration
self.osqp_iter += leaf.num_iter
# Update total time to solve OSQP problems
self.osqp_solve_time += leaf.osqp_solve_time
# 1) If infeasible or unbounded, then return (prune)
if leaf.status == osqp.constant('OSQP_PRIMAL_INFEASIBLE') or \
leaf.status == osqp.constant('OSQP_DUAL_INFEASIBLE'):
return
# 2) If lower bound is greater than upper bound, then return (prune)
if leaf.lower > self.upper_glob:
return
# 3) If integer feasible, then
# - update best solution
# - update best upper bound
# - prune all leaves with lower bound greater than best upper bound
if (self.is_int_feas(leaf.x, leaf)):
# Update best solution so far
self.x = leaf.x
# Update upper bound
self.upper_glob = leaf.lower
def bound_and_branch(self, leaf):
"""
Analize result from leaf solution and bound
"""
# Update total number of OSQP ADMM iteration
self.osqp_iter += leaf.num_iter
# Update total time to solve OSQP problems
self.osqp_solve_time += leaf.osqp_solve_time
# 1) If infeasible or unbounded, then return (prune)
if leaf.status == osqp.constant('OSQP_PRIMAL_INFEASIBLE') or \
leaf.status == osqp.constant('OSQP_DUAL_INFEASIBLE'):
return
# 2) If lower bound is greater than upper bound, then return (prune)
if leaf.lower > self.upper_glob:
return
# 3) If integer feasible, then
# - update best solution
# - update best upper bound
# - prune all leaves with lower bound greater than best upper bound
if (self.is_int_feas(leaf.x, leaf)):
# Update best solution so far
self.x = leaf.x
# Update upper bound
self.upper_glob = leaf.lower
# Prune all nodes
# DEBUG: Problems that hit max_iter are infeasible
# if self.status == osqp.constant('OSQP_MAX_ITER_REACHED'):
# self.status = osqp.constant('OSQP_PRIMAL_INFEASIBLE')
# Store number of iterations
self.num_iter = results.info.iter
# Store solve time
self.osqp_solve_time = results.info.run_time
# Store solver solution
self.x = results.x
self.y = results.y
# Enforce integer variables to be exactly within the bounds
if self.status == osqp.constant('OSQP_SOLVED') or \
self.status == osqp.constant('OSQP_MAX_ITER_REACHED'):
# import ipdb; ipdb.set_trace()
n_int = self.data.n_int
i_idx = self.data.i_idx
self.x[i_idx] = \
np.minimum(np.maximum(self.x[i_idx],
self.l[-n_int:]),
self.u[-n_int:])
# if any(self.x[i_idx] < self.l[-n_int:]):
# import ipdb; ipdb.set_trace()
# if any(self.x[i_idx] > self.u[-n_int:]):
# import ipdb; ipdb.set_trace()
# Update objective value of relaxed problem (lower bound)
self.lower = self.data.compute_obj_val(self.x)
def print_progress(self, leaf):
"""
Print progress at each iteration
"""
if self.upper_glob == np.inf:
gap = " --- "
else:
gap = "%8.2f%%" % \
((self.upper_glob - self.lower_glob)/abs(self.lower_glob)*100)
if leaf.status == osqp.constant('OSQP_PRIMAL_INFEASIBLE') or \
leaf.status == osqp.constant('OSQP_DUAL_INFEASIBLE'):
obj = np.inf
else:
obj = leaf.lower
if leaf.intinf is None:
intinf = " ---"
else:
intinf = "%5d" % leaf.intinf
print("%4d\t%4d\t %10.2e\t%4d\t%s\t %10.2e\t%10.2e\t%s\t%5d" %
(self.iter_num, len(self.leaves), obj,
leaf.depth, intinf, self.lower_glob,
self.upper_glob, gap, leaf.num_iter), end='')
if leaf.status == osqp.constant('OSQP_MAX_ITER_REACHED'):
leaf.status == osqp.constant('OSQP_DUAL_INFEASIBLE'):
obj = np.inf
else:
obj = leaf.lower
if leaf.intinf is None:
intinf = " ---"
else:
intinf = "%5d" % leaf.intinf
print("%4d\t%4d\t %10.2e\t%4d\t%s\t %10.2e\t%10.2e\t%s\t%5d" %
(self.iter_num, len(self.leaves), obj,
leaf.depth, intinf, self.lower_glob,
self.upper_glob, gap, leaf.num_iter), end='')
if leaf.status == osqp.constant('OSQP_MAX_ITER_REACHED'):
print("!")
else:
print("")
# if self.status == osqp.constant('OSQP_MAX_ITER_REACHED'):
# self.status = osqp.constant('OSQP_PRIMAL_INFEASIBLE')
# Store number of iterations
self.num_iter = results.info.iter
# Store solve time
self.osqp_solve_time = results.info.run_time
# Store solver solution
self.x = results.x
self.y = results.y
# Enforce integer variables to be exactly within the bounds
if self.status == osqp.constant('OSQP_SOLVED') or \
self.status == osqp.constant('OSQP_MAX_ITER_REACHED'):
# import ipdb; ipdb.set_trace()
n_int = self.data.n_int
i_idx = self.data.i_idx
self.x[i_idx] = \
np.minimum(np.maximum(self.x[i_idx],
self.l[-n_int:]),
self.u[-n_int:])
# if any(self.x[i_idx] < self.l[-n_int:]):
# import ipdb; ipdb.set_trace()
# if any(self.x[i_idx] > self.u[-n_int:]):
# import ipdb; ipdb.set_trace()
# Update objective value of relaxed problem (lower bound)
self.lower = self.data.compute_obj_val(self.x)
def print_progress(self, leaf):
"""
Print progress at each iteration
"""
if self.upper_glob == np.inf:
gap = " --- "
else:
gap = "%8.2f%%" % \
((self.upper_glob - self.lower_glob)/abs(self.lower_glob)*100)
if leaf.status == osqp.constant('OSQP_PRIMAL_INFEASIBLE') or \
leaf.status == osqp.constant('OSQP_DUAL_INFEASIBLE'):
obj = np.inf
else:
obj = leaf.lower
if leaf.intinf is None:
intinf = " ---"
else:
intinf = "%5d" % leaf.intinf
print("%4d\t%4d\t %10.2e\t%4d\t%s\t %10.2e\t%10.2e\t%s\t%5d" %
(self.iter_num, len(self.leaves), obj,
leaf.depth, intinf, self.lower_glob,
self.upper_glob, gap, leaf.num_iter), end='')
if leaf.status == osqp.constant('OSQP_MAX_ITER_REACHED'):
print("!")
# Number of OSQP ADMM iterations
self.num_iter = 0
# Time to solve the QP
self.osqp_solve_time = 0
# Warm-start variables which are also the relaxed solutions
if x0 is None:
x0 = np.zeros(self.data.n)
if y0 is None:
y0 = np.zeros(self.data.m + self.data.n_int)
self.x = x0
self.y = y0
# Set QP solver return status
self.status = osqp.constant('OSQP_UNSOLVED')
# Add next variable elements
self.nextvar_idx = None # Index of next variable within solution x
# Index of constraint to change for branching
# on next variable
self.constr_idx = None