SCIP-SDP  4.0.0
SdpVarfixer.c
Go to the documentation of this file.
1 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2 /* */
3 /* This file is part of SCIPSDP - a solving framework for mixed-integer */
4 /* semidefinite programs based on SCIP. */
5 /* */
6 /* Copyright (C) 2011-2013 Discrete Optimization, TU Darmstadt */
7 /* EDOM, FAU Erlangen-Nürnberg */
8 /* 2014-2021 Discrete Optimization, TU Darmstadt */
9 /* */
10 /* */
11 /* This program is free software; you can redistribute it and/or */
12 /* modify it under the terms of the GNU Lesser General Public License */
13 /* as published by the Free Software Foundation; either version 3 */
14 /* of the License, or (at your option) any later version. */
15 /* */
16 /* This program is distributed in the hope that it will be useful, */
17 /* but WITHOUT ANY WARRANTY; without even the implied warranty of */
18 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
19 /* GNU Lesser General Public License for more details. */
20 /* */
21 /* You should have received a copy of the GNU Lesser General Public License */
22 /* along with this program; if not, write to the Free Software */
23 /* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.*/
24 /* */
25 /* */
26 /* Based on SCIP - Solving Constraint Integer Programs */
27 /* Copyright (C) 2002-2021 Zuse Institute Berlin */
28 /* SCIP is distributed under the terms of the SCIP Academic Licence, */
29 /* see file COPYING in the SCIP distribution. */
30 /* */
31 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
32 
38 #include "scip/type_misc.h"
39 #include "scip/def.h"
40 #include "scip/pub_message.h" /* for debug and error message */
41 #include "scip/pub_misc.h" /* for sorting */
42 #include "SdpVarfixer.h"
43 
44 /* turn off lint warnings for whole file: */
45 /*lint --e{788,818}*/
46 
47 /* defines for lexicographic sorting macros */
48 #define SORTTPL_NAMEEXT IntIntReal
49 #define SORTTPL_KEYTYPE int
50 #define SORTTPL_FIELD1TYPE SCIP_Real
51 #include "sorttpllex.c" /*lint !e451*/
52 
53 
58  int* row,
59  int* col,
60  SCIP_Real* val,
61  int length
62  )
63 {
64  SCIPlexSortIntIntReal(row, col, val, length);
65 
66 #ifndef NDEBUG
67  {
68  int j;
69  for (j = 0; j < length-1; ++j)
70  {
71  assert( row[j] <= row[j+1] );
72  assert( row[j] != row[j+1] || col[j] <= col[j+1] );
73  }
74  }
75 #endif
76 }
77 
87  BMS_BLKMEM* blkmem,
88  SCIP_Real epsilon,
89  int* originrow,
90  int* origincol,
91  SCIP_Real* originval,
92  int originlength,
93  SCIP_Bool originsorted,
94  SCIP_Real scalar,
95  int* targetrow,
96  int* targetcol,
97  SCIP_Real* targetval,
98  int* targetlength,
100  int targetmemory
102  )
103 { /*lint --e{679}*/
104  /*lint --e{850}*/
105  int ind;
106  int i;
107  int nleftshifts; /* if some nonzeros of the target arrays get deleted, this saves the number of spots the following entries have to be moved
108  * to the left */
109  int naddednonz; /* this gives the number of nonzeros that were added to the end of the arrays (this does NOT include those that were added in
110  * the middle of the arrays by decreasing the number of leftshifts) */
111  int insertionpos;
112  SCIP_Bool debugmsg; /* should a debug message about insufficient length be thrown */
113 
114  assert ( blkmem != NULL );
115  assert ( originlength == 0 || (originlength > 0 && originrow != NULL && origincol != NULL && originval != NULL) );
116  assert ( targetrow != NULL );
117  assert ( targetcol != NULL );
118  assert ( targetval != NULL );
119  assert ( targetlength != NULL );
120  assert ( *targetlength >= 0 );
121 
122  /* sort the target and origin arrays first by row and then by col to make searching for entries easier */
123  SCIPsdpVarfixerSortRowCol(targetrow, targetcol, targetval, *targetlength);
124 
125  if ( ! originsorted )
126  SCIPsdpVarfixerSortRowCol(originrow, origincol, originval, originlength);
127 
128  ind = 0; /* this will be used to traverse the nonzeros of the target arrays */
129  naddednonz = 0;
130  nleftshifts = 0;
131  debugmsg = FALSE;
132 
133  /* iterate over all nonzeroes */
134  for (i = 0; i < originlength; i++)
135  {
136  /* search the target arrays for an entry at this position, as both the origin and the target arrays are sorted, we go on until
137  * we find an entry that is not < as the current entry in the origin arrays according to this sorting, if this has equal row/col,
138  * we have found the entry we have to edit, if it is >, then we know, that there is no identical entry, and we can just add a new
139  * entry for this row and col */
140  while (ind < *targetlength && (targetrow[ind] < originrow[i] || (targetrow[ind] == originrow[i] && targetcol[ind] < origincol[i])))
141  {
142  /* shift the target nonzeros to the left if needed */
143  if ( nleftshifts > 0 )
144  {
145  targetrow[ind - nleftshifts] = targetrow[ind];
146  targetcol[ind - nleftshifts] = targetcol[ind];
147  targetval[ind - nleftshifts] = targetval[ind];
148  }
149  ind++;
150  }
151 
152  if ( ind < *targetlength && (targetrow[ind] == originrow[i] && targetcol[ind] == origincol[i]) )
153  {
154  /* add to the old entry */
155 
156  /* shift the entry to the left if needed and change the value */
157  if ( nleftshifts > 0 )
158  {
159  targetrow[ind - nleftshifts] = targetrow[ind];
160  targetcol[ind - nleftshifts] = targetcol[ind];
161  }
162  targetval[ind - nleftshifts] = targetval[ind] + scalar * originval[i];
163 
164  /* there could be multiple entries to add with identical row and col, so look for further ones in the next entries until there
165  * are no more */
166  while (i + 1 < originlength && originrow[i + 1] == targetrow[ind - nleftshifts] && origincol[i + 1] == targetcol[ind - nleftshifts])
167  {
168  targetval[ind - nleftshifts] += scalar * originval[i + 1];
169  i++;
170  }
171 
172  if ( REALABS(targetval[ind - nleftshifts]) < epsilon )
173  {
174  /* the nonzero became zero */
175  nleftshifts++;
176  }
177  ind++; /* as we already added all origin-entries belonging to this row/col and also shifted the entry, we can continue with the next one */
178  }
179  else /* create a new entry */
180  {
181  if ( nleftshifts > 0 )
182  {
183  /* we can add the nonzero at one of the empty spots */
184  insertionpos = ind - nleftshifts;
185  nleftshifts--; /* as one empty spot was filled, all remaining nonzeros should be moved one less position to the left */
186  }
187  else
188  {
189  /* add it to the end */
190  insertionpos = *targetlength + naddednonz;
191  naddednonz++;
192  }
193 
194  if ( insertionpos < targetmemory )
195  {
196  /* add the nonzero to the computed position */
197  targetrow[insertionpos] = originrow[i];
198  targetcol[insertionpos] = origincol[i];
199  targetval[insertionpos] = scalar * originval[i];
200 
201  /* there could be multiple entries to add with identical row and col, so look for further ones in the next entries until there are no more */
202  while (i + 1 < originlength && originrow[i + 1] == targetrow[insertionpos] && origincol[i + 1] == targetcol[insertionpos])
203  {
204  targetval[insertionpos] += scalar * originval[i + 1];
205  i++;
206  }
207 
208  /* if there were indeed multiple entries, check if they did cancel each other out, in that case remove the entry */
209  if ( REALABS(targetval[insertionpos]) < epsilon )
210  {
211  /* depending on where this actually zero nonzero was added, either add another leftshift to overwrite it or decrease the number of addednonz */
212  if ( insertionpos < ind )
213  nleftshifts++;
214  else
215  naddednonz--;
216  }
217  }
218  else /* the memory was not sufficient, so we will throw a debug message, we only wait until we know the final needed size */
219  debugmsg = TRUE;
220  }
221  }
222  /* shift the remaining entries of the target arrays */
223  if ( nleftshifts > 0 )
224  {
225  while (ind < *targetlength + naddednonz && ind < targetmemory)
226  {
227  targetrow[ind - nleftshifts] = targetrow[ind];
228  targetcol[ind - nleftshifts] = targetcol[ind];
229  targetval[ind - nleftshifts] = targetval[ind];
230  ind++;
231  }
232  }
233 
234  if ( debugmsg )
235  SCIPdebugMessage("insufficient memory given for SCIPsdpVarfixerMergeArrays, targetmemorys had length %d, would have needed up to %d\n",
236  targetmemory, *targetlength + naddednonz);
237 
238  *targetlength = *targetlength + naddednonz - nleftshifts;
239 
240  return SCIP_OKAY;
241 }
242 
243 
253  BMS_BLKMEM* blkmem,
254  SCIP_Real epsilon,
255  int* firstrow,
256  int* firstcol,
257  SCIP_Real* firstval,
258  int firstlength,
259  int* secondrow,
260  int* secondcol,
261  SCIP_Real* secondval,
262  int secondlength,
263  int* targetrow,
264  int* targetcol,
265  SCIP_Real* targetval,
266  int* targetlength
268  )
269 {
270  int firstind;
271  int secondind;
272  int targetind;
273  SCIP_Bool debugmsg; /* should we throw a debug message about insufficient memory */
274 
275  assert ( blkmem != NULL );
276  assert ( firstlength == 0 || (firstlength > 0 && firstrow != NULL && firstcol != NULL && firstval != NULL ) );
277  assert ( secondlength == 0 || (secondlength > 0 && secondrow != NULL && secondcol != NULL && secondval != NULL ) );
278  assert ( targetrow != NULL );
279  assert ( targetcol != NULL );
280  assert ( targetval != NULL );
281  assert ( targetlength != NULL );
282  assert ( *targetlength >= 0 );
283 
284  debugmsg = FALSE;
285 
286  /* sort both arrays by non-decreasing row and then col indices to make comparisons easier */
287  SCIPsdpVarfixerSortRowCol(firstrow, firstcol, firstval, firstlength);
288  SCIPsdpVarfixerSortRowCol(secondrow, secondcol, secondval, secondlength);
289 
290  /* as both arrays are sorted, traverse them simultanously, always adding the current entry with the lower index of either array to the
291  * target arrays (if they both have the same index, we have found entries that need to be merged) */
292  firstind = 0;
293  secondind = 0;
294  targetind = 0;
295 
296  while (firstind < firstlength && secondind < secondlength)
297  {
298  /* if the next entry of the first arrays comes before the next entry of the second arrays according to the row then col sorting, then we can
299  * insert the next entry of the first arrays, as there can't be an entry in the second arrays for the same row/col-combination */
300  if ( firstrow[firstind] < secondrow[secondind] || (firstrow[firstind] == secondrow[secondind] && firstcol[firstind] < secondcol[secondind]) )
301  {
302  if ( targetind < *targetlength )
303  {
304  targetrow[targetind] = firstrow[firstind];
305  targetcol[targetind] = firstcol[firstind];
306  targetval[targetind] = firstval[firstind];
307  }
308  else
309  debugmsg = TRUE;
310 
311  targetind++;
312  firstind++;
313  }
314  /* if the next entry of the second array comes first, we insert it */
315  else if ( firstrow[firstind] > secondrow[secondind] || (firstrow[firstind] == secondrow[secondind] && firstcol[firstind] > secondcol[secondind]) )
316  {
317  if ( targetind < *targetlength )
318  {
319  targetrow[targetind] = secondrow[secondind];
320  targetcol[targetind] = secondcol[secondind];
321  targetval[targetind] = secondval[secondind];
322  }
323  else
324  debugmsg = TRUE;
325  secondind++;
326 
327  /* as the second arrays may have duplicate entries, we have to check the next entry, if it has the same row/col combination, if yes, then we
328  * add it's value to the created entry in the target entries and continue */
329  while (secondind < secondlength && (secondrow[secondind] == targetrow[targetind] && secondcol[secondind] == targetcol[targetind]))
330  {
331  if ( targetind < *targetlength )
332  targetval[targetind] += secondval[secondind];
333  secondind++;
334  }
335 
336  /* if we combined multiple fixed nonzeros, it is possible that they cancelled each other out, in that case, we shouldn't add a nonzero to the
337  * target arrays (if the array was too short we didn't compute the entry, but we add it, as we want to get an upper bound on the needed size) */
338  if ( targetind >= *targetlength || REALABS(targetval[targetind]) >= epsilon )
339  targetind++;
340  }
341  /* if the next entries of both arrays are equal according to the row then col sorting, then they need to be combined */
342  else
343  {
344  if ( targetind < *targetlength )
345  {
346  targetrow[targetind] = firstrow[firstind];
347  targetcol[targetind] = firstcol[firstind];
348  targetval[targetind] = firstval[firstind] + secondval[secondind];
349  }
350  else
351  debugmsg = TRUE;
352  firstind++;
353  secondind++;
354 
355  /* as the second arrays may have duplicate entries, we have to check the next entry, if it has the same row/col combination, if yes, then we
356  * add it's value to the created entry in the target entries and continue */
357  while (secondind < secondlength && (secondrow[secondind] == targetrow[targetind] && secondcol[secondind] == targetcol[targetind]))
358  {
359  if ( targetind < *targetlength )
360  targetval[targetind] += secondval[secondind];
361  secondind++;
362  }
363 
364  /* as we combined multiple entires, it is possible that they cancelled each other out, in that case, we shouldn't add a nonzero to the
365  * target arrays (if the array was too short we didn't compute the entry, but we add it, as we want to get an upper bound on the needed size) */
366  if ( targetind >= *targetlength || REALABS(targetval[targetind]) >= epsilon )
367  targetind++;
368  }
369  }
370  /* if we reach the end of one of the two arrays, we can just add the rest of the other array to the target arrays (in case of the second still
371  * combining duplicate entries of this same array), so at most one of the following two while-queues will be non-empty, the contents of these
372  * queues are exactly the same as the corresponding if-case in the above while-queue (+ checking for the length of the target arrays) */
373  while (firstind < firstlength)
374  {
375  if ( targetind < *targetlength )
376  {
377  targetrow[targetind] = firstrow[firstind];
378  targetcol[targetind] = firstcol[firstind];
379  targetval[targetind] = firstval[firstind];
380  }
381  else
382  debugmsg = TRUE;
383  firstind++;
384  targetind++;
385  }
386 
387  while (secondind < secondlength)
388  {
389  if ( targetind < *targetlength )
390  {
391  targetrow[targetind] = secondrow[secondind];
392  targetcol[targetind] = secondcol[secondind];
393  targetval[targetind] = secondval[secondind];
394  }
395  else
396  debugmsg = TRUE;
397  secondind++;
398 
399  /* as the second arrays may have duplicate entries, we have to check the next entry, if it has the same row/col combination, if yes, then we
400  * add it's value to the created entry in the target entries and continue */
401  while (secondind < secondlength && (secondrow[secondind] == targetrow[targetind] && secondcol[secondind] == targetcol[targetind]))
402  {
403  if ( targetind < *targetlength )
404  targetval[targetind] += secondval[secondind];
405  secondind++;
406  }
407 
408  /* if we combined multiple fixed nonzeros, it is possible that they cancelled each other out, in that case, we shouldn't add a nonzero to the
409  * target arrays */
410  if ( targetind >= *targetlength || REALABS(targetval[targetind]) >= epsilon )
411  targetind++;
412  }
413 
414  if ( debugmsg )
415  {
416  SCIPdebugMessage("Insufficient arraylength in SCIPsdpVarfixerMergeArraysIntoNew, given targetarray-length was %d, would have needed %d",
417  *targetlength, targetind);
418  }
419 
420  *targetlength = targetind;
421 
422  return SCIP_OKAY;
423 }
void SCIPsdpVarfixerSortRowCol(int *row, int *col, SCIP_Real *val, int length)
Definition: SdpVarfixer.c:57
template functions for lexicographic sorting
adds the main functionality to fix/unfix/(multi-)aggregate variables by merging two three-tuple-array...
SCIP_RETCODE SCIPsdpVarfixerMergeArrays(BMS_BLKMEM *blkmem, SCIP_Real epsilon, int *originrow, int *origincol, SCIP_Real *originval, int originlength, SCIP_Bool originsorted, SCIP_Real scalar, int *targetrow, int *targetcol, SCIP_Real *targetval, int *targetlength, int targetmemory)
Definition: SdpVarfixer.c:86
SCIP_RETCODE SCIPsdpVarfixerMergeArraysIntoNew(BMS_BLKMEM *blkmem, SCIP_Real epsilon, int *firstrow, int *firstcol, SCIP_Real *firstval, int firstlength, int *secondrow, int *secondcol, SCIP_Real *secondval, int secondlength, int *targetrow, int *targetcol, SCIP_Real *targetval, int *targetlength)
Definition: SdpVarfixer.c:252