Commit 11c1a153 authored by Pavel Smirnov's avatar Pavel Smirnov

added multithreading

parent 9652a5f7
...@@ -3,7 +3,6 @@ MINPSC_PATH=tools/minpsc ...@@ -3,7 +3,6 @@ MINPSC_PATH=tools/minpsc
all: all:
make build make build
make test
rebuild: rebuild:
rm -rf $(GRID_BUILDER_PATH)/CMakeCache.txt rm -rf $(GRID_BUILDER_PATH)/CMakeCache.txt
...@@ -16,7 +15,7 @@ rebuild: ...@@ -16,7 +15,7 @@ rebuild:
make build make build
build: grid_builder minpsc build: minpsc
grid_builder: grid_builder:
cmake -B'$(GRID_BUILDER_PATH)' -H'$(GRID_BUILDER_PATH)' cmake -B'$(GRID_BUILDER_PATH)' -H'$(GRID_BUILDER_PATH)'
......
...@@ -4,9 +4,6 @@ ...@@ -4,9 +4,6 @@
#include <unordered_map> #include <unordered_map>
#include <omp.h>
#include <ilcplex/ilocplex.h>
using namespace std; using namespace std;
static wval CalcSolutionUBound(const Graph& g) { static wval CalcSolutionUBound(const Graph& g) {
...@@ -17,7 +14,7 @@ static wval CalcSolutionUBound(const Graph& g) { ...@@ -17,7 +14,7 @@ static wval CalcSolutionUBound(const Graph& g) {
return solution_ubound; return solution_ubound;
} }
static Graph BuildReducedGraph(const Graph& g) { static void BuildReducedGraph(Graph& g) {
wval solution_ubound = CalcSolutionUBound(g); wval solution_ubound = CalcSolutionUBound(g);
vector<wval> lbounds = CalcLB(g); vector<wval> lbounds = CalcLB(g);
wval solution_lbound = 0; wval solution_lbound = 0;
...@@ -37,19 +34,18 @@ static Graph BuildReducedGraph(const Graph& g) { ...@@ -37,19 +34,18 @@ static Graph BuildReducedGraph(const Graph& g) {
cout << "(" << edges_in_graph << "/" << n*(n-1)/2 << " edges left) " << endl; cout << "(" << edges_in_graph << "/" << n*(n-1)/2 << " edges left) " << endl;
for (size_t v = 0; v < n; ++v) for (size_t v = 0; v < n; ++v)
rg.SortEdges(v); rg.SortEdges(v);
return rg; g = rg;
} }
static pair<Graph, Graph> BuildLBGraph(const Graph& rg) { static pair<Graph, Graph> BuildLBGraph(const Graph& reduced_graph) {
size_t n = rg.Size(); Graph graph_a(reduced_graph.Size());
Graph ga(n); Graph graph_a_inv(reduced_graph.Size());
Graph gainv(n); for (size_t v = 0; v < reduced_graph.Size(); ++v) {
for (size_t v = 0; v < n; ++v) { const Edge& e = reduced_graph.GetEdges(v)[0];
const Edge & e = rg.GetEdges(v)[0]; graph_a.AddOrientedEdge(v, e.to, wval(0));
ga.AddOrientedEdge(v, e.to, wval(0)); graph_a_inv.AddOrientedEdge(e.to, v, wval(0));
gainv.AddOrientedEdge(e.to, v, wval(0));
} }
return {ga, gainv}; return {graph_a, graph_a_inv};
} }
static unordered_map<size_t, IloNumVar> AddObjectiveAndVariablesY( static unordered_map<size_t, IloNumVar> AddObjectiveAndVariablesY(
...@@ -246,40 +242,29 @@ static void AddAllOptimizations( ...@@ -246,40 +242,29 @@ static void AddAllOptimizations(
AddOptimization25(env, model, rg, yMapping); AddOptimization25(env, model, rg, yMapping);
} }
wval CplexEx1Connect(const Graph& g, size_t s, unordered_map<string, size_t>& info) { void CplexEx1Solver::Transform(bool optimize) {
if (g.Size() <= 1) { BuildReducedGraph(_graph);
return 0;
}
Graph rg = BuildReducedGraph(g);
size_t edgesLeft = 0;
for (size_t v = 0; v < rg.Size(); ++v) {
edgesLeft += rg.GetEdges(v).size();
}
edgesLeft /= 2;
info["EdgesLeft"] = edgesLeft;
pair<Graph, Graph> lb_graphs = BuildLBGraph(rg); // for opt 23, 24 auto lb_graphs = BuildLBGraph(_graph); // for opt 23, 24
const auto& ga = lb_graphs.first; const auto& graph_a = lb_graphs.first;
const auto& gainv = lb_graphs.second; const auto& graph_a_inv = lb_graphs.second;
IloEnv env; auto y_mapping = AddObjectiveAndVariablesY(_env, _model, _graph);
IloModel model(env); AddAllOptimizations(_env, _model, _graph, graph_a, graph_a_inv, y_mapping);
auto yMapping = AddObjectiveAndVariablesY(env, model, rg); auto x_mapping = AddVariablesX(_env, _graph);
AddAllOptimizations(env, model, rg, ga, gainv, yMapping); AddEx1Restrictions(_env, _model, _graph, y_mapping, x_mapping, _start_vertex);
}
auto xMapping = AddVariablesX(env, rg);
AddEx1Restrictions(env, model, rg, yMapping, xMapping, s);
IloCplex cplex(env); wval CplexEx1Solver::Solve() {
cplex.setOut(env.getNullStream()); IloCplex cplex(_env);
cplex.setOut(_env.getNullStream());
cplex.extract(model); cplex.extract(_model);
cplex.solve(); cplex.solve();
wval result = cplex.getObjValue(); wval result = cplex.getObjValue();
env.end(); _env.end();
return result; return result;
} }
...@@ -357,48 +342,37 @@ void AddNewEx2Restriction( ...@@ -357,48 +342,37 @@ void AddNewEx2Restriction(
} }
} }
wval CplexEx2Connect(const Graph& g, unordered_map<string, size_t>& info) { void CplexEx2Solver::Transform(bool optimize) {
if (g.Size() <= 1) { BuildReducedGraph(_graph);
return 0;
}
Graph rg = BuildReducedGraph(g);
size_t edgesLeft = 0; auto lb_graphs = BuildLBGraph(_graph); // for opt 23, 24
for (size_t v = 0; v < rg.Size(); ++v) { const auto& graph_a = lb_graphs.first;
edgesLeft += rg.GetEdges(v).size(); const auto& graph_a_inv = lb_graphs.second;
}
edgesLeft /= 2;
info["EdgesLeft"] = edgesLeft;
pair<Graph, Graph> lb_graphs = BuildLBGraph(rg); // for opt 23, 24 _y_mapping = AddObjectiveAndVariablesY(_env, _model, _graph);
const auto& ga = lb_graphs.first; AddAllOptimizations(_env, _model, _graph, graph_a, graph_a_inv, _y_mapping);
const auto& gainv = lb_graphs.second;
IloEnv env; _z_mapping = AddVariablesZ(_env, _graph);
IloModel model(env); AddEx2BaseRestrictions(_env, _model, _graph, _y_mapping, _z_mapping);
}
auto yMapping = AddObjectiveAndVariablesY(env, model, rg);
AddAllOptimizations(env, model, rg, ga, gainv, yMapping);
auto zMapping = AddVariablesZ(env, rg);
AddEx2BaseRestrictions(env, model, rg, yMapping, zMapping);
IloCplex cplex(env); wval CplexEx2Solver::Solve() {
cplex.setOut(env.getNullStream()); IloCplex cplex(_env);
cplex.setOut(_env.getNullStream());
for (size_t iterations = 0;; ++iterations) { for (size_t iterations = 0;; ++iterations) {
cplex.extract(model); cplex.extract(_model);
cplex.solve(); cplex.solve();
vector<vector<size_t>> components = FindIsolatedComponents(rg, cplex, yMapping); vector<vector<size_t>> components = FindIsolatedComponents(_graph, cplex, _y_mapping);
if (components.size() == 1) { if (components.size() == 1) {
cout << iterations << " restrictions were added" << endl; cout << iterations << " restrictions were added" << endl;
break; break;
} }
AddNewEx2Restriction(env, model, rg, components, zMapping); AddNewEx2Restriction(_env, _model, _graph, components, _z_mapping);
} }
wval result = cplex.getObjValue(); wval result = cplex.getObjValue();
env.end(); _env.end();
return result; return result;
} }
#ifndef CPLEX_CONNECT_H #ifndef CPLEX_CONNECT_H
#define CPLEX_CONNECT_H #define CPLEX_CONNECT_H
#include "solver.h"
#include "graph.h" #include "graph.h"
#include "numeric.h" #include "numeric.h"
#include <string> #include <string>
#include <unordered_map> #include <unordered_map>
#define IL_STD
#include <ilcplex/ilocplex.h>
void ToCplexEx1(const Graph& g, const std::string output_file, size_t s, bool optimize); void ToCplexEx1(const Graph& g, const std::string output_file, size_t s, bool optimize);
wval CplexEx1Connect(const Graph& g, size_t s, std::unordered_map<std::string, size_t>& info); class CplexSolver : public Solver {
public:
CplexSolver()
: _env()
, _model(_env)
{}
protected:
IloEnv _env;
IloModel _model;
};
class CplexEx1Solver : public CplexSolver {
public:
CplexEx1Solver(size_t start_vertex)
: _start_vertex(start_vertex)
{}
void Transform(bool optimize);
wval Solve();
private:
size_t _start_vertex;
};
class CplexEx2Solver : public CplexSolver {
public:
CplexEx2Solver() {}
wval CplexEx2Connect(const Graph& g, std::unordered_map<std::string, size_t>& info); void Transform(bool optimize);
wval Solve();
private:
std::unordered_map<size_t, IloNumVar> _y_mapping;
std::unordered_map<size_t, IloNumVar> _z_mapping;
};
#endif #endif
This diff is collapsed.
#ifndef DP_CONNECT_H #ifndef DP_CONNECT_H
#define DP_CONNECT_H #define DP_CONNECT_H
#include "solver.h"
#include "numeric.h" #include "numeric.h"
#include "graph.h" #include "graph.h"
...@@ -8,13 +9,30 @@ ...@@ -8,13 +9,30 @@
#include <random> #include <random>
#include <unordered_map> #include <unordered_map>
wval DpConnect( class DpSolver : public Solver {
const Graph& g, public:
const std::vector<wval>& lbounds, DpSolver(double eps, size_t seed, size_t threads_num)
double eps, : _eps(eps)
std::default_random_engine& engine, , _random_engine(seed)
bool preprocess, , _threads_num(threads_num)
std::unordered_map<std::string, size_t>& info , _transformed(false)
); , _optimize(false)
{}
void Transform(bool optimize);
wval Solve();
private:
double _eps;
std::default_random_engine _random_engine;
size_t _threads_num;
bool _optimize;
bool _transformed;
std::vector<wval> _lowerbounds;
std::vector<std::vector<size_t>> _comps;
wval _solution_upperbound;
wval _lost_answer;
};
#endif #endif
...@@ -18,6 +18,8 @@ struct Edge { ...@@ -18,6 +18,8 @@ struct Edge {
class Graph { class Graph {
public: public:
Graph() : Graph(0) {}
Graph(size_t size); Graph(size_t size);
void AddEdge(size_t u, size_t v, wval w); void AddEdge(size_t u, size_t v, wval w);
......
...@@ -35,9 +35,11 @@ bool NextSubset(vector<size_t> & subset, size_t set_size) { ...@@ -35,9 +35,11 @@ bool NextSubset(vector<size_t> & subset, size_t set_size) {
return true; return true;
} }
} } // anonymous
wval SlowSolver::Solve() {
auto& g = _graph;
wval SlowConnect(const Graph& g) {
size_t n = g.Size(); size_t n = g.Size();
if (n <= 1) { if (n <= 1) {
...@@ -87,7 +89,14 @@ wval SlowConnect(const Graph& g) { ...@@ -87,7 +89,14 @@ wval SlowConnect(const Graph& g) {
return best_solution; return best_solution;
} }
wval SlowerConnect(const Graph & g, const vector<wval> & lbounds) { void SlowerSolver::Transform(bool) {
_lowerbounds = CalcLB(_graph);
}
wval SlowerSolver::Solve() {
auto& g = _graph;
auto& lbounds = _lowerbounds;
size_t n = g.Size(); size_t n = g.Size();
if (n <= 1) if (n <= 1)
......
#ifndef SLOW_CONNECT_H #ifndef SLOW_CONNECT_H
#define SLOW_CONNECT_H #define SLOW_CONNECT_H
#include "solver.h"
#include "numeric.h" #include "numeric.h"
#include "graph.h" #include "graph.h"
#include <vector> #include <vector>
wval SlowConnect(const Graph& g); class SlowSolver : public Solver {
public:
SlowSolver() {}
void Transform(bool) {}
wval Solve();
};
wval SlowerConnect(const Graph& g, const std::vector<wval>& lbounds); class SlowerSolver : public Solver {
public:
SlowerSolver() {}
void Transform(bool optimize);
wval Solve();
private:
std::vector<wval> _lowerbounds;
};
#endif #endif
#ifndef SOLVER_H
#define SOLVER_H
#include "numeric.h"
#include "graph.h"
#include <string>
class Solver {
public:
bool SetGraph(const std::string& file_name) {
Graph graph = ReadGraph(file_name);
_graph = graph;
if (_graph.FindComponents().size() > 1) {
return false;
}
return true;
}
size_t GetEdgesNum() {
size_t edges_num = 0;
for (size_t v = 0; v < _graph.Size(); ++v) {
edges_num += _graph.GetEdges(v).size();
}
edges_num /= 2;
return edges_num;
}
size_t GetVerticesNum() {
return _graph.Size();
}
virtual void Transform(bool optimize) = 0;
virtual wval Solve() = 0;
protected:
Graph _graph;
};
#endif
...@@ -6,6 +6,15 @@ set(LIB_DIR ../../lib) ...@@ -6,6 +6,15 @@ set(LIB_DIR ../../lib)
include_directories(${LIB_DIR}) include_directories(${LIB_DIR})
add_subdirectory(${LIB_DIR} ${LIB_DIR}) add_subdirectory(${LIB_DIR} ${LIB_DIR})
set(CPLEXDIR "/opt/ibm/ILOG/CPLEX_Studio128/cplex")
set(CONCERTDIR "/opt/ibm/ILOG/CPLEX_Studio128/concert")
set(CONCERTINCDIR "${CONCERTDIR}/include")
set(CPLEXINCDIR "${CPLEXDIR}/include")
set(CCINCDIRS "-I${CPLEXINCDIR} -I${CONCERTINCDIR}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -pthread -O3 ${CCINCDIRS}")
set(MINPSC_SRCS minpsc.cpp) set(MINPSC_SRCS minpsc.cpp)
add_executable(minpsc "${MINPSC_SRCS}") add_executable(minpsc "${MINPSC_SRCS}")
......
...@@ -5,25 +5,26 @@ ...@@ -5,25 +5,26 @@
#include <dp_connect.h> #include <dp_connect.h>
#include <slow_connect.h> #include <slow_connect.h>
#include <cplex_connect.h> #include <cplex_connect.h>
#include <solver.h>
#include <algorithm_exception.h>
#include <iostream> #include <iostream>
#include <fstream> #include <fstream>
using namespace std; using namespace std;
using random_engine = default_random_engine;
struct SolvingParams { struct SolvingParams {
int Mode; int mode;
string LogFile; string log_file;
string InputFile; string input_file;
string OutputFile; string output_file;
double Eps; double eps;
int Seed; int seed;
int threads_num;
}; };
void PrintHelpMessageAndExit(int argc, char** argv) { void PrintHelpMessageAndExit(int argc, char** argv) {
cout << "To solve with DP:\n"; cout << "To solve with DP:\n";
cout << "\t" << argv[0] << " 0 <logfile> <input file> <output file> <eps> <seed>\n"; cout << "\t" << argv[0] << " 0 <logfile> <input file> <output file> <eps> <seed> <threads_num>\n";
cout << "To solve with first bruteforce:\n"; cout << "To solve with first bruteforce:\n";
cout << "\t" << argv[0] << " 1 <logfile> <input file> <output file>\n"; cout << "\t" << argv[0] << " 1 <logfile> <input file> <output file>\n";
cout << "To solve with second bruteforce:\n"; cout << "To solve with second bruteforce:\n";
...@@ -41,122 +42,84 @@ SolvingParams ParseCommandLine(int argc, char** argv) { ...@@ -41,122 +42,84 @@ SolvingParams ParseCommandLine(int argc, char** argv) {
if (argc < 3) { if (argc < 3) {
PrintHelpMessageAndExit(argc, argv); PrintHelpMessageAndExit(argc, argv);
} }
params.Mode = atoi(argv[1]); params.mode = atoi(argv[1]);
params.LogFile = argv[2]; params.log_file = argv[2];
if (params.Mode == 0 || params.Mode == 5) { if (params.mode == 0 || params.mode == 5) {
if (argc != 7) { if (argc != 8) {
PrintHelpMessageAndExit(argc, argv); PrintHelpMessageAndExit(argc, argv);
} }
params.InputFile = argv[3]; params.input_file = argv[3];
params.OutputFile = argv[4]; params.output_file = argv[4];
params.Eps = atof(argv[5]); params.eps = atof(argv[5]);
params.Seed = atoi(argv[6]); params.seed = atoi(argv[6]);
} else if (params.Mode <= 4) { params.threads_num = atoi(argv[7]);
} else if (params.mode <= 4) {
if (argc != 5) { if (argc != 5) {
PrintHelpMessageAndExit(argc, argv); PrintHelpMessageAndExit(argc, argv);
} }
params.InputFile = argv[3]; params.input_file = argv[3];
params.OutputFile = argv[4]; params.output_file = argv[4];
} else { } else {
PrintHelpMessageAndExit(argc, argv); PrintHelpMessageAndExit(argc, argv);
} }
return params; return params;
} }
void OutputSolution(wval solution, const Graph& graph, string outputFile) {
cout << "Minimal sum = " << solution << endl;
ofstream output(outputFile);
output << solution << endl;
output.close();
}
int main(int argc, char ** argv) { int main(int argc, char ** argv) {
SolvingParams params = ParseCommandLine(argc, argv); SolvingParams params = ParseCommandLine(argc, argv);
Timer globalTimer, localTimer;
Graph graph = ReadGraph(params.InputFile);
size_t edgesCount = 0; Solver* solver;
for (size_t v = 0; v < graph.Size(); ++v) { string solver_token;
edgesCount += graph.GetEdges(v).size(); if (params.mode == 0 || params.mode == 5) {
solver = new DpSolver(params.eps, params.seed, params.threads_num);
solver_token = ((params.mode == 5) ? "dp_no_opt" : "dp");
} else if (params.mode == 1) {
solver = new SlowerSolver();
solver_token = "slower";
} else if (params.mode == 2) {
solver = new SlowSolver();
solver_token = "slow";
} else if (params.mode == 3) {
solver = new CplexEx1Solver(0);
solver_token = "ex1";
} else if (params.mode == 4) {
solver = new CplexEx2Solver();
solver_token = "ex2";
} }
edgesCount /= 2;
if (graph.FindComponents().size() > 1) {
cout << "Graph is not connected." << endl;
return 0;
}
cout << "Graph read in " << localTimer.Check() << " sec" << endl;
localTimer.Zero();
wval solution;
ofstream log(params.LogFile, std::ios_base::app);
unordered_map<string, size_t> info; if (!solver->SetGraph(params.input_file)) {
throw new AlgorithmException("Graph is incorrect.");
if (params.Mode == 0) {
vector<wval> lbounds = CalcLB(graph);
cout << "Lowerbounds found in " << localTimer.Check() << " sec" << endl;
localTimer.Zero();
random_engine engine = random_engine(params.Seed);
solution = DpConnect(graph, lbounds, params.Eps, engine, true, info);
cout << "Answer found in " << localTimer.Check() << " sec" << endl;
localTimer.Zero();
log << "DP ";
} else if (params.Mode == 1) {
vector<wval> lbounds = CalcLB(graph);
cout << "Lowerbounds found in " << localTimer.Check() << " sec" << endl;
localTimer.Zero();
solution = SlowerConnect(graph, lbounds);
cout << "Answer found in " << localTimer.Check() << " sec" << endl;
localTimer.Zero();
log << "Slow ";
} else if (params.Mode == 2) {
solution = SlowConnect(graph);
cout << "Answer found in " << localTimer.Check() << " sec" << endl;
localTimer.Zero();
log << "Slowest ";
} else if (params.Mode == 3) {
solution = CplexEx1Connect(graph, 0, info);
cout << "Answer found in " << localTimer.Check() << " sec" << endl;
localTimer.Zero();
log << "CPLEX_EX1 ";
} else if (params.Mode == 4) {
solution = CplexEx2Connect(graph, info);
cout << "Answer found in " << localTimer.Check() << " sec" << endl;
localTimer.Zero();
log << "CPLEX_EX2 ";
} else if (params.Mode == 5) {
vector<wval> lbounds = CalcLB(graph);
cout << "Lowerbounds found in " << localTimer.Check() << " sec" << endl;
localTimer.Zero();
random_engine engine = random_engine(params.Seed);
solution = DpConnect(graph, lbounds, params.Eps, engine, false, info);
cout << "Answer found in " << localTimer.Check() << " sec" << endl;
localTimer.Zero();
log << "DP_no_preprocess ";
} }
OutputSolution(solution, graph, params.OutputFile); size_t initial_vert = solver->GetVerticesNum();
size_t initial_edges = solver->GetEdgesNum();
cout << "Problem solved in " << globalTimer.Check() << " sec" << endl;
cout << endl; Timer transform_timer;
solver->Transform(/*optimize*/ ((params.mode == 5) ? false : true));
log << "N = " << graph.Size() << double transform_time = transform_timer.Check();
" Time = " << globalTimer.Check() <<
" Ans = " << solution << size_t transformed_vert = solver->GetVerticesNum();
" Edges = " << edgesCount; size_t transformed_edges = solver->GetEdgesNum();
for (const auto& infoEntity : info) {
log << " " << infoEntity.first << " = " << infoEntity.second; Timer solve_timer;
} wval answer = solver->Solve();
log << endl; double solve_time = solve_timer.Check();
delete solver;
ofstream log(params.log_file, std::ios_base::app);
std::stringstream log_info;
log_info << solver_token
<< " initial_vert=" << initial_vert
<< " initial_edges=" << initial_edges
<< " transformed_vert=" << transformed_vert
<< " transformed_edges=" << transformed_edges