cplexlpcompare
Compares two LP files created in cplex format and dumps differences to files.
 All Classes Namespaces Files Functions Variables Enumerations Enumerator Friends Macros
lpcompare.cpp
Go to the documentation of this file.
1 // Copyright (c) 2014 Kerem KAT
2 //
3 // Permission is hereby granted, free of charge, to any person obtaining a copy of
4 // this software and associated documentation files(the "Software"), to deal in
5 // the Software without restriction, including without limitation the rights to
6 // use, copy, modify, merge, publish, distribute, sublicense, and / or sell copies of
7 // the Software, and to permit persons to whom the Software is furnished to do so,
8 // subject to the following conditions :
9 //
10 // The above copyright notice and this permission notice shall be included in all
11 // copies or substantial portions of the Software.
12 //
13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
15 // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR
16 // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
17 // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
18 // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19 //
20 // Do not hesisate to contact me about usage of the code or to make comments
21 // about the code. Your feedback will be appreciated.
22 //
23 // http://dissipatedheat.com/
24 // http://github.com/krk/
25 
26 #include <vector>
27 #include <iostream>
28 #include "LPModel.h"
29 #include <boost/range/algorithm/set_algorithm.hpp>
30 #include <chrono>
31 #include <boost/program_options.hpp>
32 
33 #define TIMING
34 
35 #ifdef TIMING
36 #define INIT_TIMER auto start = std::chrono::high_resolution_clock::now()
37 #define START_TIMER start = std::chrono::high_resolution_clock::now()
38 #define STOP_TIMER() ( \
39  std::chrono::duration_cast<std::chrono::milliseconds>( \
40  std::chrono::high_resolution_clock::now()-start \
41  ).count())
42 #define STOP_TIMER_SEC() (STOP_TIMER() / 60)
43 #else
44 #define INIT_TIMER
45 #define START_TIMER
46 #define STOP_TIMER() (0)
47 #define STOP_TIMER_SEC() (0)
48 #endif
49 
50 using std::cout;
51 using std::endl;
52 using lpcompare::LPModel;
53 
59 namespace po = boost::program_options;
60 
61 template <typename T>
62 void printCounts(std::string detail_name, std::vector<T> &vec, std::vector<T> &vecother);
63 
64 template <>
65 void printCounts(std::string detail_name, std::vector<lpcompare::Bound> &vec, std::vector<lpcompare::Bound> &vecother);
66 
67 template <>
68 void printCounts(std::string detail_name, std::vector<lpcompare::Constraint> &vec, std::vector<lpcompare::Constraint> &vecother);
69 
70 template <typename T>
71 void printCountsWithSort(std::string detail_name, std::vector<T> &vec, std::vector<T> &vecother);
72 
73 void printStats(LPModel *model);
74 
75 template <typename T>
76 void dumpdiff_if_requested(std::string detail_name, std::vector<T> &set1Except2, std::vector<T> &set2Except1);
77 
78 std::string first_filename;
79 std::string second_filename;
81 po::variables_map vm;
89 void setup_options(int argc, char *argv []) {
90 
91  po::positional_options_description p;
92  p.add("first", 1);
93  p.add("second", 1);
94 
95  po::options_description desc("Usage");
96  desc.add_options()
97  ("help", "show usage information")
98  ("first", po::value<std::string>(&first_filename)->required(), "model 1 cplex lp file")
99  ("second", po::value<std::string>(&second_filename)->required(), "model 2 cplex lp file")
100  ("dump-prefix", po::value<std::string>()->default_value("diffdump"), "filename prefix for difference dumps")
101  ("dump-diffs", po::value<bool>()->default_value(true), "filename prefix for difference dumps")
102  ;
103 
104  try
105  {
106  po::store(po::command_line_parser(argc, argv).
107  options(desc).positional(p).run(), vm);
108 
109  if (vm.count("help")) {
110  cout << desc << "\n";
111  exit(1);
112  }
113 
114  po::notify(vm);
115  }
116  catch (po::required_option& e)
117  {
118  std::cerr << "Error: " << e.what() << std::endl << std::endl;
119 
120  cout << desc << "\n";
121  exit(1);
122  }
123 }
124 
132 std::string get_dump_filename(std::string model, std::string detail) {
133  return vm["dump-prefix"].as<std::string>() + "-" + model + "-" + detail + ".log";
134 }
135 
142  return vm["dump-diffs"].as<bool>();
143 }
144 
145 int main(int argc, char *argv [])
146 {
147  INIT_TIMER;
148 
149  cout << "lpcompare: Compares two LP files created in cplex format and dumps differences to files." << endl;
150 
151  setup_options(argc, argv);
152 
153  LPModel* model1 = new LPModel();
154  LPModel* model2 = new LPModel();
155 
156  START_TIMER;
157  cout << "Reading first model: " << first_filename << endl;
158  if (!model1->ReadModel(first_filename)) {
159  exit(1);
160  }
161  cout << " First model:" << endl;
162  printStats(model1);
163  auto first_model_read_sec = STOP_TIMER_SEC();
164  cout << " Model read in " << first_model_read_sec << " s" << endl;
165 
166  START_TIMER;
167  cout << "Reading second model: " << second_filename << endl;
168  if (!model2->ReadModel(second_filename)) {
169  exit(1);
170  }
171  cout << " Second Model:" << endl;
172  printStats(model2);
173  auto second_model_read_sec = STOP_TIMER_SEC();
174  cout << " Model read in " << second_model_read_sec << " s" << endl;
175 
176  cout << endl;
177 
178  printCounts("Generals", model1->Generals, model2->Generals);
179  printCounts("Binaries", model1->Binaries, model2->Binaries);
180  printCounts("SosVars", model1->SosVars, model2->SosVars);
181 
182  START_TIMER;
183  printCounts("Bounds", model1->Bounds, model2->Bounds);
184  auto bounds_check_sec = STOP_TIMER_SEC();
185  cout << " Bounds check completed in " << bounds_check_sec << " s" << endl;
186 
187  START_TIMER;
188  printCounts("Constraints", model1->Constraints, model2->Constraints);
189  auto cons_check_sec = STOP_TIMER_SEC();
190  cout << " Constraints check completed in " << cons_check_sec << " s" << endl;
191 
192  return 0;
193 }
194 
200 void printStats(LPModel *model) {
201  cout << "Binaries: " << model->Binaries.size() << endl;
202  cout << "Bounds: " << model->Bounds.size() << endl;
203  cout << "Constraints: " << model->Constraints.size() << endl;
204  cout << "Generals: " << model->Generals.size() << endl;
205  cout << "SosVars: " << model->SosVars.size() << endl;
206 }
207 
214 template <typename T>
215 void dumpdiff_if_requested(std::string detail_name, std::vector<T> &set1Except2, std::vector<T> &set2Except1) {
216 
217  if (!is_diffdumps_requested())
218  return;
219 
220  if (set1Except2.size() > 0) {
221 
222  auto filename1e2 = get_dump_filename("firstEXCEPTsecond", detail_name);
223  std::ofstream out_file_1(filename1e2);
224 
225  if (!out_file_1) {
226  cout << "Cannot open or create file for writing: " << filename1e2;
227  return;
228  }
229 
230  out_file_1 << "firstEXCEPTsecond " << detail_name << std::endl;
231  for (auto item : set1Except2) {
232  out_file_1 << item << std::endl;
233  }
234 
235  out_file_1.flush();
236  out_file_1.close();
237 
238  cout << "firstEXCEPTsecond diff written for " << detail_name << " to " << filename1e2 << std::endl;
239  }
240 
241  if (set2Except1.size() > 0) {
242 
243  auto filename2e1 = get_dump_filename("secondEXCEPTfirst", detail_name);
244  std::ofstream out_file_2(filename2e1);
245 
246  if (!out_file_2) {
247  cout << "Cannot open or create file for writing: " << filename2e1;
248  return;
249  }
250 
251  out_file_2 << "secondEXCEPTfirst " << detail_name << std::endl;
252  for (auto item : set2Except1) {
253  out_file_2 << item << std::endl;
254  }
255 
256  out_file_2.flush();
257  out_file_2.close();
258 
259  cout << "secondEXCEPTfirst diff written for " << detail_name << " to " << filename2e1 << std::endl;
260  }
261 }
262 
270 template <typename T>
271 void printCounts(std::string detail_name, std::vector<T> &vec, std::vector<T> &vecother)
272 {
273  cout << detail_name << " First Model: " << vec.size() << endl;
274  cout << detail_name << " Second Model: " << vecother.size() << endl;
275 
276  std::vector<T> set1Except2;
277  std::set_difference(vec.begin(), vec.end(), vecother.begin(), vecother.end(), std::back_inserter(set1Except2));
278 
279  std::vector<T> set2Except1;
280  std::set_difference(vecother.begin(), vecother.end(), vec.begin(), vec.end(), std::back_inserter(set2Except1));
281 
282  auto size1e2 = set1Except2.size();
283  auto size2e1 = set1Except2.size();
284 
285  if (size1e2 == 0 && size2e1 == 0) {
286  cout << detail_name << " are equivalent." << endl;
287  }
288  else {
289  cout << detail_name << " First except Second: " << set1Except2.size() << endl;
290  cout << detail_name << " Second except First: " << set2Except1.size() << endl;
291  }
292 
293  dumpdiff_if_requested(detail_name, set1Except2, set2Except1);
294 }
295 
303 template <typename T>
304 void printCountsWithSort(std::string name, std::vector<T> &vec, std::vector<T> &vecother)
305 {
306  cout << name << " First Model: " << vec.size() << endl;
307  cout << name << " Second Model: " << vecother.size() << endl;
308 
309  std::vector<T> set1Except2;
310  std::vector<T> set2Except1;
311 
312  std::sort(vec.begin(), vec.end());
313  std::sort(vecother.begin(), vecother.end());
314 
315  auto it = vec.begin();
316  auto ot = vecother.begin();
317 
318  while (it != vec.end() && ot != vecother.end())
319  {
320  auto item = *it;
321  auto other = *ot;
322 
323  if (item == other) {
324  ++it;
325  ++ot;
326  }
327  else if (item < other) {
328  set1Except2.push_back(item);
329  ++it;
330  }
331  else {
332  set2Except1.push_back(other);
333  ++ot;
334  }
335  }
336 
337  while (it != vec.end())
338  {
339  set1Except2.push_back(*it);
340  ++it;
341  }
342 
343  while (ot != vecother.end())
344  {
345  set2Except1.push_back(*ot);
346  ++ot;
347  }
348 
349  auto size1e2 = set1Except2.size();
350  auto size2e1 = set2Except1.size();
351 
352  if (size1e2 == 0 && size2e1 == 0) {
353  cout << name << " are equivalent." << endl;
354  }
355  else {
356  cout << name << " First except Second: " << set1Except2.size() << endl;
357  cout << name << " Second except First: " << set2Except1.size() << endl;
358  }
359 
360  dumpdiff_if_requested(name, set1Except2, set2Except1);
361 }
362 
370 template <>
371 void printCounts(std::string detail_name, std::vector<lpcompare::Constraint> &vec, std::vector<lpcompare::Constraint> &vecother)
372 {
373  printCountsWithSort(detail_name, vec, vecother);
374 }
375 
383 template <>
384 void printCounts(std::string detail_name, std::vector<lpcompare::Bound> &vec, std::vector<lpcompare::Bound> &vecother)
385 {
386  printCountsWithSort(detail_name, vec, vecother);
387 }
#define START_TIMER
Definition: lpcompare.cpp:37
std::string second_filename
Filename of the second LP model.
Definition: lpcompare.cpp:79
Represents an LP model composed of a bounds, constraints and variables of different kinds...
Definition: LPModel.h:49
std::vector< Constraint > Constraints
Definition: LPModel.h:83
int main(int argc, char *argv[])
Definition: lpcompare.cpp:145
void printCountsWithSort(std::string detail_name, std::vector< T > &vec, std::vector< T > &vecother)
Prints count of a given detail for both models, including differences.
Definition: lpcompare.cpp:304
std::vector< std::string > SosVars
Definition: LPModel.h:81
std::string first_filename
Filename of the first LP model.
Definition: lpcompare.cpp:78
std::vector< std::string > Generals
Definition: LPModel.h:79
void setup_options(int argc, char *argv[])
Parses program arguments using boost program_options.
Definition: lpcompare.cpp:89
#define STOP_TIMER_SEC()
Definition: lpcompare.cpp:42
std::string get_dump_filename(std::string model, std::string detail)
Creates a filename to dump differences into.
Definition: lpcompare.cpp:132
void printCounts(std::string detail_name, std::vector< T > &vec, std::vector< T > &vecother)
Prints count of a given detail for both models, including differences.
Definition: lpcompare.cpp:271
std::vector< std::string > Binaries
Definition: LPModel.h:80
void printStats(LPModel *model)
Prints statistics for a model.
Definition: lpcompare.cpp:200
void dumpdiff_if_requested(std::string detail_name, std::vector< T > &set1Except2, std::vector< T > &set2Except1)
Dumps diffs to a file if dumping is requested.
Definition: lpcompare.cpp:215
#define INIT_TIMER
Definition: lpcompare.cpp:36
bool is_diffdumps_requested()
Checks if diff dumps are requested.
Definition: lpcompare.cpp:141
po::variables_map vm
Variable map for boost::program_options.
Definition: lpcompare.cpp:81
std::vector< Bound > Bounds
Definition: LPModel.h:82
bool ReadModel(std::string filename)
Parses an LP file to build an LPModel instance.
Definition: LPModel.cpp:61