forked from RalfRothenberger/GreedyMax
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathMaxCutILP.cpp
239 lines (204 loc) · 6.86 KB
/
MaxCutILP.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
#include "Snap.h"
#include <map>
#include "gurobi_c++.h"
#include <stdexcept>
using namespace std;
//LP solution
double LP(PNGraph G, FILE* F, TInt Budget) {
TInt out;
try {
GRBEnv env = GRBEnv();
GRBModel model = GRBModel(env);
// Create variables
GRBVar* x = model.addVars(G->GetNodes(), GRB_CONTINUOUS);
GRBVar* y = model.addVars(G->GetEdges(), GRB_CONTINUOUS);
// Integrate new variables and set objective to maximize (=-1)
model.set(GRB_IntAttr_ModelSense, -1);
model.update();
//IDs of networks are non-consecutive, so we need a bijection to the natural numbers from 0 to (n-1)
map<long int, long int> NodeIdToIndex;
map<long int, long int> NodeIndexToId;
int i = 0;
for (TNGraph::TNodeI NI = G->BegNI(); NI != G->EndNI(); NI++) {
NodeIdToIndex[NI.GetId()] = i;
NodeIndexToId[i] = NI.GetId();
x[i].set(GRB_DoubleAttr_UB, 1.0);
x[i].set(GRB_DoubleAttr_LB, 0.0);
x[i].set(GRB_DoubleAttr_Obj, 0.0);
++i;
}
i = 0;
for (TNGraph::TEdgeI EI = G->BegEI(); EI != G->EndEI(); EI++) {
y[i].set(GRB_DoubleAttr_UB, 1.0);
y[i].set(GRB_DoubleAttr_LB, 0.0);
y[i].set(GRB_DoubleAttr_Obj, 1.0);
++i;
}
TNGraph::TEdgeI EI = G->BegEI();
i = 0;
//for each edge, at least one end-point has to be in the VC
for (EI = G->BegEI(); EI != G->EndEI(); EI++) {
model.addConstr(y[i], GRB_LESS_EQUAL, 0.5 * x[NodeIdToIndex[EI.GetSrcNId()]] - 0.5 * x[NodeIdToIndex[EI.GetDstNId()]] + 0.5);
++i;
}
//constraint on subset size
GRBLinExpr expr = 0;
for (i = 0; i < G->GetNodes(); ++i) {
expr += x[i];
}
model.addConstr(expr, GRB_LESS_EQUAL, Budget);
// Optimize model
model.update();
model.optimize();
double sum = 0;
return model.get(GRB_DoubleAttr_ObjVal);
}
catch (GRBException e)
{
cout << "Error code = " << e.getErrorCode() << endl;
cout << e.getMessage() << endl;
}
catch (...)
{
cout << "Exception during optimization" << endl;
}
return -1;
}
//ILP solution
double ILP(PNGraph G, FILE* F, TInt Budget) {
TInt out;
try {
GRBEnv env = GRBEnv();
GRBModel model = GRBModel(env);
// Create variables
GRBVar* x = model.addVars(G->GetNodes(), GRB_BINARY);
GRBVar* y = model.addVars(G->GetEdges(), GRB_BINARY);
// Integrate new variables and set objective to maximize (=-1)
model.set(GRB_IntAttr_ModelSense, -1);
model.update();
//IDs of networks are non-consecutive, so we need a bijection to the natural numbers from 0 to (n-1)
map<long int, long int> NodeIdToIndex;
map<long int, long int> NodeIndexToId;
int i = 0;
for (TNGraph::TNodeI NI = G->BegNI(); NI != G->EndNI(); NI++) {
NodeIdToIndex[NI.GetId()] = i;
NodeIndexToId[i] = NI.GetId();
x[i].set(GRB_DoubleAttr_UB, 1.0);
x[i].set(GRB_DoubleAttr_LB, 0.0);
x[i].set(GRB_DoubleAttr_Obj, 0.0);
++i;
}
i = 0;
for (TNGraph::TEdgeI EI = G->BegEI(); EI != G->EndEI(); EI++) {
y[i].set(GRB_DoubleAttr_UB, 1.0);
y[i].set(GRB_DoubleAttr_LB, 0.0);
y[i].set(GRB_DoubleAttr_Obj, 1.0);
++i;
}
TNGraph::TEdgeI EI = G->BegEI();
//for each edge, at least one end-point has to be in the VC
i = 0;
for (EI = G->BegEI(); EI != G->EndEI(); EI++) {
model.addConstr(y[i], GRB_LESS_EQUAL, 0.5 * x[NodeIdToIndex[EI.GetSrcNId()]] - 0.5 * x[NodeIdToIndex[EI.GetDstNId()]] + 0.5);
++i;
}
//constraint on subset size
GRBLinExpr expr = 0;
for (i = 0; i < G->GetNodes(); ++i) {
expr += x[i];
}
model.addConstr(expr, GRB_LESS_EQUAL, Budget);
// Optimize model
model.update();
model.optimize();
return model.get(GRB_DoubleAttr_ObjVal);
}
catch (GRBException e)
{
cout << "Error code = " << e.getErrorCode() << endl;
cout << e.getMessage() << endl;
}
catch (...)
{
cout << "Exception during optimization" << endl;
}
return -1;
}
int main(int argc, char* argv[]) {
Env = TEnv(argc, argv, TNotify::StdNotify);
Env.PrepArgs(TStr::Fmt("Dominating Set. build: %s, %s. Time: %s", __TIME__, __DATE__, TExeTm::GetCurTm()));
TExeTm ExeTm;
Try{
const TStr InFNm = Env.GetIfArgPrefixStr("-i:", "input.txt", "Input directed graph in csv-format, i.e. one directed edge per line.");
TStr OutFNm = Env.GetIfArgPrefixStr("-o:", "output.txt", "Directory in which to save the output file. The name of the file is determined by the input parameters.");
const TInt Budget = Env.GetIfArgPrefixInt("-b:", 0, "Integer determining the maximum size of the cut set.");
const TInt P = Env.GetIfArgPrefixInt("-p:", 100, "Integer determining the maximum size of the cut set as a percentage of the total number of nodes in the graph.");
const TInt Algo = Env.GetIfArgPrefixInt("-a:", 1, "Choosing between the relaxed and the integer linear program, 0=LP, 1=ILP.");
if (OutFNm.Empty()) { OutFNm = InFNm.GetFMid(); }
try {
//check if input arguments are inside their bounds
if (InFNm == "") throw std::invalid_argument("-i: input not specified\n");
if (Algo < 0 || Algo >1) throw std::invalid_argument("-a: can only be 0 or 1\n");
if (Budget < 0) throw std::invalid_argument("-b: value must be non-negative\n");
if (P < 0 || P >100) throw std::invalid_argument("-p: value out of bounds, must be integer between 0 and 100\n");
if (OutFNm == "") throw std::invalid_argument("-o: output file not specified\n");
}catch (const std::exception &exc){
std::cerr << "ERROR:" << exc.what();
return 0;
}
// load graph
PNGraph G = TSnap::LoadEdgeList<PNGraph>(InFNm, 0, 1);
for (TNGraph::TNodeI NI = G->BegNI(); NI < G->EndNI(); NI++) {
if (G->IsEdge(NI.GetId(), NI.GetId()))
G->DelEdge(NI.GetId(), NI.GetId());
}
// extract input file name for output
string fn = InFNm.CStr();
string out = OutFNm.CStr();
size_t found = fn.find("/");
while (found != string::npos) {
fn.erase(0, found + 1);
found = fn.find("/");
}
fn = out + fn;
string appendix = "";
if (P > 0) {
appendix += "-P-";
appendix += to_string(int(P));
}
else {
appendix += "-B-";
appendix += to_string(int(Budget));
}
//set budget or relative budget accordingly
int B = Budget;
if (P == 100) {
B = G->GetNodes();
}
else if (P > 0) {
B = G->GetNodes()*P / 100;
}
//choose algo
if (Algo == 0) {
//set output ending correctly
appendix += "-LP.txt";
fn.replace(fn.end() - 4, fn.end(), appendix);
FILE* F = fopen(fn.c_str(), "wt");
double sol = ILP(G, F, B);
fprintf(F, "%f\n", sol);
fclose(F);
}
else if (Algo == 1) {
//set output ending correctly
appendix += "-ILP.txt";
fn.replace(fn.end() - 4, fn.end(), appendix);
FILE* F = fopen(fn.c_str(), "wt");
double sol = ILP(G, F, B);
fprintf(F, "%f\n", sol);
fclose(F);
}
}
Catch
printf("\nrun time: %s (%s)\n", ExeTm.GetTmStr(), TSecTm::GetCurTm().GetTmStr().CStr());
return 0;
}