SCIP-SDP  3.2.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-2020 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-2020 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 
51  int* row,
52  int* col,
53  SCIP_Real* val,
54  int length
55  )
56 {
57  int firstentry;
58  int nextentry;
59 
60  /* first sort by row indices */
61  SCIPsortIntIntReal(row, col, val, length);
62 
63  /* for those with identical row-indices now sort by non-decreasing col-index, first find all entries with the same row-index */
64  nextentry = 0;
65  while (nextentry < length)
66  {
67  firstentry = nextentry; /* the next row starts where the last one ended*/
68 
69  while (nextentry < length && row[nextentry] == row[firstentry]) /* as long as the row still matches, increase nextentry */
70  {
71  nextentry++;
72  }
73 
74  /* now sort all entries between firstentry and nextentry-1 by their col-indices */
75  SCIPsortIntReal(col + firstentry, val + firstentry, nextentry - firstentry);
76  }
77 }
78 
88  BMS_BLKMEM* blkmem,
89  SCIP_Real epsilon,
90  int* originrow,
91  int* origincol,
92  SCIP_Real* originval,
93  int originlength,
94  SCIP_Bool originsorted,
95  SCIP_Real scalar,
96  int* targetrow,
97  int* targetcol,
98  SCIP_Real* targetval,
99  int* targetlength,
101  int targetmemory
103  )
104 { /*lint --e{679}*/
105  /*lint --e{850}*/
106  int ind;
107  int i;
108  int nleftshifts; /* if some nonzeros of the target arrays get deleted, this saves the number of spots the following entries have to be moved
109  * to the left */
110  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
111  * the middle of the arrays by decreasing the number of leftshifts) */
112  int insertionpos;
113  SCIP_Bool debugmsg; /* should a debug message about insufficient length be thrown */
114 
115  assert ( blkmem != NULL );
116  assert ( originlength == 0 || (originlength > 0 && originrow != NULL && origincol != NULL && originval != NULL) );
117  assert ( targetrow != NULL );
118  assert ( targetcol != NULL );
119  assert ( targetval != NULL );
120  assert ( targetlength != NULL );
121  assert ( *targetlength >= 0 );
122 
123  /* sort the target and origin arrays first by row and then by col to make searching for entries easier */
124  SCIPsdpVarfixerSortRowCol(targetrow, targetcol, targetval, *targetlength);
125 
126  if ( ! originsorted )
127  SCIPsdpVarfixerSortRowCol(originrow, origincol, originval, originlength);
128 
129  ind = 0; /* this will be used to traverse the nonzeros of the target arrays */
130  naddednonz = 0;
131  nleftshifts = 0;
132  debugmsg = FALSE;
133 
134  /* iterate over all nonzeroes */
135  for (i = 0; i < originlength; i++)
136  {
137  /* search the target arrays for an entry at this position, as both the origin and the target arrays are sorted, we go on until
138  * 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,
139  * 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
140  * entry for this row and col */
141  while (ind < *targetlength && (targetrow[ind] < originrow[i] || (targetrow[ind] == originrow[i] && targetcol[ind] < origincol[i])))
142  {
143  /* shift the target nonzeros to the left if needed */
144  if ( nleftshifts > 0 )
145  {
146  targetrow[ind - nleftshifts] = targetrow[ind];
147  targetcol[ind - nleftshifts] = targetcol[ind];
148  targetval[ind - nleftshifts] = targetval[ind];
149  }
150  ind++;
151  }
152 
153  if ( ind < *targetlength && (targetrow[ind] == originrow[i] && targetcol[ind] == origincol[i]) )
154  {
155  /* add to the old entry */
156 
157  /* shift the entry to the left if needed and change the value */
158  if ( nleftshifts > 0 )
159  {
160  targetrow[ind - nleftshifts] = targetrow[ind];
161  targetcol[ind - nleftshifts] = targetcol[ind];
162  }
163  targetval[ind - nleftshifts] = targetval[ind] + scalar * originval[i];
164 
165  /* there could be multiple entries to add with identical row and col, so look for further ones in the next entries until there
166  * are no more */
167  while (i + 1 < originlength && originrow[i + 1] == targetrow[ind - nleftshifts] && origincol[i + 1] == targetcol[ind - nleftshifts])
168  {
169  targetval[ind - nleftshifts] += scalar * originval[i + 1];
170  i++;
171  }
172 
173  if ( REALABS(targetval[ind - nleftshifts]) < epsilon )
174  {
175  /* the nonzero became zero */
176  nleftshifts++;
177  }
178  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 */
179  }
180  else /* create a new entry */
181  {
182  if ( nleftshifts > 0 )
183  {
184  /* we can add the nonzero at one of the empty spots */
185  insertionpos = ind - nleftshifts;
186  nleftshifts--; /* as one empty spot was filled, all remaining nonzeros should be moved one less position to the left */
187  }
188  else
189  {
190  /* add it to the end */
191  insertionpos = *targetlength + naddednonz;
192  naddednonz++;
193  }
194 
195  if ( insertionpos < targetmemory )
196  {
197  /* add the nonzero to the computed position */
198  targetrow[insertionpos] = originrow[i];
199  targetcol[insertionpos] = origincol[i];
200  targetval[insertionpos] = scalar * originval[i];
201 
202  /* 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 */
203  while (i + 1 < originlength && originrow[i + 1] == targetrow[insertionpos] && origincol[i + 1] == targetcol[insertionpos])
204  {
205  targetval[insertionpos] += scalar * originval[i + 1];
206  i++;
207  }
208 
209  /* if there were indeed multiple entries, check if they did cancel each other out, in that case remove the entry */
210  if ( REALABS(targetval[insertionpos]) < epsilon )
211  {
212  /* depending on where this actually zero nonzero was added, either add another leftshift to overwrite it or decrease the number of addednonz */
213  if ( insertionpos < ind )
214  nleftshifts++;
215  else
216  naddednonz--;
217  }
218  }
219  else /* the memory was not sufficient, so we will throw a debug message, we only wait until we know the final needed size */
220  debugmsg = TRUE;
221  }
222  }
223  /* shift the remaining entries of the target arrays */
224  if ( nleftshifts > 0 )
225  {
226  while (ind < *targetlength + naddednonz && ind < targetmemory)
227  {
228  targetrow[ind - nleftshifts] = targetrow[ind];
229  targetcol[ind - nleftshifts] = targetcol[ind];
230  targetval[ind - nleftshifts] = targetval[ind];
231  ind++;
232  }
233  }
234 
235  if ( debugmsg )
236  SCIPdebugMessage("insufficient memory given for SCIPsdpVarfixerMergeArrays, targetmemorys had length %d, would have needed up to %d\n",
237  targetmemory, *targetlength + naddednonz);
238 
239  *targetlength = *targetlength + naddednonz - nleftshifts;
240 
241  return SCIP_OKAY;
242 }
243 
244 
254  BMS_BLKMEM* blkmem,
255  SCIP_Real epsilon,
256  int* firstrow,
257  int* firstcol,
258  SCIP_Real* firstval,
259  int firstlength,
260  int* secondrow,
261  int* secondcol,
262  SCIP_Real* secondval,
263  int secondlength,
264  int* targetrow,
265  int* targetcol,
266  SCIP_Real* targetval,
267  int* targetlength
269  )
270 {
271  int firstind;
272  int secondind;
273  int targetind;
274  SCIP_Bool debugmsg; /* should we throw a debug message about insufficient memory */
275 
276  assert ( blkmem != NULL );
277  assert ( firstlength == 0 || (firstlength > 0 && firstrow != NULL && firstcol != NULL && firstval != NULL ) );
278  assert ( secondlength == 0 || (secondlength > 0 && secondrow != NULL && secondcol != NULL && secondval != NULL ) );
279  assert ( targetrow != NULL );
280  assert ( targetcol != NULL );
281  assert ( targetval != NULL );
282  assert ( targetlength != NULL );
283  assert ( *targetlength >= 0 );
284 
285  debugmsg = FALSE;
286 
287  /* sort both arrays by non-decreasing row and then col indices to make comparisons easier */
288  SCIPsdpVarfixerSortRowCol(firstrow, firstcol, firstval, firstlength);
289  SCIPsdpVarfixerSortRowCol(secondrow, secondcol, secondval, secondlength);
290 
291  /* as both arrays are sorted, traverse them simultanously, always adding the current entry with the lower index of either array to the
292  * target arrays (if they both have the same index, we have found entries that need to be merged) */
293  firstind = 0;
294  secondind = 0;
295  targetind = 0;
296 
297  while (firstind < firstlength && secondind < secondlength)
298  {
299  /* 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
300  * 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 */
301  if ( firstrow[firstind] < secondrow[secondind] || (firstrow[firstind] == secondrow[secondind] && firstcol[firstind] < secondcol[secondind]) )
302  {
303  if ( targetind < *targetlength )
304  {
305  targetrow[targetind] = firstrow[firstind];
306  targetcol[targetind] = firstcol[firstind];
307  targetval[targetind] = firstval[firstind];
308  }
309  else
310  debugmsg = TRUE;
311 
312  targetind++;
313  firstind++;
314  }
315  /* if the next entry of the second array comes first, we insert it */
316  else if ( firstrow[firstind] > secondrow[secondind] || (firstrow[firstind] == secondrow[secondind] && firstcol[firstind] > secondcol[secondind]) )
317  {
318  if ( targetind < *targetlength )
319  {
320  targetrow[targetind] = secondrow[secondind];
321  targetcol[targetind] = secondcol[secondind];
322  targetval[targetind] = secondval[secondind];
323  }
324  else
325  debugmsg = TRUE;
326  secondind++;
327 
328  /* 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
329  * add it's value to the created entry in the target entries and continue */
330  while (secondind < secondlength && (secondrow[secondind] == targetrow[targetind] && secondcol[secondind] == targetcol[targetind]))
331  {
332  if ( targetind < *targetlength )
333  targetval[targetind] += secondval[secondind];
334  secondind++;
335  }
336 
337  /* 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
338  * 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) */
339  if ( targetind >= *targetlength || REALABS(targetval[targetind]) >= epsilon )
340  targetind++;
341  }
342  /* if the next entries of both arrays are equal according to the row then col sorting, then they need to be combined */
343  else
344  {
345  if ( targetind < *targetlength )
346  {
347  targetrow[targetind] = firstrow[firstind];
348  targetcol[targetind] = firstcol[firstind];
349  targetval[targetind] = firstval[firstind] + secondval[secondind];
350  }
351  else
352  debugmsg = TRUE;
353  firstind++;
354  secondind++;
355 
356  /* 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
357  * add it's value to the created entry in the target entries and continue */
358  while (secondind < secondlength && (secondrow[secondind] == targetrow[targetind] && secondcol[secondind] == targetcol[targetind]))
359  {
360  if ( targetind < *targetlength )
361  targetval[targetind] += secondval[secondind];
362  secondind++;
363  }
364 
365  /* 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
366  * 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) */
367  if ( targetind >= *targetlength || REALABS(targetval[targetind]) >= epsilon )
368  targetind++;
369  }
370  }
371  /* 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
372  * 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
373  * queues are exactly the same as the corresponding if-case in the above while-queue (+ checking for the length of the target arrays) */
374  while (firstind < firstlength)
375  {
376  if ( targetind < *targetlength )
377  {
378  targetrow[targetind] = firstrow[firstind];
379  targetcol[targetind] = firstcol[firstind];
380  targetval[targetind] = firstval[firstind];
381  }
382  else
383  debugmsg = TRUE;
384  firstind++;
385  targetind++;
386  }
387 
388  while (secondind < secondlength)
389  {
390  if ( targetind < *targetlength )
391  {
392  targetrow[targetind] = secondrow[secondind];
393  targetcol[targetind] = secondcol[secondind];
394  targetval[targetind] = secondval[secondind];
395  }
396  else
397  debugmsg = TRUE;
398  secondind++;
399 
400  /* 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
401  * add it's value to the created entry in the target entries and continue */
402  while (secondind < secondlength && (secondrow[secondind] == targetrow[targetind] && secondcol[secondind] == targetcol[targetind]))
403  {
404  if ( targetind < *targetlength )
405  targetval[targetind] += secondval[secondind];
406  secondind++;
407  }
408 
409  /* 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
410  * target arrays */
411  if ( targetind >= *targetlength || REALABS(targetval[targetind]) >= epsilon )
412  targetind++;
413  }
414 
415  if ( debugmsg )
416  {
417  SCIPdebugMessage("Insufficient arraylength in SCIPsdpVarfixerMergeArraysIntoNew, given targetarray-length was %d, would have needed %d",
418  *targetlength, targetind);
419  }
420 
421  *targetlength = targetind;
422 
423  return SCIP_OKAY;
424 }
void SCIPsdpVarfixerSortRowCol(int *row, int *col, SCIP_Real *val, int length)
Definition: SdpVarfixer.c:50
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:87
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:253