1 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2 /*                                                                           */
3 /*                  This file is part of the program and library             */
4 /*         SCIP --- Solving Constraint Integer Programs                      */
5 /*                                                                           */
6 /*    Copyright (C) 2002-2021 Konrad-Zuse-Zentrum                            */
7 /*                            fuer Informationstechnik Berlin                */
8 /*                                                                           */
9 /*  SCIP is distributed under the terms of the ZIB Academic License.         */
10 /*                                                                           */
11 /*  You should have received a copy of the ZIB Academic License              */
12 /*  along with SCIP; see the file COPYING. If not visit scipopt.org.         */
13 /*                                                                           */
14 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
15 
16 /**@file   concsolver_scip.c
17  * @ingroup PARALLEL
18  * @brief  implementation of concurrent solver interface for SCIP
19  * @author Leona Gottwald
20  */
21 
22 /*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
23 
24 #include "blockmemshell/memory.h"
25 #include "scip/boundstore.h"
26 #include "scip/concsolver.h"
27 #include "scip/concsolver_scip.h"
28 #include "scip/concurrent.h"
29 #include "scip/pub_event.h"
30 #include "scip/pub_heur.h"
31 #include "scip/pub_message.h"
32 #include "scip/pub_misc.h"
33 #include "scip/pub_paramset.h"
34 #include "scip/pub_sol.h"
35 #include "scip/pub_var.h"
36 #include "scip/scip_concurrent.h"
37 #include "scip/scip_copy.h"
38 #include "scip/scip_event.h"
39 #include "scip/scip_general.h"
40 #include "scip/scip_heur.h"
41 #include "scip/scip_mem.h"
42 #include "scip/scip_message.h"
43 #include "scip/scip_numerics.h"
44 #include "scip/scip_param.h"
45 #include "scip/scip_prob.h"
46 #include "scip/scip_sol.h"
47 #include "scip/scip_solve.h"
48 #include "scip/scip_solvingstats.h"
49 #include "scip/scip_timing.h"
50 #include "scip/syncstore.h"
51 #include <string.h>
52 
53 /* event handler for synchronization */
54 #define EVENTHDLR_NAME         "sync"
55 #define EVENTHDLR_DESC         "event handler for synchronization of concurrent scip sovlers"
56 
57 /*
58  * Data structures
59  */
60 
61 /** event handler data */
62 struct SCIP_EventhdlrData
63 {
64    int             filterpos;
65 };
66 
67 /*
68  * Callback methods of event handler
69  */
70 
71 /** destructor of event handler to free user data (called when SCIP is exiting) */
72 static
SCIP_DECL_EVENTFREE(eventFreeSync)73 SCIP_DECL_EVENTFREE(eventFreeSync)
74 {  /*lint --e{715}*/
75    SCIP_EVENTHDLRDATA* eventhdlrdata;
76 
77    assert(scip != NULL);
78    assert(eventhdlr != NULL);
79    assert(strcmp(SCIPeventhdlrGetName(eventhdlr), EVENTHDLR_NAME) == 0);
80 
81    eventhdlrdata = SCIPeventhdlrGetData(eventhdlr);
82    assert(eventhdlrdata != NULL);
83 
84    SCIPfreeBlockMemory(scip, &eventhdlrdata);
85 
86    SCIPeventhdlrSetData(eventhdlr, NULL);
87 
88    return SCIP_OKAY;
89 }
90 
91 /** initialization method of event handler (called after problem was transformed) */
92 static
SCIP_DECL_EVENTINIT(eventInitSync)93 SCIP_DECL_EVENTINIT(eventInitSync)
94 {  /*lint --e{715}*/
95    SCIP_EVENTHDLRDATA* eventhdlrdata;
96    SCIP_SYNCSTORE*  syncstore;
97 
98    assert(scip != NULL);
99    assert(eventhdlr != NULL);
100    assert(strcmp(SCIPeventhdlrGetName(eventhdlr), EVENTHDLR_NAME) == 0);
101 
102    eventhdlrdata = SCIPeventhdlrGetData(eventhdlr);
103    assert(eventhdlrdata != NULL);
104 
105    syncstore = SCIPgetSyncstore(scip);
106    assert(syncstore != NULL);
107 
108    if( eventhdlrdata->filterpos < 0 && SCIPsyncstoreIsInitialized(syncstore) )
109    {
110       /* notify SCIP that your event handler wants to react on synchronization events */
111       SCIP_CALL( SCIPcatchEvent(scip, SCIP_EVENTTYPE_SYNC, eventhdlr, NULL, &eventhdlrdata->filterpos) );
112    }
113 
114    return SCIP_OKAY;
115 }
116 
117 /** deinitialization method of event handler (called before transformed problem is freed) */
118 static
SCIP_DECL_EVENTEXIT(eventExitSync)119 SCIP_DECL_EVENTEXIT(eventExitSync)
120 {  /*lint --e{715}*/
121    SCIP_EVENTHDLRDATA* eventhdlrdata;
122 
123    assert(scip != NULL);
124    assert(eventhdlr != NULL);
125    assert(strcmp(SCIPeventhdlrGetName(eventhdlr), EVENTHDLR_NAME) == 0);
126 
127    eventhdlrdata = SCIPeventhdlrGetData(eventhdlr);
128    assert(eventhdlrdata != NULL);
129 
130    /* notify SCIP that your event handler wants to drop the event type synchronization found */
131    if( eventhdlrdata->filterpos >= 0 )
132    {
133       SCIP_CALL( SCIPdropEvent(scip, SCIP_EVENTTYPE_SYNC, eventhdlr, NULL, eventhdlrdata->filterpos) );
134       eventhdlrdata->filterpos = -1;
135    }
136 
137    return SCIP_OKAY;
138 }
139 
140 /** execution method of event handler */
141 static
SCIP_DECL_EVENTEXEC(eventExecSync)142 SCIP_DECL_EVENTEXEC(eventExecSync)
143 {  /*lint --e{715}*/
144    assert(eventhdlr != NULL);
145    assert(strcmp(SCIPeventhdlrGetName(eventhdlr), EVENTHDLR_NAME) == 0);
146    assert(event != NULL);
147    assert(scip != NULL);
148 
149    SCIP_CALL( SCIPsynchronize(scip) );
150 
151    return SCIP_OKAY;
152 }
153 
154 
155 /** includes event handler for synchronization found */
156 static
includeEventHdlrSync(SCIP * scip)157 SCIP_RETCODE includeEventHdlrSync(
158    SCIP*                 scip                /**< SCIP data structure */
159    )
160 {
161    SCIP_EVENTHDLR*     eventhdlr;
162    SCIP_EVENTHDLRDATA* eventhdlrdata;
163 
164    SCIP_CALL( SCIPallocBlockMemory(scip, &eventhdlrdata) );
165    eventhdlrdata->filterpos = -1;
166 
167    /* create event handler for events on watched variables */
168    SCIP_CALL( SCIPincludeEventhdlrBasic(scip, &eventhdlr, EVENTHDLR_NAME, EVENTHDLR_DESC, eventExecSync, eventhdlrdata) );
169    assert(eventhdlr != NULL);
170 
171    SCIP_CALL( SCIPsetEventhdlrFree(scip, eventhdlr, eventFreeSync) );
172    SCIP_CALL( SCIPsetEventhdlrInit(scip, eventhdlr, eventInitSync) );
173    SCIP_CALL( SCIPsetEventhdlrExit(scip, eventhdlr, eventExitSync) );
174 
175    return SCIP_OKAY;
176 }
177 
178 /** data for a concurrent solver type */
179 struct SCIP_ConcSolverTypeData
180 {
181    SCIP_Bool             loademphasis;       /**< should emphasis settings be loaded when creating an instance of this concurrent solver */
182    SCIP_PARAMEMPHASIS    emphasis;           /**< parameter emphasis that will be loaded if loademphasis is true */
183 };
184 
185 /** data for a concurrent solver */
186 struct SCIP_ConcSolverData
187 {
188    SCIP*                 solverscip;         /**< the concurrent solvers private SCIP datastructure */
189    SCIP_VAR**            vars;               /**< array of variables in the order of the main SCIP's variable array */
190    int                   nvars;              /**< number of variables in the above arrays */
191 };
192 
193 /** Disable dual reductions that might cut off optimal solutions. Although they keep at least
194  *  one optimal solution intact, communicating these bounds may cut off all optimal solutions,
195  *  if different optimal solutions were kept in different concurrent solvers. */
196 static
disableConflictingDualReductions(SCIP * scip)197 SCIP_RETCODE disableConflictingDualReductions(
198    SCIP*                 scip                /**< SCIP datastructure */
199    )
200 {
201    SCIP_Bool commvarbnds;
202 
203    SCIP_CALL( SCIPgetBoolParam(scip, "concurrent/commvarbnds", &commvarbnds) );
204 
205    if( !commvarbnds )
206       return SCIP_OKAY;
207 
208    SCIP_CALL( SCIPsetBoolParam(scip, "misc/allowstrongdualreds", FALSE) );
209    return SCIP_OKAY;
210 }
211 
212 /** sets the child selection rule based on the index of the concurrent solver */
213 static
setChildSelRule(SCIP_CONCSOLVER * concsolver)214 SCIP_RETCODE setChildSelRule(
215    SCIP_CONCSOLVER*      concsolver          /**< the concurrent solver */
216    )
217 {
218    SCIP_CONCSOLVERDATA*  data;
219    static char childsel[] = { 'h', 'i', 'p', 'r', 'l', 'd', 'u' };
220 
221    assert(concsolver != NULL);
222 
223    data = SCIPconcsolverGetData(concsolver);
224    assert(data != NULL);
225 
226    SCIP_CALL( SCIPsetCharParam(data->solverscip, "nodeselection/childsel", childsel[SCIPconcsolverGetIdx(concsolver) % 7]) );
227 
228    return SCIP_OKAY;
229 }
230 
231 /** initialize the concurrent SCIP solver, i.e. setup the copy of the problem and the
232  *  mapping of the variables */
233 static
initConcsolver(SCIP * scip,SCIP_CONCSOLVER * concsolver)234 SCIP_RETCODE initConcsolver(
235    SCIP*                 scip,               /**< the main SCIP instance */
236    SCIP_CONCSOLVER*      concsolver          /**< the concurrent solver to set up */
237    )
238 {
239    int                 i;
240    SCIP_VAR**          vars;
241    SCIP_Bool           valid;
242    SCIP_HASHMAP*       varmapfw;
243    SCIP_CONCSOLVERDATA* data;
244    int* varperm;
245 
246    assert(scip != NULL);
247    assert(concsolver != NULL);
248 
249    data = SCIPconcsolverGetData(concsolver);
250    assert(data != NULL);
251 
252    data->nvars = SCIPgetNVars(scip);
253    vars = SCIPgetVars(scip);
254 
255    /* create the concurrent solver's SCIP instance and set up the problem */
256    SCIP_CALL( SCIPcreate(&data->solverscip) );
257    SCIP_CALL( SCIPhashmapCreate(&varmapfw, SCIPblkmem(data->solverscip), data->nvars) );
258    SCIP_CALL( SCIPcopy(scip, data->solverscip, varmapfw, NULL, SCIPconcsolverGetName(concsolver), TRUE, FALSE, FALSE,
259          FALSE, &valid) );
260    assert(valid);
261 
262    /* allocate memory for the arrays to store the variable mapping */
263    SCIP_CALL( SCIPallocBlockMemoryArray(data->solverscip, &data->vars, data->nvars) );
264    SCIP_CALL( SCIPallocBufferArray(data->solverscip, &varperm, data->nvars) );
265 
266    /* set up the arrays for the variable mapping */
267    for( i = 0; i < data->nvars; i++ )
268    {
269       SCIP_VAR* var;
270       var = (SCIP_VAR*) SCIPhashmapGetImage(varmapfw, vars[i]);
271       assert(var != NULL);
272       varperm[SCIPvarGetIndex(var)] = i;
273       data->vars[i] = var;
274    }
275 
276    if( SCIPgetNSols(scip) != 0 )
277    {
278       SCIP_Bool stored;
279       SCIP_Real* solvals;
280       SCIP_SOL* sol = SCIPgetBestSol(scip);
281       SCIP_SOL* solversol;
282 
283       SCIP_CALL( SCIPallocBufferArray(data->solverscip, &solvals, data->nvars) );
284 
285       SCIP_CALL( SCIPgetSolVals(scip, sol, data->nvars, vars, solvals) );
286       SCIP_CALL( SCIPcreateSol(data->solverscip, &solversol, NULL) );
287       SCIP_CALL( SCIPsetSolVals(data->solverscip, solversol, data->nvars, data->vars, solvals) );
288 
289       SCIPfreeBufferArray(data->solverscip, &solvals);
290 
291       SCIP_CALL( SCIPaddSolFree(data->solverscip, &solversol, &stored) );
292 
293       assert(stored);
294    }
295 
296    /* create the concurrent data structure for the concurrent solver's SCIP */
297    /* this assert fails on check/instances/Orbitope/packorb_1-FullIns_3.cip
298     * assert(SCIPgetNOrigVars(data->solverscip) == data->nvars);
299     * also fails on check/instances/Orbitope/partorb_1-FullIns_3.cip
300     * TODO: test if this leads to any problems
301     */
302    SCIP_CALL( SCIPcreateConcurrent(data->solverscip, concsolver, varperm) );
303    SCIPfreeBufferArray(data->solverscip, &varperm);
304 
305    /* free the hashmap */
306    SCIPhashmapFree(&varmapfw);
307 
308    return SCIP_OKAY;
309 }
310 
311 /** creates an instance of a concurrent SCIP solver */
312 static
SCIP_DECL_CONCSOLVERCREATEINST(concsolverScipCreateInstance)313 SCIP_DECL_CONCSOLVERCREATEINST(concsolverScipCreateInstance)
314 {
315    SCIP_CONCSOLVERDATA*     data;
316    SCIP_CONCSOLVERTYPEDATA* typedata;
317    char*                    prefix;
318    char                     filename[SCIP_MAXSTRLEN];
319    SCIP_Bool                changechildsel;
320 
321    assert(scip != NULL);
322    assert(concsolvertype != NULL);
323    assert(concsolver != NULL);
324 
325    typedata = SCIPconcsolverTypeGetData(concsolvertype);
326 
327    SCIP_ALLOC( BMSallocMemory(&data) );
328    SCIPconcsolverSetData(concsolver, data);
329 
330    SCIP_CALL( initConcsolver(scip, concsolver) );
331 
332    /* check if emphasis setting should be loaded */
333    if( typedata->loademphasis )
334    {
335       SCIP_PARAM** params;
336       SCIP_PARAM** fixedparams;
337       int          nparams;
338       int          nfixedparams;
339       int          i;
340 
341       params = SCIPgetParams(data->solverscip);
342       nparams = SCIPgetNParams(data->solverscip);
343       SCIP_CALL( SCIPallocBufferArray(data->solverscip, &fixedparams, nparams) );
344       nfixedparams = 0;
345 
346       /* fix certain parameters before loading emphasis to avoid setting them to default values */
347       for( i = 0; i < nparams; ++i )
348       {
349          const char* paramname;
350 
351          paramname = SCIPparamGetName(params[i]);
352 
353          if( strncmp(paramname, "limits/", 7) == 0 ||
354              strncmp(paramname, "numerics/", 9) == 0 ||
355              strncmp(paramname, "memory/", 7) == 0 ||
356              strncmp(paramname, "concurrent/sync/", 16) == 0 ||
357              strncmp(paramname, "heuristics/sync/", 16) == 0 ||
358              strncmp(paramname, "propagating/sync/", 17) == 0 )
359          {
360             fixedparams[nfixedparams++] = params[i];
361             SCIP_CALL( SCIPfixParam(data->solverscip, paramname) );
362          }
363       }
364 
365       SCIP_CALL( SCIPsetEmphasis(data->solverscip, typedata->emphasis, TRUE) );
366 
367       for( i = 0; i < nfixedparams; ++i )
368          SCIP_CALL( SCIPunfixParam(data->solverscip, SCIPparamGetName(fixedparams[i])) );
369 
370       SCIPfreeBufferArray(data->solverscip, &fixedparams);
371    }
372 
373    /* load settings file if it exists */
374    SCIP_CALL( SCIPgetStringParam(scip, "concurrent/paramsetprefix", &prefix) );
375    (void) SCIPsnprintf(filename, SCIP_MAXSTRLEN, "%s%s.set", prefix, SCIPconcsolverGetName(concsolver));
376 
377    if( SCIPfileExists(filename) )
378    {
379       /* load settings file and print info message */
380       SCIPinfoMessage(scip, NULL, "reading parameter file <%s> for concurrent solver <%s>\n", filename, SCIPconcsolverGetName(concsolver));
381       SCIP_CALL( SCIPreadParams(data->solverscip, filename) );
382    }
383    else
384    {
385       /* print message about missing setting files only in verblevel full */
386       SCIPverbMessage(scip, SCIP_VERBLEVEL_FULL, NULL, "skipping non existent parameter file <%s> for concurrent solver <%s>\n",
387                       filename, SCIPconcsolverGetName(concsolver));
388    }
389 
390    /* include eventhandler for synchronization */
391    SCIP_CALL( includeEventHdlrSync(data->solverscip) );
392 
393    /* disable output for subscip */
394    SCIP_CALL( SCIPsetIntParam(data->solverscip, "display/verblevel", 0) );
395 
396    /* use wall clock time in subscips */
397    SCIP_CALL( SCIPsetIntParam(data->solverscip, "timing/clocktype", (int)SCIP_CLOCKTYPE_WALL) );
398 
399    /* don't catch ctrlc since already caught in main SCIP */
400    SCIP_CALL( SCIPsetBoolParam(data->solverscip, "misc/catchctrlc", FALSE) );
401 
402    /* one solver can do all dual reductions and share them with the other solvers */
403    if( SCIPconcsolverGetIdx(concsolver) != 0 )
404    {
405       SCIP_CALL( disableConflictingDualReductions(data->solverscip) );
406    }
407 
408    /* set different child selection rules if corresponding parameter is TRUE */
409    SCIP_CALL( SCIPgetBoolParam(scip, "concurrent/changechildsel", &changechildsel) );
410    if( changechildsel )
411    {
412       SCIP_CALL( setChildSelRule(concsolver) );
413    }
414 
415    return SCIP_OKAY;
416 }
417 
418 /** destroys an instance of a concurrent SCIP solver */
419 static
SCIP_DECL_CONCSOLVERDESTROYINST(concsolverScipDestroyInstance)420 SCIP_DECL_CONCSOLVERDESTROYINST(concsolverScipDestroyInstance)
421 {
422    SCIP_CONCSOLVERDATA* data;
423 
424    assert(concsolver != NULL);
425 
426    data = SCIPconcsolverGetData(concsolver);
427    assert(data != NULL);
428    assert(data->solverscip != NULL);
429 
430    /* free the array with the variable mapping */
431    SCIPfreeBlockMemoryArray(data->solverscip, &data->vars, data->nvars);
432 
433    /* free subscip */
434    SCIP_CALL( SCIPfree(&data->solverscip) );
435    BMSfreeMemory(&data);
436    SCIPconcsolverSetData(concsolver, NULL);
437 
438    return SCIP_OKAY;
439 }
440 
441 /** frees the data of a concurrent solver type */
442 static
SCIP_DECL_CONCSOLVERTYPEFREEDATA(concsolverTypeScipFreeData)443 SCIP_DECL_CONCSOLVERTYPEFREEDATA(concsolverTypeScipFreeData)
444 {
445    BMSfreeMemory(data);
446 }
447 
448 /** initializes the random and permutation seeds with the given one
449  *  and enables permutation of constraints and variables
450  */
451 static
SCIP_DECL_CONCSOLVERINITSEEDS(concsolverScipInitSeeds)452 SCIP_DECL_CONCSOLVERINITSEEDS(concsolverScipInitSeeds)
453 {
454    SCIP_CONCSOLVERDATA* data;
455 
456    assert(concsolver != NULL);
457 
458    data = SCIPconcsolverGetData(concsolver);
459    assert(data != NULL);
460 
461    SCIPinfoMessage(data->solverscip, NULL, "initializing seeds to %d in concurrent solver '%s'\n", (int) seed, SCIPconcsolverGetName(concsolver));
462 
463    SCIP_CALL( SCIPsetIntParam(data->solverscip, "randomization/randomseedshift", (int) seed) );
464    SCIP_CALL( SCIPsetIntParam(data->solverscip, "randomization/permutationseed", (int) seed) );
465    SCIP_CALL( SCIPsetBoolParam(data->solverscip, "randomization/permutevars", TRUE) );
466    SCIP_CALL( SCIPsetBoolParam(data->solverscip, "randomization/permuteconss", TRUE) );
467 
468    return SCIP_OKAY;
469 }
470 
471 /** installs the solving status of this concurrent solver and the solving statistics
472  *  into the given SCIP instance
473  */
474 static
SCIP_DECL_CONCSOLVERCOPYSOLVINGDATA(concsolverGetSolvingData)475 SCIP_DECL_CONCSOLVERCOPYSOLVINGDATA(concsolverGetSolvingData)
476 {
477    SCIP_CONCSOLVERDATA* data;
478    SCIP_VAR** vars;
479    int nvars;
480    int nsols;
481    SCIP_SOL** sols;
482    SCIP_Real* solvals;
483    SCIP_HEUR* heur;
484    int i;
485 
486    assert(concsolver != NULL);
487 
488    data = SCIPconcsolverGetData(concsolver);
489    assert(data != NULL);
490    assert(data->solverscip != NULL);
491 
492    assert(scip != NULL);
493    vars = SCIPgetVars(scip);
494    nvars = SCIPgetNVars(scip);
495 
496    nsols = SCIPgetNSols(data->solverscip);
497    sols = SCIPgetSols(data->solverscip);
498 
499    assert(nvars == data->nvars);
500 
501    /* allocate buffer array used for translating the solution to the given SCIP */
502    SCIP_CALL( SCIPallocBufferArray(scip, &solvals, nvars) );
503 
504    /* add the solutions to the given SCIP */
505    for( i = 0; i < nsols; ++i )
506    {
507       SCIP_SOL* sol;
508       SCIP_Bool stored;
509       SCIP_CALL( SCIPgetSolVals(data->solverscip, sols[i], nvars, data->vars, solvals) );
510 
511       heur = SCIPsolGetHeur(sols[i]);
512 
513       if( heur != NULL )
514          heur = SCIPfindHeur(scip, SCIPheurGetName(heur));
515 
516       SCIP_CALL( SCIPcreateSol(scip, &sol, heur) );
517       SCIP_CALL( SCIPsetSolVals(scip, sol, nvars, vars, solvals) );
518 
519       SCIP_CALL( SCIPcopySolStats(sols[i], sol) );
520 
521       SCIP_CALL( SCIPaddSolFree(scip, &sol, &stored) );
522    }
523 
524    /* free the buffer array */
525    SCIPfreeBufferArray(scip, &solvals);
526 
527    /* copy solving statistics and status from the solver SCIP to the given SCIP */
528    SCIP_CALL( SCIPcopyConcurrentSolvingStats(data->solverscip, scip) );
529 
530    return SCIP_OKAY;
531 }
532 
533 /** start solving the problem until the solving reaches a limit, gets interrupted, or
534  *  just finished successfully
535  */
536 static
SCIP_DECL_CONCSOLVEREXEC(concsolverScipExec)537 SCIP_DECL_CONCSOLVEREXEC(concsolverScipExec)
538 {
539    SCIP_CONCSOLVERDATA* data;
540 
541    assert(concsolver != NULL);
542 
543    data = SCIPconcsolverGetData(concsolver);
544    assert(data != NULL);
545 
546    /* print info message that solving has started */
547    SCIPinfoMessage(data->solverscip, NULL, "starting solve in concurrent solver '%s'\n", SCIPconcsolverGetName(concsolver));
548 
549    /* solve */
550    SCIP_CALL( SCIPsolve(data->solverscip) );
551 
552    /* print info message with status */
553    SCIPinfoMessage(data->solverscip, NULL, "concurrent solver '%s' stopped with status ", SCIPconcsolverGetName(concsolver));
554    SCIP_CALL( SCIPprintStatus(data->solverscip, NULL) );
555    SCIPinfoMessage(data->solverscip, NULL, "\n");
556 
557    /* set solving statistics */
558    *solvingtime = SCIPgetSolvingTime(data->solverscip);
559    *nlpiterations = SCIPgetNLPIterations(data->solverscip);
560    *nnodes = SCIPgetNNodes(data->solverscip);
561 
562    return SCIP_OKAY;
563 }
564 
565 /** stops the concurrent solver as soon as possible */
566 static
SCIP_DECL_CONCSOLVERSTOP(concsolverScipStop)567 SCIP_DECL_CONCSOLVERSTOP(concsolverScipStop)
568 {
569    SCIP_CONCSOLVERDATA* data;
570    assert(concsolver != NULL);
571 
572    data = SCIPconcsolverGetData(concsolver);
573    assert(data != NULL);
574 
575    SCIP_CALL( SCIPinterruptSolve(data->solverscip) );
576 
577    return SCIP_OKAY;
578 }
579 
580 /** writes new solutions and global boundchanges to the given synchronization data */
581 static
SCIP_DECL_CONCSOLVERSYNCWRITE(concsolverScipSyncWrite)582 SCIP_DECL_CONCSOLVERSYNCWRITE(concsolverScipSyncWrite)
583 {
584    int                    i;
585    int                    nsols;
586    SCIP_SOL**             sols;
587    SCIP_CONCSOLVERDATA*   data;
588    SCIP_BOUNDSTORE*       boundstore;
589    int                    concsolverid;
590    SCIP_STATUS            solverstatus;
591 
592    data = SCIPconcsolverGetData(concsolver);
593    assert(data != NULL);
594    concsolverid = SCIPconcsolverGetIdx(concsolver);
595    solverstatus = SCIPgetStatus(data->solverscip);
596 
597    SCIPsyncdataSetStatus(syncdata, solverstatus, concsolverid);
598    SCIPsyncdataSetLowerbound(syncdata, SCIPgetDualbound(data->solverscip));
599    SCIPsyncdataSetUpperbound(syncdata, SCIPgetPrimalbound(data->solverscip));
600 
601    *nsolsshared = 0;
602 
603    if( SCIPsyncdataGetStatus(syncdata) != SCIP_STATUS_UNKNOWN )
604       return SCIP_OKAY;
605 
606    SCIPdebugMessage("syncing in concurrent solver %s\n", SCIPconcsolverGetName(concsolver));
607 
608    /* consider at most maxcandsols many solutions, and since the solution array is sorted, we will cosider the best
609     * solutions
610     */
611    nsols = SCIPgetNSols(data->solverscip);
612    nsols = MIN(nsols, maxcandsols);
613    sols = SCIPgetSols(data->solverscip);
614 
615    for( i = 0; i < nsols; ++i )
616    {
617       if( SCIPIsConcurrentSolNew(data->solverscip, sols[i]) )
618       {
619          SCIP_Real solobj;
620          SCIP_Real* solvals;
621 
622          solobj = SCIPgetSolOrigObj(data->solverscip, sols[i]);
623 
624          SCIPdebugMessage("adding sol in concurrent solver %s\n", SCIPconcsolverGetName(concsolver));
625          SCIPsyncdataGetSolutionBuffer(syncstore, syncdata, solobj, concsolverid, &solvals);
626 
627          /* if syncstore has no place for this solution we can stop since the next solution will have
628           * a worse objective value and thus won't be accepted either
629           */
630          if( solvals == NULL )
631             break;
632 
633          ++(*nsolsshared);
634          SCIP_CALL( SCIPgetSolVals(data->solverscip, sols[i], data->nvars, data->vars, solvals) );
635 
636          /* if we have added the maximum number of solutions we can also stop */
637          if( *nsolsshared == maxsharedsols )
638             break;
639       }
640    }
641 
642    boundstore = SCIPgetConcurrentGlobalBoundChanges(data->solverscip);
643 
644    if( boundstore != NULL )
645       SCIP_CALL( SCIPsyncdataAddBoundChanges(syncstore, syncdata, boundstore) );
646 
647    SCIPsyncdataAddMemTotal(syncdata, SCIPgetMemTotal(data->solverscip));
648 
649    return SCIP_OKAY;
650 }
651 
652 /** reads the solutions and bounds from the given synchronization data */
653 static
SCIP_DECL_CONCSOLVERSYNCREAD(concsolverScipSyncRead)654 SCIP_DECL_CONCSOLVERSYNCREAD(concsolverScipSyncRead)
655 {  /*lint --e{715}*/
656    int                    i;
657    int                    nsols;
658    SCIP_Real**            solvals;
659    SCIP_CONCSOLVERDATA*   data;
660    SCIP_BOUNDSTORE*       boundstore;
661    int*                   concsolverids;
662    int                    concsolverid;
663    int                    nbndchgs;
664 
665    data = SCIPconcsolverGetData(concsolver);
666    assert(data != NULL);
667 
668    concsolverid = SCIPconcsolverGetIdx(concsolver);
669 
670    /* get solutions from synchronization data */
671    SCIPsyncdataGetSolutions(syncdata, &solvals, &concsolverids, &nsols);
672    *nsolsrecvd = 0;
673 
674    for( i = 0; i < nsols; ++i )
675    {
676       SCIP_SOL* newsol;
677 
678       /* do not add own solutions */
679       if( concsolverids[i] == concsolverid )
680          continue;
681 
682       /* solution is from other solver so translate to this solvers variable space and add it to SCIP */
683       ++(*nsolsrecvd);
684       SCIP_CALL( SCIPcreateOrigSol(data->solverscip, &newsol, NULL) );
685 
686       SCIP_CALL( SCIPsetSolVals(data->solverscip, newsol, data->nvars, data->vars, solvals[i]) );
687       SCIPdebugMessage("adding solution in concurrent solver %s\n", SCIPconcsolverGetName(concsolver));
688       SCIP_CALL( SCIPaddConcurrentSol(data->solverscip, newsol) );
689    }
690 
691    /* get bound changes from the synchronization data and add it to this concurrent solvers SCIP */
692    *ntighterbnds = 0;
693    *ntighterintbnds = 0;
694    boundstore = SCIPsyncdataGetBoundChgs(syncdata);
695    nbndchgs = SCIPboundstoreGetNChgs(boundstore);
696 
697    for( i = 0; i < nbndchgs; ++i )
698    {
699       SCIP_VAR*   var;
700       SCIP_BOUNDTYPE boundtype;
701       SCIP_Real newbound;
702 
703       var = data->vars[SCIPboundstoreGetChgVaridx(boundstore, i)];
704       boundtype = SCIPboundstoreGetChgType(boundstore, i);
705       newbound = SCIPboundstoreGetChgVal(boundstore, i);
706 
707       SCIP_CALL( SCIPvarGetProbvarBound(&var, &newbound, &boundtype) );
708 
709       /* cannot change bounds of multi-aggregated variables so dont pass this bound-change to the propagator */
710       if( SCIPvarGetStatus(var) == SCIP_VARSTATUS_MULTAGGR )
711          return SCIP_OKAY;
712 
713       /* if bound is not better than also don't pass this bound to the propagator and
714        * don't waste memory for storing this boundchange
715        */
716       if( boundtype == SCIP_BOUNDTYPE_LOWER && SCIPisGE(data->solverscip, SCIPvarGetLbGlobal(var), newbound) )
717          return SCIP_OKAY;
718 
719       if( boundtype == SCIP_BOUNDTYPE_UPPER && SCIPisLE(data->solverscip, SCIPvarGetUbGlobal(var), newbound) )
720          return SCIP_OKAY;
721 
722       /* bound is better so incremented counters for statistics and pass it to the sync propagator */
723       ++(*ntighterbnds);
724 
725       if( SCIPvarGetType(var) <= SCIP_VARTYPE_INTEGER )
726          ++(*ntighterintbnds);
727 
728       SCIP_CALL( SCIPaddConcurrentBndchg(data->solverscip, var, newbound, boundtype) );
729    }
730 
731    return SCIP_OKAY;
732 }
733 
734 
735 /** creates the concurrent SCIP solver plugins and includes them in SCIP */
SCIPincludeConcurrentScipSolvers(SCIP * scip)736 SCIP_RETCODE SCIPincludeConcurrentScipSolvers(
737    SCIP*                 scip                /**< SCIP datastructure */
738    )
739 {
740    SCIP_CONCSOLVERTYPEDATA* data;
741 
742    assert(scip != NULL);
743 
744    /* Include concurrent solvers for SCIP for all emphasis settings and without an emphasis setting.
745     * For the SCIP without an emphasis setting we set the default preferred priority to 1 and for the other types to 0
746     * so that the default concurent solve will use multiple SCIP's using settings as specified by the user in the main SCIP.
747     */
748    SCIP_CALL( SCIPallocMemory(scip, &data) );
749    data->loademphasis = FALSE;
750    SCIP_CALL( SCIPincludeConcsolverType(scip, "scip", 1.0, concsolverScipCreateInstance, concsolverScipDestroyInstance, concsolverScipInitSeeds,
751                                         concsolverScipExec, concsolverGetSolvingData, concsolverScipStop, concsolverScipSyncWrite,
752                                         concsolverScipSyncRead, concsolverTypeScipFreeData, data) );
753 
754    SCIP_CALL( SCIPallocMemory(scip, &data) );
755    data->loademphasis = TRUE;
756    data->emphasis = SCIP_PARAMEMPHASIS_DEFAULT;
757    SCIP_CALL( SCIPincludeConcsolverType(scip, "scip-default", 0.0, concsolverScipCreateInstance, concsolverScipDestroyInstance, concsolverScipInitSeeds,
758                                         concsolverScipExec, concsolverGetSolvingData, concsolverScipStop, concsolverScipSyncWrite,
759                                         concsolverScipSyncRead, concsolverTypeScipFreeData, data) );
760 
761    SCIP_CALL( SCIPallocMemory(scip, &data) );
762    data->loademphasis = TRUE;
763    data->emphasis = SCIP_PARAMEMPHASIS_CPSOLVER;
764    SCIP_CALL( SCIPincludeConcsolverType(scip, "scip-cpsolver", 0.0, concsolverScipCreateInstance, concsolverScipDestroyInstance, concsolverScipInitSeeds,
765                                         concsolverScipExec, concsolverGetSolvingData, concsolverScipStop, concsolverScipSyncWrite,
766                                         concsolverScipSyncRead, concsolverTypeScipFreeData, data) );
767 
768    SCIP_CALL( SCIPallocMemory(scip, &data) );
769    data->loademphasis = TRUE;
770    data->emphasis = SCIP_PARAMEMPHASIS_EASYCIP;
771    SCIP_CALL( SCIPincludeConcsolverType(scip, "scip-easycip", 0.0, concsolverScipCreateInstance, concsolverScipDestroyInstance, concsolverScipInitSeeds,
772                                         concsolverScipExec, concsolverGetSolvingData, concsolverScipStop, concsolverScipSyncWrite,
773                                         concsolverScipSyncRead, concsolverTypeScipFreeData, data) );
774 
775    SCIP_CALL( SCIPallocMemory(scip, &data) );
776    data->loademphasis = TRUE;
777    data->emphasis = SCIP_PARAMEMPHASIS_FEASIBILITY;
778    SCIP_CALL( SCIPincludeConcsolverType(scip, "scip-feas", 0.0, concsolverScipCreateInstance, concsolverScipDestroyInstance, concsolverScipInitSeeds,
779                                         concsolverScipExec, concsolverGetSolvingData, concsolverScipStop, concsolverScipSyncWrite,
780                                         concsolverScipSyncRead, concsolverTypeScipFreeData, data) );
781 
782    SCIP_CALL( SCIPallocMemory(scip, &data) );
783    data->loademphasis = TRUE;
784    data->emphasis = SCIP_PARAMEMPHASIS_HARDLP;
785    SCIP_CALL( SCIPincludeConcsolverType(scip, "scip-hardlp", 0.0, concsolverScipCreateInstance, concsolverScipDestroyInstance, concsolverScipInitSeeds,
786                                         concsolverScipExec, concsolverGetSolvingData, concsolverScipStop, concsolverScipSyncWrite,
787                                         concsolverScipSyncRead, concsolverTypeScipFreeData, data) );
788 
789    SCIP_CALL( SCIPallocMemory(scip, &data) );
790    data->loademphasis = TRUE;
791    data->emphasis = SCIP_PARAMEMPHASIS_OPTIMALITY;
792    SCIP_CALL( SCIPincludeConcsolverType(scip, "scip-opti", 0.0, concsolverScipCreateInstance, concsolverScipDestroyInstance, concsolverScipInitSeeds,
793                                         concsolverScipExec, concsolverGetSolvingData, concsolverScipStop, concsolverScipSyncWrite,
794                                         concsolverScipSyncRead, concsolverTypeScipFreeData, data) );
795 
796    SCIP_CALL( SCIPallocMemory(scip, &data) );
797    data->loademphasis = TRUE;
798    data->emphasis = SCIP_PARAMEMPHASIS_COUNTER;
799    SCIP_CALL( SCIPincludeConcsolverType(scip, "scip-counter", 0.0,  concsolverScipCreateInstance, concsolverScipDestroyInstance, concsolverScipInitSeeds,
800                                         concsolverScipExec, concsolverGetSolvingData, concsolverScipStop, concsolverScipSyncWrite,
801                                         concsolverScipSyncRead, concsolverTypeScipFreeData, data) );
802 
803    return SCIP_OKAY;
804 }
805