// Copyright (C) 2000, International Business Machines // Corporation and others. All Rights Reserved. #ifndef _BCP_LP_BRANCH_H #define _BCP_LP_BRANCH_H // This file is fully docified. #include "BCP_math.hpp" #include "BCP_enum_branch.hpp" #include "BCP_vector.hpp" #include "BCP_lp_result.hpp" #include "BCP_USER.hpp" #include "BCP_var.hpp" #include "BCP_cut.hpp" #include "BCP_matrix.hpp" #include "OsiBranchingObject.hpp" class OsiSolverInterface; //############################################################################# /** This class exist only so that we can extract information from OsiIntegerBranchingObject */ class BCP_lp_integer_branching_object : public OsiIntegerBranchingObject { public: BCP_lp_integer_branching_object(const OsiIntegerBranchingObject* o) : OsiIntegerBranchingObject(*o) {} ~BCP_lp_integer_branching_object() {} inline const double* childBounds(int i) const { return i==0 ? down_:up_; } }; //----------------------------------------------------------------------------- /** This class exist only so that we can extract information from OsiIntegerBranchingObject */ class BCP_lp_sos_branching_object : public OsiSOSBranchingObject { public: BCP_lp_sos_branching_object(const OsiSOSBranchingObject* o) : OsiSOSBranchingObject(*o) {} ~BCP_lp_sos_branching_object() {} }; //############################################################################# // ASSUMPTION // ---------- // All a branching object does are: // - add cuts and/or variables // - change bounds on cuts and/or variables //############################################################################# // *THINK* : Maybe the methods should be hidden??? // *THINK* : Maybe we should just use vectors instead of pointers to vectors? // *THINK* : It'd simplify things and would not be significant extra storage // *THINK* : (maybe none at all, depending pon the system operator new...) /** This class describes a generic branching object. The object may contain variables and cuts to be added to the formulation and it contains the bound changes for each child. Note that it is unlikely that the user would need any of the member functions. She should simply call its constructor to create the object. The rest is internal to BCP. */ class BCP_lp_branching_object { private: /**@name Disabled methods */ /*@{*/ /** The copy constructor is declared but not defined to disable it. */ BCP_lp_branching_object(const BCP_lp_branching_object&); /** The assignment operator is declared but not defined to disable it. */ BCP_lp_branching_object& operator=(const BCP_lp_branching_object&); /*@}*/ public: /**@name Data members */ /*@{*/ /** The number of children for this branching object. */ int child_num; /** Variables to be added to the formulation. */ BCP_vec* vars_to_add; /** Cuts to be added to the formulation. */ BCP_vec* cuts_to_add; /**@name Data members referring to forced bound changes. "Forced" means that the branching rule specifies this change. */ /*@{*/ /** Positions of variables whose bounds change ("forcibly", by branching) in the children. If a position is non-negative, it refers to a variable in the current LP formulation. If a position is negative (-i), it refers to an added variable (i-1st). */ BCP_vec* forced_var_pos; /** Positions of cuts whose bounds change ("forcibly", by branching) in the children. If a position is non-negative, it refers to a cut in the current LP formulation. If a position is negative (-i), it refers to an added cut (i-1st). */ BCP_vec* forced_cut_pos; /** Contains the actual bounds for the variables indexed by forced_var_pos. List the lower/upper bound pairs for each of the variables, for each child in turn. The length of this vector is thus child_num * 2 * forced_var_pos.size(). */ BCP_vec* forced_var_bd; /** Contains the actual bounds for the cuts indexed by forced_cut_pos. List the lower/upper bound pairs for each of the cuts, for each child in turn. The length of this vector is thus child_num * 2 * forced_cut_pos.size(). */ BCP_vec* forced_cut_bd; /*@}*/ /**@name Data members referring to implied bound changes. "Implied" means that these changes could be made by applying the forced changes and then doing logical fixing. Therefore this information is not recorded in the Tree Manager when it stores the branching rule. However, if there are implied changes, it is useful to specify them, because strong branching may be more effecive then. The interpretation of these data members is identical to their forced counterparts. */ /*@{*/ /// BCP_vec* implied_var_pos; /// BCP_vec* implied_cut_pos; /// BCP_vec* implied_var_bd; /// BCP_vec* implied_cut_bd; /*@}*/ /**@name Data members referring to presolved values. The user may have presolved the candidate. Then she may store the termcodes and objvals of the children here. */ /*@{*/ BCP_vec* objval_; BCP_vec* termcode_; /*@}*/ /*@}*/ public: /**@name Constructor and destructor */ /*@{*/ /** The constructor makes a copy of each vector passed to it. If a 0 pointer is passed for one of the arguments that means that the vector is empty. new_{vars,cuts} contains the variables/cuts to be added and [fi][vc][pb] contains the forced/implied variable/cut positions/bounds. */ explicit BCP_lp_branching_object(const int children, BCP_vec* const new_vars, BCP_vec* const new_cuts, const BCP_vec* const fvp, const BCP_vec* const fcp, const BCP_vec* const fvb, const BCP_vec* const fcb, const BCP_vec* const ivp, const BCP_vec* const icp, const BCP_vec* const ivb, const BCP_vec* const icb ) : child_num(children), vars_to_add(0), cuts_to_add(0), forced_var_pos(0), forced_cut_pos(0), forced_var_bd(0), forced_cut_bd(0), implied_var_pos(0), implied_cut_pos(0), implied_var_bd(0), implied_cut_bd(0), objval_(0), termcode_(0) { if ( ((fvp == 0) ^ (fvb == 0)) || ((fcp == 0) ^ (fcb == 0)) || ((ivp == 0) ^ (ivb == 0)) || ((icp == 0) ^ (icb == 0)) ) throw BCP_fatal_error("Bad args to BCP_lp_branching_object()\n"); if ( (fvp && (2 * children* fvp->size() != fvb->size())) || (fcp && (2 * children* fcp->size() != fcb->size())) || (ivp && (2 * children* ivp->size() != ivb->size())) || (icp && (2 * children* icp->size() != icb->size())) ) throw BCP_fatal_error("Bad args to BCP_lp_branching_object()\n"); if (new_vars) { vars_to_add = new BCP_vec(*new_vars); new_vars->clear(); } if (new_cuts) { cuts_to_add = new BCP_vec(*new_cuts); new_cuts->clear(); } if (fvp) forced_var_pos = new BCP_vec(*fvp); if (fcp) forced_cut_pos = new BCP_vec(*fcp); if (fvb) forced_var_bd = new BCP_vec(*fvb); if (fcb) forced_cut_bd = new BCP_vec(*fcb); if (ivp) implied_var_pos = new BCP_vec(*ivp); if (icp) implied_cut_pos = new BCP_vec(*icp); if (ivb) implied_var_bd = new BCP_vec(*ivb); if (icb) implied_cut_bd = new BCP_vec(*icb); } BCP_lp_branching_object(const BCP_lp_integer_branching_object& o, const int* order); BCP_lp_branching_object(const OsiSolverInterface* osi, const BCP_lp_sos_branching_object& o, const int* order); inline void set_presolve_result(const BCP_vec& objval, const BCP_vec& termcode) { objval_ = new BCP_vec(objval); termcode_ = new BCP_vec(termcode); } // NOTE: when the desctructor `delete's vars_to_add and cuts_to_add, it // will just delete the pointers in the BCP_lp_..._sets (see the // destructors of those classes). But this is intentional, because the // vars/cuts will be actually deleted only later, when the unnecessery // ones are deleted from the formulation. /** The destructor deletes each vector. */ ~BCP_lp_branching_object() { delete vars_to_add; delete cuts_to_add; delete forced_var_pos; delete forced_cut_pos; delete forced_var_bd; delete forced_cut_bd; delete implied_var_pos; delete implied_cut_pos; delete implied_var_bd; delete implied_cut_bd; delete objval_; delete termcode_; } /*@}*/ /**@name Query methods */ /*@{*/ /** Return the number of variables whose bounds are affected by the branching. (Including both forced and implied changes.) */ inline int vars_affected() const { return (forced_var_bd ? forced_var_bd->size() / (2*child_num) : 0) + (implied_var_bd ? implied_var_bd->size() / (2*child_num) : 0); } /** Return the number of cuts whose bounds are affected by the branching. (Including both forced and implied changes.) */ inline int cuts_affected() const { return (forced_cut_bd ? forced_cut_bd->size() / (2*child_num) : 0) + (implied_cut_bd ? implied_cut_bd->size() / (2*child_num) : 0); } /** Return the number of variables added in the branching. */ inline int vars_added() const { return vars_to_add ? vars_to_add->size() : 0; } /** Return the number of cuts added in the branching. */ inline int cuts_added() const { return cuts_to_add ? cuts_to_add->size() : 0; } /** Return a const iterator to the position in the forced variable bound changes where the new bounds for the index-th child start. This method should be invoked only if the appropriate data member is non-0. */ inline BCP_vec::const_iterator forced_var_bd_child(const int index)const { return forced_var_bd->entry(2 * forced_var_pos->size() * index); } /** Return a const iterator to the position in the forced cut bound changes where the new bounds for the index-th child start. This method should be invoked only if the appropriate data member is non-0. */ inline BCP_vec::const_iterator forced_cut_bd_child(const int index)const { return forced_cut_bd->entry(2 * forced_cut_pos->size() * index); } /** Return a const iterator to the position in the implied variable bound changes where the new bounds for the index-th child start. This method should be invoked only if the appropriate data member is non-0. */ inline BCP_vec::const_iterator implied_var_bd_child(const int index)const{ return implied_var_bd->entry(2 * implied_var_pos->size() * index); } /** Return a const iterator to the position in the implied cut bound changes where the new bounds for the index-th child start. This method should be invoked only if the appropriate data member is non-0. */ inline BCP_vec::const_iterator implied_cut_bd_child(const int index)const{ return implied_cut_bd->entry(2 * implied_cut_pos->size() * index); } /*@}*/ /**@name Modifying methods */ /*@{*/ // this routine reorders the positions/bounds as well so that the positions // are in increasing order (the vars/cuts are already added to the problem, // no need to reorder those, too) /** This method "cleans up" the positions and bounds. First it re-indexes the negative positions starting from added_vars_start for the variables (and from added_cuts_start for the cuts). Then it reorders the positions (and follows that ordering with the bounds) so that the positions will be in increasing order. */ void init_pos_for_added(const int added_vars_start, const int added_cuts_start); /** This method invokes the appropriate methods of lp to apply the forced and implied bound changes of the child_ind-th child. */ void apply_child_bd(OsiSolverInterface* lp, const int child_ind) const; /** This method prints out some information about the branching object. (The positions, new bounds, the primal value of the variables.) */ void print_branching_info(const int orig_varnum, const double* x, const double * obj) const; /*@}*/ }; //############################################################################# /** A presolved branching object candidate. During strong branching all children of a branching object (of class \URL[BCP_lp_branching_object]{BCP_lp_branching_object.html}) are presolved to gain some insight how good it would be to branch on that particular branching object. The results of the presolved children are stored in an object of this type as well as the instructions what to do with each child in case this object is selected for branching. */ class BCP_presolved_lp_brobj { private: /**@name Disabled methods */ /*@{*/ /** The copy constructor is declared but not defined to disable it. */ BCP_presolved_lp_brobj(const BCP_presolved_lp_brobj&); /** The assignment operator is declared but not defined to disable it. */ BCP_presolved_lp_brobj& operator=(const BCP_presolved_lp_brobj&); /*@}*/ private: /**@name Data members */ /*@{*/ /** A pointer to the branching object (created internally or by the user) whose presolved data is stored in this object */ BCP_lp_branching_object* _candidate; /** A vector of lp results holding the actual presolved data. */ BCP_vec _lpres; // what to do with each child /** The action to be taken for each child (send back to the TM, keep for diving, prune it). Note that this member is created if and only if the object is selected for branching. */ BCP_vec _child_action; /** The user data to be passed around with the child nodes. Note that this member is created if and only if the object is selected for branching. */ BCP_vec _user_data; /** */ BCP_vec< BCP_vec > _new_cuts; BCP_vec< BCP_vec > _new_rows; /*@}*/ public: /**@name Constructor and destructor */ /*@{*/ /** The only one way to construct a presolved branching object is to create it from a regular branching object.*/ explicit BCP_presolved_lp_brobj(BCP_lp_branching_object* candidate) : _candidate(candidate), _lpres(), _child_action(candidate->child_num, BCP_ReturnChild), _user_data(candidate->child_num, NULL) { _lpres.reserve(candidate->child_num); for (int i = candidate->child_num; i; --i) { _lpres.unchecked_push_back(new BCP_lp_result); _new_cuts.push_back(BCP_vec()); _new_rows.push_back(BCP_vec()); } } /** The destructor simply deletes every member (deletes every lpres in the vector). */ ~BCP_presolved_lp_brobj() { purge_ptr_vector(_lpres); purge_ptr_vector(_user_data); for (int i = _new_cuts.size() - 1; i >= 0; --i) { purge_ptr_vector(_new_cuts[i]); purge_ptr_vector(_new_rows[i]); } } /*@}*/ /**@name Query methods */ /*@{*/ /** Return a pointer to the candidate.
*THINK* : is it necessary to have a non-const version??? */ inline BCP_lp_branching_object* candidate() { return _candidate; } /** Return a const pointer to the candidate. */ inline const BCP_lp_branching_object* candidate() const { return _candidate; } /** Return a const reference to the presolved results of the child_ind-th child. */ inline const BCP_lp_result& lpres(const int child_ind) const { return *(_lpres[child_ind]); } /** Return a reference to the actions to be taken. A non-const method is needed, since this is the easiest way to set the entries. Maybe it'd be cleaner to have a separate set method... */ inline BCP_vec& action() { return _child_action; } /** Return a const reference to the actions to be taken. */ inline const BCP_vec& action() const { return _child_action; } /** Return a reference to the user data vector. A non-const method is needed, since this is the easiest way to set the entries. Maybe it'd be cleaner to have a separate set method... */ inline BCP_vec& user_data() { return _user_data; } /** Return a const reference to the user data vector. */ inline const BCP_vec& user_data() const { return _user_data; } /** Return true if every children can be fathomed. (The lower bound for each is above objval_limit.) */ bool fathomable(const double objval_limit) const; /** Return true if at least one child had numerical difficulties while presolving. */ bool had_numerical_problems() const; /*@}*/ /**@name Modifying methods */ /*@{*/ inline void initialize_lower_bound(const double val) { for (int i = _candidate->child_num - 1; i >= 0; --i) { _lpres[i]->fake_objective_value(val); } } inline void keep_no_child() { for (int i = _child_action.size() - 1; i >= 0; --i) { _child_action[i] = BCP_ReturnChild; } } inline bool is_pruned() const { for (int i = _child_action.size() - 1; i >= 0; --i) { if (_child_action[i] != BCP_FathomChild) return false; } return true; } /** Fill up obj with the lower bound on each child. */ inline void get_lower_bounds(BCP_vec& obj) { obj.clear(); obj.reserve(_candidate->child_num); const int num_children = _lpres.size(); for (int i = 0; i < num_children; ++i) obj.unchecked_push_back(_lpres[i]->objval()); } /** Fill up the lower bounds on the children with the content of obj.*/ inline void set_lower_bounds(const BCP_vec& obj) { const int num_children = _lpres.size(); for (int i = 0; i < num_children; ++i) _lpres[i]->fake_objective_value(obj[i]); } /** Extract the lp results from the LP solver for the child_ind-th child. This is done immediately after presolving the child. */ inline void get_results(OsiSolverInterface& lp, const int child_ind) { _lpres[child_ind]->get_results(lp); } /** Examine the termination codes for the children and for those that do not have a valid lower bound fake the objective value depending on the termination code:
  • primal infeasibility / dual objective limit: BCP_DBL_MAX
  • iteration limit : maximum of the lower bound at termination and itlim_objval
  • abandoned : itlim_objval
*/ void fake_objective_values(const double itlim_objval); /** Set the appropriate fields of all _lpres to the given termcode and objval if the termcode is "normal". If not normal (like the cases in fake_objective_values(), then apply the same rules. */ void set_objective_values(const BCP_vec& obj, const BCP_vec& termcode, const double itlim_objval); /** swap the two presolved branching object */ void swap(BCP_presolved_lp_brobj& rhs) { std::swap(_candidate, rhs._candidate); _lpres.swap(rhs._lpres); _child_action.swap(rhs._child_action); _user_data.swap(rhs._user_data); _new_cuts.swap(rhs._new_cuts); _new_rows.swap(rhs._new_rows); } inline BCP_vec< BCP_vec >& get_new_cuts() { return _new_cuts; } inline BCP_vec< BCP_vec >& get_new_rows() { return _new_rows; } /*@}*/ }; #endif