Skip to content

Commit

Permalink
sync indent, polish on SAT code; add RINS option for CP-SAT
Browse files Browse the repository at this point in the history
  • Loading branch information
lperron committed Mar 13, 2019
1 parent 2f06801 commit 2b9adc6
Show file tree
Hide file tree
Showing 8 changed files with 101 additions and 22 deletions.
6 changes: 4 additions & 2 deletions ortools/algorithms/knapsack_solver_for_cuts.cc
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,8 @@ double KnapsackSolverForCuts::Solve(TimeLimit* time_limit,

SearchQueue search_queue;
const KnapsackAssignmentForCuts assignment(kNoSelection, true);
auto root_node = absl::make_unique<KnapsackSearchNodeForCuts>(nullptr, assignment);
auto root_node =
absl::make_unique<KnapsackSearchNodeForCuts>(nullptr, assignment);
root_node->set_current_profit(GetCurrentProfit());
root_node->set_profit_upper_bound(GetAggregatedProfitUpperBound());
root_node->set_next_item_id(GetNextItemId());
Expand Down Expand Up @@ -436,7 +437,8 @@ bool KnapsackSolverForCuts::MakeNewNode(const KnapsackSearchNodeForCuts& node,
}

// The node is relevant.
auto relevant_node = absl::make_unique<KnapsackSearchNodeForCuts>(&node, assignment);
auto relevant_node =
absl::make_unique<KnapsackSearchNodeForCuts>(&node, assignment);
relevant_node->set_current_profit(new_node.current_profit());
relevant_node->set_profit_upper_bound(new_node.profit_upper_bound());
relevant_node->set_next_item_id(new_node.next_item_id());
Expand Down
1 change: 0 additions & 1 deletion ortools/algorithms/knapsack_solver_for_cuts.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@
#include "ortools/base/int_type.h"
#include "ortools/base/int_type_indexed_vector.h"
#include "ortools/base/logging.h"
#include "ortools/base/ptr_util.h"
#include "ortools/util/time_limit.h"

namespace operations_research {
Expand Down
47 changes: 47 additions & 0 deletions ortools/sat/cp_model_lns.cc
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@
#include <numeric>

#include "ortools/sat/cp_model.pb.h"
#include "ortools/sat/cp_model_loader.h"
#include "ortools/sat/cp_model_utils.h"
#include "ortools/sat/linear_programming_constraint.h"
#include "ortools/util/random_engine.h"

namespace operations_research {
Expand Down Expand Up @@ -409,5 +411,50 @@ Neighborhood SchedulingTimeWindowNeighborhoodGenerator::Generate(
initial_solution, helper_);
}

Neighborhood RelaxationInducedNeighborhoodGenerator::Generate(
const CpSolverResponse& initial_solution, int64 seed,
double difficulty) const {
Neighborhood neighborhood;
neighborhood.cp_model = helper_.ModelProto();

const int num_active_vars = helper_.ActiveVariables().size();
const int num_model_vars = helper_.ModelProto().variables_size();
const int target_size =
num_model_vars - std::ceil(difficulty * num_active_vars);
if (target_size == num_active_vars) {
neighborhood.is_reduced = false;
return neighborhood;
}

auto* mapping = model_.Get<CpModelMapping>();
auto* lp_dispatcher = model_.Get<LinearProgrammingDispatcher>();

if (lp_dispatcher == nullptr) {
neighborhood.is_reduced = false;
return neighborhood;
}

std::vector<int> fixed_variables;
for (int i = 0; i < num_model_vars; ++i) {
if (!helper_.IsActive(i)) continue;
if (mapping->IsInteger(i)) {
const IntegerVariable var = mapping->Integer(i);
const IntegerVariable positive_var = PositiveVariable(var);
LinearProgrammingConstraint* lp =
gtl::FindWithDefault(*lp_dispatcher, positive_var, nullptr);
if (lp == nullptr) continue;
const double lp_value = (lp->GetSolutionValue(positive_var));

if (std::abs(lp_value - initial_solution.solution(i)) < 1e-4) {
// Fix this var.
fixed_variables.push_back(i);
if (fixed_variables.size() >= target_size) break;
}
}
}

return helper_.FixGivenVariables(initial_solution, fixed_variables);
}

} // namespace sat
} // namespace operations_research
19 changes: 19 additions & 0 deletions ortools/sat/cp_model_lns.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include "absl/types/span.h"
#include "ortools/base/integral_types.h"
#include "ortools/sat/cp_model.pb.h"
#include "ortools/sat/model.h"

namespace operations_research {
namespace sat {
Expand Down Expand Up @@ -208,6 +209,24 @@ class SchedulingTimeWindowNeighborhoodGenerator : public NeighborhoodGenerator {
double difficulty) const final;
};

// Generates a neighborhood by fixing the variables who have same solution value
// as their linear relaxation. This was published in "Exploring relaxation
// induced neighborhoods to improve MIP solutions" 2004 by E. Danna et.
// TODO(user): This simulates RINS only at root node. Try to exploit it on
// other nodes as well.
class RelaxationInducedNeighborhoodGenerator : public NeighborhoodGenerator {
public:
explicit RelaxationInducedNeighborhoodGenerator(
NeighborhoodGeneratorHelper const* helper, const Model& model,
const std::string& name)
: NeighborhoodGenerator(name, helper), model_(model) {}

Neighborhood Generate(const CpSolverResponse& initial_solution, int64 seed,
double difficulty) const final;

const Model& model_;
};

} // namespace sat
} // namespace operations_research

Expand Down
6 changes: 6 additions & 0 deletions ortools/sat/cp_model_solver.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1922,6 +1922,12 @@ CpSolverResponse SolveCpModelWithLNS(
generators.push_back(absl::make_unique<SchedulingNeighborhoodGenerator>(
&helper, "scheduling_random_lns"));
}
if (parameters->use_rins_lns()) {
// TODO(user): Only do it if we have a linear relaxation.
generators.push_back(
absl::make_unique<RelaxationInducedNeighborhoodGenerator>(
&helper, *model, "rins"));
}

// The "optimal" difficulties do not have to be the same for different
// generators.
Expand Down
26 changes: 14 additions & 12 deletions ortools/sat/python/cp_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -959,7 +959,8 @@ def AddReservoirConstraintWithActive(self, times, demands, actives,
model_ct = self.__model.constraints[ct.Index()]
model_ct.reservoir.times.extend([self.GetOrMakeIndex(x) for x in times])
model_ct.reservoir.demands.extend(demands)
model_ct.reservoir.actives.extend([self.GetOrMakeIndex(x) for x in actives])
model_ct.reservoir.actives.extend(
[self.GetOrMakeIndex(x) for x in actives])
model_ct.reservoir.min_level = min_level
model_ct.reservoir.max_level = max_level
return ct
Expand Down Expand Up @@ -1220,8 +1221,8 @@ def GetOrMakeIndex(self, arg):
cp_model_helper.AssertIsInt64(arg)
return self.GetOrMakeIndexFromConstant(arg)
else:
raise TypeError('NotSupported: model.GetOrMakeIndex(' + str(arg) +
')')
raise TypeError(
'NotSupported: model.GetOrMakeIndex(' + str(arg) + ')')

def GetOrMakeBooleanIndex(self, arg):
"""Returns an index from a boolean expression."""
Expand All @@ -1235,8 +1236,8 @@ def GetOrMakeBooleanIndex(self, arg):
cp_model_helper.AssertIsBoolean(arg)
return self.GetOrMakeIndexFromConstant(arg)
else:
raise TypeError('NotSupported: model.GetOrMakeBooleanIndex(' +
str(arg) + ')')
raise TypeError(
'NotSupported: model.GetOrMakeBooleanIndex(' + str(arg) + ')')

def GetIntervalIndex(self, arg):
if not isinstance(arg, IntervalVar):
Expand Down Expand Up @@ -1289,8 +1290,8 @@ def _SetObjective(self, obj, minimize):
self.__model.objective.offset = obj
self.__model.objective.scaling_factor = 1
else:
raise TypeError('TypeError: ' + str(obj) +
' is not a valid objective')
raise TypeError(
'TypeError: ' + str(obj) + ' is not a valid objective')

def Minimize(self, obj):
"""Sets the objective of the model to minimize(obj)."""
Expand Down Expand Up @@ -1333,11 +1334,11 @@ def AssertIsBooleanVariable(self, x):
if isinstance(x, IntVar):
var = self.__model.variables[x.Index()]
if len(var.domain) != 2 or var.domain[0] < 0 or var.domain[1] > 1:
raise TypeError('TypeError: ' + str(x) +
' is not a boolean variable')
raise TypeError(
'TypeError: ' + str(x) + ' is not a boolean variable')
elif not isinstance(x, _NotBooleanVariable):
raise TypeError('TypeError: ' + str(x) +
' is not a boolean variable')
raise TypeError(
'TypeError: ' + str(x) + ' is not a boolean variable')


def EvaluateLinearExpression(expression, solution):
Expand Down Expand Up @@ -1550,7 +1551,8 @@ def Value(self, expression):
elif isinstance(expr, IntVar):
value += coef * self.SolutionIntegerValue(expr.Index())
elif isinstance(expr, _NotBooleanVariable):
value += coef * (1 - self.SolutionIntegerValue(expr.Not().Index()))
value += coef * (
1 - self.SolutionIntegerValue(expr.Not().Index()))
return value


Expand Down
13 changes: 7 additions & 6 deletions ortools/sat/python/visualization.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,9 @@ def SeedRandomColor(self, seed=0):
random.seed(seed)

def RandomColor(self):
return 'rgb(%i,%i,%i)' % (random.randint(0, 255), random.randint(
0, 255), random.randint(0, 255))
return 'rgb(%i,%i,%i)' % (random.randint(0, 255),
random.randint(0, 255),
random.randint(0, 255))


def DisplayJobshop(starts, durations, machines, name):
Expand Down Expand Up @@ -144,8 +145,8 @@ def AddXScale(self, step=1):
self.__dwg.line((o, y), (self.__sizex * s + o, y), stroke='black'))
for i in range(0, int(self.__sizex) + 1, step):
self.__dwg.add(
self.__dwg.line((o + i * s, y - dy), (o + i * s, y + dy),
stroke='black'))
self.__dwg.line(
(o + i * s, y - dy), (o + i * s, y + dy), stroke='black'))

def AddYScale(self, step=1):
"""Add an scale on the y axis."""
Expand All @@ -157,8 +158,8 @@ def AddYScale(self, step=1):
self.__dwg.line((x, o), (x, self.__sizey * s + o), stroke='black'))
for i in range(0, int(self.__sizey) + 1, step):
self.__dwg.add(
self.__dwg.line((x - dx, i * s + o), (x + dx, i * s + o),
stroke='black'))
self.__dwg.line(
(x - dx, i * s + o), (x + dx, i * s + o), stroke='black'))

def AddTitle(self, title):
"""Add a title to the drawing."""
Expand Down
5 changes: 4 additions & 1 deletion ortools/sat/sat_parameters.proto
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ option java_multiple_files = true;
// Contains the definitions for all the sat algorithm parameters and their
// default values.
//
// NEXT TAG: 129
// NEXT TAG: 130
message SatParameters {
// ==========================================================================
// Branching and polarity
Expand Down Expand Up @@ -663,6 +663,9 @@ message SatParameters {
optional int32 lns_num_threads = 102 [default = 1];
optional bool lns_focus_on_decision_variables = 105 [default = false];

// Turns on relaxation induced neighborhood generator.
optional bool use_rins_lns = 129 [default = false];

// Randomize fixed search.
optional bool randomize_search = 103 [default = false];

Expand Down

0 comments on commit 2b9adc6

Please sign in to comment.