001package net.sf.cpsolver.ifs.solver; 002 003import java.io.File; 004import java.io.FileOutputStream; 005import java.io.IOException; 006import java.lang.reflect.Constructor; 007import java.util.ArrayList; 008import java.util.List; 009import java.util.StringTokenizer; 010 011import net.sf.cpsolver.ifs.extension.ConflictStatistics; 012import net.sf.cpsolver.ifs.extension.Extension; 013import net.sf.cpsolver.ifs.extension.MacPropagation; 014import net.sf.cpsolver.ifs.heuristics.NeighbourSelection; 015import net.sf.cpsolver.ifs.heuristics.StandardNeighbourSelection; 016import net.sf.cpsolver.ifs.heuristics.ValueSelection; 017import net.sf.cpsolver.ifs.heuristics.VariableSelection; 018import net.sf.cpsolver.ifs.model.Model; 019import net.sf.cpsolver.ifs.model.Neighbour; 020import net.sf.cpsolver.ifs.model.Value; 021import net.sf.cpsolver.ifs.model.Variable; 022import net.sf.cpsolver.ifs.perturbations.DefaultPerturbationsCounter; 023import net.sf.cpsolver.ifs.perturbations.PerturbationsCounter; 024import net.sf.cpsolver.ifs.solution.GeneralSolutionComparator; 025import net.sf.cpsolver.ifs.solution.Solution; 026import net.sf.cpsolver.ifs.solution.SolutionComparator; 027import net.sf.cpsolver.ifs.termination.GeneralTerminationCondition; 028import net.sf.cpsolver.ifs.termination.TerminationCondition; 029import net.sf.cpsolver.ifs.util.DataProperties; 030import net.sf.cpsolver.ifs.util.JProf; 031import net.sf.cpsolver.ifs.util.Progress; 032import net.sf.cpsolver.ifs.util.ToolBox; 033 034/** 035 * IFS Solver. <br> 036 * <br> 037 * The iterative forward search (IFS) algorithm is based on ideas of local 038 * search methods. However, in contrast to classical local search techniques, it 039 * operates over feasible, though not necessarily complete solutions. In such a 040 * solution, some variables can be left unassigned. Still all hard constraints 041 * on assigned variables must be satisfied. Similarly to backtracking based 042 * algorithms, this means that there are no violations of hard constraints. <br> 043 * <br> 044 * This search works in iterations. During each step, an unassigned or assigned 045 * variable is initially selected. Typically an unassigned variable is chosen 046 * like in backtracking-based search. An assigned variable may be selected when 047 * all variables are assigned but the solution is not good enough (for example, 048 * when there are still many violations of soft constraints). Once a variable is 049 * selected, a value from its domain is chosen for assignment. Even if the best 050 * value is selected (whatever ???best??? means), its assignment to the selected 051 * variable may cause some hard conflicts with already assigned variables. Such 052 * conflicting variables are removed from the solution and become unassigned. 053 * Finally, the selected value is assigned to the selected variable. <br> 054 * <br> 055 * Algorithm schema: 056 * <ul> 057 * <code> 058 * procedure net.sf.cpsolver.ifs(initial) // initial solution is the parameter <br> 059 * iteration = 0; // iteration counter <br> 060 * current = initial; // current (partial) feasible solution <br> 061 * best = initial; // best solution <br> 062 * while canContinue(current, iteration) do <br> 063 * iteration = iteration + 1; <br> 064 * variable = selectVariable(current); <br> 065 * value = selectValue(current, variable); <br> 066 * UNASSIGN(current, CONFLICTING_VARIABLES(current, variable, value)); <br> 067 * ASSIGN(current, variable, value); <br> 068 * if better(current, best) then best = current; <br> 069 * end while <br> 070 * return best;<br> 071 * end procedure <br> 072 * </code> 073 * </ul> 074 * <br> 075 * The algorithm attempts to move from one (partial) feasible solution to 076 * another via repetitive assignment of a selected value to a selected variable. 077 * During this search, the feasibility of all hard constraints in each iteration 078 * step is enforced by unassigning the conflicting variables. The search is 079 * terminated when the requested solution is found or when there is a timeout, 080 * expressed e.g., as a maximal number of iterations or available time being 081 * reached. The best solution found is then returned. <br> 082 * <br> 083 * The above algorithm schema is parameterized by several functions, namely: 084 * <ul> 085 * <li>the termination condition (function canContinue, see 086 * {@link TerminationCondition}), 087 * <li>the solution comparator (function better, see {@link SolutionComparator} 088 * ), 089 * <li>the neighbour selection (function selectNeighbour, see 090 * {@link NeighbourSelection}) and 091 * </ul> 092 * <br> 093 * Usage: 094 * <ul> 095 * <code> 096 * DataProperties cfg = ToolBox.loadProperties(inputCfg); //input configuration<br> 097 * Solver solver = new Solver(cfg);<br> 098 * solver.setInitalSolution(model); //sets initial solution<br> 099 * <br> 100 * solver.start(); //server is executed in a thread<br> 101 * <br> 102 * try { //wait untill the server finishes<br> 103 * solver.getSolverThread().join(); <br> 104 * } catch (InterruptedException e) {} <br> 105 * <br> 106 * Solution solution = solver.lastSolution(); //last solution<br> 107 * solution.restoreBest(); //restore best solution ever found<br> 108 * </code> 109 * </ul> 110 * <br> 111 * Solver's parameters: <br> 112 * <table border='1'> 113 * <tr> 114 * <th>Parameter</th> 115 * <th>Type</th> 116 * <th>Comment</th> 117 * </tr> 118 * <tr> 119 * <td>General.SaveBestUnassigned</td> 120 * <td>{@link Integer}</td> 121 * <td>During the search, solution is saved when it is the best ever found 122 * solution and if the number of assigned variables is less or equal this 123 * parameter (if set to -1, the solution is always saved)</td> 124 * </tr> 125 * <tr> 126 * <td>General.Seed</td> 127 * <td>{@link Long}</td> 128 * <td>If set, random number generator is initialized with this seed</td> 129 * </tr> 130 * <tr> 131 * <td>General.SaveConfiguration</td> 132 * <td>{@link Boolean}</td> 133 * <td>If true, given configuration is stored into the output folder (during 134 * initialization of the solver, 135 * ${General.Output}/${General.ProblemName}.properties)</td> 136 * </tr> 137 * <tr> 138 * <td>Solver.AutoConfigure</td> 139 * <td>{@link Boolean}</td> 140 * <td>If true, IFS Solver is configured according to the following parameters</td> 141 * </tr> 142 * <tr> 143 * <td>Termination.Class</td> 144 * <td>{@link String}</td> 145 * <td>Fully qualified class name of the termination condition (see 146 * {@link TerminationCondition}, e.g. {@link GeneralTerminationCondition})</td> 147 * </tr> 148 * <tr> 149 * <td>Comparator.Class</td> 150 * <td>{@link String}</td> 151 * <td>Fully qualified class name of the solution comparator (see 152 * {@link SolutionComparator}, e.g. {@link GeneralSolutionComparator})</td> 153 * </tr> 154 * <tr> 155 * <td>Neighbour.Class</td> 156 * <td>{@link String}</td> 157 * <td>Fully qualified class name of the neighbour selection criterion (see 158 * {@link NeighbourSelection}, e.g. {@link StandardNeighbourSelection})</td> 159 * </tr> 160 * <tr> 161 * <td>PerturbationCounter.Class</td> 162 * <td>{@link String}</td> 163 * <td>Fully qualified class name of the perturbation counter in case of solving 164 * minimal perturbation problem (see {@link PerturbationsCounter}, e.g. 165 * {@link DefaultPerturbationsCounter})</td> 166 * </tr> 167 * <tr> 168 * <td>Extensions.Classes</td> 169 * <td>{@link String}</td> 170 * <td>Semi-colon separated list of fully qualified class names of IFS 171 * extensions (see {@link Extension}, e.g. {@link ConflictStatistics} or 172 * {@link MacPropagation})</td> 173 * </tr> 174 * </table> 175 * 176 * @see SolverListener 177 * @see Model 178 * @see Solution 179 * @see TerminationCondition 180 * @see SolutionComparator 181 * @see PerturbationsCounter 182 * @see VariableSelection 183 * @see ValueSelection 184 * @see Extension 185 * 186 * @version IFS 1.2 (Iterative Forward Search)<br> 187 * Copyright (C) 2006 - 2010 Tomáš Müller<br> 188 * <a href="mailto:muller@unitime.org">muller@unitime.org</a><br> 189 * <a href="http://muller.unitime.org">http://muller.unitime.org</a><br> 190 * <br> 191 * This library is free software; you can redistribute it and/or modify 192 * it under the terms of the GNU Lesser General Public License as 193 * published by the Free Software Foundation; either version 3 of the 194 * License, or (at your option) any later version. <br> 195 * <br> 196 * This library is distributed in the hope that it will be useful, but 197 * WITHOUT ANY WARRANTY; without even the implied warranty of 198 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 199 * Lesser General Public License for more details. <br> 200 * <br> 201 * You should have received a copy of the GNU Lesser General Public 202 * License along with this library; if not see <http://www.gnu.org/licenses/>. 203 **/ 204 205public class Solver<V extends Variable<V, T>, T extends Value<V, T>> { 206 public static int THREAD_PRIORITY = 3; 207 /** log */ 208 protected static org.apache.log4j.Logger sLogger = org.apache.log4j.Logger.getLogger(Solver.class); 209 /** current solution */ 210 protected Solution<V, T> iCurrentSolution = null; 211 /** last solution (after IFS Solver finishes) */ 212 protected Solution<V, T> iLastSolution = null; 213 214 /** solver is stopped */ 215 protected boolean iStop = false; 216 217 /** solver thread */ 218 protected SolverThread iSolverThread = null; 219 /** configuration */ 220 private DataProperties iProperties = null; 221 222 private TerminationCondition<V, T> iTerminationCondition = null; 223 private SolutionComparator<V, T> iSolutionComparator = null; 224 private PerturbationsCounter<V, T> iPerturbationsCounter = null; 225 private NeighbourSelection<V, T> iNeighbourSelection = null; 226 private List<Extension<V, T>> iExtensions = new ArrayList<Extension<V, T>>(); 227 private List<SolverListener<V, T>> iSolverListeners = new ArrayList<SolverListener<V, T>>(); 228 private int iSaveBestUnassigned = 0; 229 230 private boolean iUpdateProgress = true; 231 232 private Progress iProgress; 233 234 /** 235 * Constructor. 236 * 237 * @param properties 238 * input configuration 239 */ 240 public Solver(DataProperties properties) { 241 iProperties = properties; 242 } 243 244 /** Dispose solver */ 245 public void dispose() { 246 iExtensions.clear(); 247 iSolverListeners.clear(); 248 iTerminationCondition = null; 249 iSolutionComparator = null; 250 iPerturbationsCounter = null; 251 iNeighbourSelection = null; 252 } 253 254 private boolean iValueExtraUsed = false; 255 private boolean iVariableExtraUsed = false; 256 257 /** Sets termination condition */ 258 public void setTerminalCondition(TerminationCondition<V, T> terminationCondition) { 259 iTerminationCondition = terminationCondition; 260 } 261 262 /** Sets solution comparator */ 263 public void setSolutionComparator(SolutionComparator<V, T> solutionComparator) { 264 iSolutionComparator = solutionComparator; 265 } 266 267 /** Sets neighbour selection criterion */ 268 public void setNeighbourSelection(NeighbourSelection<V, T> neighbourSelection) { 269 iNeighbourSelection = neighbourSelection; 270 } 271 272 /** Sets perturbation counter (minimal perturbation problem) */ 273 public void setPerturbationsCounter(PerturbationsCounter<V, T> perturbationsCounter) { 274 iPerturbationsCounter = perturbationsCounter; 275 } 276 277 /** Add an IFS extension */ 278 public void addExtension(Extension<V, T> extension) { 279 if (extension.useValueExtra() && iValueExtraUsed) { 280 sLogger.warn("Unable to add an extension " + extension + " -- value extra is already used."); 281 return; 282 } 283 if (extension.useVariableExtra() && iVariableExtraUsed) { 284 sLogger.warn("Unable to add extension " + extension + " -- variable extra is already used."); 285 return; 286 } 287 iValueExtraUsed = iValueExtraUsed | extension.useValueExtra(); 288 iValueExtraUsed = iVariableExtraUsed | extension.useVariableExtra(); 289 iExtensions.add(extension); 290 } 291 292 /** Returns termination condition */ 293 public TerminationCondition<V, T> getTerminationCondition() { 294 return iTerminationCondition; 295 } 296 297 /** Returns solution comparator */ 298 public SolutionComparator<V, T> getSolutionComparator() { 299 return iSolutionComparator; 300 } 301 302 /** Returns neighbour selection criterion */ 303 public NeighbourSelection<V, T> getNeighbourSelection() { 304 return iNeighbourSelection; 305 } 306 307 /** Returns perturbation counter (minimal perturbation problem) */ 308 public PerturbationsCounter<V, T> getPerturbationsCounter() { 309 return iPerturbationsCounter; 310 } 311 312 /** Returns list of all used extensions */ 313 public List<Extension<V, T>> getExtensions() { 314 return iExtensions; 315 } 316 317 /** Adds a solver listener */ 318 public void addSolverListener(SolverListener<V, T> listener) { 319 iSolverListeners.add(listener); 320 } 321 322 /** Removes a solver listener */ 323 public void removeSolverListener(SolverListener<V, T> listener) { 324 iSolverListeners.remove(listener); 325 } 326 327 /** Registered solver listeners */ 328 public List<SolverListener<V, T>> getSolverListeners() { 329 return iSolverListeners; 330 } 331 332 /** Returns configuration */ 333 public DataProperties getProperties() { 334 return iProperties; 335 } 336 337 /** 338 * Automatic configuratin of the solver -- when Solver.AutoConfigure is true 339 */ 340 @SuppressWarnings("unchecked") 341 protected void autoConfigure() { 342 try { 343 boolean mpp = getProperties().getPropertyBoolean("General.MPP", false); 344 345 String terminationConditionClassName = getProperties().getProperty( 346 "Termination.Class", 347 (mpp ? "net.sf.cpsolver.ifs.termination.MPPTerminationCondition" 348 : "net.sf.cpsolver.ifs.termination.GeneralTerminationCondition")); 349 sLogger.info("Using " + terminationConditionClassName); 350 Class<?> terminationConditionClass = Class.forName(terminationConditionClassName); 351 Constructor<?> terminationConditionConstructor = terminationConditionClass 352 .getConstructor(new Class[] { DataProperties.class }); 353 setTerminalCondition((TerminationCondition<V, T>) terminationConditionConstructor 354 .newInstance(new Object[] { getProperties() })); 355 356 String solutionComparatorClassName = getProperties().getProperty( 357 "Comparator.Class", 358 (mpp ? "net.sf.cpsolver.ifs.solution.MPPSolutionComparator" 359 : "net.sf.cpsolver.ifs.solution.GeneralSolutionComparator")); 360 sLogger.info("Using " + solutionComparatorClassName); 361 Class<?> solutionComparatorClass = Class.forName(solutionComparatorClassName); 362 Constructor<?> solutionComparatorConstructor = solutionComparatorClass 363 .getConstructor(new Class[] { DataProperties.class }); 364 setSolutionComparator((SolutionComparator<V, T>) solutionComparatorConstructor 365 .newInstance(new Object[] { getProperties() })); 366 367 String neighbourSelectionClassName = getProperties().getProperty("Neighbour.Class", 368 "net.sf.cpsolver.ifs.heuristics.StandardNeighbourSelection"); 369 sLogger.info("Using " + neighbourSelectionClassName); 370 Class<?> neighbourSelectionClass = Class.forName(neighbourSelectionClassName); 371 Constructor<?> neighbourSelectionConstructor = neighbourSelectionClass 372 .getConstructor(new Class[] { DataProperties.class }); 373 setNeighbourSelection((NeighbourSelection<V, T>) neighbourSelectionConstructor 374 .newInstance(new Object[] { getProperties() })); 375 376 String perturbationCounterClassName = getProperties().getProperty("PerturbationCounter.Class", 377 "net.sf.cpsolver.ifs.perturbations.DefaultPerturbationsCounter"); 378 sLogger.info("Using " + perturbationCounterClassName); 379 Class<?> perturbationCounterClass = Class.forName(perturbationCounterClassName); 380 Constructor<?> perturbationCounterConstructor = perturbationCounterClass 381 .getConstructor(new Class[] { DataProperties.class }); 382 setPerturbationsCounter((PerturbationsCounter<V, T>) perturbationCounterConstructor 383 .newInstance(new Object[] { getProperties() })); 384 385 for (Extension<V, T> extension : iExtensions) { 386 extension.unregister(iCurrentSolution.getModel()); 387 } 388 iExtensions.clear(); 389 String extensionClassNames = getProperties().getProperty("Extensions.Classes", null); 390 if (extensionClassNames != null) { 391 StringTokenizer extensionClassNameTokenizer = new StringTokenizer(extensionClassNames, ";"); 392 while (extensionClassNameTokenizer.hasMoreTokens()) { 393 String extensionClassName = extensionClassNameTokenizer.nextToken().trim(); 394 if (extensionClassName.isEmpty()) continue; 395 sLogger.info("Using " + extensionClassName); 396 Class<?> extensionClass = Class.forName(extensionClassName); 397 Constructor<?> extensionConstructor = extensionClass.getConstructor(new Class[] { Solver.class, 398 DataProperties.class }); 399 addExtension((Extension<V, T>) extensionConstructor.newInstance(new Object[] { this, 400 getProperties() })); 401 } 402 } 403 } catch (Exception e) { 404 sLogger.error("Unable to autoconfigure solver.", e); 405 } 406 } 407 408 /** Clears best solution */ 409 public void clearBest() { 410 if (iCurrentSolution != null) 411 iCurrentSolution.clearBest(); 412 } 413 414 /** Sets initial solution */ 415 public void setInitalSolution(Solution<V, T> solution) { 416 iCurrentSolution = solution; 417 iLastSolution = null; 418 } 419 420 /** Sets initial solution */ 421 public void setInitalSolution(Model<V, T> model) { 422 iCurrentSolution = new Solution<V, T>(model, 0, 0); 423 iLastSolution = null; 424 } 425 426 /** Starts solver */ 427 public void start() { 428 iSolverThread = new SolverThread(); 429 iSolverThread.setPriority(THREAD_PRIORITY); 430 iSolverThread.start(); 431 } 432 433 /** Returns solver's thread */ 434 public Thread getSolverThread() { 435 return iSolverThread; 436 } 437 438 /** Initialization */ 439 public void init() { 440 } 441 442 /** True, when solver should update progress (see {@link Progress}) */ 443 private boolean isUpdateProgress() { 444 return iUpdateProgress; 445 } 446 447 /** True, when solver should update progress (see {@link Progress}) */ 448 public void setUpdateProgress(boolean updateProgress) { 449 iUpdateProgress = updateProgress; 450 } 451 452 /** Last solution (when solver finishes) */ 453 public Solution<V, T> lastSolution() { 454 return (iLastSolution == null ? iCurrentSolution : iLastSolution); 455 } 456 457 /** Current solution (during the search) */ 458 public Solution<V, T> currentSolution() { 459 return iCurrentSolution; 460 } 461 462 public void initSolver() { 463 long seed = getProperties().getPropertyLong("General.Seed", System.currentTimeMillis()); 464 ToolBox.setSeed(seed); 465 466 iSaveBestUnassigned = getProperties().getPropertyInt("General.SaveBestUnassigned", 0); 467 468 clearBest(); 469 if (iProperties.getPropertyBoolean("Solver.AutoConfigure", true)) { 470 autoConfigure(); 471 } 472 473 // register extensions 474 for (Extension<V, T> extension : iExtensions) { 475 extension.register(iCurrentSolution.getModel()); 476 } 477 478 // register solution 479 iCurrentSolution.init(Solver.this); 480 481 // register and intialize neighbour selection 482 getNeighbourSelection().init(Solver.this); 483 484 // register and intialize perturbations counter 485 if (getPerturbationsCounter() != null) 486 getPerturbationsCounter().init(Solver.this); 487 488 // save initial configuration 489 if (iProperties.getPropertyBoolean("General.SaveConfiguration", false)) { 490 FileOutputStream f = null; 491 try { 492 f = new FileOutputStream(iProperties.getProperty("General.Output") + File.separator 493 + iProperties.getProperty("General.ProblemName", "ifs") + ".properties"); 494 iProperties.store(f, iProperties.getProperty("General.ProblemNameLong", "Iterative Forward Search") 495 + " -- configuration file"); 496 f.flush(); 497 f.close(); 498 f = null; 499 } catch (Exception e) { 500 sLogger.error("Unable to store configuration file :-(", e); 501 } finally { 502 try { 503 if (f != null) 504 f.close(); 505 } catch (IOException e) { 506 } 507 } 508 } 509 } 510 511 /** Stop running solver */ 512 public void stopSolver() { 513 stopSolver(true); 514 } 515 516 /** Stop running solver */ 517 public void stopSolver(boolean join) { 518 if (getSolverThread() != null) { 519 iStop = true; 520 if (join) { 521 try { 522 getSolverThread().join(); 523 } catch (InterruptedException ex) { 524 } 525 } 526 } 527 } 528 529 /** True, if the solver is running */ 530 public boolean isRunning() { 531 return (getSolverThread() != null); 532 } 533 534 /** Called when the solver is stopped */ 535 protected void onStop() { 536 } 537 538 /** Called when the solver is started */ 539 protected void onStart() { 540 } 541 542 /** Called when the solver is finished */ 543 protected void onFinish() { 544 } 545 546 /** Called when the solver fails */ 547 protected void onFailure() { 548 } 549 550 /** Called in each iteration, after a neighbour is assigned */ 551 protected void onAssigned(double startTime) { 552 } 553 554 /** Solver thread */ 555 protected class SolverThread extends Thread { 556 557 /** Solving rutine */ 558 @Override 559 public void run() { 560 try { 561 iStop = false; 562 // Sets thread name 563 setName("Solver"); 564 565 // Initialization 566 iProgress = Progress.getInstance(iCurrentSolution.getModel()); 567 iProgress.setStatus("Solving problem ..."); 568 iProgress.setPhase("Initializing solver"); 569 initSolver(); 570 onStart(); 571 572 double startTime = JProf.currentTimeSec(); 573 if (isUpdateProgress()) { 574 if (iCurrentSolution.getBestInfo() == null) { 575 iProgress.setPhase("Searching for initial solution ...", iCurrentSolution.getModel() 576 .variables().size()); 577 } else { 578 iProgress.setPhase("Improving found solution ..."); 579 } 580 } 581 long prog = 9999; 582 sLogger.info("Initial solution:" + ToolBox.dict2string(iCurrentSolution.getInfo(), 1)); 583 if ((iSaveBestUnassigned < 0 || iSaveBestUnassigned >= iCurrentSolution.getModel() 584 .nrUnassignedVariables()) 585 && (iCurrentSolution.getBestInfo() == null || getSolutionComparator().isBetterThanBestSolution( 586 iCurrentSolution))) { 587 if (iCurrentSolution.getModel().nrUnassignedVariables() == 0) 588 sLogger.info("Complete solution " + ToolBox.dict2string(iCurrentSolution.getInfo(), 1) 589 + " was found."); 590 synchronized (iCurrentSolution) { 591 iCurrentSolution.saveBest(); 592 } 593 } 594 595 if (iCurrentSolution.getModel().variables().isEmpty()) { 596 iProgress.error("Nothing to solve."); 597 iStop = true; 598 } 599 600 // Iterations: until solver can continue 601 while (!iStop && getTerminationCondition().canContinue(iCurrentSolution)) { 602 // Neighbour selection 603 Neighbour<V, T> neighbour = getNeighbourSelection().selectNeighbour(iCurrentSolution); 604 for (SolverListener<V, T> listener : iSolverListeners) { 605 if (!listener.neighbourSelected(iCurrentSolution.getIteration(), neighbour)) { 606 neighbour = null; 607 continue; 608 } 609 } 610 if (neighbour == null) { 611 sLogger.debug("No neighbour selected."); 612 synchronized (iCurrentSolution) { // still update the 613 // solution (increase 614 // iteration etc.) 615 iCurrentSolution.update(JProf.currentTimeSec() - startTime); 616 } 617 continue; 618 } 619 620 // Assign selected value to the selected variable 621 synchronized (iCurrentSolution) { 622 neighbour.assign(iCurrentSolution.getIteration()); 623 iCurrentSolution.update(JProf.currentTimeSec() - startTime); 624 } 625 626 onAssigned(startTime); 627 628 // Check if the solution is the best ever found one 629 if ((iSaveBestUnassigned < 0 || iSaveBestUnassigned >= iCurrentSolution.getModel() 630 .nrUnassignedVariables()) 631 && (iCurrentSolution.getBestInfo() == null || getSolutionComparator() 632 .isBetterThanBestSolution(iCurrentSolution))) { 633 if (iCurrentSolution.getModel().nrUnassignedVariables() == 0) { 634 iProgress.debug("Complete solution of value " + iCurrentSolution.getModel().getTotalValue() 635 + " was found."); 636 } 637 synchronized (iCurrentSolution) { 638 iCurrentSolution.saveBest(); 639 } 640 } 641 642 // Increment progress bar 643 if (isUpdateProgress()) { 644 if (iCurrentSolution.getBestInfo() != null 645 && iCurrentSolution.getModel().getBestUnassignedVariables() == 0) { 646 prog++; 647 if (prog == 10000) { 648 iProgress.setPhase("Improving found solution ..."); 649 prog = 0; 650 } else { 651 iProgress.setProgress(prog / 100); 652 } 653 } else if ((iCurrentSolution.getBestInfo() == null || iCurrentSolution.getModel() 654 .getBestUnassignedVariables() > 0) 655 && (iCurrentSolution.getModel().variables().size() - iCurrentSolution.getModel() 656 .nrUnassignedVariables()) > iProgress.getProgress()) { 657 iProgress.setProgress(iCurrentSolution.getModel().variables().size() 658 - iCurrentSolution.getModel().nrUnassignedVariables()); 659 } 660 } 661 662 } 663 664 // Finalization 665 iLastSolution = iCurrentSolution; 666 667 iProgress.setPhase("Done", 1); 668 iProgress.incProgress(); 669 670 iSolverThread = null; 671 if (iStop) { 672 sLogger.debug("Solver stopped."); 673 iProgress.setStatus("Solver stopped."); 674 onStop(); 675 } else { 676 sLogger.debug("Solver done."); 677 iProgress.setStatus("Solver done."); 678 onFinish(); 679 } 680 } catch (Exception ex) { 681 sLogger.error(ex.getMessage(), ex); 682 iProgress.fatal("Solver failed, reason:" + ex.getMessage(), ex); 683 iProgress.setStatus("Solver failed."); 684 onFailure(); 685 } 686 iSolverThread = null; 687 } 688 } 689 690 /** Return true if {@link Solver#stopSolver()} was called */ 691 public boolean isStop() { 692 return iStop; 693 } 694 695}