From 3ef163bcd0214543562703b2d34b3b42870e0d56 Mon Sep 17 00:00:00 2001 From: Gustavo Delazeri Date: Mon, 20 Jul 2026 18:30:23 +0200 Subject: [PATCH 1/4] add patch from issue1199 implementing the new LP interface --- .../landmark_cost_partitioning_algorithms.cc | 6 +- src/search/lp/cplex_solver_interface.cc | 196 ++++-------------- src/search/lp/cplex_solver_interface.h | 66 +----- src/search/lp/lp_solver.cc | 62 ++++-- src/search/lp/lp_solver.h | 42 ++-- src/search/lp/solver_interface.h | 7 +- src/search/lp/soplex_solver_interface.cc | 125 +++++++++-- src/search/lp/soplex_solver_interface.h | 5 +- .../delete_relaxation_if_constraints.cc | 94 ++++++++- .../delete_relaxation_rr_constraints.cc | 118 +++++++++-- .../operator_counting/lm_cut_constraints.cc | 12 +- .../operator_counting/pho_constraints.cc | 22 +- .../state_equation_constraints.cc | 23 +- src/search/potentials/potential_optimizer.cc | 23 +- 14 files changed, 498 insertions(+), 303 deletions(-) diff --git a/src/search/landmarks/landmark_cost_partitioning_algorithms.cc b/src/search/landmarks/landmark_cost_partitioning_algorithms.cc index 18a1c0b4bc..fc7e754c14 100644 --- a/src/search/landmarks/landmark_cost_partitioning_algorithms.cc +++ b/src/search/landmarks/landmark_cost_partitioning_algorithms.cc @@ -190,12 +190,12 @@ lp::LinearProgram OptimalCostPartitioningAlgorithm::build_initial_lp() { say that the operator's total cost must fall between 0 and the real operator cost. */ - lp_constraints.resize(num_rows, lp::LPConstraint(0.0, 0.0)); + lp_constraints.resize(num_rows, lp::LPConstraint(lp::Sense::LE, 0.0)); for (size_t op_id = 0; op_id < operator_costs.size(); ++op_id) { - lp_constraints[op_id].set_lower_bound(0); - lp_constraints[op_id].set_upper_bound(operator_costs[op_id]); + lp_constraints[op_id].set_right_hand_side(operator_costs[op_id]); } + /* Coefficients of constraints will be updated and recreated in each state. We ignore them for the initial LP. */ return lp::LinearProgram( diff --git a/src/search/lp/cplex_solver_interface.cc b/src/search/lp/cplex_solver_interface.cc index 54d77ae2a0..76778b6a48 100644 --- a/src/search/lp/cplex_solver_interface.cc +++ b/src/search/lp/cplex_solver_interface.cc @@ -67,23 +67,7 @@ static void freeProblem(CPXENVptr env, CPXLPptr *problem) { CPX_CALL(CPXfreeprob, env, problem); } -static tuple bounds_to_sense_rhs_range( - double lb, double ub) { - if (lb <= -CPX_INFBOUND && ub >= CPX_INFBOUND) { - // CPLEX does not support <= or >= constraints without bounds. - return {'R', -CPX_INFBOUND, 2 * CPX_INFBOUND}; - } else if (lb <= -CPX_INFBOUND) { - return {'L', ub, 0}; - } else if (ub >= CPX_INFBOUND) { - return {'G', lb, 0}; - } else if (lb == ub) { - return {'E', lb, 0}; - } else { - return {'R', lb, ub - lb}; - } -} - -static int sense_to_cplex_sense(LPObjectiveSense sense) { +static int model_sense_to_cplex_sense(LPObjectiveSense sense) { if (sense == LPObjectiveSense::MINIMIZE) { return CPX_MIN; } else { @@ -91,6 +75,26 @@ static int sense_to_cplex_sense(LPObjectiveSense sense) { } } +static char constraint_sense_to_cplex_sense( + lp::Sense constraint_sense) { + switch (constraint_sense) { + case lp::Sense::LE: + return 'L'; + break; + case lp::Sense::GE: + return 'G'; + break; + case lp::Sense::EQ: + return 'E'; + break; + default: + cerr << "Unsupported constraint sense: " + << static_cast(constraint_sense) << endl; + utils::exit_with(utils::ExitCode::SEARCH_CRITICAL_ERROR); + } +} + + void CplexSolverInterface::CplexMatrix::assign_column_by_column( const named_vector::NamedVector &constraints, int num_cols) { coefficients.clear(); @@ -202,53 +206,31 @@ void CplexSolverInterface::CplexColumnsInfo::assign( } void CplexSolverInterface::CplexRowsInfo::assign( - const named_vector::NamedVector &constraints, int offset, - bool dense_range_values) { + const named_vector::NamedVector &constraints) { rhs.clear(); sense.clear(); - range_values.clear(); - range_indices.clear(); - int num_rows = constraints.size(); - sense.resize(num_rows); - rhs.resize(num_rows); - if (dense_range_values) { - range_values.resize(num_rows, 0); + if (num_rows > 0) { + sense.resize(num_rows); + rhs.resize(num_rows); } for (int row_index = 0; row_index < num_rows; ++row_index) { const LPConstraint &constraint = constraints[row_index]; - double lb = constraint.get_lower_bound(); - double ub = constraint.get_upper_bound(); - const auto &[sense_value, rhs_value, range_value] = - bounds_to_sense_rhs_range(lb, ub); - sense[row_index] = sense_value; + double rhs_value = constraint.get_right_hand_side(); + lp::Sense constraint_sense = constraint.get_sense(); + char cplex_sense = constraint_sense_to_cplex_sense(constraint_sense); + sense[row_index] = cplex_sense; rhs[row_index] = rhs_value; - if (sense_value == 'R') { - if (dense_range_values) { - range_values[row_index] = range_value; - } else { - range_values.push_back(range_value); - range_indices.push_back(offset + row_index); - } - } } assert(static_cast(rhs.size()) == constraints.size()); assert(static_cast(sense.size()) == constraints.size()); - assert(static_cast(range_values.size()) <= constraints.size()); - assert( - (dense_range_values && - (static_cast(range_values.size()) == constraints.size()) && - (range_indices.size() == 0)) || - (!dense_range_values && (range_values.size() == range_indices.size()))); } CplexSolverInterface::CplexSolverInterface() : env(nullptr), problem(nullptr), is_mip(false), - num_permanent_constraints(0), - num_unsatisfiable_constraints(0), - num_unsatisfiable_temp_constraints(0) { + num_permanent_constraints(0){ int status = 0; env = CPXopenCPLEX(&status); if (status) { @@ -269,41 +251,6 @@ CplexSolverInterface::~CplexSolverInterface() { } } -bool CplexSolverInterface::is_trivially_unsolvable() const { - return num_unsatisfiable_constraints + num_unsatisfiable_temp_constraints > - 0; -} - -void CplexSolverInterface::change_constraint_bounds( - int index, double lb, double ub) { - double current_lb = constraint_lower_bounds[index]; - double current_ub = constraint_upper_bounds[index]; - if (current_lb == lb && current_ub == ub) { - return; - } - const auto &[sense, rhs, range] = bounds_to_sense_rhs_range(lb, ub); - - CPX_CALL(CPXchgsense, env, problem, 1, &index, &sense); - CPX_CALL(CPXchgrhs, env, problem, 1, &index, &rhs); - CPX_CALL(CPXchgrngval, env, problem, 1, &index, &range); - - if (current_lb > current_ub && lb <= ub) { - if (index < num_permanent_constraints) { - --num_unsatisfiable_constraints; - } else { - --num_unsatisfiable_temp_constraints; - } - } else if (current_lb <= current_ub && lb > ub) { - if (index < num_permanent_constraints) { - ++num_unsatisfiable_constraints; - } else { - ++num_unsatisfiable_temp_constraints; - } - } - constraint_lower_bounds[index] = lb; - constraint_upper_bounds[index] = ub; -} - void CplexSolverInterface::load_problem(const LinearProgram &lp) { if (problem) { freeProblem(env, &problem); @@ -316,25 +263,17 @@ void CplexSolverInterface::load_problem(const LinearProgram &lp) { return v.is_integer; }); - const named_vector::NamedVector &constraints = - lp.get_constraints(); - num_permanent_constraints = constraints.size(); - num_unsatisfiable_constraints = 0; - for (const LPConstraint &constraint : constraints) { - if (constraint.get_lower_bound() > constraint.get_upper_bound()) { - ++num_unsatisfiable_constraints; - } - } - + const named_vector::NamedVector &constraints = lp.get_constraints(); + num_permanent_constraints = constraints.size(); matrix.assign_column_by_column(constraints, variables.size()); columns.assign(variables); rows.assign(constraints); CPX_CALL( CPXcopylp, env, problem, variables.size(), constraints.size(), - sense_to_cplex_sense(lp.get_sense()), columns.get_objective(), + model_sense_to_cplex_sense(lp.get_sense()), columns.get_objective(), rows.get_rhs(), rows.get_sense(), matrix.get_starts(), matrix.get_counts(), matrix.get_indices(), matrix.get_coefficients(), - columns.get_lb(), columns.get_ub(), rows.get_range_values()); + columns.get_lb(), columns.get_ub(), nullptr); if (is_mip) { CPX_CALL(CPXcopyctype, env, problem, columns.get_type()); @@ -342,13 +281,6 @@ void CplexSolverInterface::load_problem(const LinearProgram &lp) { assert(CPXgetprobtype(env, problem) == CPXPROB_LP); } - constraint_lower_bounds.clear(); - constraint_upper_bounds.clear(); - for (const LPConstraint &constraint : constraints) { - constraint_lower_bounds.push_back(constraint.get_lower_bound()); - constraint_upper_bounds.push_back(constraint.get_upper_bound()); - } - // Optionally set names. if (!lp.get_objective_name().empty()) { CPX_CALL(CPXchgprobname, env, problem, lp.get_objective_name().c_str()); @@ -369,14 +301,8 @@ void CplexSolverInterface::load_problem(const LinearProgram &lp) { void CplexSolverInterface::add_temporary_constraints( const named_vector::NamedVector &constraints) { - for (const LPConstraint &constraint : constraints) { - if (constraint.get_lower_bound() > constraint.get_upper_bound()) { - ++num_unsatisfiable_temp_constraints; - } - } - matrix.assign_row_by_row(constraints); - rows.assign(constraints, get_num_constraints(), false); + rows.assign(constraints); CplexNameData row_names(constraints); // CPXaddrows can add new variables as well, but we do not want any. static const int num_extra_columns = 0; @@ -386,21 +312,6 @@ void CplexSolverInterface::add_temporary_constraints( matrix.get_num_nonzeros(), rows.get_rhs(), rows.get_sense(), matrix.get_starts(), matrix.get_indices(), matrix.get_coefficients(), extra_column_names, row_names.get_names()); - - /* - If there are any ranged rows, we have to set up their ranges with a - separate call. - */ - if (rows.get_num_ranged_rows() > 0) { - CPX_CALL( - CPXchgrngval, env, problem, rows.get_num_ranged_rows(), - rows.get_range_indices(), rows.get_range_values()); - } - - for (const LPConstraint &constraint : constraints) { - constraint_lower_bounds.push_back(constraint.get_lower_bound()); - constraint_upper_bounds.push_back(constraint.get_upper_bound()); - } } void CplexSolverInterface::clear_temporary_constraints() { @@ -408,10 +319,6 @@ void CplexSolverInterface::clear_temporary_constraints() { int end = get_num_constraints() - 1; if (start <= end) { CPX_CALL(CPXdelrows, env, problem, start, end); - num_unsatisfiable_temp_constraints = 0; - - constraint_lower_bounds.resize(num_permanent_constraints); - constraint_upper_bounds.resize(num_permanent_constraints); } } @@ -434,12 +341,13 @@ void CplexSolverInterface::set_objective_coefficient( CPX_CALL(CPXchgobj, env, problem, 1, &index, &coefficient); } -void CplexSolverInterface::set_constraint_lower_bound(int index, double bound) { - change_constraint_bounds(index, bound, constraint_upper_bounds[index]); +void CplexSolverInterface::set_constraint_rhs(int index, double right_hand_side) { + CPX_CALL(CPXchgrhs, env, problem, 1, &index, &right_hand_side); } -void CplexSolverInterface::set_constraint_upper_bound(int index, double bound) { - change_constraint_bounds(index, constraint_lower_bounds[index], bound); +void CplexSolverInterface::set_constraint_sense(int index, lp::Sense sense) { + char cplex_sense = constraint_sense_to_cplex_sense(sense); + CPX_CALL(CPXchgsense, env, problem, 1, &index, &cplex_sense); } void CplexSolverInterface::set_variable_lower_bound(int index, double bound) { @@ -457,9 +365,7 @@ void CplexSolverInterface::set_mip_gap(double gap) { } void CplexSolverInterface::solve() { - if (is_trivially_unsolvable()) { - return; - } else if (is_mip) { + if (is_mip) { CPX_CALL(CPXmipopt, env, problem); } else { CPX_CALL(CPXlpopt, env, problem); @@ -467,23 +373,12 @@ void CplexSolverInterface::solve() { } void CplexSolverInterface::write_lp(const string &filename) const { - if (is_trivially_unsolvable()) { - cerr << "The LP has trivially unsatisfiable constraints that are not " - << "accurately represented in CPLEX. Writing it to a file would " - << "misrepresent the LP." << endl; - utils::exit_with(utils::ExitCode::SEARCH_CRITICAL_ERROR); - } // By not passing in a filetype, we let CPLEX infer it from the filename. static const char *filetype = nullptr; CPX_CALL(CPXwriteprob, env, problem, filename.c_str(), filetype); } void CplexSolverInterface::print_failure_analysis() const { - if (is_trivially_unsolvable()) { - cout << "LP/MIP is infeasible because of a trivially unsatisfiable " - << "constraint" << endl; - return; - } int status = CPXgetstat(env, problem); switch (status) { case CPX_STAT_OPTIMAL: @@ -515,25 +410,16 @@ void CplexSolverInterface::print_failure_analysis() const { } bool CplexSolverInterface::is_infeasible() const { - if (is_trivially_unsolvable()) { - return true; - } int status = CPXgetstat(env, problem); return status == CPX_STAT_INFEASIBLE || status == CPXMIP_INFEASIBLE; } bool CplexSolverInterface::is_unbounded() const { - if (is_trivially_unsolvable()) { - return false; - } int status = CPXgetstat(env, problem); return status == CPX_STAT_UNBOUNDED; } bool CplexSolverInterface::has_optimal_solution() const { - if (is_trivially_unsolvable()) { - return false; - } int status = CPXgetstat(env, problem); switch (status) { case CPX_STAT_OPTIMAL: diff --git a/src/search/lp/cplex_solver_interface.h b/src/search/lp/cplex_solver_interface.h index 1c2e171065..570512ca6a 100644 --- a/src/search/lp/cplex_solver_interface.h +++ b/src/search/lp/cplex_solver_interface.h @@ -31,24 +31,6 @@ class CplexSolverInterface : public SolverInterface { bool is_mip; int num_permanent_constraints; - /* - Our public interface allows using constraints of the form - LB <= expression <= UB - In cases where LB > UB, this constraint is trivially unsatisfiable. - CPLEX does not represent constraints like this and instead uses range - values, where the constraint is represented like this - expression - RNG = LB - where RNG is a variable restricted to take values from 0 to (UB - LB). - If LB > UB, the semantic instead is that RNG takes negative values between - (UB - LB) and 0. This means that in CPLEX, the constraint never is - trivially unsolvable. We still set the range value and the right-hand side - as described above but use negative range values to represent trivially - unsatisfiable constraints. The following two counters track how many such - constraints we have in the permanent and the temporary constraints. - */ - int num_unsatisfiable_constraints; - int num_unsatisfiable_temp_constraints; - /* Matrix data in CPLEX format for loading a new problem. Matrix entries are stored in sparse form: non-zero coefficients are either stored @@ -131,7 +113,8 @@ class CplexSolverInterface : public SolverInterface { // Objective value of each column (variable) std::vector objective; public: - void assign(const named_vector::NamedVector &variables); + void assign( + const named_vector::NamedVector &variables); double *get_lb() { return to_cplex_array(lb); } @@ -149,37 +132,17 @@ class CplexSolverInterface : public SolverInterface { class CplexRowsInfo { // Right-hand side value of a row std::vector rhs; - // Sense of a row (Greater or equal, Less or equal, Equal, or Range) + // Sense of a row (Greater or equal, Less or equal, or Equal) std::vector sense; - /* - If the sense of a row is Range, then its value is restricted to the - interval (RHS, RHS + range_value). - */ - std::vector range_values; - /* - In case not all rows specify a sense, this gives the indices of the - rows that are ranged rows. - */ - std::vector range_indices; public: - void assign( - const named_vector::NamedVector &constraints, - int offset = 0, bool dense_range_values = true); + void assign(const named_vector::NamedVector &constraints); + double *get_rhs() { return to_cplex_array(rhs); } char *get_sense() { return to_cplex_array(sense); } - double *get_range_values() { - return to_cplex_array(range_values); - } - int *get_range_indices() { - return to_cplex_array(range_indices); - } - int get_num_ranged_rows() { - return range_indices.size(); - } }; class CplexNameData { @@ -233,21 +196,6 @@ class CplexSolverInterface : public SolverInterface { CplexColumnsInfo columns; CplexRowsInfo rows; std::vector objective_indices; - - /* - We store a copy of the current constraint bounds. We need to know the - current bounds when changing bounds, and accessing them through the CPLEX - interface has a significant overhead. Storing these vectors overlaps with - storing CplexRowsInfo above. The difference is that CplexRowsInfo stores - more information and we reuse it for temporary constraints, while we want - to keep the following vectors always synchronized with the full LP - (permanent and temporary constraints). - */ - std::vector constraint_lower_bounds; - std::vector constraint_upper_bounds; - - bool is_trivially_unsolvable() const; - void change_constraint_bounds(int index, double lb, double ub); public: CplexSolverInterface(); virtual ~CplexSolverInterface() override; @@ -261,8 +209,8 @@ class CplexSolverInterface : public SolverInterface { const std::vector &coefficients) override; virtual void set_objective_coefficient( int index, double coefficient) override; - virtual void set_constraint_lower_bound(int index, double bound) override; - virtual void set_constraint_upper_bound(int index, double bound) override; + virtual void set_constraint_rhs(int index, double right_hand_side) override; + virtual void set_constraint_sense(int index, lp::Sense sense) override; virtual void set_variable_lower_bound(int index, double bound) override; virtual void set_variable_upper_bound(int index, double bound) override; virtual void set_mip_gap(double gap) override; diff --git a/src/search/lp/lp_solver.cc b/src/search/lp/lp_solver.cc index 37b35952d3..e98ad9b26a 100644 --- a/src/search/lp/lp_solver.cc +++ b/src/search/lp/lp_solver.cc @@ -6,12 +6,29 @@ #ifdef HAS_SOPLEX #include "soplex_solver_interface.h" #endif +#ifdef HAS_GUROBI +#include "gurobi_solver_interface.h" +#endif +#ifdef HAS_HIGHS +#include "highs_solver_interface.h" +#endif #include "../plugins/plugin.h" using namespace std; namespace lp { + +std::ostream& operator<<(std::ostream& os, Sense s) { + switch (s) { + case Sense::GE: return os << ">="; + case Sense::LE: return os << "<="; + case Sense::EQ: return os << "=="; + } + return os; +} + + void add_lp_solver_option_to_feature(plugins::Feature &feature) { feature.add_option( "lpsolver", @@ -29,8 +46,8 @@ tuple get_lp_solver_arguments_from_options( return make_tuple(opts.get("lpsolver")); } -LPConstraint::LPConstraint(double lower_bound, double upper_bound) - : lower_bound(lower_bound), upper_bound(upper_bound) { +LPConstraint::LPConstraint(Sense sense, double right_hand_side) + : sense(sense), right_hand_side(right_hand_side) { } void LPConstraint::clear() { @@ -47,15 +64,9 @@ void LPConstraint::insert(int index, double coefficient) { coefficients.push_back(coefficient); } +// TODO: double check that this preserves the semantics of the old implementation. ostream &LPConstraint::dump( ostream &stream, const LinearProgram *program) const { - double infinity = numeric_limits::infinity(); - if (program) { - infinity = program->get_infinity(); - } - if (lower_bound != -infinity) { - stream << lower_bound << " <= "; - } for (size_t i = 0; i < variables.size(); ++i) { if (i != 0) stream << " + "; @@ -69,11 +80,7 @@ ostream &LPConstraint::dump( } stream << coefficients[i] << " * " << variable_name; } - if (upper_bound != infinity) { - stream << " <= " << upper_bound; - } else if (lower_bound == -infinity) { - stream << " <= infinity"; - } + stream << get_sense() << get_right_hand_side(); return stream; } @@ -137,6 +144,21 @@ LPSolver::LPSolver(LPSolverType solver_type) { missing_solver = "SoPlex"; #endif break; + case LPSolverType::GUROBI: +#ifdef HAS_GUROBI + pimpl = make_unique(); +#else + missing_solver = "Gurobi"; +#endif + break; + case LPSolverType::HIGHS: +#ifdef HAS_HIGHS + pimpl = make_unique(); +#else + missing_solver = "HiGHS"; +#endif + break; + default: ABORT("Unknown LP solver type."); } @@ -175,12 +197,12 @@ void LPSolver::set_objective_coefficient(int index, double coefficient) { pimpl->set_objective_coefficient(index, coefficient); } -void LPSolver::set_constraint_lower_bound(int index, double bound) { - pimpl->set_constraint_lower_bound(index, bound); +void LPSolver::set_constraint_rhs(int index, double right_hand_side) { + pimpl->set_constraint_rhs(index, right_hand_side); } -void LPSolver::set_constraint_upper_bound(int index, double bound) { - pimpl->set_constraint_upper_bound(index, bound); +void LPSolver::set_constraint_sense(int index, Sense sense) { + pimpl->set_constraint_sense(index, sense); } void LPSolver::set_variable_lower_bound(int index, double bound) { @@ -245,5 +267,7 @@ void LPSolver::print_statistics() const { static plugins::TypedEnumPlugin _enum_plugin( {{"cplex", "commercial solver by IBM"}, - {"soplex", "open source solver by ZIB"}}); + {"soplex", "open source solver by ZIB"}, + {"highs", "open source solver by the HiGHS team"}, + {"gurobi", "commercial solver by Gurobi"}}); } diff --git a/src/search/lp/lp_solver.h b/src/search/lp/lp_solver.h index 88baaedb86..f2a302123c 100644 --- a/src/search/lp/lp_solver.h +++ b/src/search/lp/lp_solver.h @@ -17,7 +17,9 @@ class Options; namespace lp { enum class LPSolverType { CPLEX, - SOPLEX + SOPLEX, + HIGHS, + GUROBI }; enum class LPObjectiveSense { @@ -31,13 +33,22 @@ std::tuple get_lp_solver_arguments_from_options( class LinearProgram; +enum class Sense { + GE, // ax >= b + LE, // ax <= b + EQ // ax = b +}; + +std::ostream& operator<<(std::ostream& os, Sense s); + + class LPConstraint { std::vector variables; std::vector coefficients; - double lower_bound; - double upper_bound; + Sense sense; + double right_hand_side; public: - LPConstraint(double lower_bound, double upper_bound); + LPConstraint(Sense sense, double right_hand_side); const std::vector &get_variables() const { return variables; @@ -46,17 +57,20 @@ class LPConstraint { return coefficients; } - double get_lower_bound() const { - return lower_bound; + double get_right_hand_side() const { + return right_hand_side; } - void set_lower_bound(double lb) { - lower_bound = lb; + + Sense get_sense() const { + return sense; } - double get_upper_bound() const { - return upper_bound; + + void set_right_hand_side(double rhs) { + right_hand_side = rhs; } - void set_upper_bound(double ub) { - upper_bound = ub; + + void set_sense(Sense s) { + sense = s; } void clear(); @@ -128,8 +142,8 @@ class LPSolver { void set_objective_coefficients(const std::vector &coefficients); void set_objective_coefficient(int index, double coefficient); - void set_constraint_lower_bound(int index, double bound); - void set_constraint_upper_bound(int index, double bound); + void set_constraint_rhs(int index, double right_hand_side); + void set_constraint_sense(int index, Sense sense); void set_variable_lower_bound(int index, double bound); void set_variable_upper_bound(int index, double bound); diff --git a/src/search/lp/solver_interface.h b/src/search/lp/solver_interface.h index 8699a6930c..e636812eea 100644 --- a/src/search/lp/solver_interface.h +++ b/src/search/lp/solver_interface.h @@ -12,6 +12,7 @@ class NamedVector; namespace lp { class LinearProgram; class LPConstraint; +enum class Sense; class SolverInterface { public: @@ -26,8 +27,8 @@ class SolverInterface { virtual void set_objective_coefficients( const std::vector &coefficients) = 0; virtual void set_objective_coefficient(int index, double coefficient) = 0; - virtual void set_constraint_lower_bound(int index, double bound) = 0; - virtual void set_constraint_upper_bound(int index, double bound) = 0; + virtual void set_constraint_rhs(int index, double right_hand_side) = 0; + virtual void set_constraint_sense(int index, lp::Sense sense) = 0; virtual void set_variable_lower_bound(int index, double bound) = 0; virtual void set_variable_upper_bound(int index, double bound) = 0; @@ -70,4 +71,4 @@ class SolverInterface { }; } -#endif +#endif \ No newline at end of file diff --git a/src/search/lp/soplex_solver_interface.cc b/src/search/lp/soplex_solver_interface.cc index c38f776368..986133fcca 100644 --- a/src/search/lp/soplex_solver_interface.cc +++ b/src/search/lp/soplex_solver_interface.cc @@ -18,6 +18,7 @@ static int get_obj_sense(LPObjectiveSense sense) { static LPRowSetReal constraints_to_row_set( const named_vector::NamedVector &constraints) { + int num_rows = constraints.size(); int num_nonzeros = 0; for (const LPConstraint &constraint : constraints) { @@ -25,18 +26,40 @@ static LPRowSetReal constraints_to_row_set( } LPRowSetReal rows(num_rows, num_nonzeros); + for (const LPConstraint &constraint : constraints) { const vector variables = constraint.get_variables(); const vector coefficients = constraint.get_coefficients(); - int num_entries = coefficients.size(); + + const int num_entries = static_cast(coefficients.size()); soplex::DSVectorReal entries(num_entries); for (int i = 0; i < num_entries; ++i) { entries.add(variables[i], coefficients[i]); } - rows.add( - constraint.get_lower_bound(), entries, - constraint.get_upper_bound()); + + const double b = constraint.get_right_hand_side(); + const lp::Sense s = constraint.get_sense(); + + double lhs, rhs; + switch (s) { + case lp::Sense::LE: + lhs = -soplex::infinity; + rhs = b; + break; + case lp::Sense::GE: + lhs = b; + rhs = soplex::infinity; + break; + case lp::Sense::EQ: + lhs = b; + rhs = b; + break; + default: + throw std::logic_error("invalid sense"); + } + rows.add(lhs, entries, rhs); } + return rows; } @@ -71,12 +94,25 @@ void SoPlexSolverInterface::load_problem(const LinearProgram &lp) { soplex.addRowsReal(constraints_to_row_set(lp.get_constraints())); num_permanent_constraints = lp.get_constraints().size(); num_temporary_constraints = 0; + + const auto &cons = lp.get_constraints(); + const int m = static_cast(cons.size()); + for (int r = 0; r < m; ++r) { + const auto &c = cons[r]; + constraint_senses.push_back(c.get_sense()); + } + } void SoPlexSolverInterface::add_temporary_constraints( const named_vector::NamedVector &constraints) { soplex.addRowsReal(constraints_to_row_set(constraints)); - num_temporary_constraints = constraints.size(); + num_temporary_constraints += constraints.size(); + + for (int i = 0; i < (int)constraints.size(); ++i) { + const auto &c = constraints[i]; + constraint_senses.push_back(c.get_sense()); + } } void SoPlexSolverInterface::clear_temporary_constraints() { @@ -84,12 +120,13 @@ void SoPlexSolverInterface::clear_temporary_constraints() { int first = num_permanent_constraints; int last = first + num_temporary_constraints - 1; soplex.removeRowRangeReal(first, last, nullptr); + constraint_senses.resize(num_permanent_constraints); num_temporary_constraints = 0; } } double SoPlexSolverInterface::get_infinity() const { - return infinity; + return infinity; // soplex::infinity } void SoPlexSolverInterface::set_objective_coefficients( @@ -105,15 +142,15 @@ void SoPlexSolverInterface::set_objective_coefficient( soplex.changeObjReal(index, coefficient); } -void SoPlexSolverInterface::set_constraint_lower_bound( - int index, double bound) { - soplex.changeLhsReal(index, bound); -} - -void SoPlexSolverInterface::set_constraint_upper_bound( - int index, double bound) { - soplex.changeRhsReal(index, bound); -} +//void SoPlexSolverInterface::set_constraint_lower_bound( +// int index, double bound) { +// soplex.changeLhsReal(index, bound); +//} +// +//void SoPlexSolverInterface::set_constraint_upper_bound( +// int index, double bound) { +// soplex.changeRhsReal(index, bound); +//} void SoPlexSolverInterface::set_variable_lower_bound(int index, double bound) { soplex.changeLowerReal(index, bound); @@ -240,4 +277,62 @@ bool SoPlexSolverInterface::has_temporary_constraints() const { void SoPlexSolverInterface::print_statistics() const { soplex.printStatistics(cout); } + +void SoPlexSolverInterface::set_constraint_rhs(int index, double b) { + const lp::Sense sense = constraint_senses[index]; + + if(sense == lp::Sense::GE) { + soplex.changeLhsReal(index, b); + } + else if(sense == lp::Sense::LE) { + soplex.changeRhsReal(index, b); + } + else if(sense == lp::Sense::EQ) { + soplex.changeLhsReal(index, b); + soplex.changeRhsReal(index, b); + } else { + throw std::runtime_error("Error: Unknown constraint sense."); + } +} + +void SoPlexSolverInterface::set_constraint_sense(int index, lp::Sense sense) { + const double lhs = soplex.lhsReal(index); + const double rhs = soplex.rhsReal(index); + + const bool lhs_is_neginf = (lhs == -infinity); + const bool rhs_is_posinf = (rhs == infinity); + + constraint_senses[index] = sense; + + double b; + if (lhs_is_neginf && !rhs_is_posinf) { + b = rhs; + } else if (!lhs_is_neginf && rhs_is_posinf) { + b = lhs; + } else if (!lhs_is_neginf && !rhs_is_posinf && lhs == rhs) { + b = rhs; + } else { + throw std::logic_error("invalid constraint"); + b = rhs; // keep compiler happy + } + + switch (sense) { + case lp::Sense::LE: + soplex.changeLhsReal(index, -infinity); + soplex.changeRhsReal(index, b); + break; + case lp::Sense::GE: + soplex.changeLhsReal(index, b); + soplex.changeRhsReal(index, infinity); + break; + case lp::Sense::EQ: + soplex.changeLhsReal(index, b); + soplex.changeRhsReal(index, b); + break; + } +} + + + + } diff --git a/src/search/lp/soplex_solver_interface.h b/src/search/lp/soplex_solver_interface.h index 6960f264e1..7579d33218 100644 --- a/src/search/lp/soplex_solver_interface.h +++ b/src/search/lp/soplex_solver_interface.h @@ -27,6 +27,7 @@ class SoPlexSolverInterface : public SolverInterface { mutable soplex::SoPlex soplex; int num_permanent_constraints; int num_temporary_constraints; + std::vector constraint_senses; public: SoPlexSolverInterface(); @@ -40,8 +41,8 @@ class SoPlexSolverInterface : public SolverInterface { const std::vector &coefficients) override; virtual void set_objective_coefficient( int index, double coefficient) override; - virtual void set_constraint_lower_bound(int index, double bound) override; - virtual void set_constraint_upper_bound(int index, double bound) override; + virtual void set_constraint_rhs(int index, double right_hand_side) override; + virtual void set_constraint_sense(int index, lp::Sense sense) override; virtual void set_variable_lower_bound(int index, double bound) override; virtual void set_variable_upper_bound(int index, double bound) override; diff --git a/src/search/operator_counting/delete_relaxation_if_constraints.cc b/src/search/operator_counting/delete_relaxation_if_constraints.cc index e52818edb4..1ed1b9a3ce 100644 --- a/src/search/operator_counting/delete_relaxation_if_constraints.cc +++ b/src/search/operator_counting/delete_relaxation_if_constraints.cc @@ -102,7 +102,6 @@ void DeleteRelaxationIFConstraints::create_constraints( const TaskProxy &task_proxy, lp::LinearProgram &lp) { LPVariables &variables = lp.get_variables(); LPConstraints &constraints = lp.get_constraints(); - double infinity = lp.get_infinity(); OperatorsProxy ops = task_proxy.get_operators(); VariablesProxy vars = task_proxy.get_variables(); @@ -126,7 +125,19 @@ void DeleteRelaxationIFConstraints::create_constraints( constraint_ids[var_id].resize(var.get_domain_size()); for (int value = 0; value < var.get_domain_size(); ++value) { constraint_ids[var_id][value] = constraints.size(); - constraints.emplace_back(0, infinity); + + /* + OLD INTERFACE CODE + constraints.emplace_back(0, infinity); + NEW INTERFACE CODE + constraints.emplace_back(lp::Sense::GE, 0); + + This creates a constraint of the form 0 <= ax <= infinity, which is equivalent to ax >= 0.\ + TODO: double check. + */ + constraints.emplace_back(lp::Sense::GE, 0); + + /* We add "- R_f" here, collect the achiever below and adapt the lower bound in each iteration, i.e., in update_constraints. */ @@ -149,7 +160,17 @@ void DeleteRelaxationIFConstraints::create_constraints( for (OperatorProxy op : ops) { for (EffectProxy eff : op.get_effects()) { FactPair f = eff.get_fact().get_pair(); - lp::LPConstraint constraint(0, infinity); + /* + OLD INTERFACE CODE + lp::LPConstraint constraint(0, infinity); + NEW INTERFACE CODE + lp::LPConstraint constraint(lp::Sense::GE, 0); + + This creates a constraint of the form 0 <= ax <= infinity, which is equivalent to ax >= 0. + TODO: double check. + */ + lp::LPConstraint constraint(lp::Sense::GE, 0); + constraint.insert(get_var_op_used(op), 1); constraint.insert(get_var_first_achiever(op, f), -1); constraints.push_back(constraint); @@ -162,7 +183,16 @@ void DeleteRelaxationIFConstraints::create_constraints( */ for (OperatorProxy op : ops) { for (FactProxy f : op.get_preconditions()) { - lp::LPConstraint constraint(0, infinity); + /* + OLD INTERFACE CODE + lp::LPConstraint constraint(0, infinity); + NEW INTERFACE CODE + lp::LPConstraint constraint(lp::Sense::GE, 0); + + This creates a constraint of the form 0 <= ax <= infinity, which is equivalent to ax >= 0. + TODO: double check. + */ + lp::LPConstraint constraint(lp::Sense::GE, 0); constraint.insert(get_var_fact_reached(f.get_pair()), 1); constraint.insert(get_var_op_used(op), -1); constraints.push_back(constraint); @@ -176,7 +206,16 @@ void DeleteRelaxationIFConstraints::create_constraints( */ for (OperatorProxy op : ops) { for (FactProxy f : op.get_preconditions()) { - lp::LPConstraint constraint(0, infinity); + /* + OLD INTERFACE CODE + lp::LPConstraint constraint(0, infinity); + NEW INTERFACE CODE + lp::LPConstraint constraint(lp::Sense::GE, 0); + + This creates a constraint of the form 0 <= ax <= infinity, which is equivalent to ax >= 0. + TODO: double check. + */ + lp::LPConstraint constraint(lp::Sense::GE, 0); constraint.insert(get_var_op_time(op), 1); constraint.insert(get_var_fact_time(f.get_pair()), -1); constraints.push_back(constraint); @@ -195,7 +234,16 @@ void DeleteRelaxationIFConstraints::create_constraints( for (OperatorProxy op : ops) { for (EffectProxy eff : op.get_effects()) { FactPair f = eff.get_fact().get_pair(); - lp::LPConstraint constraint(1 - M, infinity); + /* + OLD INTERFACE CODE + lp::LPConstraint constraint(1 - M, infinity); + NEW INTERFACE CODE + lp::LPConstraint constraint(lp::Sense::GE, 1 - M); + + This creates a constraint of the form 1 - M <= ax <= infinity, which is equivalent to ax >= 1 - M. + TODO: double check. + */ + lp::LPConstraint constraint(lp::Sense::GE, 1 - M); constraint.insert(get_var_fact_time(f), 1); constraint.insert(get_var_op_time(op), -1); constraint.insert(get_var_first_achiever(op, f), -M); @@ -209,7 +257,16 @@ void DeleteRelaxationIFConstraints::create_constraints( U_o <= C_o for each operator o. */ for (OperatorProxy op : ops) { - lp::LPConstraint constraint(0, infinity); + /* + OLD INTERFACE CODE + lp::LPConstraint constraint(0, infinity); + NEW INTERFACE CODE + lp::LPConstraint constraint(lp::Sense::GE, 0); + + This creates a constraint of the form 0 <= ax <= infinity, which is equivalent to ax >= 0. + TODO: double check. + */ + lp::LPConstraint constraint(lp::Sense::GE, 0); constraint.insert(op.get_id(), 1); constraint.insert(get_var_op_used(op), -1); constraints.push_back(constraint); @@ -227,13 +284,30 @@ bool DeleteRelaxationIFConstraints::update_constraints( const State &state, lp::LPSolver &lp_solver) { // Unset old bounds. for (FactPair f : last_state) { - lp_solver.set_constraint_lower_bound(get_constraint_id(f), 0); + /* + OLD INTERFACE CODE + lp_solver.set_constraint_lower_bound(get_constraint_id(f), 0); + NEW INTERFACE CODE + lp_solver.set_constraint_rhs(get_constraint_id(f), 0); + + All created constraints have sense >= (lp::Sense::GE), so setting the rhs to 0 is equivalent to setting the lower bound to 0. + TODO: double check. + */ + lp_solver.set_constraint_rhs(get_constraint_id(f), 0); } last_state.clear(); // Set new bounds. for (FactProxy f : state) { - lp_solver.set_constraint_lower_bound( - get_constraint_id(f.get_pair()), -1); + /* + OLD INTERFACE CODE + lp_solver.set_constraint_lower_bound(get_constraint_id(f.get_pair()), -1); + NEW INTERFACE CODE + lp_solver.set_constraint_rhs(get_constraint_id(f.get_pair()), -1); + + All created constraints have sense >= (lp::Sense::GE), so setting the rhs to -1 is equivalent to setting the lower bound to -1. + TODO: double check. + */ + lp_solver.set_constraint_rhs(get_constraint_id(f.get_pair()), -1); last_state.push_back(f.get_pair()); } return false; diff --git a/src/search/operator_counting/delete_relaxation_rr_constraints.cc b/src/search/operator_counting/delete_relaxation_rr_constraints.cc index 65fe249ebd..14765ce45e 100644 --- a/src/search/operator_counting/delete_relaxation_rr_constraints.cc +++ b/src/search/operator_counting/delete_relaxation_rr_constraints.cc @@ -286,7 +286,6 @@ void DeleteRelaxationRRConstraints::create_constraints( lp::LinearProgram &lp) { LPVariables &variables = lp.get_variables(); LPConstraints &constraints = lp.get_constraints(); - double infinity = lp.get_infinity(); OperatorsProxy ops = task_proxy.get_operators(); VariablesProxy vars = task_proxy.get_variables(); @@ -309,7 +308,16 @@ void DeleteRelaxationRRConstraints::create_constraints( constraint_offsets.push_back(constraints.size()); for (int value_p = 0; value_p < var_p.get_domain_size(); ++value_p) { FactPair fact_p(var_id_p, value_p); - lp::LPConstraint constraint(0, 0); + /* + OLD INTERFACE CODE + lp::LPConstraint constraint(0, 0); + NEW INTERFACE CODE + lp::LPConstraint constraint(lp::Sense::EQ, 0); + + This creates a constraint of the form 0 <= ax <= 0, which is equivalent to ax = 0. + TODO: double check. + */ + lp::LPConstraint constraint(lp::Sense::EQ, 0); constraint.insert(lp_var_ids.id_of_fp(fact_p), 1); constraints.push_back(move(constraint)); } @@ -348,7 +356,20 @@ void DeleteRelaxationRRConstraints::create_constraints( pair key = make_pair(pre, eff); if (!constraint3_ids.contains(key)) { constraint3_ids[key] = constraints.size(); - lp::LPConstraint constraint(0, 1); + /* + OLD INTERFACE CODE + lp::LPConstraint constraint(0, 1); + NEW INTERFACE CODE + lp::LPConstraint constraint(lp::Sense::GE, 0); + + This creates a constraint sum_{a in A where q in pre(a) and p in add(a)} f_{p,a} <= f_q, which is equivalent to 0 <= f_q - sum_{a in A where q in pre(a) and p in add(a)} f_{p,a}. + Variables f_q have a lower bound of 0 and an upper bound of 1. + Variables f_{p,a} have a lower bound of 0 and an upper bound of 1. + The upper bound of the constraint is necessarily 1. We only need to set the lower bound to 0 to get the correct semantics. + + TODO: double check. + */ + lp::LPConstraint constraint(lp::Sense::GE, 0); constraint.insert(lp_var_ids.id_of_fp(pre), 1); constraints.push_back(move(constraint)); } @@ -388,7 +409,16 @@ void DeleteRelaxationRRConstraints::create_constraints( for (OperatorProxy op : ops) { for (EffectProxy eff_proxy : op.get_effects()) { FactPair eff = eff_proxy.get_fact().get_pair(); - lp::LPConstraint constraint(0, infinity); + /* + OLD INTERFACE CODE + lp::LPConstraint constraint(0, infinity); + NEW INTERFACE CODE + lp::LPConstraint constraint(lp::Sense::GE, 0); + + This creates a constraint of the form 0 <= ax <= infinity, which is equivalent to ax >= 0. + TODO: double check. + */ + lp::LPConstraint constraint(lp::Sense::GE, 0); constraint.insert(lp_var_ids.id_of_fpa(eff, op), -1); constraint.insert(op.get_id(), 1); constraints.push_back(move(constraint)); @@ -401,7 +431,6 @@ void DeleteRelaxationRRConstraints::create_constraints_ve( const DeleteRelaxationRRConstraints::LPVariableIDs &lp_var_ids, lp::LinearProgram &lp) { LPConstraints &constraints = lp.get_constraints(); - double infinity = lp.get_infinity(); OperatorsProxy ops = task_proxy.get_operators(); /* @@ -417,7 +446,16 @@ void DeleteRelaxationRRConstraints::create_constraints_ve( FactPair pre = pre_proxy.get_pair(); for (EffectProxy eff_proxy : op.get_effects()) { FactPair eff = eff_proxy.get_fact().get_pair(); - lp::LPConstraint constraint(0, infinity); + /* + OLD INTERFACE CODE + lp::LPConstraint constraint(0, infinity); + NEW INTERFACE CODE + lp::LPConstraint constraint(lp::Sense::GE, 0); + + This creates a constraint of the form 0 <= ax <= infinity, which is equivalent to ax >= 0. + TODO: double check. + */ + lp::LPConstraint constraint(lp::Sense::GE, 0); constraint.insert(lp_var_ids.id_of_e(make_pair(pre, eff)), 1); constraint.insert(lp_var_ids.id_of_fpa(eff, op), -1); constraints.push_back(move(constraint)); @@ -439,7 +477,16 @@ void DeleteRelaxationRRConstraints::create_constraints_ve( pair reverse_edge = make_pair(edge.second, edge.first); if (lp_var_ids.has_e(reverse_edge)) { - lp::LPConstraint constraint(-infinity, 1); + /* + OLD INTERFACE CODE + lp::LPConstraint constraint(-infinity, 1); + NEW INTERFACE CODE + lp::LPConstraint constraint(lp::Sense::LE, 1); + + This creates a constraint of the form -infinity <= ax <= 1, which is equivalent to ax <= 1. + TODO: double check. + */ + lp::LPConstraint constraint(lp::Sense::LE, 1); constraint.insert(lp_var_ids.id_of_e(edge), 1); constraint.insert(lp_var_ids.id_of_e(reverse_edge), 1); constraints.push_back(move(constraint)); @@ -457,7 +504,16 @@ void DeleteRelaxationRRConstraints::create_constraints_ve( not have both p_i ordered before p_j, and p_j ordered before p_k. */ for (auto [pi, pj, pk] : ve_graph.get_delta()) { - lp::LPConstraint constraint(-infinity, 1); + /* + OLD INTERFACE CODE + lp::LPConstraint constraint(-infinity, 1); + NEW INTERFACE CODE + lp::LPConstraint constraint(lp::Sense::LE, 1); + + This creates a constraint of the form -infinity <= ax <= 1, which is equivalent to ax <= 1. + TODO: double check. + */ + lp::LPConstraint constraint(lp::Sense::LE, 1); constraint.insert(lp_var_ids.id_of_e(make_pair(pi, pj)), 1); constraint.insert(lp_var_ids.id_of_e(make_pair(pj, pk)), 1); constraint.insert(lp_var_ids.id_of_e(make_pair(pi, pk)), -1); @@ -481,7 +537,6 @@ void DeleteRelaxationRRConstraints::create_constraints_tl( preconditions, we have to achieve p_i before p_j. */ LPConstraints &constraints = lp.get_constraints(); - double infinity = lp.get_infinity(); int num_facts = 0; for (VariableProxy var : task_proxy.get_variables()) { num_facts += var.get_domain_size(); @@ -496,7 +551,16 @@ void DeleteRelaxationRRConstraints::create_constraints_tl( // Prevail conditions are compiled away in the paper. continue; } - lp::LPConstraint constraint(-infinity, num_facts - 1); + /* + OLD INTERFACE CODE + lp::LPConstraint constraint(-infinity, num_facts - 1); + NEW INTERFACE CODE + lp::LPConstraint constraint(lp::Sense::LE, num_facts - 1); + + This creates a constraint of the form -infinity <= ax <= num_facts - 1, which is equivalent to ax <= num_facts - 1. + TODO: double check. + */ + lp::LPConstraint constraint(lp::Sense::LE, num_facts - 1); constraint.insert(lp_var_ids.id_of_t(pre), 1); constraint.insert(lp_var_ids.id_of_t(eff), -1); constraint.insert(lp_var_ids.id_of_fpa(eff, op), num_facts); @@ -541,15 +605,41 @@ bool DeleteRelaxationRRConstraints::update_constraints( int con_id; for (FactPair f : last_state) { con_id = get_constraint_id(f); - lp_solver.set_constraint_lower_bound(con_id, 0); - lp_solver.set_constraint_upper_bound(con_id, 0); + /* + OLD INTERFACE CODE + lp_solver.set_constraint_lower_bound(con_id, 0); + lp_solver.set_constraint_upper_bound(con_id, 0); + NEW INTERFACE CODE + lp_solver.set_constraint_sense(con_id, lp::Sense::EQ); + lp_solver.set_constraint_rhs(con_id, 0); + + This creates a constraint of the form 0 <= ax <= 0, which is equivalent to ax = 0. + + TODO: double check, + TODO: double check if the updated constraints already are equality constraints, in which case we don't need to update the sense. + */ + lp_solver.set_constraint_sense(con_id, lp::Sense::EQ); + lp_solver.set_constraint_rhs(con_id, 0); } last_state.clear(); // Set new bounds. for (FactProxy f : state) { con_id = get_constraint_id(f.get_pair()); - lp_solver.set_constraint_lower_bound(con_id, 1); - lp_solver.set_constraint_upper_bound(con_id, 1); + /* + OLD INTERFACE CODE + lp_solver.set_constraint_lower_bound(con_id, 1); + lp_solver.set_constraint_upper_bound(con_id, 1); + NEW INTERFACE CODE + lp_solver.set_constraint_sense(con_id, lp::Sense::EQ); + lp_solver.set_constraint_rhs(con_id, 1); + + This creates a constraint of the form 1 <= ax <= 1, which is equivalent to ax = 1. + + TODO: double check, + TODO: double check if the updated constraints already are equality constraints, in which case we don't need to update the sense. + */ + lp_solver.set_constraint_sense(con_id, lp::Sense::EQ); + lp_solver.set_constraint_rhs(con_id, 1); last_state.push_back(f.get_pair()); } return false; diff --git a/src/search/operator_counting/lm_cut_constraints.cc b/src/search/operator_counting/lm_cut_constraints.cc index 0f0a9ad9cb..794f9cbf38 100644 --- a/src/search/operator_counting/lm_cut_constraints.cc +++ b/src/search/operator_counting/lm_cut_constraints.cc @@ -22,11 +22,19 @@ bool LMCutConstraints::update_constraints( const State &state, lp::LPSolver &lp_solver) { assert(landmark_generator); named_vector::NamedVector constraints; - double infinity = lp_solver.get_infinity(); bool dead_end = landmark_generator->compute_landmarks( state, nullptr, [&](const vector &op_ids, int /*cost*/) { - constraints.emplace_back(1.0, infinity); + /* + OLD INTERFACE CODE + constraints.emplace_back(1.0, infinity); + NEW INTERFACE CODE + constraints.emplace_back(lp::Sense::GE, 1.0); + + This creates a constraint of the form 1.0 <= ax <= infinity, which is equivalent to ax >= 1.0. + TODO: double check. + */ + constraints.emplace_back(lp::Sense::GE, 1.0); lp::LPConstraint &landmark_constraint = constraints.back(); for (int op_id : op_ids) { landmark_constraint.insert(op_id, 1.0); diff --git a/src/search/operator_counting/pho_constraints.cc b/src/search/operator_counting/pho_constraints.cc index 3e874b4b76..3e8c30c628 100644 --- a/src/search/operator_counting/pho_constraints.cc +++ b/src/search/operator_counting/pho_constraints.cc @@ -38,7 +38,16 @@ void PhOConstraints::initialize_constraints( lp.get_constraints(); constraint_offset = constraints.size(); for (const shared_ptr &pdb : *pdbs) { - constraints.emplace_back(0, lp.get_infinity()); + /* + OLD INTERFACE CODE + constraints.emplace_back(0, lp.get_infinity()); + NEW INTERFACE CODE + constraints.emplace_back(lp::Sense::GE, 0); + + This creates a constraint of the form 0 <= ax <= infinity, which is equivalent to ax >= 0. + TODO: double check. + */ + constraints.emplace_back(lp::Sense::GE, 0); lp::LPConstraint &constraint = constraints.back(); for (OperatorProxy op : task_proxy.get_operators()) { if (pdbs::is_operator_relevant(pdb->get_pattern(), op)) { @@ -58,7 +67,16 @@ bool PhOConstraints::update_constraints( if (h == numeric_limits::max()) { return true; } - lp_solver.set_constraint_lower_bound(constraint_id, h); + /* + OLD INTERFACE CODE + lp_solver.set_constraint_lower_bound(constraint_id, h); + NEW INTERFACE CODE + lp_solver.set_constraint_rhs(constraint_id, h); + + All created constraints have sense >= (lp::Sense::GE), so setting the rhs to h is equivalent to setting the lower bound to h. + TODO: double check. + */ + lp_solver.set_constraint_rhs(constraint_id, h); } return false; } diff --git a/src/search/operator_counting/state_equation_constraints.cc b/src/search/operator_counting/state_equation_constraints.cc index 81112fcfbf..29cb657c6f 100644 --- a/src/search/operator_counting/state_equation_constraints.cc +++ b/src/search/operator_counting/state_equation_constraints.cc @@ -58,7 +58,16 @@ void StateEquationConstraints::add_constraints( named_vector::NamedVector &constraints, double infinity) { for (vector &var_propositions : propositions) { for (Proposition &prop : var_propositions) { - lp::LPConstraint constraint(-infinity, infinity); + /* + OLD INTERFACE CODE + lp::LPConstraint constraint(-infinity, infinity); + NEW INTERFACE CODE + lp::LPConstraint constraint(lp::Sense::GE, -infinity); + + StateEquationConstraints::update_constraints updates the lower bound of the constraint, making it effectively a constraint of the form ax >= b. + TODO: double check + */ + lp::LPConstraint constraint(lp::Sense::GE, -infinity); add_indices_to_constraint(constraint, prop.always_produced_by, 1.0); add_indices_to_constraint( constraint, prop.sometimes_produced_by, 1.0); @@ -111,8 +120,16 @@ bool StateEquationConstraints::update_constraints( if (goal_state[var] == value) { ++lower_bound; } - lp_solver.set_constraint_lower_bound( - prop.constraint_index, lower_bound); + /* + OLD INTERFACE CODE + lp_solver.set_constraint_lower_bound(prop.constraint_index, lower_bound); + NEW INTERFACE CODE + lp_solver.set_constraint_rhs(prop.constraint_index, lower_bound); + + All created constraints have sense >= (lp::Sense::GE), so setting the rhs to lower_bound is equivalent to setting the lower bound to lower_bound. + TODO: double check. + */ + lp_solver.set_constraint_rhs(prop.constraint_index, lower_bound); } } } diff --git a/src/search/potentials/potential_optimizer.cc b/src/search/potentials/potential_optimizer.cc index 6a1c5955cd..3a0012d9cf 100644 --- a/src/search/potentials/potential_optimizer.cc +++ b/src/search/potentials/potential_optimizer.cc @@ -118,7 +118,16 @@ void PotentialOptimizer::construct_lp() { for (FactProxy pre : op.get_preconditions()) { var_to_precondition[pre.get_variable().get_id()] = pre.get_value(); } - lp::LPConstraint constraint(-infinity, op.get_cost()); + /* + OLD INTERFACE CODE + lp::LPConstraint constraint(-infinity, op.get_cost()); + NEW INTERFACE CODE + lp::LPConstraint constraint(lp::Sense::LE, op.get_cost()); + + This creates a constraint of the form -infinity <= ax <= cost(o), which is equivalent to ax <= cost(o). + TODO: double check. + */ + lp::LPConstraint constraint(lp::Sense::LE, op.get_cost()); vector> coefficients; for (EffectProxy effect : op.get_effects()) { VariableProxy var = effect.get_fact().get_variable(); @@ -185,7 +194,17 @@ void PotentialOptimizer::construct_lp() { // Create constraint: P_{V=v} <= P_{V=u} // Note that we could eliminate variables P_{V=u} if V is // undefined in the goal. - lp::LPConstraint constraint(-infinity, 0); + + /* + OLD INTERFACE CODE + lp::LPConstraint constraint(-infinity, 0); + NEW INTERFACE CODE + lp::LPConstraint constraint(lp::Sense::LE, 0); + + This creates a constraint of the form -infinity <= ax <= 0, which is equivalent to ax <= 0. + TODO: double check. + */ + lp::LPConstraint constraint(lp::Sense::LE, 0); constraint.insert(val_lp, 1); constraint.insert(undef_val_lp, -1); lp_constraints.push_back(constraint); From 70863992b2a73fa3f336c822cf768f145f200f95 Mon Sep 17 00:00:00 2001 From: Gustavo Delazeri Date: Tue, 21 Jul 2026 11:13:43 +0200 Subject: [PATCH 2/4] cleanup code --- .../landmark_cost_partitioning_algorithms.cc | 1 - src/search/lp/cplex_solver_interface.cc | 5 +- src/search/lp/cplex_solver_interface.h | 3 +- src/search/lp/lp_solver.cc | 26 +---- src/search/lp/lp_solver.h | 4 +- src/search/lp/solver_interface.h | 2 +- src/search/lp/soplex_solver_interface.cc | 103 +++++++----------- .../delete_relaxation_if_constraints.cc | 75 ------------- .../delete_relaxation_rr_constraints.cc | 93 ---------------- .../operator_counting/lm_cut_constraints.cc | 9 -- .../operator_counting/pho_constraints.cc | 18 --- .../state_equation_constraints.cc | 18 --- src/search/potentials/potential_optimizer.cc | 19 ---- 13 files changed, 48 insertions(+), 328 deletions(-) diff --git a/src/search/landmarks/landmark_cost_partitioning_algorithms.cc b/src/search/landmarks/landmark_cost_partitioning_algorithms.cc index fc7e754c14..4f71254526 100644 --- a/src/search/landmarks/landmark_cost_partitioning_algorithms.cc +++ b/src/search/landmarks/landmark_cost_partitioning_algorithms.cc @@ -195,7 +195,6 @@ lp::LinearProgram OptimalCostPartitioningAlgorithm::build_initial_lp() { lp_constraints[op_id].set_right_hand_side(operator_costs[op_id]); } - /* Coefficients of constraints will be updated and recreated in each state. We ignore them for the initial LP. */ return lp::LinearProgram( diff --git a/src/search/lp/cplex_solver_interface.cc b/src/search/lp/cplex_solver_interface.cc index 76778b6a48..f4e5c26c0b 100644 --- a/src/search/lp/cplex_solver_interface.cc +++ b/src/search/lp/cplex_solver_interface.cc @@ -94,7 +94,6 @@ static char constraint_sense_to_cplex_sense( } } - void CplexSolverInterface::CplexMatrix::assign_column_by_column( const named_vector::NamedVector &constraints, int num_cols) { coefficients.clear(); @@ -217,7 +216,7 @@ void CplexSolverInterface::CplexRowsInfo::assign( for (int row_index = 0; row_index < num_rows; ++row_index) { const LPConstraint &constraint = constraints[row_index]; double rhs_value = constraint.get_right_hand_side(); - lp::Sense constraint_sense = constraint.get_sense(); + lp::Sense constraint_sense = constraint.get_sense(); char cplex_sense = constraint_sense_to_cplex_sense(constraint_sense); sense[row_index] = cplex_sense; rhs[row_index] = rhs_value; @@ -264,7 +263,7 @@ void CplexSolverInterface::load_problem(const LinearProgram &lp) { }); const named_vector::NamedVector &constraints = lp.get_constraints(); - num_permanent_constraints = constraints.size(); + num_permanent_constraints = constraints.size(); matrix.assign_column_by_column(constraints, variables.size()); columns.assign(variables); rows.assign(constraints); diff --git a/src/search/lp/cplex_solver_interface.h b/src/search/lp/cplex_solver_interface.h index 570512ca6a..0d631b729e 100644 --- a/src/search/lp/cplex_solver_interface.h +++ b/src/search/lp/cplex_solver_interface.h @@ -113,8 +113,7 @@ class CplexSolverInterface : public SolverInterface { // Objective value of each column (variable) std::vector objective; public: - void assign( - const named_vector::NamedVector &variables); + void assign(const named_vector::NamedVector &variables); double *get_lb() { return to_cplex_array(lb); } diff --git a/src/search/lp/lp_solver.cc b/src/search/lp/lp_solver.cc index e98ad9b26a..dbe2ac4721 100644 --- a/src/search/lp/lp_solver.cc +++ b/src/search/lp/lp_solver.cc @@ -6,12 +6,6 @@ #ifdef HAS_SOPLEX #include "soplex_solver_interface.h" #endif -#ifdef HAS_GUROBI -#include "gurobi_solver_interface.h" -#endif -#ifdef HAS_HIGHS -#include "highs_solver_interface.h" -#endif #include "../plugins/plugin.h" @@ -28,7 +22,6 @@ std::ostream& operator<<(std::ostream& os, Sense s) { return os; } - void add_lp_solver_option_to_feature(plugins::Feature &feature) { feature.add_option( "lpsolver", @@ -64,7 +57,6 @@ void LPConstraint::insert(int index, double coefficient) { coefficients.push_back(coefficient); } -// TODO: double check that this preserves the semantics of the old implementation. ostream &LPConstraint::dump( ostream &stream, const LinearProgram *program) const { for (size_t i = 0; i < variables.size(); ++i) { @@ -142,20 +134,6 @@ LPSolver::LPSolver(LPSolverType solver_type) { pimpl = make_unique(); #else missing_solver = "SoPlex"; -#endif - break; - case LPSolverType::GUROBI: -#ifdef HAS_GUROBI - pimpl = make_unique(); -#else - missing_solver = "Gurobi"; -#endif - break; - case LPSolverType::HIGHS: -#ifdef HAS_HIGHS - pimpl = make_unique(); -#else - missing_solver = "HiGHS"; #endif break; @@ -267,7 +245,5 @@ void LPSolver::print_statistics() const { static plugins::TypedEnumPlugin _enum_plugin( {{"cplex", "commercial solver by IBM"}, - {"soplex", "open source solver by ZIB"}, - {"highs", "open source solver by the HiGHS team"}, - {"gurobi", "commercial solver by Gurobi"}}); + {"soplex", "open source solver by ZIB"}}); } diff --git a/src/search/lp/lp_solver.h b/src/search/lp/lp_solver.h index f2a302123c..b177e05a9f 100644 --- a/src/search/lp/lp_solver.h +++ b/src/search/lp/lp_solver.h @@ -17,9 +17,7 @@ class Options; namespace lp { enum class LPSolverType { CPLEX, - SOPLEX, - HIGHS, - GUROBI + SOPLEX }; enum class LPObjectiveSense { diff --git a/src/search/lp/solver_interface.h b/src/search/lp/solver_interface.h index e636812eea..742a84bf2f 100644 --- a/src/search/lp/solver_interface.h +++ b/src/search/lp/solver_interface.h @@ -71,4 +71,4 @@ class SolverInterface { }; } -#endif \ No newline at end of file +#endif diff --git a/src/search/lp/soplex_solver_interface.cc b/src/search/lp/soplex_solver_interface.cc index 986133fcca..df69571edc 100644 --- a/src/search/lp/soplex_solver_interface.cc +++ b/src/search/lp/soplex_solver_interface.cc @@ -24,42 +24,37 @@ static LPRowSetReal constraints_to_row_set( for (const LPConstraint &constraint : constraints) { num_nonzeros += constraint.get_coefficients().size(); } - LPRowSetReal rows(num_rows, num_nonzeros); - for (const LPConstraint &constraint : constraints) { const vector variables = constraint.get_variables(); const vector coefficients = constraint.get_coefficients(); - - const int num_entries = static_cast(coefficients.size()); + int num_entries = coefficients.size(); soplex::DSVectorReal entries(num_entries); for (int i = 0; i < num_entries; ++i) { entries.add(variables[i], coefficients[i]); } - const double b = constraint.get_right_hand_side(); const lp::Sense s = constraint.get_sense(); - double lhs, rhs; switch (s) { - case lp::Sense::LE: - lhs = -soplex::infinity; - rhs = b; - break; - case lp::Sense::GE: - lhs = b; - rhs = soplex::infinity; - break; - case lp::Sense::EQ: - lhs = b; - rhs = b; - break; - default: - throw std::logic_error("invalid sense"); + case lp::Sense::LE: + lhs = -soplex::infinity; + rhs = b; + break; + case lp::Sense::GE: + lhs = b; + rhs = soplex::infinity; + break; + case lp::Sense::EQ: + lhs = b; + rhs = b; + break; + default: + cerr << "Invalid constraint sense code: " << static_cast(s) << endl; + utils::exit_with(utils::ExitCode::SEARCH_CRITICAL_ERROR); } rows.add(lhs, entries, rhs); } - return rows; } @@ -94,21 +89,16 @@ void SoPlexSolverInterface::load_problem(const LinearProgram &lp) { soplex.addRowsReal(constraints_to_row_set(lp.get_constraints())); num_permanent_constraints = lp.get_constraints().size(); num_temporary_constraints = 0; - - const auto &cons = lp.get_constraints(); - const int m = static_cast(cons.size()); - for (int r = 0; r < m; ++r) { - const auto &c = cons[r]; - constraint_senses.push_back(c.get_sense()); + constraint_senses.clear(); + for (const LPConstraint &constraint : lp.get_constraints()) { + constraint_senses.push_back(constraint.get_sense()); } - } void SoPlexSolverInterface::add_temporary_constraints( const named_vector::NamedVector &constraints) { soplex.addRowsReal(constraints_to_row_set(constraints)); num_temporary_constraints += constraints.size(); - for (int i = 0; i < (int)constraints.size(); ++i) { const auto &c = constraints[i]; constraint_senses.push_back(c.get_sense()); @@ -126,7 +116,7 @@ void SoPlexSolverInterface::clear_temporary_constraints() { } double SoPlexSolverInterface::get_infinity() const { - return infinity; // soplex::infinity + return infinity; } void SoPlexSolverInterface::set_objective_coefficients( @@ -142,16 +132,6 @@ void SoPlexSolverInterface::set_objective_coefficient( soplex.changeObjReal(index, coefficient); } -//void SoPlexSolverInterface::set_constraint_lower_bound( -// int index, double bound) { -// soplex.changeLhsReal(index, bound); -//} -// -//void SoPlexSolverInterface::set_constraint_upper_bound( -// int index, double bound) { -// soplex.changeRhsReal(index, bound); -//} - void SoPlexSolverInterface::set_variable_lower_bound(int index, double bound) { soplex.changeLowerReal(index, bound); } @@ -279,7 +259,7 @@ void SoPlexSolverInterface::print_statistics() const { } void SoPlexSolverInterface::set_constraint_rhs(int index, double b) { - const lp::Sense sense = constraint_senses[index]; + const lp::Sense sense = constraint_senses[index]; if(sense == lp::Sense::GE) { soplex.changeLhsReal(index, b); @@ -291,7 +271,8 @@ void SoPlexSolverInterface::set_constraint_rhs(int index, double b) { soplex.changeLhsReal(index, b); soplex.changeRhsReal(index, b); } else { - throw std::runtime_error("Error: Unknown constraint sense."); + cerr << "Invalid constraint sense code: " << static_cast(sense) << endl; + utils::exit_with(utils::ExitCode::SEARCH_CRITICAL_ERROR); } } @@ -302,8 +283,6 @@ void SoPlexSolverInterface::set_constraint_sense(int index, lp::Sense sense) { const bool lhs_is_neginf = (lhs == -infinity); const bool rhs_is_posinf = (rhs == infinity); - constraint_senses[index] = sense; - double b; if (lhs_is_neginf && !rhs_is_posinf) { b = rhs; @@ -312,27 +291,29 @@ void SoPlexSolverInterface::set_constraint_sense(int index, lp::Sense sense) { } else if (!lhs_is_neginf && !rhs_is_posinf && lhs == rhs) { b = rhs; } else { - throw std::logic_error("invalid constraint"); b = rhs; // keep compiler happy + cerr << "Invalid constraint." << endl; + utils::exit_with(utils::ExitCode::SEARCH_CRITICAL_ERROR); } switch (sense) { - case lp::Sense::LE: - soplex.changeLhsReal(index, -infinity); - soplex.changeRhsReal(index, b); - break; - case lp::Sense::GE: - soplex.changeLhsReal(index, b); - soplex.changeRhsReal(index, infinity); - break; - case lp::Sense::EQ: - soplex.changeLhsReal(index, b); - soplex.changeRhsReal(index, b); - break; + case lp::Sense::LE: + soplex.changeLhsReal(index, -infinity); + soplex.changeRhsReal(index, b); + break; + case lp::Sense::GE: + soplex.changeLhsReal(index, b); + soplex.changeRhsReal(index, infinity); + break; + case lp::Sense::EQ: + soplex.changeLhsReal(index, b); + soplex.changeRhsReal(index, b); + break; + default: + cerr << "Invalid constraint sense code: " << static_cast(sense) << endl; + utils::exit_with(utils::ExitCode::SEARCH_CRITICAL_ERROR); } -} - - - + constraint_senses[index] = sense; +} } diff --git a/src/search/operator_counting/delete_relaxation_if_constraints.cc b/src/search/operator_counting/delete_relaxation_if_constraints.cc index 1ed1b9a3ce..72c1378514 100644 --- a/src/search/operator_counting/delete_relaxation_if_constraints.cc +++ b/src/search/operator_counting/delete_relaxation_if_constraints.cc @@ -125,19 +125,8 @@ void DeleteRelaxationIFConstraints::create_constraints( constraint_ids[var_id].resize(var.get_domain_size()); for (int value = 0; value < var.get_domain_size(); ++value) { constraint_ids[var_id][value] = constraints.size(); - - /* - OLD INTERFACE CODE - constraints.emplace_back(0, infinity); - NEW INTERFACE CODE - constraints.emplace_back(lp::Sense::GE, 0); - - This creates a constraint of the form 0 <= ax <= infinity, which is equivalent to ax >= 0.\ - TODO: double check. - */ constraints.emplace_back(lp::Sense::GE, 0); - /* We add "- R_f" here, collect the achiever below and adapt the lower bound in each iteration, i.e., in update_constraints. */ @@ -160,17 +149,7 @@ void DeleteRelaxationIFConstraints::create_constraints( for (OperatorProxy op : ops) { for (EffectProxy eff : op.get_effects()) { FactPair f = eff.get_fact().get_pair(); - /* - OLD INTERFACE CODE - lp::LPConstraint constraint(0, infinity); - NEW INTERFACE CODE - lp::LPConstraint constraint(lp::Sense::GE, 0); - - This creates a constraint of the form 0 <= ax <= infinity, which is equivalent to ax >= 0. - TODO: double check. - */ lp::LPConstraint constraint(lp::Sense::GE, 0); - constraint.insert(get_var_op_used(op), 1); constraint.insert(get_var_first_achiever(op, f), -1); constraints.push_back(constraint); @@ -183,15 +162,6 @@ void DeleteRelaxationIFConstraints::create_constraints( */ for (OperatorProxy op : ops) { for (FactProxy f : op.get_preconditions()) { - /* - OLD INTERFACE CODE - lp::LPConstraint constraint(0, infinity); - NEW INTERFACE CODE - lp::LPConstraint constraint(lp::Sense::GE, 0); - - This creates a constraint of the form 0 <= ax <= infinity, which is equivalent to ax >= 0. - TODO: double check. - */ lp::LPConstraint constraint(lp::Sense::GE, 0); constraint.insert(get_var_fact_reached(f.get_pair()), 1); constraint.insert(get_var_op_used(op), -1); @@ -206,15 +176,6 @@ void DeleteRelaxationIFConstraints::create_constraints( */ for (OperatorProxy op : ops) { for (FactProxy f : op.get_preconditions()) { - /* - OLD INTERFACE CODE - lp::LPConstraint constraint(0, infinity); - NEW INTERFACE CODE - lp::LPConstraint constraint(lp::Sense::GE, 0); - - This creates a constraint of the form 0 <= ax <= infinity, which is equivalent to ax >= 0. - TODO: double check. - */ lp::LPConstraint constraint(lp::Sense::GE, 0); constraint.insert(get_var_op_time(op), 1); constraint.insert(get_var_fact_time(f.get_pair()), -1); @@ -234,15 +195,6 @@ void DeleteRelaxationIFConstraints::create_constraints( for (OperatorProxy op : ops) { for (EffectProxy eff : op.get_effects()) { FactPair f = eff.get_fact().get_pair(); - /* - OLD INTERFACE CODE - lp::LPConstraint constraint(1 - M, infinity); - NEW INTERFACE CODE - lp::LPConstraint constraint(lp::Sense::GE, 1 - M); - - This creates a constraint of the form 1 - M <= ax <= infinity, which is equivalent to ax >= 1 - M. - TODO: double check. - */ lp::LPConstraint constraint(lp::Sense::GE, 1 - M); constraint.insert(get_var_fact_time(f), 1); constraint.insert(get_var_op_time(op), -1); @@ -257,15 +209,6 @@ void DeleteRelaxationIFConstraints::create_constraints( U_o <= C_o for each operator o. */ for (OperatorProxy op : ops) { - /* - OLD INTERFACE CODE - lp::LPConstraint constraint(0, infinity); - NEW INTERFACE CODE - lp::LPConstraint constraint(lp::Sense::GE, 0); - - This creates a constraint of the form 0 <= ax <= infinity, which is equivalent to ax >= 0. - TODO: double check. - */ lp::LPConstraint constraint(lp::Sense::GE, 0); constraint.insert(op.get_id(), 1); constraint.insert(get_var_op_used(op), -1); @@ -284,29 +227,11 @@ bool DeleteRelaxationIFConstraints::update_constraints( const State &state, lp::LPSolver &lp_solver) { // Unset old bounds. for (FactPair f : last_state) { - /* - OLD INTERFACE CODE - lp_solver.set_constraint_lower_bound(get_constraint_id(f), 0); - NEW INTERFACE CODE - lp_solver.set_constraint_rhs(get_constraint_id(f), 0); - - All created constraints have sense >= (lp::Sense::GE), so setting the rhs to 0 is equivalent to setting the lower bound to 0. - TODO: double check. - */ lp_solver.set_constraint_rhs(get_constraint_id(f), 0); } last_state.clear(); // Set new bounds. for (FactProxy f : state) { - /* - OLD INTERFACE CODE - lp_solver.set_constraint_lower_bound(get_constraint_id(f.get_pair()), -1); - NEW INTERFACE CODE - lp_solver.set_constraint_rhs(get_constraint_id(f.get_pair()), -1); - - All created constraints have sense >= (lp::Sense::GE), so setting the rhs to -1 is equivalent to setting the lower bound to -1. - TODO: double check. - */ lp_solver.set_constraint_rhs(get_constraint_id(f.get_pair()), -1); last_state.push_back(f.get_pair()); } diff --git a/src/search/operator_counting/delete_relaxation_rr_constraints.cc b/src/search/operator_counting/delete_relaxation_rr_constraints.cc index 14765ce45e..625664c906 100644 --- a/src/search/operator_counting/delete_relaxation_rr_constraints.cc +++ b/src/search/operator_counting/delete_relaxation_rr_constraints.cc @@ -308,15 +308,6 @@ void DeleteRelaxationRRConstraints::create_constraints( constraint_offsets.push_back(constraints.size()); for (int value_p = 0; value_p < var_p.get_domain_size(); ++value_p) { FactPair fact_p(var_id_p, value_p); - /* - OLD INTERFACE CODE - lp::LPConstraint constraint(0, 0); - NEW INTERFACE CODE - lp::LPConstraint constraint(lp::Sense::EQ, 0); - - This creates a constraint of the form 0 <= ax <= 0, which is equivalent to ax = 0. - TODO: double check. - */ lp::LPConstraint constraint(lp::Sense::EQ, 0); constraint.insert(lp_var_ids.id_of_fp(fact_p), 1); constraints.push_back(move(constraint)); @@ -356,19 +347,6 @@ void DeleteRelaxationRRConstraints::create_constraints( pair key = make_pair(pre, eff); if (!constraint3_ids.contains(key)) { constraint3_ids[key] = constraints.size(); - /* - OLD INTERFACE CODE - lp::LPConstraint constraint(0, 1); - NEW INTERFACE CODE - lp::LPConstraint constraint(lp::Sense::GE, 0); - - This creates a constraint sum_{a in A where q in pre(a) and p in add(a)} f_{p,a} <= f_q, which is equivalent to 0 <= f_q - sum_{a in A where q in pre(a) and p in add(a)} f_{p,a}. - Variables f_q have a lower bound of 0 and an upper bound of 1. - Variables f_{p,a} have a lower bound of 0 and an upper bound of 1. - The upper bound of the constraint is necessarily 1. We only need to set the lower bound to 0 to get the correct semantics. - - TODO: double check. - */ lp::LPConstraint constraint(lp::Sense::GE, 0); constraint.insert(lp_var_ids.id_of_fp(pre), 1); constraints.push_back(move(constraint)); @@ -409,15 +387,6 @@ void DeleteRelaxationRRConstraints::create_constraints( for (OperatorProxy op : ops) { for (EffectProxy eff_proxy : op.get_effects()) { FactPair eff = eff_proxy.get_fact().get_pair(); - /* - OLD INTERFACE CODE - lp::LPConstraint constraint(0, infinity); - NEW INTERFACE CODE - lp::LPConstraint constraint(lp::Sense::GE, 0); - - This creates a constraint of the form 0 <= ax <= infinity, which is equivalent to ax >= 0. - TODO: double check. - */ lp::LPConstraint constraint(lp::Sense::GE, 0); constraint.insert(lp_var_ids.id_of_fpa(eff, op), -1); constraint.insert(op.get_id(), 1); @@ -446,15 +415,6 @@ void DeleteRelaxationRRConstraints::create_constraints_ve( FactPair pre = pre_proxy.get_pair(); for (EffectProxy eff_proxy : op.get_effects()) { FactPair eff = eff_proxy.get_fact().get_pair(); - /* - OLD INTERFACE CODE - lp::LPConstraint constraint(0, infinity); - NEW INTERFACE CODE - lp::LPConstraint constraint(lp::Sense::GE, 0); - - This creates a constraint of the form 0 <= ax <= infinity, which is equivalent to ax >= 0. - TODO: double check. - */ lp::LPConstraint constraint(lp::Sense::GE, 0); constraint.insert(lp_var_ids.id_of_e(make_pair(pre, eff)), 1); constraint.insert(lp_var_ids.id_of_fpa(eff, op), -1); @@ -477,15 +437,6 @@ void DeleteRelaxationRRConstraints::create_constraints_ve( pair reverse_edge = make_pair(edge.second, edge.first); if (lp_var_ids.has_e(reverse_edge)) { - /* - OLD INTERFACE CODE - lp::LPConstraint constraint(-infinity, 1); - NEW INTERFACE CODE - lp::LPConstraint constraint(lp::Sense::LE, 1); - - This creates a constraint of the form -infinity <= ax <= 1, which is equivalent to ax <= 1. - TODO: double check. - */ lp::LPConstraint constraint(lp::Sense::LE, 1); constraint.insert(lp_var_ids.id_of_e(edge), 1); constraint.insert(lp_var_ids.id_of_e(reverse_edge), 1); @@ -504,15 +455,6 @@ void DeleteRelaxationRRConstraints::create_constraints_ve( not have both p_i ordered before p_j, and p_j ordered before p_k. */ for (auto [pi, pj, pk] : ve_graph.get_delta()) { - /* - OLD INTERFACE CODE - lp::LPConstraint constraint(-infinity, 1); - NEW INTERFACE CODE - lp::LPConstraint constraint(lp::Sense::LE, 1); - - This creates a constraint of the form -infinity <= ax <= 1, which is equivalent to ax <= 1. - TODO: double check. - */ lp::LPConstraint constraint(lp::Sense::LE, 1); constraint.insert(lp_var_ids.id_of_e(make_pair(pi, pj)), 1); constraint.insert(lp_var_ids.id_of_e(make_pair(pj, pk)), 1); @@ -551,15 +493,6 @@ void DeleteRelaxationRRConstraints::create_constraints_tl( // Prevail conditions are compiled away in the paper. continue; } - /* - OLD INTERFACE CODE - lp::LPConstraint constraint(-infinity, num_facts - 1); - NEW INTERFACE CODE - lp::LPConstraint constraint(lp::Sense::LE, num_facts - 1); - - This creates a constraint of the form -infinity <= ax <= num_facts - 1, which is equivalent to ax <= num_facts - 1. - TODO: double check. - */ lp::LPConstraint constraint(lp::Sense::LE, num_facts - 1); constraint.insert(lp_var_ids.id_of_t(pre), 1); constraint.insert(lp_var_ids.id_of_t(eff), -1); @@ -605,19 +538,6 @@ bool DeleteRelaxationRRConstraints::update_constraints( int con_id; for (FactPair f : last_state) { con_id = get_constraint_id(f); - /* - OLD INTERFACE CODE - lp_solver.set_constraint_lower_bound(con_id, 0); - lp_solver.set_constraint_upper_bound(con_id, 0); - NEW INTERFACE CODE - lp_solver.set_constraint_sense(con_id, lp::Sense::EQ); - lp_solver.set_constraint_rhs(con_id, 0); - - This creates a constraint of the form 0 <= ax <= 0, which is equivalent to ax = 0. - - TODO: double check, - TODO: double check if the updated constraints already are equality constraints, in which case we don't need to update the sense. - */ lp_solver.set_constraint_sense(con_id, lp::Sense::EQ); lp_solver.set_constraint_rhs(con_id, 0); } @@ -625,19 +545,6 @@ bool DeleteRelaxationRRConstraints::update_constraints( // Set new bounds. for (FactProxy f : state) { con_id = get_constraint_id(f.get_pair()); - /* - OLD INTERFACE CODE - lp_solver.set_constraint_lower_bound(con_id, 1); - lp_solver.set_constraint_upper_bound(con_id, 1); - NEW INTERFACE CODE - lp_solver.set_constraint_sense(con_id, lp::Sense::EQ); - lp_solver.set_constraint_rhs(con_id, 1); - - This creates a constraint of the form 1 <= ax <= 1, which is equivalent to ax = 1. - - TODO: double check, - TODO: double check if the updated constraints already are equality constraints, in which case we don't need to update the sense. - */ lp_solver.set_constraint_sense(con_id, lp::Sense::EQ); lp_solver.set_constraint_rhs(con_id, 1); last_state.push_back(f.get_pair()); diff --git a/src/search/operator_counting/lm_cut_constraints.cc b/src/search/operator_counting/lm_cut_constraints.cc index 794f9cbf38..b638f14130 100644 --- a/src/search/operator_counting/lm_cut_constraints.cc +++ b/src/search/operator_counting/lm_cut_constraints.cc @@ -25,15 +25,6 @@ bool LMCutConstraints::update_constraints( bool dead_end = landmark_generator->compute_landmarks( state, nullptr, [&](const vector &op_ids, int /*cost*/) { - /* - OLD INTERFACE CODE - constraints.emplace_back(1.0, infinity); - NEW INTERFACE CODE - constraints.emplace_back(lp::Sense::GE, 1.0); - - This creates a constraint of the form 1.0 <= ax <= infinity, which is equivalent to ax >= 1.0. - TODO: double check. - */ constraints.emplace_back(lp::Sense::GE, 1.0); lp::LPConstraint &landmark_constraint = constraints.back(); for (int op_id : op_ids) { diff --git a/src/search/operator_counting/pho_constraints.cc b/src/search/operator_counting/pho_constraints.cc index 3e8c30c628..19a081b125 100644 --- a/src/search/operator_counting/pho_constraints.cc +++ b/src/search/operator_counting/pho_constraints.cc @@ -38,15 +38,6 @@ void PhOConstraints::initialize_constraints( lp.get_constraints(); constraint_offset = constraints.size(); for (const shared_ptr &pdb : *pdbs) { - /* - OLD INTERFACE CODE - constraints.emplace_back(0, lp.get_infinity()); - NEW INTERFACE CODE - constraints.emplace_back(lp::Sense::GE, 0); - - This creates a constraint of the form 0 <= ax <= infinity, which is equivalent to ax >= 0. - TODO: double check. - */ constraints.emplace_back(lp::Sense::GE, 0); lp::LPConstraint &constraint = constraints.back(); for (OperatorProxy op : task_proxy.get_operators()) { @@ -67,15 +58,6 @@ bool PhOConstraints::update_constraints( if (h == numeric_limits::max()) { return true; } - /* - OLD INTERFACE CODE - lp_solver.set_constraint_lower_bound(constraint_id, h); - NEW INTERFACE CODE - lp_solver.set_constraint_rhs(constraint_id, h); - - All created constraints have sense >= (lp::Sense::GE), so setting the rhs to h is equivalent to setting the lower bound to h. - TODO: double check. - */ lp_solver.set_constraint_rhs(constraint_id, h); } return false; diff --git a/src/search/operator_counting/state_equation_constraints.cc b/src/search/operator_counting/state_equation_constraints.cc index 29cb657c6f..c2c1eeb707 100644 --- a/src/search/operator_counting/state_equation_constraints.cc +++ b/src/search/operator_counting/state_equation_constraints.cc @@ -58,15 +58,6 @@ void StateEquationConstraints::add_constraints( named_vector::NamedVector &constraints, double infinity) { for (vector &var_propositions : propositions) { for (Proposition &prop : var_propositions) { - /* - OLD INTERFACE CODE - lp::LPConstraint constraint(-infinity, infinity); - NEW INTERFACE CODE - lp::LPConstraint constraint(lp::Sense::GE, -infinity); - - StateEquationConstraints::update_constraints updates the lower bound of the constraint, making it effectively a constraint of the form ax >= b. - TODO: double check - */ lp::LPConstraint constraint(lp::Sense::GE, -infinity); add_indices_to_constraint(constraint, prop.always_produced_by, 1.0); add_indices_to_constraint( @@ -120,15 +111,6 @@ bool StateEquationConstraints::update_constraints( if (goal_state[var] == value) { ++lower_bound; } - /* - OLD INTERFACE CODE - lp_solver.set_constraint_lower_bound(prop.constraint_index, lower_bound); - NEW INTERFACE CODE - lp_solver.set_constraint_rhs(prop.constraint_index, lower_bound); - - All created constraints have sense >= (lp::Sense::GE), so setting the rhs to lower_bound is equivalent to setting the lower bound to lower_bound. - TODO: double check. - */ lp_solver.set_constraint_rhs(prop.constraint_index, lower_bound); } } diff --git a/src/search/potentials/potential_optimizer.cc b/src/search/potentials/potential_optimizer.cc index 3a0012d9cf..cbda84684a 100644 --- a/src/search/potentials/potential_optimizer.cc +++ b/src/search/potentials/potential_optimizer.cc @@ -118,15 +118,6 @@ void PotentialOptimizer::construct_lp() { for (FactProxy pre : op.get_preconditions()) { var_to_precondition[pre.get_variable().get_id()] = pre.get_value(); } - /* - OLD INTERFACE CODE - lp::LPConstraint constraint(-infinity, op.get_cost()); - NEW INTERFACE CODE - lp::LPConstraint constraint(lp::Sense::LE, op.get_cost()); - - This creates a constraint of the form -infinity <= ax <= cost(o), which is equivalent to ax <= cost(o). - TODO: double check. - */ lp::LPConstraint constraint(lp::Sense::LE, op.get_cost()); vector> coefficients; for (EffectProxy effect : op.get_effects()) { @@ -194,16 +185,6 @@ void PotentialOptimizer::construct_lp() { // Create constraint: P_{V=v} <= P_{V=u} // Note that we could eliminate variables P_{V=u} if V is // undefined in the goal. - - /* - OLD INTERFACE CODE - lp::LPConstraint constraint(-infinity, 0); - NEW INTERFACE CODE - lp::LPConstraint constraint(lp::Sense::LE, 0); - - This creates a constraint of the form -infinity <= ax <= 0, which is equivalent to ax <= 0. - TODO: double check. - */ lp::LPConstraint constraint(lp::Sense::LE, 0); constraint.insert(val_lp, 1); constraint.insert(undef_val_lp, -1); From 382c282cbcefb3704861f4350cb9cc86643dfb6e Mon Sep 17 00:00:00 2001 From: Gustavo Delazeri Date: Tue, 21 Jul 2026 11:14:09 +0200 Subject: [PATCH 3/4] format code --- src/search/lp/cplex_solver_interface.cc | 11 +-- src/search/lp/cplex_solver_interface.h | 2 +- src/search/lp/lp_solver.cc | 11 ++- src/search/lp/lp_solver.h | 9 +-- src/search/lp/soplex_solver_interface.cc | 76 +++++++++---------- .../delete_relaxation_rr_constraints.cc | 4 +- .../state_equation_constraints.cc | 3 +- 7 files changed, 60 insertions(+), 56 deletions(-) diff --git a/src/search/lp/cplex_solver_interface.cc b/src/search/lp/cplex_solver_interface.cc index f4e5c26c0b..1c80b785e7 100644 --- a/src/search/lp/cplex_solver_interface.cc +++ b/src/search/lp/cplex_solver_interface.cc @@ -75,8 +75,7 @@ static int model_sense_to_cplex_sense(LPObjectiveSense sense) { } } -static char constraint_sense_to_cplex_sense( - lp::Sense constraint_sense) { +static char constraint_sense_to_cplex_sense(lp::Sense constraint_sense) { switch (constraint_sense) { case lp::Sense::LE: return 'L'; @@ -229,7 +228,7 @@ CplexSolverInterface::CplexSolverInterface() : env(nullptr), problem(nullptr), is_mip(false), - num_permanent_constraints(0){ + num_permanent_constraints(0) { int status = 0; env = CPXopenCPLEX(&status); if (status) { @@ -262,7 +261,8 @@ void CplexSolverInterface::load_problem(const LinearProgram &lp) { return v.is_integer; }); - const named_vector::NamedVector &constraints = lp.get_constraints(); + const named_vector::NamedVector &constraints = + lp.get_constraints(); num_permanent_constraints = constraints.size(); matrix.assign_column_by_column(constraints, variables.size()); columns.assign(variables); @@ -340,7 +340,8 @@ void CplexSolverInterface::set_objective_coefficient( CPX_CALL(CPXchgobj, env, problem, 1, &index, &coefficient); } -void CplexSolverInterface::set_constraint_rhs(int index, double right_hand_side) { +void CplexSolverInterface::set_constraint_rhs( + int index, double right_hand_side) { CPX_CALL(CPXchgrhs, env, problem, 1, &index, &right_hand_side); } diff --git a/src/search/lp/cplex_solver_interface.h b/src/search/lp/cplex_solver_interface.h index 0d631b729e..fbd26d672c 100644 --- a/src/search/lp/cplex_solver_interface.h +++ b/src/search/lp/cplex_solver_interface.h @@ -135,7 +135,7 @@ class CplexSolverInterface : public SolverInterface { std::vector sense; public: void assign(const named_vector::NamedVector &constraints); - + double *get_rhs() { return to_cplex_array(rhs); } diff --git a/src/search/lp/lp_solver.cc b/src/search/lp/lp_solver.cc index dbe2ac4721..6773bb4f6f 100644 --- a/src/search/lp/lp_solver.cc +++ b/src/search/lp/lp_solver.cc @@ -13,11 +13,14 @@ using namespace std; namespace lp { -std::ostream& operator<<(std::ostream& os, Sense s) { +std::ostream &operator<<(std::ostream &os, Sense s) { switch (s) { - case Sense::GE: return os << ">="; - case Sense::LE: return os << "<="; - case Sense::EQ: return os << "=="; + case Sense::GE: + return os << ">="; + case Sense::LE: + return os << "<="; + case Sense::EQ: + return os << "=="; } return os; } diff --git a/src/search/lp/lp_solver.h b/src/search/lp/lp_solver.h index b177e05a9f..a8783ea04b 100644 --- a/src/search/lp/lp_solver.h +++ b/src/search/lp/lp_solver.h @@ -32,13 +32,12 @@ std::tuple get_lp_solver_arguments_from_options( class LinearProgram; enum class Sense { - GE, // ax >= b - LE, // ax <= b - EQ // ax = b + GE, // ax >= b + LE, // ax <= b + EQ // ax = b }; -std::ostream& operator<<(std::ostream& os, Sense s); - +std::ostream &operator<<(std::ostream &os, Sense s); class LPConstraint { std::vector variables; diff --git a/src/search/lp/soplex_solver_interface.cc b/src/search/lp/soplex_solver_interface.cc index df69571edc..038e642b8b 100644 --- a/src/search/lp/soplex_solver_interface.cc +++ b/src/search/lp/soplex_solver_interface.cc @@ -18,7 +18,6 @@ static int get_obj_sense(LPObjectiveSense sense) { static LPRowSetReal constraints_to_row_set( const named_vector::NamedVector &constraints) { - int num_rows = constraints.size(); int num_nonzeros = 0; for (const LPConstraint &constraint : constraints) { @@ -37,21 +36,22 @@ static LPRowSetReal constraints_to_row_set( const lp::Sense s = constraint.get_sense(); double lhs, rhs; switch (s) { - case lp::Sense::LE: - lhs = -soplex::infinity; - rhs = b; - break; - case lp::Sense::GE: - lhs = b; - rhs = soplex::infinity; - break; - case lp::Sense::EQ: - lhs = b; - rhs = b; - break; - default: - cerr << "Invalid constraint sense code: " << static_cast(s) << endl; - utils::exit_with(utils::ExitCode::SEARCH_CRITICAL_ERROR); + case lp::Sense::LE: + lhs = -soplex::infinity; + rhs = b; + break; + case lp::Sense::GE: + lhs = b; + rhs = soplex::infinity; + break; + case lp::Sense::EQ: + lhs = b; + rhs = b; + break; + default: + cerr << "Invalid constraint sense code: " << static_cast(s) + << endl; + utils::exit_with(utils::ExitCode::SEARCH_CRITICAL_ERROR); } rows.add(lhs, entries, rhs); } @@ -261,17 +261,16 @@ void SoPlexSolverInterface::print_statistics() const { void SoPlexSolverInterface::set_constraint_rhs(int index, double b) { const lp::Sense sense = constraint_senses[index]; - if(sense == lp::Sense::GE) { + if (sense == lp::Sense::GE) { soplex.changeLhsReal(index, b); - } - else if(sense == lp::Sense::LE) { + } else if (sense == lp::Sense::LE) { soplex.changeRhsReal(index, b); - } - else if(sense == lp::Sense::EQ) { + } else if (sense == lp::Sense::EQ) { soplex.changeLhsReal(index, b); soplex.changeRhsReal(index, b); } else { - cerr << "Invalid constraint sense code: " << static_cast(sense) << endl; + cerr << "Invalid constraint sense code: " << static_cast(sense) + << endl; utils::exit_with(utils::ExitCode::SEARCH_CRITICAL_ERROR); } } @@ -281,7 +280,7 @@ void SoPlexSolverInterface::set_constraint_sense(int index, lp::Sense sense) { const double rhs = soplex.rhsReal(index); const bool lhs_is_neginf = (lhs == -infinity); - const bool rhs_is_posinf = (rhs == infinity); + const bool rhs_is_posinf = (rhs == infinity); double b; if (lhs_is_neginf && !rhs_is_posinf) { @@ -297,21 +296,22 @@ void SoPlexSolverInterface::set_constraint_sense(int index, lp::Sense sense) { } switch (sense) { - case lp::Sense::LE: - soplex.changeLhsReal(index, -infinity); - soplex.changeRhsReal(index, b); - break; - case lp::Sense::GE: - soplex.changeLhsReal(index, b); - soplex.changeRhsReal(index, infinity); - break; - case lp::Sense::EQ: - soplex.changeLhsReal(index, b); - soplex.changeRhsReal(index, b); - break; - default: - cerr << "Invalid constraint sense code: " << static_cast(sense) << endl; - utils::exit_with(utils::ExitCode::SEARCH_CRITICAL_ERROR); + case lp::Sense::LE: + soplex.changeLhsReal(index, -infinity); + soplex.changeRhsReal(index, b); + break; + case lp::Sense::GE: + soplex.changeLhsReal(index, b); + soplex.changeRhsReal(index, infinity); + break; + case lp::Sense::EQ: + soplex.changeLhsReal(index, b); + soplex.changeRhsReal(index, b); + break; + default: + cerr << "Invalid constraint sense code: " << static_cast(sense) + << endl; + utils::exit_with(utils::ExitCode::SEARCH_CRITICAL_ERROR); } constraint_senses[index] = sense; diff --git a/src/search/operator_counting/delete_relaxation_rr_constraints.cc b/src/search/operator_counting/delete_relaxation_rr_constraints.cc index 625664c906..f84c7eda51 100644 --- a/src/search/operator_counting/delete_relaxation_rr_constraints.cc +++ b/src/search/operator_counting/delete_relaxation_rr_constraints.cc @@ -437,7 +437,7 @@ void DeleteRelaxationRRConstraints::create_constraints_ve( pair reverse_edge = make_pair(edge.second, edge.first); if (lp_var_ids.has_e(reverse_edge)) { - lp::LPConstraint constraint(lp::Sense::LE, 1); + lp::LPConstraint constraint(lp::Sense::LE, 1); constraint.insert(lp_var_ids.id_of_e(edge), 1); constraint.insert(lp_var_ids.id_of_e(reverse_edge), 1); constraints.push_back(move(constraint)); @@ -455,7 +455,7 @@ void DeleteRelaxationRRConstraints::create_constraints_ve( not have both p_i ordered before p_j, and p_j ordered before p_k. */ for (auto [pi, pj, pk] : ve_graph.get_delta()) { - lp::LPConstraint constraint(lp::Sense::LE, 1); + lp::LPConstraint constraint(lp::Sense::LE, 1); constraint.insert(lp_var_ids.id_of_e(make_pair(pi, pj)), 1); constraint.insert(lp_var_ids.id_of_e(make_pair(pj, pk)), 1); constraint.insert(lp_var_ids.id_of_e(make_pair(pi, pk)), -1); diff --git a/src/search/operator_counting/state_equation_constraints.cc b/src/search/operator_counting/state_equation_constraints.cc index c2c1eeb707..d5ffd735a2 100644 --- a/src/search/operator_counting/state_equation_constraints.cc +++ b/src/search/operator_counting/state_equation_constraints.cc @@ -111,7 +111,8 @@ bool StateEquationConstraints::update_constraints( if (goal_state[var] == value) { ++lower_bound; } - lp_solver.set_constraint_rhs(prop.constraint_index, lower_bound); + lp_solver.set_constraint_rhs( + prop.constraint_index, lower_bound); } } } From b8a5d57333a7aa4330ac789cd55813ad2b9e19cf Mon Sep 17 00:00:00 2001 From: Gustavo Delazeri Date: Mon, 27 Jul 2026 18:06:03 +0200 Subject: [PATCH 4/4] fix code formatting --- src/search/lp/lp_solver.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search/lp/lp_solver.cc b/src/search/lp/lp_solver.cc index 6773bb4f6f..1cc35c0b2f 100644 --- a/src/search/lp/lp_solver.cc +++ b/src/search/lp/lp_solver.cc @@ -13,7 +13,7 @@ using namespace std; namespace lp { -std::ostream &operator<<(std::ostream &os, Sense s) { +ostream &operator<<(ostream &os, Sense s) { switch (s) { case Sense::GE: return os << ">=";