分支定界法基本框架

This commit is contained in:
2025-05-23 17:59:24 +08:00
parent 26ac3ddebd
commit d4456e9632
7 changed files with 353 additions and 98 deletions

View File

@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.16.0)
project(Solver)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# include(./FindGUROBI.cmake)

View File

@ -87,17 +87,28 @@ namespace sv {
EQUAL
};
enum class VarType {
CONTINUOUS,
BINARY,
INTEGER,
};
class Var {
public:
Var(double coef = 1) :col(0), val(0), coeffs(coef) {};
friend class Model;
friend class LinSolver;
friend class Expr;
Var(double coef = 1, VarType type_ = VarType::CONTINUOUS);
double get(DoubleAttr attr);
int get(IntAttr attr);
friend class Model;
friend class Expr;
private:
double coeffs;
double val;
int col;
VarType type;
};
Expr operator+(const Expr& x, const Expr& y);
@ -131,7 +142,7 @@ namespace sv {
Expr(double constant = 0.0);
Expr(Var var, double coeff = 1.0);
friend class Model;
friend class LinSolver;
friend Expr operator+(const Expr& x, const Expr& y);
friend Expr operator+(const Expr& x);

View File

@ -1,5 +1,17 @@
#pragma once
#include <vector>
class Node {
namespace sv {
class Var;
class Model;
class Node
{
public:
Model* mdl;
double lower_bound;
double upper_bound;
bool is_integer;
std::vector<Var> branch_list;
};
}

View File

@ -3,31 +3,59 @@
namespace sv {
class Model
{
class LinSolver {
public:
Model();
Var* addVars(int col);
friend class Model;
LinSolver();
~LinSolver();
LinSolver(const LinSolver& solver);
LinSolver& operator=(const LinSolver& solver);
Var* addVars(int col, VarType type);
const Var& getVar(int idx);
void addConstr(const Expr& expr, ConstrOper sense, double rhs);
void setObjective(Expr obje, int sense = MDL_MAXIMIZE);
void print();
rtn optimize();
double get(DoubleAttr attr);
int get(IntAttr attr);
private:
rtn optimize();
protected:
double _simplex();
rtn _pivot(std::pair<size_t, size_t>& p);
rtn feasible_solution();
void _gaussian(std::pair<size_t, size_t> p);
Var* vars;
matrix mt;
matrix mt_cvt;
size_t cn, bn;
std::vector<int> basic;
rtn rtn_;
double res_;
double obj_;
int sense;
};
class Model
{
public:
Model();
rtn optimize();
Var* addVars(int col, VarType type);
void addConstr(const Expr& expr, ConstrOper sense, double rhs);
void setObjective(Expr obje, int sense = MDL_MAXIMIZE);
double get(DoubleAttr attr);
int get(IntAttr attr);
private:
LinSolver solver;
};
}

View File

@ -1,6 +1,16 @@
#include "common.hpp"
#include <iostream>
using namespace sv;
Var::Var(double coef, VarType type_) :
col(0),
val(0),
coeffs(coef),
type(type_)
{
};
double Var::get(DoubleAttr attr)
{
switch (attr) {

View File

@ -6,18 +6,24 @@
using namespace std;
using namespace sv;
int main(int argc, char* argv[])
{
Model mdl;
Var* vars = mdl.addVars(2);
Var* vars = mdl.addVars(2, VarType::INTEGER);
mdl.addConstr(vars[0] + vars[1], ConstrOper::LESS_EQUAL, 2);
mdl.addConstr(vars[0] + vars[1], ConstrOper::GREATER_EQUAL, 1);
mdl.addConstr(5 * vars[0] - 2 * vars[1], ConstrOper::GREATER_EQUAL, -2);
mdl.setObjective(vars[0] + 2 * vars[1], MDL_MAXIMIZE);
//mdl.addConstr(vars[0] + vars[1], ConstrOper::LESS_EQUAL, 2);
//mdl.addConstr(vars[0] + vars[1], ConstrOper::GREATER_EQUAL, 1);
//mdl.addConstr(5 * vars[0] - 2 * vars[1], ConstrOper::GREATER_EQUAL, -2);
//mdl.setObjective(vars[0] + 2 * vars[1], MDL_MAXIMIZE);
mdl.addConstr(2 * vars[0] + vars[1], ConstrOper::LESS_EQUAL, 10);
mdl.addConstr(3 * vars[0] + 6 * vars[1], ConstrOper::LESS_EQUAL, 40);
mdl.setObjective(100 * vars[0] + 150 * vars[1], MDL_MAXIMIZE);
switch(mdl.optimize()) {
case OPTIMAL:
cout << "OPTIMAL SOLUTION: " << mdl.get(DoubleAttr::Obj) << endl;

View File

@ -4,7 +4,8 @@
#include <iostream>
#include <cassert>
#include <algorithm>
#include <stack>
#include <cassert>
using namespace sv;
using std::make_pair;
@ -13,35 +14,107 @@ using std::cout;
using std::endl;
using std::vector;
Model::Model():
struct Node
{
LinSolver solver;
double lower_bound;
double upper_bound;
};
LinSolver::LinSolver() :
cn(0),
bn(0),
bn(1),
sense(0),
vars(nullptr)
{
}
Var* Model::addVars(int num)
sv::LinSolver::~LinSolver()
{
delete[] vars;
}
sv::LinSolver::LinSolver(const LinSolver& solver)
: vars(nullptr),
cn(solver.cn),
bn(solver.bn),
mt(solver.mt),
basic(solver.basic),
rtn_(solver.rtn_),
obj_(solver.obj_),
sense(solver.sense)
{
delete[] vars;
vars = nullptr;
if (solver.vars != nullptr && cn > 0) {
vars = new Var[cn];
for (int i = 0; i < cn; i++) {
vars[i] = solver.vars[i];
}
}
}
LinSolver& sv::LinSolver::operator=(const LinSolver& solver)
{
if (this == &solver) {
return *this;
}
delete[] vars; // <20>ͷ<EFBFBD>ԭ<EFBFBD><D4AD><EFBFBD>ڴ<EFBFBD>
vars = nullptr;
cn = solver.cn;
if (cn > 0) {
vars = new Var[cn];
for (int i = 0; i < cn; i++) {
vars[i] = solver.vars[i]; // <20><EFBFBD><EEBFBD>
}
}
mt = solver.mt;
cn = solver.cn, bn = solver.bn;
basic = solver.basic;
rtn_ = solver.rtn_;
obj_ = solver.obj_;
sense = solver.sense;
return *this;
}
Var* LinSolver::addVars(int num, VarType type)
{
Var* old = vars;
vars = new Var[cn + num];
for (int c = cn; c < cn + num; c++) {
vars[c].col = c;
vars[c].type = type;
}
//for (int i = 0; i < cn; i++) {
// *(vars + i) = *(old + i);
//}
memcpy(vars, old, sizeof(Var) * cn);
delete[] old;
cn += num;
return vars;
}
void Model::addConstr(const Expr& expr, ConstrOper sense, double rhs)
const Var& sv::LinSolver::getVar(int idx)
{
assert(idx >= 0 && idx < cn); // <20><><EFBFBD><EFBFBD>Խ<EFBFBD><D4BD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
return vars[idx];
}
void LinSolver::addConstr(const Expr& expr, ConstrOper sense, double rhs)
{
if (sense == ConstrOper::LESS_EQUAL) {
bn++;
mt.push_back(vector<double>(1, rhs - expr.constant));
mt.back().insert(mt.back().end(), expr.coeffs.begin(), expr.coeffs.end());
}
else if (sense == ConstrOper::GREATER_EQUAL) {
bn++;
mt.push_back(vector<double>(1, expr.constant - rhs));
for (int coeff : expr.coeffs) {
mt.back().push_back(-coeff);
@ -57,7 +130,7 @@ void Model::addConstr(const Expr& expr, ConstrOper sense, double rhs)
}
}
void Model::setObjective(Expr obje, int _sense)
void LinSolver::setObjective(Expr obje, int _sense)
{
assert(_sense == 1 || _sense == -1);
if (sense == 0) {
@ -78,33 +151,178 @@ void Model::setObjective(Expr obje, int _sense)
}
}
}
for (int i = 0; i < mt.front().size(); i++) {
mt.front()[i] = _sense * mt.front()[i];
}
sense = _sense;
}
void Model::print()
rtn LinSolver::optimize()
{
for (size_t i = 0; i < mt.size(); i++) {
for (size_t j = 0; j < mt[0].size(); j++) {
cout << mt[i][j] << "\t";
assert(sense);
mt_cvt = mt;
rtn_ = feasible_solution();
if (rtn_ == LOADED) {
obj_ = _simplex();
}
cn = mt_cvt.front().size() - bn;
for (int row = 1; row < bn; row++) {
if (basic[row - 1] - 1 <= cn) {
vars[basic[row - 1] - 1].val = mt_cvt[row].front();
}
}
return rtn_;
}
void LinSolver::print()
{
for (size_t i = 0; i < mt_cvt.size(); i++) {
for (size_t j = 0; j < mt_cvt[0].size(); j++) {
cout << mt_cvt[i][j] << "\t";
}
cout << endl;
}
}
rtn Model::optimize()
Model::Model()
{
bn = mt.size();
for (int row = 1; row < bn; row++) {
mt.front().push_back(0);
for (int col = 1; col < bn; col++) {
mt[row].push_back(col == row ? 1 : 0);
}
}
cn = mt.front().size();
for (int i = 0; i < cn; i++) {
mt.front()[i] = sense * mt.front()[i];
}
rtn Model::optimize()
{
solver.optimize();
if (solver.rtn_ != OPTIMAL) {
return solver.rtn_;
}
double global_upper_bound = solver.obj_, global_lower_bound = 0;
std::stack<Node> list_;
Node incumbent_node, root_node;
root_node.solver = solver;
root_node.upper_bound = global_upper_bound, root_node.lower_bound = global_lower_bound;
list_.push(root_node);
int cnt = 0;
while (list_.size() && global_upper_bound - global_lower_bound > 1e-10) {
cout << ++cnt << endl;
Node current_node = list_.top();
list_.pop();
current_node.solver.optimize();
if (current_node.solver.get(IntAttr::Status) == OPTIMAL) {
int branch_var_index = -1;
for (int i = 0; i < current_node.solver.get(IntAttr::NumVars); i++) {
if (current_node.solver.vars[i].type == VarType::INTEGER) {
if (fabs(int(current_node.solver.vars[i].val) - current_node.solver.vars[i].val) > 1e-10) {
branch_var_index = i;
break;
}
}
}
if (branch_var_index == -1) {
current_node.lower_bound = current_node.solver.obj_;
current_node.upper_bound = current_node.solver.obj_;
if (current_node.lower_bound > global_lower_bound) {
global_lower_bound = current_node.lower_bound;
incumbent_node = current_node;
}
}
else {
if (current_node.upper_bound >= global_lower_bound) {
const Var& branch_var = current_node.solver.getVar(branch_var_index);
int left_var_bound = branch_var.val;
int right_var_bound = branch_var.val + 1;
Node left_node = current_node;
left_node.solver.addConstr(branch_var, ConstrOper::LESS_EQUAL, left_var_bound);
list_.push(left_node);
Node right_node = current_node;
right_node.solver.addConstr(branch_var, ConstrOper::GREATER_EQUAL, right_var_bound);
list_.push(right_node);
}
}
}
}
solver.rtn_ = incumbent_node.solver.rtn_;
solver.obj_ = incumbent_node.solver.obj_;
for (int i = 0; i < solver.cn; i++) {
solver.vars[i].val = incumbent_node.solver.vars[i].val;
}
return solver.rtn_;
}
Var* sv::Model::addVars(int col, VarType type)
{
return solver.addVars(col, type);
}
void sv::Model::addConstr(const Expr& expr, ConstrOper sense, double rhs)
{
return solver.addConstr(expr, sense, rhs);
}
void sv::Model::setObjective(Expr obje, int sense)
{
return solver.setObjective(obje, sense);
}
double sv::Model::get(DoubleAttr attr)
{
return solver.get(attr);
}
int sv::Model::get(IntAttr attr)
{
return solver.get(attr);
}
double LinSolver::get(DoubleAttr attr)
{
return -sense * obj_;
}
int LinSolver::get(IntAttr attr)
{
switch (attr) {
case IntAttr::NumVars:
return cn;
case IntAttr::Status:
return rtn_;
}
return -1;
}
double LinSolver::_simplex()
{
pair<size_t, size_t> t;
while (1) {
rtn_ = _pivot(t);
if (rtn_ == OPTIMAL || rtn_ == UNBOUNDED) {
break;
}
_gaussian(t);
}
return obj_ = mt_cvt.front().front();
}
rtn LinSolver::feasible_solution()
{
for (int row = 1; row < bn; row++) {
mt_cvt.front().push_back(0);
for (int col = 1; col < bn; col++) {
mt_cvt[row].push_back(col == row ? 1 : 0);
}
}
cn = mt_cvt.front().size();
basic.clear();
for (size_t i = 1; i < bn; i++) {
basic.push_back(cn - bn + i);
}
@ -112,7 +330,7 @@ rtn Model::optimize()
// === <20>жϳ<D0B6>ʼ<EFBFBD><CABC><EFBFBD>Ƿ<EFBFBD>Ϊ<EFBFBD><CEAA><EFBFBD>н<EFBFBD> ===
bool initial_feasible = true;
for (int row = 1; row < bn; row++) {
if (mt[row].front() < 0) {
if (mt_cvt[row].front() < 0) {
initial_feasible = false;
break;
}
@ -120,14 +338,14 @@ rtn Model::optimize()
// === <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʼ<EFBFBD><CABC><EFBFBD>н<EFBFBD> ===
if (!initial_feasible) {
vector<double> coeff = mt.front();
mt.front() = vector<double>(cn, .0);
mt.front().push_back(1);
vector<double> coeff = mt_cvt.front();
mt_cvt.front() = vector<double>(cn, .0);
mt_cvt.front().push_back(1);
pair<size_t, size_t> t = { -1 ,cn };
for (int row = 1; row < bn; row++) {
mt[row].push_back(-1);
if (t.first == -1 || mt[row].front() < mt[t.first].front()) {
mt_cvt[row].push_back(-1);
if (t.first == -1 || mt_cvt[row].front() < mt_cvt[t.first].front()) {
t.first = row;
}
}
@ -141,8 +359,8 @@ rtn Model::optimize()
// if the x0 in B, we should pivot it.
auto iter = find(basic.begin(), basic.end(), cn);
if (iter != basic.end()) {
for (int col = 1; col < mt.front().size(); col++) {
if (fabs(mt.front()[col]) > 1e-10) {
for (int col = 1; col < mt_cvt.front().size(); col++) {
if (fabs(mt_cvt.front()[col]) > 1e-10) {
t = make_pair(iter - basic.begin() + 1, col);
_gaussian(t);
break;
@ -151,58 +369,28 @@ rtn Model::optimize()
}
for (int row = 0; row < bn; row++) {
mt[row].pop_back();
mt_cvt[row].pop_back();
}
// recover the coefficient line
for (int col = 0; col < cn; col++) {
mt.front()[col] = coeff[col];
mt_cvt.front()[col] = coeff[col];
}
for (int row = 1; row <= basic.size(); row++) {
int norm = mt.front()[basic[row - 1]];
int norm = mt_cvt.front()[basic[row - 1]];
for (int col = 0; col < cn; col++) {
mt.front()[col] -= norm * mt[row][col];
mt_cvt.front()[col] -= norm * mt_cvt[row][col];
}
}
}
return LOADED;
}
res_ = -sense * _simplex();
for (int row = 1; row < bn; row++) {
if (basic[row - 1] - 1 < cn - bn) {
vars[basic[row - 1] - 1].val = mt[row].front();
}
}
return rtn_;
}
double Model::get(DoubleAttr attr)
{
return res_;
}
int Model::get(IntAttr attr)
{
return cn - bn;
}
double Model::_simplex()
{
pair<size_t, size_t> t;
while (1) {
rtn_ = _pivot(t);
if (rtn_ == OPTIMAL || rtn_ == UNBOUNDED) {
break;
}
_gaussian(t);
}
return mt.front().front();
}
rtn Model::_pivot(pair<size_t, size_t>& p)
rtn LinSolver::_pivot(pair<size_t, size_t>& p)
{
p = make_pair(0, 0);
double cmin = INT_MAX;
vector<double> coef = mt.front();
vector<double> coef = mt_cvt.front();
// === <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ԫ<EFBFBD><D4AA><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Сֵ ===
for (size_t i = 1; i < coef.size(); i++) {
@ -216,8 +404,8 @@ rtn Model::_pivot(pair<size_t, size_t>& p)
}
double bmin = INT_MAX;
for (size_t row = 1; row < bn; row++) {
double tmp = mt[row].front() / mt[row][p.second];
if (mt[row][p.second] > 0 && bmin > tmp) {
double tmp = mt_cvt[row].front() / mt_cvt[row][p.second];
if (mt_cvt[row][p.second] > 0 && bmin > tmp) {
bmin = tmp;
p.first = row;
}
@ -228,7 +416,7 @@ rtn Model::_pivot(pair<size_t, size_t>& p)
}
for (auto iter = basic.begin(); iter != basic.end(); iter++) {
if (mt[p.first][*iter] != 0) {
if (mt_cvt[p.first][*iter] != 0) {
*iter = p.second;
break;
}
@ -237,14 +425,14 @@ rtn Model::_pivot(pair<size_t, size_t>& p)
return PIVOT;
}
void Model::_gaussian(pair<size_t, size_t> p)
void LinSolver::_gaussian(pair<size_t, size_t> p)
{
size_t x = p.first, y = p.second;
// === <20><><EFBFBD>й<EFBFBD>һ<EFBFBD><D2BB> ===
double norm = mt[x][y];
for (size_t col = 0; col < mt[x].size(); col++) {
mt[x][col] /= norm;
double norm = mt_cvt[x][y];
for (size_t col = 0; col < mt_cvt[x].size(); col++) {
mt_cvt[x][col] /= norm;
}
// === <20><><EFBFBD><EFBFBD><EFBFBD>б任 ===
@ -252,10 +440,10 @@ void Model::_gaussian(pair<size_t, size_t> p)
if (row == x) {
continue;
}
if (mt[row][y] != 0) {
double norm = mt[row][y];
for (size_t col = 0; col < mt[x].size(); col++) {
mt[row][col] = mt[row][col] - norm * mt[x][col];
if (mt_cvt[row][y] != 0) {
double norm = mt_cvt[row][y];
for (size_t col = 0; col < mt_cvt[x].size(); col++) {
mt_cvt[row][col] = mt_cvt[row][col] - norm * mt_cvt[x][col];
}
}
}