1    	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2    	/*                                                                           */
3    	/*                  This file is part of the program and library             */
4    	/*         SCIP --- Solving Constraint Integer Programs                      */
5    	/*                                                                           */
6    	/*  Copyright (c) 2002-2023 Zuse Institute Berlin (ZIB)                      */
7    	/*                                                                           */
8    	/*  Licensed under the Apache License, Version 2.0 (the "License");          */
9    	/*  you may not use this file except in compliance with the License.         */
10   	/*  You may obtain a copy of the License at                                  */
11   	/*                                                                           */
12   	/*      http://www.apache.org/licenses/LICENSE-2.0                           */
13   	/*                                                                           */
14   	/*  Unless required by applicable law or agreed to in writing, software      */
15   	/*  distributed under the License is distributed on an "AS IS" BASIS,        */
16   	/*  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */
17   	/*  See the License for the specific language governing permissions and      */
18   	/*  limitations under the License.                                           */
19   	/*                                                                           */
20   	/*  You should have received a copy of the Apache-2.0 license                */
21   	/*  along with SCIP; see the file LICENSE. If not visit scipopt.org.         */
22   	/*                                                                           */
23   	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
24   	
25   	/**@file   concsolver.c
26   	 * @ingroup OTHER_CFILES
27   	 * @brief  methods for concurrent solvers
28   	 * @author Leona Gottwald
29   	 */
30   	
31   	/*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
32   	
33   	#include <assert.h>
34   	#include <string.h>
35   	
36   	#include "scip/concsolver.h"
37   	#include "scip/set.h"
38   	#include "scip/scip.h"
39   	#include "scip/concurrent.h"
40   	
41   	#include "scip/struct_concsolver.h"
42   	#include "scip/struct_stat.h"
43   	#include "scip/struct_scip.h"
44   	#include "blockmemshell/memory.h"
45   	#include "scip/syncstore.h"
46   	#include "scip/boundstore.h"
47   	#include "scip/clock.h"
48   	
49   	
50   	/** internal method for creating a concurrent solver type */
51   	static
52   	SCIP_RETCODE doConcsolverTypeCreate(
53   	   SCIP_CONCSOLVERTYPE** concsolvertype,     /**< pointer to concurrent solver data structure */
54   	   SCIP_SET*             set,                /**< global SCIP settings */
55   	   SCIP_MESSAGEHDLR*     messagehdlr,        /**< message handler */
56   	   BMS_BLKMEM*           blkmem,             /**< block memory for parameter settings */
57   	   const char*           name,               /**< name of concurrent solver */
58   	   SCIP_Real             prefpriodefault,    /**< the default preferred priority of this concurrent solver type */
59   	   SCIP_DECL_CONCSOLVERCREATEINST ((*concsolvercreateinst)),/**< data copy method of concurrent solver */
60   	   SCIP_DECL_CONCSOLVERDESTROYINST ((*concsolverdestroyinst)),/**< data copy method of concurrent solver */
61   	   SCIP_DECL_CONCSOLVERINITSEEDS ((*concsolverinitseeds)),/**< initialize random seeds of concurrent solver */
62   	   SCIP_DECL_CONCSOLVEREXEC ((*concsolverexec)),/**< execution method of concurrent solver */
63   	   SCIP_DECL_CONCSOLVERCOPYSOLVINGDATA ((*concsolvercopysolvdata)),/**< method to copy solving data */
64   	   SCIP_DECL_CONCSOLVERSTOP ((*concsolverstop)),/**< terminate solving in concurrent solver */
65   	   SCIP_DECL_CONCSOLVERSYNCWRITE ((*concsolversyncwrite)),/**< synchronization method of concurrent solver */
66   	   SCIP_DECL_CONCSOLVERSYNCREAD ((*concsolversyncread)),/**< synchronization method of concurrent solver */
67   	   SCIP_DECL_CONCSOLVERTYPEFREEDATA ((*concsolvertypefreedata)),/**< method to free data of concurrent solver type */
68   	   SCIP_CONCSOLVERTYPEDATA* data             /**< the concurent solver type's data */
69   	   )
70   	{
71   	   char paramname[SCIP_MAXSTRLEN];
72   	   char paramdesc[SCIP_MAXSTRLEN];
73   	
74   	   assert(concsolvertype != NULL);
75   	   assert(name != NULL);
76   	   assert(prefpriodefault >= 0.0 && prefpriodefault <= 1.0);
77   	
78   	   assert(concsolvercreateinst != NULL);
79   	   assert(concsolverdestroyinst != NULL);
80   	   assert(concsolverexec != NULL);
81   	   assert(concsolvercopysolvdata != NULL);
82   	   assert(concsolverstop != NULL);
83   	   assert(concsolversyncwrite != NULL);
84   	   assert(concsolversyncread != NULL);
85   	
86   	   SCIP_ALLOC( BMSallocMemory(concsolvertype) );
87   	   BMSclearMemory(*concsolvertype);
88   	
89   	   SCIP_ALLOC( BMSduplicateMemoryArray(&(*concsolvertype)->name, name, strlen(name) + 1) );
90   	
91   	   (*concsolvertype)->data = data;
92   	   (*concsolvertype)->ninstances = 0;
93   	   (*concsolvertype)->concsolvercreateinst = concsolvercreateinst;
94   	   (*concsolvertype)->concsolverdestroyinst = concsolverdestroyinst;
95   	   (*concsolvertype)->concsolverinitseeds = concsolverinitseeds;
96   	   (*concsolvertype)->concsolverexec = concsolverexec;
97   	   (*concsolvertype)->concsolvercopysolvdata = concsolvercopysolvdata;
98   	   (*concsolvertype)->concsolverstop = concsolverstop;
99   	   (*concsolvertype)->concsolversyncwrite = concsolversyncwrite;
100  	   (*concsolvertype)->concsolversyncread = concsolversyncread;
101  	   (*concsolvertype)->concsolvertypefreedata = concsolvertypefreedata;
102  	
103  	   (void) SCIPsnprintf(paramname, SCIP_MAXSTRLEN, "concurrent/%s/prefprio", name);
104  	   (void) SCIPsnprintf(paramdesc, SCIP_MAXSTRLEN, "the preferred number concurrent solvers of type <%s> with respect to the number of threads", name);
105  	   SCIP_CALL( SCIPsetAddRealParam(set, messagehdlr, blkmem, paramname, paramdesc,
106  	                                  &(*concsolvertype)->prefprio, FALSE, prefpriodefault, 0.0, 1.0,
107  	                                  NULL, NULL) ); /*lint !e740*/
108  	
109  	   return SCIP_OKAY;
110  	}
111  	
112  	/** creates a concurrent solver type */
113  	SCIP_RETCODE SCIPconcsolverTypeCreate(
114  	   SCIP_CONCSOLVERTYPE** concsolvertype,     /**< pointer to concurrent solver data structure */
115  	   SCIP_SET*             set,                /**< global SCIP settings */
116  	   SCIP_MESSAGEHDLR*     messagehdlr,        /**< message handler */
117  	   BMS_BLKMEM*           blkmem,             /**< block memory for parameter settings */
118  	   const char*           name,               /**< name of concurrent solver */
119  	   SCIP_Real             prefpriodefault,    /**< the default preferred priority of this concurrent solver type */
120  	   SCIP_DECL_CONCSOLVERCREATEINST ((*concsolvercreateinst)),/**< data copy method of concurrent solver */
121  	   SCIP_DECL_CONCSOLVERDESTROYINST ((*concsolverdestroyinst)),/**< data copy method of concurrent solver */
122  	   SCIP_DECL_CONCSOLVERINITSEEDS ((*concsolverinitseeds)),/**< initialize random seeds of concurrent solver */
123  	   SCIP_DECL_CONCSOLVEREXEC ((*concsolverexec)),/**< execution method of concurrent solver */
124  	   SCIP_DECL_CONCSOLVERCOPYSOLVINGDATA ((*concsolvercopysolvdata)),/**< method to copy solving data */
125  	   SCIP_DECL_CONCSOLVERSTOP ((*concsolverstop)),/**< terminate solving in concurrent solver */
126  	   SCIP_DECL_CONCSOLVERSYNCWRITE ((*concsolversyncwrite)),/**< synchronization method of concurrent solver */
127  	   SCIP_DECL_CONCSOLVERSYNCREAD ((*concsolversyncread)),/**< synchronization method of concurrent solver */
128  	   SCIP_DECL_CONCSOLVERTYPEFREEDATA ((*concsolvertypefreedata)),/**< method to free data of concurrent solver type */
129  	   SCIP_CONCSOLVERTYPEDATA* data             /**< the concurent solver type's data */
130  	   )
131  	{
132  	   assert(concsolvertype != NULL);
133  	   assert(name != NULL);
134  	   assert(prefpriodefault >= 0.0 && prefpriodefault <= 1.0);
135  	
136  	   assert(concsolvercreateinst != NULL);
137  	   assert(concsolverdestroyinst != NULL);
138  	   assert(concsolverexec != NULL);
139  	   assert(concsolvercopysolvdata != NULL);
140  	   assert(concsolverstop != NULL);
141  	   assert(concsolversyncwrite != NULL);
142  	   assert(concsolversyncread != NULL);
143  	
144  	   SCIP_CALL_FINALLY( doConcsolverTypeCreate(concsolvertype, set, messagehdlr, blkmem,
145  	      name, prefpriodefault, concsolvercreateinst, concsolverdestroyinst, concsolverinitseeds, concsolverexec,
146  	      concsolvercopysolvdata, concsolverstop, concsolversyncwrite, concsolversyncread, concsolvertypefreedata, data),
147  	      SCIPconcsolverTypeFree(concsolvertype) );
148  	
149  	   return SCIP_OKAY;
150  	}
151  	
152  	/** frees all memory of a concurrent solver type */
153  	void SCIPconcsolverTypeFree(
154  	   SCIP_CONCSOLVERTYPE** concsolvertype      /**< pointer to concurrent solver data structure */
155  	   )
156  	{
157  	   assert(concsolvertype != NULL);
158  	   if( *concsolvertype == NULL )
159  	      return;
160  	
161  	   if( (*concsolvertype)->concsolvertypefreedata != NULL )
162  	      (*concsolvertype)->concsolvertypefreedata(&(*concsolvertype)->data);
163  	
164  	   BMSfreeMemoryArrayNull(&(*concsolvertype)->name);
165  	   BMSfreeMemory(concsolvertype);
166  	}
167  	
168  	/** gets the data of a concurrent solver type */
169  	SCIP_CONCSOLVERTYPEDATA* SCIPconcsolverTypeGetData(
170  	   SCIP_CONCSOLVERTYPE*  concsolvertype      /**< concurrent solver type */
171  	   )
172  	{
173  	   assert(concsolvertype != NULL);
174  	
175  	   return concsolvertype->data;
176  	}
177  	
178  	/** sets the data of a concurrent solver type */
179  	void SCIPconcsolverTypeSetData(
180  	   SCIP_CONCSOLVERTYPE*  concsolvertype,     /**< concurrent solver type */
181  	   SCIP_CONCSOLVERTYPEDATA* data             /**< the concurrent solver's data */
182  	   )
183  	{
184  	   assert(concsolvertype != NULL);
185  	
186  	   concsolvertype->data = data;
187  	}
188  	
189  	/** gets the name of a concurrent solver type */
190  	char* SCIPconcsolverTypeGetName(
191  	   SCIP_CONCSOLVERTYPE*  concsolvertype      /**< concurrent solver type */
192  	   )
193  	{
194  	   assert(concsolvertype != NULL);
195  	
196  	   return concsolvertype->name;
197  	}
198  	
199  	/** gets the preferred priority from a concurrent solver type */
200  	SCIP_Real SCIPconcsolverTypeGetPrefPrio(
201  	   SCIP_CONCSOLVERTYPE*  concsolvertype      /**< concurrent solver type */
202  	   )
203  	{
204  	   assert(concsolvertype != NULL);
205  	
206  	   return concsolvertype->prefprio;
207  	}
208  	
209  	/** creates an instance of the given concurrent solver type */
210  	SCIP_RETCODE SCIPconcsolverCreateInstance(
211  	   SCIP_SET*             set,                /**< global SCIP settings */
212  	   SCIP_CONCSOLVERTYPE*  concsolvertype,     /**< concurrent solver type to create */
213  	   SCIP_CONCSOLVER**     concsolver          /**< pointer to return concurrent solver instance */
214  	   )
215  	{
216  	   char instancename[SCIP_MAXSTRLEN];
217  	
218  	   ++concsolvertype->ninstances;
219  	   (void) SCIPsnprintf(instancename, SCIP_MAXSTRLEN, "%s-%i", concsolvertype->name, concsolvertype->ninstances);
220  	
221  	   SCIP_ALLOC( BMSallocMemory(concsolver) );
222  	   SCIP_ALLOC( BMSduplicateMemoryArray(&(*concsolver)->name, instancename, strlen(instancename) + 1) );
223  	
224  	   (*concsolver)->type = concsolvertype;
225  	
226  	   /* initialize counters for statistics  */
227  	   (*concsolver)->nsolsrecvd = 0;
228  	   (*concsolver)->nsolsshared = 0;
229  	   (*concsolver)->ntighterbnds = 0;
230  	   (*concsolver)->ntighterintbnds = 0;
231  	   SCIP_CALL( SCIPcreateWallClock(set->scip, &(*concsolver)->totalsynctime) );
232  	
233  	   /* initialize synchronization fields */
234  	   (*concsolver)->nsyncs = 0;
235  	   (*concsolver)->syncdelay = 0.0;
236  	
237  	   /* in deterministic mode use number of nonzeros and variables to get a good initial synchronization frequency
238  	    * in opportunistic mode use the frequency as set by the user
239  	    */
240  	   if( set->parallel_mode == (int) SCIP_PARA_DETERMINISTIC )
241  	      (*concsolver)->syncfreq = 0.01 * set->scip->stat->nnz * SCIPgetNVars(set->scip) * set->concurrent_freqinit;
242  	   else
243  	      (*concsolver)->syncfreq = set->concurrent_freqinit;
244  	
245  	   (*concsolver)->syncdata = NULL;
246  	
247  	   SCIPdebugMessage("concsolver %s initialized sync freq to %f\n", (*concsolver)->name, (*concsolver)->syncfreq);
248  	   /* register concurrent solver */
249  	   (*concsolver)->idx = SCIPgetNConcurrentSolvers(set->scip);
250  	   SCIP_CALL( concsolvertype->concsolvercreateinst(set->scip, concsolvertype, *concsolver) );
251  	   SCIP_CALL( SCIPaddConcurrentSolver(set->scip, *concsolver) );
252  	
253  	   return SCIP_OKAY;
254  	}
255  	
256  	/** destroys an instance of the given concurrent solver */
257  	SCIP_RETCODE SCIPconcsolverDestroyInstance(
258  	   SCIP_SET*             set,                /**< global SCIP settings */
259  	   SCIP_CONCSOLVER**     concsolver          /**< concurrent solver */
260  	   )
261  	{
262  	   assert(concsolver != NULL);
263  	   assert((*concsolver)->type != NULL);
264  	   assert(set != NULL);
265  	   assert((*concsolver)->type->concsolverdestroyinst != NULL);
266  	
267  	   SCIP_CALL( (*concsolver)->type->concsolverdestroyinst(set->scip, *concsolver) );
268  	   --(*concsolver)->type->ninstances;
269  	
270  	   SCIP_CALL( SCIPfreeClock(set->scip, &(*concsolver)->totalsynctime) );
271  	   BMSfreeMemoryArray(&(*concsolver)->name);
272  	
273  	   BMSfreeMemory(concsolver);
274  	
275  	   return SCIP_OKAY;
276  	}
277  	
278  	/** gets the data of a concurrent solver */
279  	SCIP_CONCSOLVERDATA* SCIPconcsolverGetData(
280  	   SCIP_CONCSOLVER*      concsolver          /**< concurrent solver */
281  	   )
282  	{
283  	   assert(concsolver != NULL);
284  	
285  	   return concsolver->data;
286  	}
287  	
288  	/** sets the data of a concurrent solver */
289  	void SCIPconcsolverSetData(
290  	   SCIP_CONCSOLVER*      concsolver,         /**< concurrent solver */
291  	   SCIP_CONCSOLVERDATA*  data                /**< the concurrent solver's data */
292  	   )
293  	{
294  	   assert(concsolver != NULL);
295  	
296  	   concsolver->data = data;
297  	}
298  	
299  	/** gets the name of a concurrent solver */
300  	char* SCIPconcsolverGetName(
301  	   SCIP_CONCSOLVER*      concsolver          /**< concurrent solver */
302  	   )
303  	{
304  	   assert(concsolver != NULL);
305  	
306  	   return concsolver->name;
307  	}
308  	
309  	/** initializes the random seeds of a concurrent solver */
310  	SCIP_RETCODE SCIPconcsolverInitSeeds(
311  	   SCIP_CONCSOLVER*      concsolver,         /**< concurrent solver */
312  	   unsigned int          seed                /**< seed for initializing the solver's internal random seeds */
313  	   )
314  	{
315  	   assert(concsolver != NULL);
316  	   assert(concsolver->type != NULL);
317  	
318  	   if( concsolver->type->concsolverinitseeds != NULL )
319  	      SCIP_CALL( concsolver->type->concsolverinitseeds(concsolver, seed) );
320  	
321  	   return SCIP_OKAY;
322  	}
323  	
324  	/** start the solving process of a concurrent solver */
325  	SCIP_RETCODE SCIPconcsolverExec(
326  	   SCIP_CONCSOLVER*      concsolver          /**< concurrent solver */
327  	   )
328  	{
329  	   assert(concsolver != NULL);
330  	   assert(concsolver->type != NULL);
331  	   assert(concsolver->type->concsolverexec != NULL);
332  	
333  	   /* set the stopped flag to false */
334  	   concsolver->stopped = FALSE;
335  	
336  	   /* then call the execute callback */
337  	   SCIP_CALL( concsolver->type->concsolverexec(concsolver, &concsolver->solvingtime, &concsolver->nlpiterations, &concsolver->nnodes) );
338  	
339  	   return SCIP_OKAY;
340  	}
341  	
342  	/** gets solving data of concurrent solver and stores it in the given SCIP instance */
343  	SCIP_RETCODE SCIPconcsolverGetSolvingData(
344  	   SCIP_CONCSOLVER*      concsolver,         /**< concurrent solver */
345  	   SCIP*                 scip                /**< SCIP datastructure */
346  	   )
347  	{
348  	   assert(concsolver != NULL);
349  	   assert(concsolver->type != NULL);
350  	   assert(concsolver->type->concsolvercopysolvdata != NULL);
351  	
352  	   return concsolver->type->concsolvercopysolvdata(concsolver, scip);
353  	}
354  	
355  	/** interrupt solving in a concurrent solver */
356  	SCIP_RETCODE SCIPconcsolverStop(
357  	   SCIP_CONCSOLVER*      concsolver          /**< concurrent solver */
358  	   )
359  	{
360  	   assert(concsolver != NULL);
361  	   assert(concsolver->type != NULL);
362  	   assert(concsolver->type->concsolverstop != NULL);
363  	
364  	   SCIP_CALL( concsolver->type->concsolverstop(concsolver) );
365  	
366  	   /* set the stopped flag to true */
367  	   concsolver->stopped = TRUE;
368  	
369  	   return SCIP_OKAY;
370  	}
371  	
372  	/** let the given concurrent solver synchronize, i.e. pass its own solutions and bounds to
373  	 *  the SPI.
374  	 */
375  	SCIP_RETCODE SCIPconcsolverSync(
376  	   SCIP_CONCSOLVER*      concsolver,         /**< concurrent solver */
377  	   SCIP_SET*             set                 /**< global SCIP settings */
378  	   )
379  	{
380  	   SCIP_SYNCDATA*   syncdata;
381  	   SCIP_SYNCSTORE*  syncstore;
382  	   int              nsols;
383  	   int              ntighterintbnds;
384  	   int              ntighterbnds;
385  	   SCIP_CONCSOLVERTYPE* concsolvertype;
386  	
387  	   assert(concsolver != NULL);
388  	   assert(concsolver->type != NULL);
389  	   assert(concsolver->type->concsolversyncwrite != NULL);
390  	   assert(concsolver->type->concsolversyncread != NULL);
391  	
392  	   if( concsolver->stopped )
393  	      return SCIP_OKAY;
394  	
395  	   SCIP_CALL( SCIPstartClock(set->scip, concsolver->totalsynctime) );
396  	
397  	   concsolvertype = concsolver->type;
398  	
399  	   syncstore = SCIPgetSyncstore(set->scip);
400  	   assert(syncstore != NULL);
401  	
402  	   SCIP_CALL( SCIPsyncstoreStartSync(syncstore, concsolver->nsyncs, &syncdata) );
403  	
404  	   if( syncdata == NULL )
405  	   {
406  	      SCIP_CALL( SCIPstopClock(set->scip, concsolver->totalsynctime) );
407  	      return SCIP_OKAY;
408  	   }
409  	
410  	   SCIPdebugMessage("concsolver %s starts sync %lli\n", concsolver->name, concsolver->nsyncs);
411  	
412  	   SCIP_CALL( concsolvertype->concsolversyncwrite(concsolver, syncstore, syncdata, set->concurrent_nbestsols, set->concurrent_maxnsols, &nsols) );
413  	   concsolver->nsolsshared += nsols;
414  	
415  	   if( SCIPsyncdataGetStatus(syncdata) != SCIP_STATUS_UNKNOWN )
416  	   {
417  	      SCIP_CALL( SCIPconcsolverStop(concsolver) );
418  	   }
419  	   else if( SCIPsyncdataGetNSynced(syncdata) == SCIPsyncstoreGetNSolvers(syncstore) - 1 )
420  	   {
421  	      /* if this is the last concurrent solver that is synchronizing for this synchronization data
422  	       * it will adjust the synchronization frequency using the progress on the gap
423  	       */
424  	      SCIP_Bool lbok;
425  	      SCIP_Bool ubok;
426  	      SCIP_Real progress;
427  	      SCIP_Real prevub;
428  	      SCIP_Real prevlb;
429  	      SCIP_Real newub;
430  	      SCIP_Real newlb;
431  	      SCIP_Real freqfactor;
432  	      SCIP_Real newsyncfreq;
433  	      SCIP_SYNCDATA* prevsync;
434  	
435  	      if( concsolver->nsyncs == 0 )
436  	      {
437  	         SCIPsyncdataSetSyncFreq(syncstore, syncdata, concsolver->syncfreq);
438  	      }
439  	      else
440  	      {
441  	         prevsync = SCIPsyncstoreGetSyncdata(syncstore, concsolver->nsyncs - 1);
442  	         assert(SCIPsyncdataGetNSynced(prevsync) == SCIPsyncstoreGetNSolvers(syncstore));
443  	
444  	         prevub = SCIPsyncdataGetUpperbound(prevsync);
445  	         prevlb = SCIPsyncdataGetLowerbound(prevsync);
446  	         newub = SCIPsyncdataGetUpperbound(syncdata);
447  	         newlb = SCIPsyncdataGetLowerbound(syncdata);
448  	         lbok = prevlb > -SCIPsetInfinity(set);
449  	         ubok = prevub < SCIPsetInfinity(set);
450  	
451  	         if( lbok && ubok )
452  	            progress = SCIPrelDiff(prevub - prevlb, newub - newlb);
453  	         else if( lbok )
454  	            progress = SCIPrelDiff(newlb, prevlb);
455  	         else if( ubok )
456  	            progress = SCIPrelDiff(prevub, newub);
457  	         else if( !SCIPsetIsInfinity(set, -newlb) || !SCIPsetIsInfinity(set, newub) ||
458  	                  SCIPboundstoreGetNChgs(SCIPsyncdataGetBoundChgs(syncdata)) > 0 )
459  	            progress = set->concurrent_targetprogress;
460  	         else
461  	            progress = 0.0;
462  	
463  	         /* should not be negative */
464  	         progress = MAX(progress, 0.0);
465  	         assert(SCIPsetIsGE(set, progress, 0.0));
466  	
467  	         if( progress < 0.5 * set->concurrent_targetprogress )
468  	            freqfactor = set->concurrent_freqfactor;
469  	         else if( progress > 2 * set->concurrent_targetprogress )
470  	            freqfactor = 0.5 + 0.5 / set->concurrent_freqfactor;
471  	         else
472  	            freqfactor = 1.0;
473  	
474  	         SCIPdebugMessage("syncfreq is %g and freqfactor is %f due to progress %f\n", concsolver->syncfreq, freqfactor, progress);
475  	         newsyncfreq = concsolver->syncfreq * freqfactor;
476  	         SCIPsyncdataSetSyncFreq(syncstore, syncdata, newsyncfreq);
477  	         SCIPdebugMessage("new syncfreq is %g\n", SCIPsyncdataGetSyncFreq(syncdata));
478  	      }
479  	   }
480  	
481  	   SCIPdebugMessage("concsolver %s finishing sync %lli\n", concsolver->name, concsolver->nsyncs);
482  	
483  	   SCIP_CALL( SCIPsyncstoreFinishSync(syncstore, &syncdata) );
484  	   ++concsolver->nsyncs;
485  	
486  	   concsolver->syncdelay += concsolver->timesincelastsync;
487  	
488  	   syncdata = SCIPsyncstoreGetNextSyncdata(syncstore, concsolver->syncdata, concsolver->syncfreq, concsolver->nsyncs, &concsolver->syncdelay);
489  	
490  	   while( syncdata != NULL )
491  	   {
492  	      SCIP_CALL( SCIPsyncstoreEnsureAllSynced(syncstore, syncdata) );
493  	      concsolver->syncdata = syncdata;
494  	      SCIP_CALL( concsolvertype->concsolversyncread(concsolver, syncstore, syncdata, &nsols, &ntighterbnds, &ntighterintbnds) );
495  	      concsolver->ntighterbnds += ntighterbnds;
496  	      concsolver->ntighterintbnds += ntighterintbnds;
497  	      concsolver->nsolsrecvd += nsols;
498  	      SCIPdebugMessage("syncfreq before reading the next syncdata is %g\n", concsolver->syncfreq);
499  	      concsolver->syncfreq = SCIPsyncdataGetSyncFreq(concsolver->syncdata);
500  	      SCIPdebugMessage("syncfreq after reading the next syncdata is %g\n", concsolver->syncfreq);
501  	      syncdata = SCIPsyncstoreGetNextSyncdata(syncstore, concsolver->syncdata, concsolver->syncfreq, concsolver->nsyncs, &concsolver->syncdelay);
502  	   }
503  	
504  	   SCIP_CALL( SCIPstopClock(set->scip, concsolver->totalsynctime) );
505  	
506  	   return SCIP_OKAY;
507  	}
508  	
509  	/** gets the current synchronization frequency of the concurent solver */
510  	SCIP_Real SCIPconcsolverGetSyncFreq(
511  	   SCIP_CONCSOLVER*      concsolver          /**< concurrent solver */
512  	   )
513  	{
514  	   assert(concsolver != NULL);
515  	
516  	   return concsolver->syncfreq;
517  	}
518  	
519  	/** gets the total memory used by the concurent solver */
520  	SCIP_Longint SCIPconcsolverGetMemTotal(
521  	   SCIP_CONCSOLVER*      concsolver          /**< concurrent solver */
522  	   )
523  	{
524  	   assert(concsolver != NULL);
525  	
526  	   return concsolver->syncdata != NULL ? SCIPsyncdataGetMemTotal(concsolver->syncdata) : 0;
527  	}
528  	
529  	/** sets the time elapsed since the last synchronization. Must be set before the synchronization is
530  	 *  started.
531  	 */
532  	void SCIPconcsolverSetTimeSinceLastSync(
533  	   SCIP_CONCSOLVER*      concsolver,         /**< concurrent solver */
534  	   SCIP_Real             time                /**< the time passed since the last synchronization */
535  	   )
536  	{
537  	   assert(concsolver != NULL);
538  	
539  	   concsolver->timesincelastsync = time;
540  	}
541  	
542  	/** gets the solving time of the concurrent solver */
543  	SCIP_Real SCIPconcsolverGetSolvingTime(
544  	   SCIP_CONCSOLVER*      concsolver          /**< concurrent solver */
545  	   )
546  	{
547  	   assert(concsolver != NULL);
548  	
549  	   return concsolver->solvingtime;
550  	}
551  	
552  	/** gets the time spent for synchronization for the concurrent solver */
553  	SCIP_Real SCIPconcsolverGetSyncTime(
554  	   SCIP_CONCSOLVER*      concsolver          /**< concurrent solver */
555  	   )
556  	{
557  	   assert(concsolver != NULL);
558  	
559  	   return SCIPclockGetTime(concsolver->totalsynctime);
560  	}
561  	
562  	/** gets the number of lp iterations the concurrent solver used */
563  	SCIP_Longint SCIPconcsolverGetNLPIterations(
564  	   SCIP_CONCSOLVER*      concsolver          /**< concurrent solver */
565  	   )
566  	{
567  	   assert(concsolver != NULL);
568  	
569  	   return concsolver->nlpiterations;
570  	}
571  	
572  	/** gets the number of branch and bound nodes the concurrent solver used */
573  	SCIP_Longint SCIPconcsolverGetNNodes(
574  	   SCIP_CONCSOLVER*      concsolver          /**< concurrent solver */
575  	   )
576  	{
577  	   assert(concsolver != NULL);
578  	
579  	   return concsolver->nnodes;
580  	}
581  	
582  	/** gets the number of solutions the concurrent solver received during synchronization */
583  	SCIP_Longint SCIPconcsolverGetNSolsRecvd(
584  	   SCIP_CONCSOLVER*      concsolver          /**< concurrent solver */
585  	   )
586  	{
587  	   assert(concsolver != NULL);
588  	
589  	   return concsolver->nsolsrecvd;
590  	}
591  	
592  	/** gets the number of solutions the concurrent solver shared during synchronization */
593  	SCIP_Longint SCIPconcsolverGetNSolsShared(
594  	   SCIP_CONCSOLVER*      concsolver          /**< concurrent solver */
595  	   )
596  	{
597  	   assert(concsolver != NULL);
598  	
599  	   return concsolver->nsolsshared;
600  	}
601  	
602  	/** gets the number of tighter global variable bounds the solver received */
603  	SCIP_Longint SCIPconcsolverGetNTighterBnds(
604  	   SCIP_CONCSOLVER*      concsolver          /**< concurrent solver */
605  	   )
606  	{
607  	   assert(concsolver != NULL);
608  	
609  	   return concsolver->ntighterbnds;
610  	}
611  	
612  	/** gets the number of tighter global variable bounds of integer variables the solver received */
613  	SCIP_Longint SCIPconcsolverGetNTighterIntBnds(
614  	   SCIP_CONCSOLVER*      concsolver          /**< concurrent solver */
615  	   )
616  	{
617  	   assert(concsolver != NULL);
618  	
619  	   return concsolver->ntighterintbnds;
620  	}
621  	
622  	/** gets index of concurrent solver */
623  	int SCIPconcsolverGetIdx(
624  	   SCIP_CONCSOLVER*      concsolver          /**< concurrent solver */
625  	   )
626  	{
627  	   assert(concsolver != NULL);
628  	
629  	   return concsolver->idx;
630  	}
631