Scippy

GCG

Branch-and-Price & Column Generation for Everyone

clscons_consnamelevenshtein.cpp
Go to the documentation of this file.
1 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2 /* */
3 /* This file is part of the program */
4 /* GCG --- Generic Column Generation */
5 /* a Dantzig-Wolfe decomposition based extension */
6 /* of the branch-cut-and-price framework */
7 /* SCIP --- Solving Constraint Integer Programs */
8 /* */
9 /* Copyright (C) 2010-2021 Operations Research, RWTH Aachen University */
10 /* Zuse Institute Berlin (ZIB) */
11 /* */
12 /* This program is free software; you can redistribute it and/or */
13 /* modify it under the terms of the GNU Lesser General Public License */
14 /* as published by the Free Software Foundation; either version 3 */
15 /* of the License, or (at your option) any later version. */
16 /* */
17 /* This program is distributed in the hope that it will be useful, */
18 /* but WITHOUT ANY WARRANTY; without even the implied warranty of */
19 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
20 /* GNU Lesser General Public License for more details. */
21 /* */
22 /* You sh
23 ould have received a copy of the GNU Lesser General Public License */
24 /* along with this program; if not, write to the Free Software */
25 /* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.*/
26 /* */
27 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
28 
29 /**@file clscons_consnamelevenshtein.cpp
30  * @ingroup CLASSIFIERS
31  * @brief classifies constraints according to levenshtein distance graph of their names
32  */
33 
34 /*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
35 
37 #include "cons_decomp.h"
38 #include "cons_decomp.hpp"
39 #include <vector>
40 #include <stdio.h>
41 #include <sstream>
42 #include <queue>
43 
44 #include "class_detprobdata.h"
45 
46 #include "class_conspartition.h"
47 #include "scip_misc.h"
48 
49 /* classifier properties */
50 #define DEC_CLASSIFIERNAME "consnamelevenshtein" /**< name of classifier */
51 #define DEC_DESC "constraint names (according to levenshtein distance graph)" /**< short description of classification*/
52 #define DEC_PRIORITY 0
53 
54 #define DEC_ENABLED FALSE
55 
56 
57 /*
58  * Data structures
59  */
60 
61 /** classifier handler data */
63 {
64 };
65 
66 
67 /*
68  * Local methods
69  */
70 
71 /* put your local methods here, and declare them static */
72 
73 
74 /*
75  * classifier callback methods
76  */
77 
78 /** destructor of classifier to free user data (called when GCG is exiting) */
79 #define classifierFree NULL
80 
81 /** returns levenshtein distance between two strings */
83  std::string s,
84  std::string t
85 )
86 {
87  /* easy cases */
88  if( s.compare( t ) == 0 )
89  return 0;
90  if( s.length() == 0 )
91  return t.length();
92  if( t.length() == 0 )
93  return s.length();
94 
95  /* vectors to store integer distances */
96  std::vector<int> prev( t.length() + 1 );
97  std::vector<int> curr( t.length() + 1 );
98 
99  /* initialize prev (previous row of distances) */
100  for( size_t i = 0; i < prev.size(); ++i )
101  {
102  prev[i] = i;
103  }
104  for( size_t i = 0; i < s.length(); ++i )
105  {
106  /* calculate curr (row distances) from the previous one */
107 
108  curr[0] = i + 1;
109 
110  /* fill remaining of row using 'Bellman' equality */
111  for( size_t j = 0; j < t.length(); ++j )
112  {
113  int cost = ( s[i] == t[j] ) ? 0 : 1;
114  curr[j + 1] = std::min( curr[j] + 1, std::min( prev[j + 1] + 1, prev[j] + cost ) );
115  }
116 
117  /* copy curr to prev for next iteration */
118  for( size_t j = 0; j < prev.size(); ++j )
119  prev[j] = curr[j];
120  }
121 
122  return curr[t.length()];
123 }
124 
125 
126 
127 static
128 DEC_DECL_CONSCLASSIFY(classifierClassify) {
129  gcg::DETPROBDATA* detprobdata;
130  if( transformed )
131  {
132  detprobdata = GCGconshdlrDecompGetDetprobdataPresolved(scip);
133  }
134  else
135  {
136  detprobdata = GCGconshdlrDecompGetDetprobdataOrig(scip);
137  }
138 
139  std::vector < std::string > consnamesToCompare(detprobdata->getNConss(), "");
140  std::vector<int> nConssConstype;
141  std::vector<int> classForCons(detprobdata->getNConss(), - 1);
142  std::vector<bool> alreadyReached(detprobdata->getNConss(), false);
143  std::queue<int> helpqueue;
144  int nUnreachedConss = detprobdata->getNConss();
145  int currentClass = - 1;
146  int nmaxconss = 5000;
147  int connectivity = 1;
148 
149  std::stringstream classifierName;
150  classifierName << "lev-dist-" << connectivity;
151  gcg::ConsPartition* classifier = new gcg::ConsPartition(scip, classifierName.str().c_str(), 0, detprobdata->getNConss());
152 
153  /* if number of conss exceeds this number, skip calculating such a classifier */
154  if( detprobdata->getNConss() > nmaxconss )
155  {
156 
157  SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, " skipped levenshtein distance based constraint classes calculating since number of constraints %d exceeds limit %d \n", detprobdata->getNConss(), nmaxconss );
158  delete classifier;
159  return SCIP_ERROR;
160  }
161 
162  std::vector<std::vector<int>> levenshteindistances(detprobdata->getNConss(), std::vector<int>( detprobdata->getNConss(), - 1 ));
163 
164  /* read consnames */
165  for( int i = 0; i < detprobdata->getNConss(); ++ i )
166  {
167  consnamesToCompare[i] = std::string(SCIPconsGetName(detprobdata->getCons(i)));
168  }
169 
170  /* calculate levenshtein distances pairwise */
171  for( int i = 0; i < detprobdata->getNConss(); ++ i )
172  {
173  for( int j = i + 1; j < detprobdata->getNConss(); ++ j )
174  {
175  levenshteindistances[i][j] = calcLevenshteinDistance(consnamesToCompare[i], consnamesToCompare[j]);
176  levenshteindistances[j][i] = levenshteindistances[i][j];
177  }
178  }
179 
180  /* repeat doing breadth first search until every constraint is assigned to a class */
181  while( nUnreachedConss > 0 )
182  {
183  int firstUnreached = - 1;
184  currentClass ++;
185  assert( helpqueue.empty() );
186  for( int i = 0; i < detprobdata->getNConss(); ++ i )
187  {
188  if( classForCons[i] == - 1 )
189  {
190  firstUnreached = i;
191  break;
192  }
193  }
194 
195  helpqueue.push(firstUnreached);
196  alreadyReached[firstUnreached] = true;
197  classForCons[firstUnreached] = currentClass;
198  -- nUnreachedConss;
199 
200  /* consider all constraints which are connected to the current constraint by means of levenshtein distance */
201  while( ! helpqueue.empty() )
202  {
203  int nodecons = helpqueue.front();
204  helpqueue.pop();
205  for( int j = 0; j < detprobdata->getNConss(); ++ j )
206  {
207 
208  if( alreadyReached[j] )
209  continue;
210 
211  if( j == nodecons )
212  continue;
213 
214  if( levenshteindistances[j][nodecons] > connectivity )
215  continue;
216 
217  alreadyReached[j] = true;
218  classForCons[j] = currentClass;
219  -- nUnreachedConss;
220  helpqueue.push(j);
221  }
222  }
223 
224  /* create a new class with found constraints in ConsPartition*/
225  std::stringstream text;
226  text << "This class contains all constraints with a name similar to \"" << consnamesToCompare[firstUnreached] << "\".";
227  classifier->addClass(consnamesToCompare[firstUnreached].c_str(), text.str().c_str(), gcg::BOTH);
228  }
229 
230  /* assign constraint indices to classes */
231  for( int i = 0; i < detprobdata->getNConss(); ++ i )
232  {
233  classifier->assignConsToClass(i, classForCons[i]);
234  }
235 
236  SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, " Consclassifier levenshtein: connectivity of %d yields a classification with %d different constraint classes. \n", connectivity, currentClass + 1);
237 
238  detprobdata->addConsPartition(classifier);
239  return SCIP_OKAY;
240 }
241 
242 /*
243  * classifier specific interface methods
244  */
245 
247 
248  SCIP *scip /**< SCIP data structure */
249 ) {
250  DEC_CLASSIFIERDATA* classifierdata = NULL;
251 
252  SCIP_CALL(
254  classifierFree, classifierClassify));
255 
256  return SCIP_OKAY;
257 }
int getNConss()
returns the number of variables considered in the detprobdata
#define DEC_PRIORITY
SCIP_RETCODE SCIPincludeConsClassifierConsnameLevenshtein(SCIP *scip)
constraint handler for structure detection
void assignConsToClass(int consindex, int classindex)
#define DEC_CLASSIFIERNAME
class representing a partition of a set of constraints
DETPROBDATA * GCGconshdlrDecompGetDetprobdataOrig(SCIP *scip)
help method to access detprobdata for unpresolved problem
various SCIP helper methods
#define classifierFree
SCIP_CONS * getCons(int consIndex)
returns the SCIP constraint related to a constraint index
static DEC_DECL_CONSCLASSIFY(classifierClassify)
int addClass(const char *name, const char *desc, CONS_DECOMPINFO decompInfo)
C++ interface of cons_decomp.
#define DEC_DESC
classifies constraints according to levenshtein distance graph of their names
#define DEC_ENABLED
DETPROBDATA * GCGconshdlrDecompGetDetprobdataPresolved(SCIP *scip)
help method to access detprobdata for transformed problem
int calcLevenshteinDistance(std::string s, std::string t)
void addConsPartition(ConsPartition *partition)
adds a constraint partition if it is no duplicate of an existing constraint partition
SCIP_RETCODE DECincludeConsClassifier(SCIP *scip, const char *name, const char *description, int priority, SCIP_Bool enabled, DEC_CLASSIFIERDATA *classifierdata, DEC_DECL_FREECONSCLASSIFIER((*freeClassifier)),)
class storing partialdecs and the problem matrix