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