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