1
2 #include <string.h>
3 #include "commonlib.h"
4 #include "lp_lib.h"
5 #include "lp_report.h"
6 #include "lp_SOS.h"
7
8 #ifdef FORTIFY
9 # include "lp_fortify.h"
10 #endif
11
12
13 /*
14 Specially Ordered Set (SOS) routines - w/interface for lp_solve v5.0+
15 ----------------------------------------------------------------------------------
16 Author: Kjell Eikland
17 Contact: kjell.eikland@broadpark.no
18 License terms: LGPL.
19
20 Requires: lp_lib.h
21
22 Release notes:
23 v1.0 1 September 2003 Complete package for SOS creation and use in a LP
24 setting. Notable feature of this implementation
25 compared to those in other commercial systems is
26 the generalization to SOS'es of "unlimited" order.
27 v1.1 8 December 2003 Added variable (index) deletion method.
28 v1.2 17 December 2004 Added bound change tracking functionality.
29 v1.3 18 September 2005 Added sparse SOS handling to speed up processing
30 of large number of SOS'es.
31
32 ----------------------------------------------------------------------------------
33 */
34
35 /* SOS group functions */
create_SOSgroup(lprec * lp)36 STATIC SOSgroup *create_SOSgroup(lprec *lp)
37 {
38 SOSgroup *group;
39
40 group = (SOSgroup *) calloc(1, sizeof(*group));
41 group->lp = lp;
42 group->sos_alloc = SOS_START_SIZE;
43 group->sos_list = (SOSrec **) malloc((group->sos_alloc) * sizeof(*group->sos_list));
44 return(group);
45 }
46
resize_SOSgroup(SOSgroup * group)47 STATIC void resize_SOSgroup(SOSgroup *group)
48 {
49 if(group->sos_count == group->sos_alloc) {
50 group->sos_alloc = (int)((double) group->sos_alloc*RESIZEFACTOR);
51 group->sos_list = (SOSrec **) realloc(group->sos_list,
52 (group->sos_alloc) * sizeof(*group->sos_list));
53 }
54 }
55
append_SOSgroup(SOSgroup * group,SOSrec * SOS)56 STATIC int append_SOSgroup(SOSgroup *group, SOSrec *SOS)
57 {
58 int i, k;
59 SOSrec *SOSHold;
60
61 /* Check if we should resize */
62 resize_SOSgroup(group);
63
64 /* First append to the end of the list */
65 group->sos_list[group->sos_count] = SOS;
66 group->sos_count++;
67 i = abs(SOS->type);
68 SETMAX(group->maxorder, i);
69 if(i == 1)
70 group->sos1_count++;
71 k = group->sos_count;
72 SOS->tagorder = k;
73
74 /* Sort the SOS list by given priority */
75 for(i = group->sos_count-1; i > 0; i--) {
76 if(group->sos_list[i]->priority < group->sos_list[i-1]->priority) {
77 SOSHold = group->sos_list[i];
78 group->sos_list[i] = group->sos_list[i-1];
79 group->sos_list[i-1] = SOSHold;
80 if(SOSHold == SOS)
81 k = i; /* This is the index in the [1..> range */
82 }
83 else
84 break;
85 }
86 /* Return the list index of the new SOS */
87 return( k );
88 }
89
90
clean_SOSgroup(SOSgroup * group,MYBOOL forceupdatemap)91 STATIC int clean_SOSgroup(SOSgroup *group, MYBOOL forceupdatemap)
92 {
93 int i, n, k;
94 SOSrec *SOS;
95
96 if(group == NULL)
97 return( 0 );
98
99 /* Delete any SOS without members or trivial member count */
100 n = 0;
101 if(group->sos_alloc > 0) {
102 group->maxorder = 0;
103 for(i = group->sos_count; i > 0; i--) {
104 SOS = group->sos_list[i-1];
105 k = SOS->members[0];
106 if((k == 0) || /* Empty */
107 ((k == abs(SOS->type)) && (k <= 2))) { /* Trivial */
108 delete_SOSrec(group, i);
109 n++;
110 }
111 else {
112 SETMAX(group->maxorder, abs(SOS->type));
113 }
114 }
115 if((n > 0) || forceupdatemap)
116 SOS_member_updatemap(group);
117 }
118 return( n );
119 }
120
121
free_SOSgroup(SOSgroup ** group)122 STATIC void free_SOSgroup(SOSgroup **group)
123 {
124 int i;
125
126 if((group == NULL) || (*group == NULL))
127 return;
128 if((*group)->sos_alloc > 0) {
129 for(i = 0; i < (*group)->sos_count; i++)
130 free_SOSrec((*group)->sos_list[i]);
131 FREE((*group)->sos_list);
132 FREE((*group)->membership);
133 FREE((*group)->memberpos);
134 }
135 FREE(*group);
136 }
137
138 /* SOS record functions */
create_SOSrec(SOSgroup * group,char * name,int type,int priority,int size,int * variables,REAL * weights)139 STATIC SOSrec *create_SOSrec(SOSgroup *group, char *name, int type, int priority, int size, int *variables, REAL *weights)
140 {
141 SOSrec *SOS;
142
143 SOS = (SOSrec *) calloc(1 , sizeof(*SOS));
144 SOS->parent = group;
145 SOS->type = type;
146 if(name == NULL)
147 SOS->name = NULL;
148 else
149 {
150 allocCHAR(group->lp, &SOS->name, (int) (strlen(name)+1), FALSE);
151 strcpy(SOS->name, name);
152 }
153 if(type < 0)
154 type = abs(type);
155 SOS->tagorder = 0;
156 SOS->size = 0;
157 SOS->priority = priority;
158 SOS->members = NULL;
159 SOS->weights = NULL;
160 SOS->membersSorted = NULL;
161 SOS->membersMapped = NULL;
162
163 if(size > 0)
164 size = append_SOSrec(SOS, size, variables, weights);
165
166 return(SOS);
167 }
168
169
append_SOSrec(SOSrec * SOS,int size,int * variables,REAL * weights)170 STATIC int append_SOSrec(SOSrec *SOS, int size, int *variables, REAL *weights)
171 {
172 int i, oldsize, newsize, nn;
173 lprec *lp = SOS->parent->lp;
174
175 oldsize = SOS->size;
176 newsize = oldsize + size;
177 nn = abs(SOS->type);
178
179 /* Shift existing active data right (normally zero) */
180 if(SOS->members == NULL)
181 allocINT(lp, &SOS->members, 1+newsize+1+nn, TRUE);
182 else {
183 allocINT(lp, &SOS->members, 1+newsize+1+nn, AUTOMATIC);
184 for(i = newsize+1+nn; i > newsize+1; i--)
185 SOS->members[i] = SOS->members[i-size];
186 }
187 SOS->members[0] = newsize;
188 SOS->members[newsize+1] = nn;
189
190 /* Copy the new data into the arrays */
191 if(SOS->weights == NULL)
192 allocREAL(lp, &SOS->weights, 1+newsize, TRUE);
193 else
194 allocREAL(lp, &SOS->weights, 1+newsize, AUTOMATIC);
195 for(i = oldsize+1; i <= newsize; i++) {
196 SOS->members[i] = variables[i-oldsize-1];
197 if((SOS->members[i] < 1) || (SOS->members[i] > lp->columns))
198 report(lp, IMPORTANT, "append_SOS_rec: Invalid SOS variable definition for index %d\n", SOS->members[i]);
199 else {
200 if(SOS->isGUB)
201 lp->var_type[SOS->members[i]] |= ISGUB;
202 else
203 lp->var_type[SOS->members[i]] |= ISSOS;
204 }
205 if(weights == NULL)
206 SOS->weights[i] = i; /* Follow standard, which is sorted ascending */
207 else
208 SOS->weights[i] = weights[i-oldsize-1];
209 SOS->weights[0] += SOS->weights[i];
210 }
211
212 /* Sort the new paired lists ascending by weight (simple bubble sort) */
213 i = sortByREAL(SOS->members, SOS->weights, newsize, 1, TRUE);
214 if(i > 0)
215 report(lp, DETAILED, "append_SOS_rec: Non-unique SOS variable weight for index %d\n", i);
216
217 /* Define mapping arrays to search large SOS's faster */
218 allocINT(lp, &SOS->membersSorted, newsize, AUTOMATIC);
219 allocINT(lp, &SOS->membersMapped, newsize, AUTOMATIC);
220 for(i = oldsize+1; i <= newsize; i++) {
221 SOS->membersSorted[i - 1] = SOS->members[i];
222 SOS->membersMapped[i - 1] = i;
223 }
224 sortByINT(SOS->membersMapped, SOS->membersSorted, newsize, 0, TRUE);
225
226 /* Confirm the new size */
227 SOS->size = newsize;
228
229 return(newsize);
230
231 }
232
make_SOSchain(lprec * lp,MYBOOL forceresort)233 STATIC int make_SOSchain(lprec *lp, MYBOOL forceresort)
234 {
235 int i, j, k, n;
236 MYBOOL *hold = NULL;
237 REAL *order, sum, weight;
238 SOSgroup *group = lp->SOS;
239
240 /* PART A: Resort individual SOS member lists, if specified */
241 if(forceresort)
242 SOS_member_sortlist(group, 0);
243
244 /* PART B: Tally SOS variables and create master SOS variable list */
245 n = 0;
246 for(i = 0; i < group->sos_count; i++)
247 n += group->sos_list[i]->size;
248 lp->sos_vars = n;
249 if(lp->sos_vars > 0) /* Prevent memory loss in case of multiple solves */
250 FREE(lp->sos_priority);
251 allocINT(lp, &lp->sos_priority, n, FALSE);
252 allocREAL(lp, &order, n, FALSE);
253
254 /* Move variable data to the master SOS list and sort by ascending weight */
255 n = 0;
256 sum = 0;
257 for(i = 0; i < group->sos_count; i++) {
258 for(j = 1; j <= group->sos_list[i]->size; j++) {
259 lp->sos_priority[n] = group->sos_list[i]->members[j];
260 weight = group->sos_list[i]->weights[j];
261 sum += weight;
262 order[n] = sum;
263 n++;
264 }
265 }
266 hpsortex(order, n, 0, sizeof(*order), FALSE, compareREAL, lp->sos_priority);
267 FREE(order);
268
269 /* Remove duplicate SOS variables */
270 allocMYBOOL(lp, &hold, lp->columns+1, TRUE);
271 k = 0;
272 for(i = 0; i < n; i++) {
273 j = lp->sos_priority[i];
274 if(!hold[j]) {
275 hold[j] = TRUE;
276 if(k < i)
277 lp->sos_priority[k] = j;
278 k++;
279 }
280 }
281 FREE(hold);
282
283 /* Adjust the size of the master variable list, if necessary */
284 if(k < lp->sos_vars) {
285 allocINT(lp, &lp->sos_priority, k, AUTOMATIC);
286 lp->sos_vars = k;
287 }
288
289 return( k );
290
291 }
292
293
delete_SOSrec(SOSgroup * group,int sosindex)294 STATIC MYBOOL delete_SOSrec(SOSgroup *group, int sosindex)
295 {
296 #ifdef Paranoia
297 if((sosindex <= 0) || (sosindex > group->sos_count)) {
298 report(group->lp, IMPORTANT, "delete_SOSrec: Invalid SOS index %d\n", sosindex);
299 return(FALSE);
300 }
301 #endif
302
303 /* Delete and free the SOS record */
304 if(abs(SOS_get_type(group, sosindex)) == 1)
305 group->sos1_count--;
306 free_SOSrec(group->sos_list[sosindex-1]);
307 while(sosindex < group->sos_count) {
308 group->sos_list[sosindex-1] = group->sos_list[sosindex];
309 sosindex++;
310 }
311 group->sos_count--;
312
313 /* Update maxorder */
314 group->maxorder = 0;
315 for(sosindex = 0; sosindex < group->sos_count; sosindex++) {
316 SETMAX(group->maxorder, abs(group->sos_list[sosindex]->type));
317 }
318
319 return(TRUE);
320 }
321
322
free_SOSrec(SOSrec * SOS)323 STATIC void free_SOSrec(SOSrec *SOS)
324 {
325 if(SOS->name != NULL)
326 FREE(SOS->name);
327 if(SOS->size > 0) {
328 FREE(SOS->members);
329 FREE(SOS->weights);
330 FREE(SOS->membersSorted);
331 FREE(SOS->membersMapped);
332 }
333 FREE(SOS);
334 }
335
336
SOS_member_sortlist(SOSgroup * group,int sosindex)337 STATIC MYBOOL SOS_member_sortlist(SOSgroup *group, int sosindex)
338 /* Routine to (re-)sort SOS member arrays for faster access to large SOSes */
339 {
340 int i, n;
341 int *list;
342 lprec *lp = group->lp;
343 SOSrec *SOS;
344
345 #ifdef Paranoia
346 if((sosindex < 0) || (sosindex > group->sos_count)) {
347 report(lp, IMPORTANT, "SOS_member_sortlist: Invalid SOS index %d\n", sosindex);
348 return(FALSE);
349 }
350 #endif
351
352 if((sosindex == 0) && (group->sos_count == 1))
353 sosindex = 1;
354
355 if(sosindex == 0) {
356 for(i = 1; i <= group->sos_count; i++) {
357 if(!SOS_member_sortlist(group, i))
358 return(FALSE);
359 }
360 }
361 else {
362 SOS = group->sos_list[sosindex-1];
363 list = SOS->members;
364 n = list[0];
365 /* Make sure that the arrays are properly allocated and sized */
366 if(n != group->sos_list[sosindex-1]->size) {
367 allocINT(lp, &SOS->membersSorted, n, AUTOMATIC);
368 allocINT(lp, &SOS->membersMapped, n, AUTOMATIC);
369 group->sos_list[sosindex-1]->size = n;
370 }
371 /* Reload the arrays and do the sorting */
372 for(i = 1; i <= n; i++) {
373 SOS->membersSorted[i - 1] = list[i];
374 SOS->membersMapped[i - 1] = i;
375 }
376 sortByINT(SOS->membersMapped, SOS->membersSorted, n, 0, TRUE);
377 }
378 return( TRUE );
379 }
380
SOS_member_updatemap(SOSgroup * group)381 STATIC int SOS_member_updatemap(SOSgroup *group)
382 {
383 int i, j, k, n, nvars = 0,
384 *list, *tally = NULL;
385 SOSrec *rec;
386 lprec *lp = group->lp;
387
388 /* (Re)-initialize usage arrays */
389 allocINT(lp, &group->memberpos, lp->columns+1, AUTOMATIC);
390 allocINT(lp, &tally, lp->columns+1, TRUE);
391
392 /* Get each variable's SOS membership count */
393 for(i = 0; i < group->sos_count; i++) {
394 rec = group->sos_list[i];
395 n = rec->size;
396 list = rec->members;
397 for(j = 1; j <= n; j++) {
398 k = list[j];
399 #ifdef Paranoia
400 if((k < 1) || (k > lp->columns))
401 report(lp, SEVERE, "SOS_member_updatemap: Member %j of SOS number %d is out of column range (%d)\n",
402 j, i+1, k);
403 #endif
404 tally[k]++;
405 }
406
407 }
408
409 /* Compute pointer into column-sorted array */
410 group->memberpos[0] = 0;
411 for(i = 1; i <= lp->columns; i++) {
412 n = tally[i];
413 if(n > 0)
414 nvars++;
415 group->memberpos[i] = group->memberpos[i-1] + n;
416 }
417 n = group->memberpos[lp->columns];
418 MEMCOPY(tally+1, group->memberpos, lp->columns);
419
420 /* Load the column-sorted SOS indeces / pointers */
421 allocINT(lp, &group->membership, n+1, AUTOMATIC);
422 for(i = 0; i < group->sos_count; i++) {
423 rec = group->sos_list[i];
424 n = rec->size;
425 list = rec->members;
426 for(j = 1; j <= n; j++) {
427 k = tally[list[j]]++;
428 #ifdef Paranoia
429 if(k > group->memberpos[lp->columns])
430 report(lp, SEVERE, "SOS_member_updatemap: Member mapping for variable %j of SOS number %d is invalid\n",
431 list[j], i+1);
432 #endif
433 group->membership[k] = i+1;
434 }
435 }
436 FREE(tally);
437
438 return( nvars );
439 }
440
441
SOS_shift_col(SOSgroup * group,int sosindex,int column,int delta,LLrec * usedmap,MYBOOL forceresort)442 STATIC MYBOOL SOS_shift_col(SOSgroup *group, int sosindex, int column, int delta, LLrec *usedmap, MYBOOL forceresort)
443 /* Routine to adjust SOS indeces for variable insertions or deletions;
444 Note: SOS_shift_col must be called before make_SOSchain! */
445 {
446 int i, ii, n, nn, nr;
447 int changed;
448 int *list;
449 REAL *weights;
450
451 #ifdef Paranoia
452 lprec *lp = group->lp;
453
454 if((sosindex < 0) || (sosindex > group->sos_count)) {
455 report(lp, IMPORTANT, "SOS_shift_col: Invalid SOS index %d\n", sosindex);
456 return(FALSE);
457 }
458 else if((column < 1) || (delta == 0)) {
459 report(lp, IMPORTANT, "SOS_shift_col: Invalid column %d specified with delta %d\n",
460 column, delta);
461 return(FALSE);
462 }
463 #endif
464
465 if((sosindex == 0) && (group->sos_count == 1))
466 sosindex = 1;
467
468 if(sosindex == 0) {
469 for(i = 1; i <= group->sos_count; i++) {
470 if(!SOS_shift_col(group, i, column, delta, usedmap, forceresort))
471 return(FALSE);
472 }
473 }
474 else {
475 list = group->sos_list[sosindex-1]->members;
476 weights = group->sos_list[sosindex-1]->weights;
477 n = list[0];
478 nn = list[n+1];
479
480 /* Case where variable indeces are to be incremented */
481 if(delta > 0) {
482 for(i = 1; i <= n; i++) {
483 if(list[i] >= column)
484 list[i] += delta;
485 }
486 }
487 /* Case where variables are to be deleted/indeces decremented */
488 else {
489 changed = 0;
490 if(usedmap != NULL) {
491 int *newidx = NULL;
492 /* Defer creation of index mapper until we are sure that a
493 member of this SOS is actually targeted for deletion */
494 if(newidx == NULL) {
495 allocINT(group->lp, &newidx, group->lp->columns+1, TRUE);
496 for(i = firstActiveLink(usedmap), ii = 1; i != 0;
497 i = nextActiveLink(usedmap, i), ii++)
498 newidx[i] = ii;
499 }
500 for(i = 1, ii = 0; i <= n; i++) {
501 nr = list[i];
502 /* Check if this SOS variable should be deleted */
503 if(!isActiveLink(usedmap, nr))
504 continue;
505
506 /* If the index is "high" then make adjustment and shift */
507 changed++;
508 ii++;
509 list[ii] = newidx[nr];
510 weights[ii] = weights[i];
511 }
512 FREE(newidx);
513 }
514 else
515 for(i = 1, ii = 0; i <= n; i++) {
516 nr = list[i];
517 /* Check if this SOS variable should be deleted */
518 if((nr >= column) && (nr < column-delta))
519 continue;
520 /* If the index is "high" then decrement */
521 if(nr > column) {
522 changed++;
523 nr += delta;
524 }
525 ii++;
526 list[ii] = nr;
527 weights[ii] = weights[i];
528 }
529 /* Update the SOS length / type indicators */
530 if(ii < n) {
531 list[0] = ii;
532 list[ii+1] = nn;
533 }
534
535 /* Update mapping arrays to search large SOS's faster */
536 if(forceresort && ((ii < n) || (changed > 0)))
537 SOS_member_sortlist(group, sosindex);
538 }
539
540 }
541 return(TRUE);
542
543 }
544
SOS_member_count(SOSgroup * group,int sosindex)545 int SOS_member_count(SOSgroup *group, int sosindex)
546 {
547 SOSrec *SOS;
548
549 #ifdef Paranoia
550 if((sosindex < 0) || (sosindex > group->sos_count)) {
551 report(group->lp, IMPORTANT, "SOS_member_count: Invalid SOS index %d\n", sosindex);
552 return( -1 );
553 }
554 #endif
555 SOS = group->sos_list[sosindex-1];
556 return( SOS->members[0] );
557 }
558
SOS_member_delete(SOSgroup * group,int sosindex,int member)559 int SOS_member_delete(SOSgroup *group, int sosindex, int member)
560 {
561 int *list, i, i2, k, n, nn = 0;
562 SOSrec *SOS;
563 lprec *lp = group->lp;
564
565 #ifdef Paranoia
566 if((sosindex < 0) || (sosindex > group->sos_count)) {
567 report(group->lp, IMPORTANT, "SOS_member_delete: Invalid SOS index %d\n", sosindex);
568 return( -1 );
569 }
570 #endif
571
572 if(sosindex == 0) {
573 for(i = group->memberpos[member-1]; i < group->memberpos[member]; i++) {
574 k = group->membership[i];
575 n = SOS_member_delete(group, k, member);
576 if(n >= 0)
577 nn += n;
578 else
579 return( n );
580 }
581 /* We must update the mapper */
582 k = group->memberpos[member];
583 i = group->memberpos[member-1];
584 n = group->memberpos[lp->columns] - k;
585 if(n > 0)
586 MEMCOPY(group->membership + i, group->membership + k, n);
587 for(i = member; i <= lp->columns; i++)
588 group->memberpos[i] = group->memberpos[i-1];
589 }
590 else {
591 SOS = group->sos_list[sosindex-1];
592 list = SOS->members;
593 n = list[0];
594
595 /* Find the offset of the member */
596 i = 1;
597 while((i <= n) && (abs(list[i]) != member))
598 i++;
599 if(i > n)
600 return( -1 );
601 nn++;
602
603 /* Shift remaining members *and* the active count one position left */
604 while(i <= n) {
605 list[i] = list[i+1];
606 i++;
607 }
608 list[0]--;
609 SOS->size--;
610
611 /* Do the same with the active list one position left */
612 i = n + 1;
613 i2 = i + list[n];
614 k = i + 1;
615 while(i < i2) {
616 if(abs(list[k]) == member)
617 k++;
618 list[i] = list[k];
619 i++;
620 k++;
621 }
622 }
623
624 return( nn );
625 }
626
SOS_get_type(SOSgroup * group,int sosindex)627 int SOS_get_type(SOSgroup *group, int sosindex)
628 {
629 #ifdef Paranoia
630 if((sosindex < 1) || (sosindex > group->sos_count)) {
631 report(group->lp, IMPORTANT, "SOS_get_type: Invalid SOS index %d\n", sosindex);
632 return(FALSE);
633 }
634 #endif
635
636 return(group->sos_list[sosindex-1]->type);
637 }
638
639
SOS_infeasible(SOSgroup * group,int sosindex)640 int SOS_infeasible(SOSgroup *group, int sosindex)
641 {
642 int i, n, nn, varnr, failindex, *list;
643 lprec *lp = group->lp;
644
645 #ifdef Paranoia
646 if((sosindex < 0) || (sosindex > group->sos_count)) {
647 report(lp, IMPORTANT, "SOS_infeasible: Invalid SOS index %d\n", sosindex);
648 return(FALSE);
649 }
650 #endif
651
652 if(sosindex == 0 && group->sos_count == 1)
653 sosindex = 1;
654
655 failindex = 0;
656 if(sosindex == 0) {
657 for(i = 1; i <= group->sos_count; i++) {
658 failindex = SOS_infeasible(group, i);
659 if(failindex > 0) break;
660 }
661 }
662 else {
663 list = group->sos_list[sosindex-1]->members;
664 n = list[0];
665 nn = list[n+1];
666 /* Find index of next lower-bounded variable */
667 for(i = 1; i <= n; i++) {
668 varnr = abs(list[i]);
669 if((lp->orig_lowbo[lp->rows + varnr] > 0) &&
670 !((lp->sc_vars > 0) && is_semicont(lp, varnr)))
671 break;
672 }
673
674 /* Find if there is another lower-bounded variable beyond the type window */
675 i = i+nn;
676 while(i <= n) {
677 varnr = abs(list[i]);
678 if((lp->orig_lowbo[lp->rows + varnr] > 0) &&
679 !((lp->sc_vars > 0) && is_semicont(lp, varnr)))
680 break;
681 i++;
682 }
683 if(i <= n)
684 failindex = abs(list[i]);
685 }
686 return(failindex);
687 }
688
689
SOS_member_index(SOSgroup * group,int sosindex,int member)690 int SOS_member_index(SOSgroup *group, int sosindex, int member)
691 {
692 int n;
693 SOSrec *SOS;
694
695 SOS = group->sos_list[sosindex-1];
696 n = SOS->members[0];
697
698 n = searchFor(member, SOS->membersSorted, n, 0, FALSE);
699 if(n >= 0)
700 n = SOS->membersMapped[n];
701
702 return(n);
703 }
704
705
SOS_memberships(SOSgroup * group,int varnr)706 int SOS_memberships(SOSgroup *group, int varnr)
707 {
708 int i, n = 0;
709 lprec *lp;
710
711 /* Check if there is anything to do */
712 if((group == NULL) || (SOS_count(lp = group->lp) == 0))
713 return( n );
714
715 #ifdef Paranoia
716 if((varnr < 0) || (varnr > lp->columns)) {
717 report(lp, IMPORTANT, "SOS_memberships: Invalid variable index %d given\n", varnr);
718 return( n );
719 }
720 #endif
721
722 if(varnr == 0) {
723 for(i = 1; i <= lp->columns; i++)
724 if(group->memberpos[i] > group->memberpos[i-1])
725 n++;
726 }
727 else
728 n = group->memberpos[varnr] - group->memberpos[varnr-1];
729
730 return( n );
731 }
732
733
SOS_is_member(SOSgroup * group,int sosindex,int column)734 int SOS_is_member(SOSgroup *group, int sosindex, int column)
735 {
736 int i, n = FALSE, *list;
737 lprec *lp;
738
739 if(group == NULL)
740 return( FALSE );
741 lp = group->lp;
742
743 #ifdef Paranoia
744 if((sosindex < 0) || (sosindex > group->sos_count)) {
745 report(lp, IMPORTANT, "SOS_is_member: Invalid SOS index %d\n", sosindex);
746 return(n);
747 }
748 #endif
749
750 if(sosindex == 0) {
751 if(lp->var_type[column] & (ISSOS | ISGUB))
752 n = (MYBOOL) (SOS_memberships(group, column) > 0);
753 }
754 else if(lp->var_type[column] & (ISSOS | ISGUB)) {
755
756 /* Search for the variable */
757 i = SOS_member_index(group, sosindex, column);
758
759 /* Signal active status if found, otherwise return FALSE */
760 if(i > 0) {
761 list = group->sos_list[sosindex-1]->members;
762 if(list[i] < 0)
763 n = -TRUE;
764 else
765 n = TRUE;
766 }
767 }
768 return(n);
769 }
770
771
SOS_is_member_of_type(SOSgroup * group,int column,int sostype)772 MYBOOL SOS_is_member_of_type(SOSgroup *group, int column, int sostype)
773 {
774 int i, k, n;
775
776 if(group != NULL)
777 for(i = group->memberpos[column-1]; i < group->memberpos[column]; i++) {
778 k = group->membership[i];
779 n = SOS_get_type(group, k);
780 if(((n == sostype) ||
781 ((sostype == SOSn) && (n > 2))) && SOS_is_member(group, k, column))
782 return(TRUE);
783 }
784 return(FALSE);
785 }
786
787
SOS_set_GUB(SOSgroup * group,int sosindex,MYBOOL state)788 MYBOOL SOS_set_GUB(SOSgroup *group, int sosindex, MYBOOL state)
789 {
790 int i;
791
792 #ifdef Paranoia
793 if((sosindex < 0) || (sosindex > group->sos_count)) {
794 report(group->lp, IMPORTANT, "SOS_set_GUB: Invalid SOS index %d\n", sosindex);
795 return(FALSE);
796 }
797 #endif
798 if((sosindex == 0) && (group->sos_count == 1))
799 sosindex = 1;
800
801 if(sosindex == 0) {
802 for(i = 1; i <= group->sos_count; i++)
803 SOS_set_GUB(group, i, state);
804 }
805 else
806 group->sos_list[sosindex-1]->isGUB = state;
807 return(TRUE);
808 }
809
810
SOS_is_GUB(SOSgroup * group,int sosindex)811 MYBOOL SOS_is_GUB(SOSgroup *group, int sosindex)
812 {
813 int i;
814
815 #ifdef Paranoia
816 if((sosindex < 0) || (sosindex > group->sos_count)) {
817 report(group->lp, IMPORTANT, "SOS_is_GUB: Invalid SOS index %d\n", sosindex);
818 return(FALSE);
819 }
820 #endif
821
822 if((sosindex == 0) && (group->sos_count == 1))
823 sosindex = 1;
824
825 if(sosindex == 0) {
826 for(i = 1; i <= group->sos_count; i++) {
827 if(SOS_is_GUB(group, i))
828 return(TRUE);
829 }
830 return(FALSE);
831 }
832 else
833 return( group->sos_list[sosindex-1]->isGUB );
834 }
835
836
SOS_is_marked(SOSgroup * group,int sosindex,int column)837 MYBOOL SOS_is_marked(SOSgroup *group, int sosindex, int column)
838 {
839 int i, k, n, *list;
840 lprec *lp;
841
842 if(group == NULL)
843 return( FALSE );
844 lp = group->lp;
845
846 #ifdef Paranoia
847 if((sosindex < 0) || (sosindex > group->sos_count)) {
848 report(lp, IMPORTANT, "SOS_is_marked: Invalid SOS index %d\n", sosindex);
849 return(FALSE);
850 }
851 #endif
852
853 if(!(lp->var_type[column] & (ISSOS | ISGUB)))
854 return(FALSE);
855
856 if(sosindex == 0) {
857 for(i = group->memberpos[column-1]; i < group->memberpos[column]; i++) {
858 k = group->membership[i];
859 n = SOS_is_marked(group, k, column);
860 if(n)
861 return(TRUE);
862 }
863 }
864 else {
865 list = group->sos_list[sosindex-1]->members;
866 n = list[0];
867
868 /* Search for the variable (normally always faster to do linear search here) */
869 column = -column;
870 for(i = 1; i <= n; i++)
871 if(list[i] == column)
872 return(TRUE);
873 }
874 return(FALSE);
875 }
876
877
SOS_is_active(SOSgroup * group,int sosindex,int column)878 MYBOOL SOS_is_active(SOSgroup *group, int sosindex, int column)
879 {
880 int i, n, nn, *list;
881 lprec *lp = group->lp;
882
883 #ifdef Paranoia
884 if((sosindex < 0) || (sosindex > group->sos_count)) {
885 report(lp, IMPORTANT, "SOS_is_active: Invalid SOS index %d\n", sosindex);
886 return(FALSE);
887 }
888 #endif
889
890 if(!(lp->var_type[column] & (ISSOS | ISGUB)))
891 return(FALSE);
892
893 if(sosindex == 0) {
894 for(i = group->memberpos[column-1]; i < group->memberpos[column]; i++) {
895 nn = group->membership[i];
896 n = SOS_is_active(group, nn, column);
897 if(n)
898 return(TRUE);
899 }
900 }
901 else {
902
903 list = group->sos_list[sosindex-1]->members;
904 n = list[0]+1;
905 nn = list[n];
906
907 /* Scan the active (non-zero) SOS index list */
908 for(i = 1; (i <= nn) && (list[n+i] != 0); i++)
909 if(list[n+i] == column)
910 return(TRUE);
911 }
912 return(FALSE);
913 }
914
915
SOS_is_full(SOSgroup * group,int sosindex,int column,MYBOOL activeonly)916 MYBOOL SOS_is_full(SOSgroup *group, int sosindex, int column, MYBOOL activeonly)
917 {
918 int i, nn, n, *list;
919 lprec *lp = group->lp;
920
921 #ifdef Paranoia
922 if((sosindex < 0) || (sosindex > group->sos_count)) {
923 report(lp, IMPORTANT, "SOS_is_full: Invalid SOS index %d\n", sosindex);
924 return(FALSE);
925 }
926 #endif
927
928 if(!(lp->var_type[column] & (ISSOS | ISGUB)))
929 return(FALSE);
930
931 if(sosindex == 0) {
932 for(i = group->memberpos[column-1]; i < group->memberpos[column]; i++) {
933 nn = group->membership[i];
934 if(SOS_is_full(group, nn, column, activeonly))
935 return(TRUE);
936 }
937 }
938 else if(SOS_is_member(group, sosindex, column)) {
939
940 list = group->sos_list[sosindex-1]->members;
941 n = list[0]+1;
942 nn = list[n];
943
944 /* Info: Last item in the active list is non-zero if the current SOS is full */
945 if(list[n+nn] != 0)
946 return(TRUE);
947
948 if(!activeonly) {
949 /* Spool to last active variable */
950 for(i = nn-1; (i > 0) && (list[n+i] == 0); i--);
951 /* Having found it, check if subsequent variables are set (via bounds) as inactive */
952 if(i > 0) {
953 nn -= i; /* Compute unused active slots */
954 i = SOS_member_index(group, sosindex, list[n+i]);
955 for(; (nn > 0) && (list[i] < 0); i++, nn--);
956 if(nn == 0)
957 return(TRUE);
958 }
959 }
960 }
961
962 return(FALSE);
963 }
964
965
SOS_can_activate(SOSgroup * group,int sosindex,int column)966 MYBOOL SOS_can_activate(SOSgroup *group, int sosindex, int column)
967 {
968 int i, n, nn, *list;
969 lprec *lp;
970
971 if(group == NULL)
972 return( FALSE );
973 lp = group->lp;
974
975 #ifdef Paranoia
976 if((sosindex < 0) || (sosindex > group->sos_count)) {
977 report(lp, IMPORTANT, "SOS_can_activate: Invalid SOS index %d\n", sosindex);
978 return(FALSE);
979 }
980 #endif
981
982 if(!(lp->var_type[column] & (ISSOS | ISGUB)))
983 return(FALSE);
984
985 if(sosindex == 0) {
986 for(i = group->memberpos[column-1]; i < group->memberpos[column]; i++) {
987 nn = group->membership[i];
988 n = SOS_can_activate(group, nn, column);
989 if(n == FALSE)
990 return(FALSE);
991 }
992 }
993 else if(SOS_is_member(group, sosindex, column)) {
994
995 list = group->sos_list[sosindex-1]->members;
996 n = list[0]+1;
997 nn = list[n];
998
999 /* Accept if the SOS is empty */
1000 if(list[n+1] == 0)
1001 return(TRUE);
1002
1003 /* Cannot activate a variable if the SOS is full */
1004 if(list[n+nn] != 0)
1005 return(FALSE);
1006
1007 /* Check if we can set variable active in SOS2..SOSn
1008 (must check left and right neighbours if one variable is already active) */
1009 if(nn > 1) {
1010
1011 /* Find the variable that was last activated;
1012 Also check that the candidate variable is not already active */
1013 for(i = 1; i <= nn; i++) {
1014 if(list[n+i] == 0)
1015 break;
1016 if(list[n+i] == column)
1017 return(FALSE);
1018 }
1019 i--;
1020 nn = list[n+i];
1021
1022 /* SOS accepts an additional variable; confirm neighbourness of candidate;
1023 Search for the SOS set index of the last activated variable */
1024 n = list[0];
1025 for(i = 1; i <= n; i++)
1026 if(abs(list[i]) == nn) break;
1027 if(i > n) {
1028 report(lp, CRITICAL, "SOS_can_activate: Internal index error at SOS %d\n", sosindex);
1029 return(FALSE);
1030 }
1031
1032 /* SOS accepts an additional variable; confirm neighbourness of candidate */
1033
1034 /* Check left neighbour */
1035 if((i > 1) && (list[i-1] == column))
1036 return(TRUE);
1037 /* Check right neighbour */
1038 if((i < n) && (list[i+1] == column))
1039 return(TRUE);
1040
1041 /* It is not the right neighbour; return false */
1042 return(FALSE);
1043 }
1044 }
1045 return(TRUE);
1046 }
1047
1048
SOS_set_marked(SOSgroup * group,int sosindex,int column,MYBOOL asactive)1049 MYBOOL SOS_set_marked(SOSgroup *group, int sosindex, int column, MYBOOL asactive)
1050 {
1051 int i, n, nn, *list;
1052 lprec *lp = group->lp;
1053
1054 #ifdef Paranoia
1055 if((sosindex < 0) || (sosindex > group->sos_count)) {
1056 report(lp, IMPORTANT, "SOS_set_marked: Invalid SOS index %d\n", sosindex);
1057 return(FALSE);
1058 }
1059 #endif
1060
1061 if(!(lp->var_type[column] & (ISSOS | ISGUB)))
1062 return(FALSE);
1063
1064 if(sosindex == 0) {
1065
1066 /* Define an IBM-"SOS3" member variable temporarily as integer, if it is
1067 not already a permanent integer; is reset in SOS_unmark */
1068 if(asactive && !is_int(lp, column) && SOS_is_member_of_type(group, column, SOS3)) {
1069 lp->var_type[column] |= ISSOSTEMPINT;
1070 set_int(lp, column, TRUE);
1071 }
1072
1073 nn = 0;
1074 for(i = group->memberpos[column-1]; i < group->memberpos[column]; i++) {
1075 n = group->membership[i];
1076 if(SOS_set_marked(group, n, column, asactive))
1077 nn++;
1078 }
1079 return((MYBOOL) (nn == group->sos_count));
1080 }
1081 else {
1082 list = group->sos_list[sosindex-1]->members;
1083 n = list[0]+1;
1084 nn = list[n];
1085
1086 /* Search for the variable */
1087 i = SOS_member_index(group, sosindex, column);
1088
1089 /* First mark active in the set member list as used */
1090 if((i > 0) && (list[i] > 0))
1091 list[i] *= -1;
1092 else
1093 return(TRUE);
1094
1095 /* Then move the variable to the live list */
1096 if(asactive) {
1097 for(i = 1; i <= nn; i++) {
1098 if(list[n+i] == column)
1099 return(FALSE);
1100 else if(list[n+i] == 0) {
1101 list[n+i] = column;
1102 return(FALSE);
1103 }
1104 }
1105 }
1106 return(TRUE);
1107 }
1108 }
1109
1110
SOS_unmark(SOSgroup * group,int sosindex,int column)1111 MYBOOL SOS_unmark(SOSgroup *group, int sosindex, int column)
1112 {
1113 int i, n, nn, *list;
1114 MYBOOL isactive;
1115 lprec *lp = group->lp;
1116
1117 #ifdef Paranoia
1118 if((sosindex < 0) || (sosindex > group->sos_count)) {
1119 report(lp, IMPORTANT, "SOS_unmark: Invalid SOS index %d\n", sosindex);
1120 return(FALSE);
1121 }
1122 #endif
1123
1124 if(!(lp->var_type[column] & (ISSOS | ISGUB)))
1125 return(FALSE);
1126
1127
1128 if(sosindex == 0) {
1129
1130 /* Undefine a SOS3 member variable that has temporarily been set as integer */
1131 if(lp->var_type[column] & ISSOSTEMPINT) {
1132 lp->var_type[column] &= !ISSOSTEMPINT;
1133 set_int(lp, column, FALSE);
1134 }
1135
1136 nn = 0;
1137 for(i = group->memberpos[column-1]; i < group->memberpos[column]; i++) {
1138 n = group->membership[i];
1139 if(SOS_unmark(group, n, column))
1140 nn++;
1141 }
1142 return((MYBOOL) (nn == group->sos_count));
1143 }
1144 else {
1145 list = group->sos_list[sosindex-1]->members;
1146 n = list[0]+1;
1147 nn = list[n];
1148
1149 /* Search for the variable */
1150 i = SOS_member_index(group, sosindex, column);
1151
1152 /* Restore sign in main list */
1153 if((i > 0) && (list[i] < 0))
1154 list[i] *= -1;
1155 else
1156 return(TRUE);
1157
1158 /* Find the variable in the active list... */
1159 isactive = SOS_is_active(group, sosindex, column);
1160 if(isactive) {
1161 for(i = 1; i <= nn; i++)
1162 if(list[n+i] == column)
1163 break;
1164 /* ...shrink the list if found, otherwise return error */
1165 if(i <= nn) {
1166 for(; i<nn; i++)
1167 list[n+i] = list[n+i+1];
1168 list[n+nn] = 0;
1169 return(TRUE);
1170 }
1171 return(FALSE);
1172 }
1173 else
1174 return(TRUE);
1175 }
1176 }
1177
1178
SOS_fix_unmarked(SOSgroup * group,int sosindex,int variable,REAL * bound,REAL value,MYBOOL isupper,int * diffcount,DeltaVrec * changelog)1179 int SOS_fix_unmarked(SOSgroup *group, int sosindex, int variable, REAL *bound, REAL value, MYBOOL isupper,
1180 int *diffcount, DeltaVrec *changelog)
1181 {
1182 int i, ii, count, n, nn, nLeft, nRight, *list;
1183 lprec *lp = group->lp;
1184
1185 #ifdef Paranoia
1186 if((sosindex < 0) || (sosindex > group->sos_count)) {
1187 report(lp, IMPORTANT, "SOS_fix_unmarked: Invalid SOS index %d\n", sosindex);
1188 return(FALSE);
1189 }
1190 #endif
1191
1192 count = 0;
1193 if(sosindex == 0) {
1194 for(i = group->memberpos[variable-1]; i < group->memberpos[variable]; i++) {
1195 n = group->membership[i];
1196 count += SOS_fix_unmarked(group, n, variable, bound, value, isupper, diffcount, changelog);
1197 }
1198 }
1199 else {
1200 list = group->sos_list[sosindex-1]->members;
1201 n = list[0]+1;
1202
1203 /* Count the number of active and free SOS variables */
1204 nn = list[n];
1205 for(i = 1; i <= nn; i++) {
1206 if(list[n+i] == 0)
1207 break;
1208 }
1209 i--;
1210 i = nn - i; /* Establish the number of unused slots */
1211
1212 /* Determine the free SOS variable window */
1213 if(i == nn) {
1214 nLeft = 0;
1215 nRight = SOS_member_index(group, sosindex, variable);
1216 }
1217 else {
1218 nLeft = SOS_member_index(group, sosindex, list[n+1]);
1219 if(variable == list[n+1])
1220 nRight = nLeft;
1221 else
1222 nRight = SOS_member_index(group, sosindex, variable);
1223 }
1224
1225 nRight += i; /* Loop (nRight+1)..n */
1226
1227 /* Fix variables outside of the free SOS variable window */
1228 for(i = 1; i < n; i++) {
1229 /* Skip the SOS variable window */
1230 if((i >= nLeft) && (i <= nRight))
1231 continue;
1232 /* Otherwise proceed to set bound */
1233 ii = list[i];
1234 if(ii > 0) {
1235 ii += lp->rows;
1236 if(bound[ii] != value) {
1237 /* Verify that we don't violate original bounds */
1238 if(isupper && (value < lp->orig_lowbo[ii]))
1239 return(-ii);
1240 else if(!isupper && (value > lp->orig_upbo[ii]))
1241 return(-ii);
1242 /* OK, set the new bound */
1243 count++;
1244 if(changelog == NULL)
1245 bound[ii] = value;
1246 else
1247 modifyUndoLadder(changelog, ii, bound, value);
1248
1249 }
1250 if((diffcount != NULL) && (lp->solution[ii] != value))
1251 (*diffcount)++;
1252 }
1253 }
1254 }
1255 return(count);
1256 }
1257
SOS_get_candidates(SOSgroup * group,int sosindex,int column,MYBOOL excludetarget,REAL * upbound,REAL * lobound)1258 int *SOS_get_candidates(SOSgroup *group, int sosindex, int column, MYBOOL excludetarget,
1259 REAL *upbound, REAL *lobound)
1260 {
1261 int i, ii, j, n, nn = 0, *list, *candidates = NULL;
1262 lprec *lp = group->lp;
1263
1264 if(group == NULL)
1265 return( candidates );
1266
1267 #ifdef Paranoia
1268 if(sosindex > group->sos_count) {
1269 report(lp, IMPORTANT, "SOS_get_candidates: Invalid index %d\n", sosindex);
1270 return( candidates );
1271 }
1272 #endif
1273
1274 /* Determine SOS target(s); note that if "sosindex" is negative, only
1275 the first non-empty SOS where "column" is a member is processed */
1276 if(sosindex <= 0) {
1277 i = 0;
1278 ii = group->sos_count;
1279 }
1280 else {
1281 i = sosindex - 1;
1282 ii = sosindex;
1283 }
1284
1285 /* Tally candidate usage */
1286 allocINT(lp, &candidates, lp->columns+1, TRUE);
1287 for(; i < ii; i++) {
1288 if(!SOS_is_member(group, i+1, column))
1289 continue;
1290 list = group->sos_list[i]->members;
1291 n = list[0];
1292 while(n > 0) {
1293 j = list[n];
1294 if((j > 0) && (upbound[lp->rows+j] > 0)) {
1295 if(lobound[lp->rows+j] > 0) {
1296 report(lp, IMPORTANT, "SOS_get_candidates: Invalid non-zero lower bound setting\n");
1297 n = 0;
1298 goto Finish;
1299 }
1300 if(candidates[j] == 0)
1301 nn++;
1302 candidates[j]++;
1303 }
1304 n--;
1305 }
1306 if((sosindex < 0) && (nn > 1))
1307 break;
1308 }
1309
1310 /* Condense the list into indeces */
1311 n = 0;
1312 for(i = 1; i <= lp->columns; i++) {
1313 if((candidates[i] > 0) && (!excludetarget || (i != column))) {
1314 n++;
1315 candidates[n] = i;
1316 }
1317 }
1318
1319 /* Finalize */
1320 Finish:
1321 candidates[0] = n;
1322 if(n == 0)
1323 FREE(candidates);
1324
1325 return( candidates);
1326
1327 }
1328
SOS_fix_list(SOSgroup * group,int sosindex,int variable,REAL * bound,int * varlist,MYBOOL isleft,DeltaVrec * changelog)1329 int SOS_fix_list(SOSgroup *group, int sosindex, int variable, REAL *bound,
1330 int *varlist, MYBOOL isleft, DeltaVrec *changelog)
1331 {
1332 int i, ii, jj, count = 0;
1333 REAL value = 0;
1334 lprec *lp = group->lp;
1335
1336 #ifdef Paranoia
1337 if((sosindex < 0) || (sosindex > group->sos_count)) {
1338 report(lp, IMPORTANT, "SOS_fix_list: Invalid index %d\n", sosindex);
1339 return(FALSE);
1340 }
1341 #endif
1342
1343 if(sosindex == 0) {
1344 for(i = group->memberpos[variable-1]; i < group->memberpos[variable]; i++) {
1345 ii = group->membership[i];
1346 count += SOS_fix_list(group, ii, variable, bound, varlist, isleft, changelog);
1347 }
1348 }
1349 else {
1350
1351 /* Establish the number of unmarked variables in the left window
1352 (note that "variable" should have been marked previously) */
1353 ii = varlist[0] / 2;
1354 if(isleft) {
1355 i = 1;
1356 if(isleft == AUTOMATIC)
1357 ii = varlist[0];
1358 }
1359 else {
1360 i = ii + 1;
1361 ii = varlist[0];
1362 }
1363
1364 /* Loop over members to fix values at the new bound (zero) */
1365 while(i <= ii) {
1366 if(SOS_is_member(group, sosindex, varlist[i])) {
1367 jj = lp->rows + varlist[i];
1368
1369 /* Verify that we don't violate original bounds */
1370 if(value < lp->orig_lowbo[jj])
1371 return( -jj );
1372 /* OK, set the new bound */
1373 count++;
1374 if(changelog == NULL)
1375 bound[jj] = value;
1376 else
1377 modifyUndoLadder(changelog, jj, bound, value);
1378 }
1379 i++;
1380 }
1381
1382 }
1383 return( count );
1384 }
1385
SOS_is_satisfied(SOSgroup * group,int sosindex,REAL * solution)1386 int SOS_is_satisfied(SOSgroup *group, int sosindex, REAL *solution)
1387 /* Determine if the SOS is satisfied for the current solution vector;
1388 The return code is in the range [-2..+2], depending on the type of
1389 satisfaction. Positive return value means too many non-zero values,
1390 negative value means set incomplete:
1391
1392 -2: Set member count not full (SOS3)
1393 -1: Set member count not full
1394 0: Set is full (also returned if the SOS index is invalid)
1395 1: Too many non-zero sequential variables
1396 2: Set consistency error
1397
1398 */
1399 {
1400 int i, n, nn, count, *list;
1401 int type, status = 0;
1402 lprec *lp = group->lp;
1403
1404 #ifdef Paranoia
1405 if((sosindex < 0) || (sosindex > group->sos_count)) {
1406 report(lp, IMPORTANT, "SOS_is_satisfied: Invalid index %d\n", sosindex);
1407 return( SOS_COMPLETE );
1408 }
1409 #endif
1410
1411 if((sosindex == 0) && (group->sos_count == 1))
1412 sosindex = 1;
1413
1414 if(sosindex == 0) {
1415 for(i = 1; i <= group->sos_count; i++) {
1416 status = SOS_is_satisfied(group, i, solution);
1417 if((status != SOS_COMPLETE) && (status != SOS_INCOMPLETE))
1418 break;
1419 }
1420 }
1421 else {
1422 type = SOS_get_type(group, sosindex);
1423 list = group->sos_list[sosindex-1]->members;
1424 n = list[0]+1;
1425 nn = list[n];
1426
1427 /* Count the number of active SOS variables */
1428 for(i = 1; i <= nn; i++) {
1429 if(list[n+i] == 0)
1430 break;
1431 }
1432 count = i-1;
1433 if(count == nn)
1434 status = SOS_COMPLETE; /* Set is full */
1435 else
1436 status = SOS_INCOMPLETE; /* Set is partial */
1437
1438 /* Find index of the first active variable; fail if some are non-zero */
1439 if(count > 0) {
1440 nn = list[n+1];
1441 for(i = 1; i < n; i++) {
1442 if((abs(list[i]) == nn) || (solution[lp->rows + abs(list[i])] != 0))
1443 break;
1444 }
1445 if(abs(list[i]) != nn)
1446 status = SOS_INTERNALERROR; /* Set consistency error (leading set variables are non-zero) */
1447 else {
1448 /* Scan active SOS variables until we find a non-zero value */
1449 while(count > 0) {
1450 if(solution[lp->rows + abs(list[i])] != 0)
1451 break;
1452 i++;
1453 count--;
1454 }
1455 /* Scan active non-zero SOS variables; break at first non-zero (rest required to be zero) */
1456 while(count > 0) {
1457 if(solution[lp->rows + abs(list[i])] == 0)
1458 break;
1459 i++;
1460 count--;
1461 }
1462 if(count > 0)
1463 status = SOS_INTERNALERROR; /* Set consistency error (active set variables are zero) */
1464 }
1465 }
1466 else {
1467 i = 1;
1468 /* There are no active variables; see if we have happened to find a valid header */
1469 while((i < n) && (solution[lp->rows + abs(list[i])] == 0))
1470 i++;
1471 count = 0;
1472 while((i < n) && (count <= nn) && (solution[lp->rows + abs(list[i])] != 0)) {
1473 count++;
1474 i++;
1475 }
1476 if(count > nn)
1477 status = SOS_INFEASIBLE; /* Too-many sequential non-zero variables */
1478 }
1479
1480 /* Scan the trailing set of SOS variables; fail if some are non-zero */
1481 if(status <= 0) {
1482 n--;
1483 while(i <= n) {
1484 if(solution[lp->rows + abs(list[i])] != 0)
1485 break;
1486 i++;
1487 }
1488 if(i <= n)
1489 status = SOS_INFEASIBLE; /* Too-many sequential non-zero variables */
1490
1491 /* Code member deficiency for SOS3 separately */
1492 else if((status == -1) && (type <= SOS3))
1493 status = SOS3_INCOMPLETE;
1494 }
1495
1496 }
1497 return( status );
1498 }
1499
SOS_is_feasible(SOSgroup * group,int sosindex,REAL * solution)1500 MYBOOL SOS_is_feasible(SOSgroup *group, int sosindex, REAL *solution)
1501 /* Determine if the SOS is feasible up to the current SOS variable */
1502 {
1503 int i, n, nn, *list;
1504 MYBOOL status = TRUE;
1505 lprec *lp = group->lp;
1506
1507 #ifdef Paranoia
1508 if((sosindex < 0) || (sosindex > group->sos_count)) {
1509 report(lp, IMPORTANT, "SOS_is_feasible: Invalid SOS index %d\n", sosindex);
1510 return( 0 );
1511 }
1512 #endif
1513
1514 if((sosindex == 0) && (group->sos_count == 1))
1515 sosindex = 1;
1516
1517 if(sosindex == 0) {
1518 for(i = 1; status && (i <= group->sos_count); i++) {
1519 status = SOS_is_feasible(group, i, solution);
1520 }
1521 }
1522 else {
1523 list = group->sos_list[sosindex-1]->members;
1524 n = list[0]+1;
1525 nn = list[n];
1526 if(nn <= 2)
1527 return(status);
1528
1529 /* Find if we have a gap in the non-zero solution values */
1530 i = 1;
1531 sosindex = 0;
1532 while((i <= nn) && (list[n+i] != 0)) {
1533 while((i <= nn) && (list[n+i] != 0) && (solution[lp->rows+list[n+i]] == 0))
1534 i++;
1535 if((i <= nn) && (list[n+i] != 0)) {
1536 i++; /* Step to next */
1537 while((i <= nn) && (list[n+i] != 0) && (solution[lp->rows+list[n+i]] != 0))
1538 i++;
1539 sosindex++;
1540 }
1541 i++; /* Step to next */
1542 }
1543 status = (MYBOOL) (sosindex <= 1);
1544 }
1545 return(status);
1546 }
1547