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'><caption>Related Solver Parameters</caption> 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 * @author Tomáš Müller 184 * @version IFS 1.3 (Iterative Forward Search)<br> 185 * Copyright (C) 2006 - 2014 Tomáš Müller<br> 186 * <a href="mailto:muller@unitime.org">muller@unitime.org</a><br> 187 * <a href="http://muller.unitime.org">http://muller.unitime.org</a><br> 188 * <br> 189 * This library is free software; you can redistribute it and/or modify 190 * it under the terms of the GNU Lesser General Public License as 191 * published by the Free Software Foundation; either version 3 of the 192 * License, or (at your option) any later version. <br> 193 * <br> 194 * This library is distributed in the hope that it will be useful, but 195 * WITHOUT ANY WARRANTY; without even the implied warranty of 196 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 197 * Lesser General Public License for more details. <br> 198 * <br> 199 * You should have received a copy of the GNU Lesser General Public 200 * License along with this library; if not see <a href='http://www.gnu.org/licenses'>http://www.gnu.org/licenses</a>. 201 * 202 * @param <V> Variable 203 * @param <T> Value 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.logging.log4j.Logger sLogger = org.apache.logging.log4j.LogManager.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 protected List<SolverListener<V, T>> iSolverListeners = new ArrayList<SolverListener<V, T>>(); 228 protected int iSaveBestUnassigned = 0; 229 230 private boolean iUpdateProgress = true; 231 232 protected 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 /** Sets termination condition 255 * @param terminationCondition termination condition 256 **/ 257 public void setTerminalCondition(TerminationCondition<V, T> terminationCondition) { 258 iTerminationCondition = terminationCondition; 259 } 260 261 /** Sets solution comparator 262 * @param solutionComparator solution comparator 263 **/ 264 public void setSolutionComparator(SolutionComparator<V, T> solutionComparator) { 265 iSolutionComparator = solutionComparator; 266 } 267 268 /** Sets neighbour selection criterion 269 * @param neighbourSelection neighbour selection criterion 270 **/ 271 public void setNeighbourSelection(NeighbourSelection<V, T> neighbourSelection) { 272 iNeighbourSelection = neighbourSelection; 273 } 274 275 /** Sets perturbation counter (minimal perturbation problem) 276 * @param perturbationsCounter perturbation counter 277 **/ 278 public void setPerturbationsCounter(PerturbationsCounter<V, T> perturbationsCounter) { 279 iPerturbationsCounter = perturbationsCounter; 280 } 281 282 /** Add an IFS extension 283 * @param extension an extension 284 **/ 285 public void addExtension(Extension<V, T> extension) { 286 iExtensions.add(extension); 287 } 288 289 /** Returns termination condition 290 * @return termination condition 291 **/ 292 public TerminationCondition<V, T> getTerminationCondition() { 293 return iTerminationCondition; 294 } 295 296 /** Returns solution comparator 297 * @return solution comparator 298 **/ 299 public SolutionComparator<V, T> getSolutionComparator() { 300 return iSolutionComparator; 301 } 302 303 /** Returns neighbour selection criterion 304 * @return neighbour selection criterion 305 **/ 306 public NeighbourSelection<V, T> getNeighbourSelection() { 307 return iNeighbourSelection; 308 } 309 310 /** Returns perturbation counter (minimal perturbation problem) 311 * @return perturbation counter 312 **/ 313 public PerturbationsCounter<V, T> getPerturbationsCounter() { 314 return iPerturbationsCounter; 315 } 316 317 /** Returns list of all used extensions 318 * @return list of all registered extensions 319 **/ 320 public List<Extension<V, T>> getExtensions() { 321 return iExtensions; 322 } 323 324 /** Adds a solver listener 325 * @param listener solver listener 326 **/ 327 public void addSolverListener(SolverListener<V, T> listener) { 328 iSolverListeners.add(listener); 329 } 330 331 /** Removes a solver listener 332 * @param listener solver listener 333 **/ 334 public void removeSolverListener(SolverListener<V, T> listener) { 335 iSolverListeners.remove(listener); 336 } 337 338 /** Registered solver listeners 339 * @return list of all registered solver listeners 340 **/ 341 public List<SolverListener<V, T>> getSolverListeners() { 342 return iSolverListeners; 343 } 344 345 /** Returns configuration 346 * @return solver configuration 347 **/ 348 public DataProperties getProperties() { 349 return iProperties; 350 } 351 352 /** 353 * Automatic configuratin of the solver -- when Solver.AutoConfigure is true 354 */ 355 @SuppressWarnings("unchecked") 356 protected void autoConfigure() { 357 try { 358 boolean mpp = getProperties().getPropertyBoolean("General.MPP", false); 359 360 String terminationConditionClassName = getProperties().getProperty( 361 "Termination.Class", 362 (mpp ? "org.cpsolver.ifs.termination.MPPTerminationCondition" 363 : "org.cpsolver.ifs.termination.GeneralTerminationCondition")); 364 sLogger.info("Using " + terminationConditionClassName); 365 Class<?> terminationConditionClass = Class.forName(terminationConditionClassName); 366 Constructor<?> terminationConditionConstructor = terminationConditionClass 367 .getConstructor(new Class[] { DataProperties.class }); 368 setTerminalCondition((TerminationCondition<V, T>) terminationConditionConstructor 369 .newInstance(new Object[] { getProperties() })); 370 371 String solutionComparatorClassName = getProperties().getProperty( 372 "Comparator.Class", 373 (mpp ? "org.cpsolver.ifs.solution.MPPSolutionComparator" 374 : "org.cpsolver.ifs.solution.GeneralSolutionComparator")); 375 sLogger.info("Using " + solutionComparatorClassName); 376 Class<?> solutionComparatorClass = Class.forName(solutionComparatorClassName); 377 Constructor<?> solutionComparatorConstructor = solutionComparatorClass 378 .getConstructor(new Class[] { DataProperties.class }); 379 setSolutionComparator((SolutionComparator<V, T>) solutionComparatorConstructor 380 .newInstance(new Object[] { getProperties() })); 381 382 String neighbourSelectionClassName = getProperties().getProperty("Neighbour.Class", 383 "org.cpsolver.ifs.heuristics.StandardNeighbourSelection"); 384 sLogger.info("Using " + neighbourSelectionClassName); 385 Class<?> neighbourSelectionClass = Class.forName(neighbourSelectionClassName); 386 Constructor<?> neighbourSelectionConstructor = neighbourSelectionClass 387 .getConstructor(new Class[] { DataProperties.class }); 388 setNeighbourSelection((NeighbourSelection<V, T>) neighbourSelectionConstructor 389 .newInstance(new Object[] { getProperties() })); 390 391 String perturbationCounterClassName = getProperties().getProperty("PerturbationCounter.Class", 392 "org.cpsolver.ifs.perturbations.DefaultPerturbationsCounter"); 393 sLogger.info("Using " + perturbationCounterClassName); 394 Class<?> perturbationCounterClass = Class.forName(perturbationCounterClassName); 395 Constructor<?> perturbationCounterConstructor = perturbationCounterClass 396 .getConstructor(new Class[] { DataProperties.class }); 397 setPerturbationsCounter((PerturbationsCounter<V, T>) perturbationCounterConstructor 398 .newInstance(new Object[] { getProperties() })); 399 400 for (Extension<V, T> extension : iExtensions) { 401 extension.unregister(iCurrentSolution.getModel()); 402 } 403 iExtensions.clear(); 404 String extensionClassNames = getProperties().getProperty("Extensions.Classes", null); 405 if (extensionClassNames != null) { 406 StringTokenizer extensionClassNameTokenizer = new StringTokenizer(extensionClassNames, ";"); 407 while (extensionClassNameTokenizer.hasMoreTokens()) { 408 String extensionClassName = extensionClassNameTokenizer.nextToken().trim(); 409 if (extensionClassName.isEmpty()) continue; 410 sLogger.info("Using " + extensionClassName); 411 Class<?> extensionClass = Class.forName(extensionClassName); 412 Constructor<?> extensionConstructor = extensionClass.getConstructor(new Class[] { Solver.class, 413 DataProperties.class }); 414 addExtension((Extension<V, T>) extensionConstructor.newInstance(new Object[] { this, 415 getProperties() })); 416 } 417 } 418 } catch (Exception e) { 419 sLogger.error("Unable to autoconfigure solver.", e); 420 } 421 } 422 423 /** Clears best solution */ 424 public void clearBest() { 425 if (iCurrentSolution != null) 426 iCurrentSolution.clearBest(); 427 } 428 429 /** Sets initial solution 430 * @param solution initial solution 431 **/ 432 public void setInitalSolution(Solution<V, T> solution) { 433 iCurrentSolution = solution; 434 iLastSolution = null; 435 } 436 437 /** Sets initial solution 438 * @param model problem model 439 **/ 440 public void setInitalSolution(Model<V, T> model) { 441 setInitalSolution(new Solution<V, T>(model, new DefaultSingleAssignment<V, T>(), 0, 0)); 442 } 443 444 /** Starts solver */ 445 public void start() { 446 iSolverThread = new SolverThread(); 447 iSolverThread.setPriority(THREAD_PRIORITY); 448 iSolverThread.start(); 449 } 450 451 /** Returns solver's thread 452 * @return solver's thread 453 **/ 454 public Thread getSolverThread() { 455 return iSolverThread; 456 } 457 458 /** Initialization */ 459 public void init() { 460 } 461 462 /** True, when solver should update progress (see {@link Progress}) 463 * @return true if the solver should update process 464 **/ 465 protected boolean isUpdateProgress() { 466 return iUpdateProgress; 467 } 468 469 /** True, when solver should update progress (see {@link Progress}) 470 * @param updateProgress true if the solver should update process (default is true) 471 **/ 472 public void setUpdateProgress(boolean updateProgress) { 473 iUpdateProgress = updateProgress; 474 } 475 476 /** Last solution (when solver finishes) 477 * @return last solution 478 **/ 479 public Solution<V, T> lastSolution() { 480 return (iLastSolution == null ? iCurrentSolution : iLastSolution); 481 } 482 483 /** Current solution (during the search) 484 * @return current solution 485 **/ 486 public Solution<V, T> currentSolution() { 487 return iCurrentSolution; 488 } 489 490 public void initSolver() { 491 long seed = getProperties().getPropertyLong("General.Seed", System.currentTimeMillis()); 492 ToolBox.setSeed(seed); 493 494 iSaveBestUnassigned = getProperties().getPropertyInt("General.SaveBestUnassigned", 0); 495 496 clearBest(); 497 if (iProperties.getPropertyBoolean("Solver.AutoConfigure", true)) { 498 autoConfigure(); 499 } 500 501 // register extensions 502 for (Extension<V, T> extension : iExtensions) { 503 extension.register(iCurrentSolution.getModel()); 504 } 505 506 // register solution 507 iCurrentSolution.init(Solver.this); 508 509 // register and intialize neighbour selection 510 getNeighbourSelection().init(Solver.this); 511 512 // register and intialize perturbations counter 513 if (getPerturbationsCounter() != null) 514 getPerturbationsCounter().init(Solver.this); 515 516 // save initial configuration 517 if (iProperties.getPropertyBoolean("General.SaveConfiguration", false)) { 518 FileOutputStream f = null; 519 try { 520 f = new FileOutputStream(iProperties.getProperty("General.Output") + File.separator 521 + iProperties.getProperty("General.ProblemName", "ifs") + ".properties"); 522 iProperties.store(f, iProperties.getProperty("General.ProblemNameLong", "Iterative Forward Search") 523 + " -- configuration file"); 524 f.flush(); 525 f.close(); 526 f = null; 527 } catch (Exception e) { 528 sLogger.error("Unable to store configuration file :-(", e); 529 } finally { 530 try { 531 if (f != null) 532 f.close(); 533 } catch (IOException e) { 534 } 535 } 536 } 537 } 538 539 /** Stop running solver */ 540 public void stopSolver() { 541 stopSolver(true); 542 } 543 544 /** Stop running solver 545 * @param join wait for the solver thread to finish 546 **/ 547 public void stopSolver(boolean join) { 548 if (getSolverThread() != null) { 549 iStop = true; 550 if (join) { 551 try { 552 getSolverThread().join(); 553 } catch (InterruptedException ex) { 554 } 555 } 556 } 557 } 558 559 /** True, if the solver is running 560 * @return true if the solver is running 561 **/ 562 public boolean isRunning() { 563 return (getSolverThread() != null); 564 } 565 566 /** Called when the solver is stopped */ 567 protected void onStop() { 568 } 569 570 /** Called when the solver is started */ 571 protected void onStart() { 572 } 573 574 /** Called when the solver is finished */ 575 protected void onFinish() { 576 } 577 578 /** Called when the solver fails */ 579 protected void onFailure() { 580 } 581 582 /** Called in each iteration, after a neighbour is assigned 583 * @param startTime solver start time in seconds 584 * @param solution current solution 585 **/ 586 protected void onAssigned(double startTime, Solution<V, T> solution) { 587 } 588 589 /** 590 * Returns true if the solver works only with one solution (regardless the number of threads it is using) 591 * @return true 592 */ 593 public boolean hasSingleSolution() { 594 return currentSolution().getAssignment() instanceof DefaultSingleAssignment; 595 } 596 597 /** Solver thread */ 598 protected class SolverThread extends Thread { 599 600 /** Solving rutine */ 601 @Override 602 public void run() { 603 try { 604 iStop = false; 605 // Sets thread name 606 setName("Solver"); 607 608 // Initialization 609 iProgress = Progress.getInstance(iCurrentSolution.getModel()); 610 iProgress.setStatus("Solving problem ..."); 611 iProgress.setPhase("Initializing solver"); 612 initSolver(); 613 onStart(); 614 615 double startTime = JProf.currentTimeSec(); 616 int timeout = getProperties().getPropertyInt("Termination.TimeOut", 1800); 617 if (isUpdateProgress()) { 618 if (iCurrentSolution.getBestInfo() == null) { 619 iProgress.setPhase("Searching for initial solution ...", iCurrentSolution.getModel() 620 .variables().size()); 621 } else { 622 iProgress.setPhase("Improving found solution ..."); 623 } 624 } 625 sLogger.info("Initial solution:" + ToolBox.dict2string(iCurrentSolution.getExtendedInfo(), 1)); 626 if ((iSaveBestUnassigned < 0 || iSaveBestUnassigned >= iCurrentSolution.getAssignment().nrUnassignedVariables(iCurrentSolution.getModel())) 627 && (iCurrentSolution.getBestInfo() == null || getSolutionComparator().isBetterThanBestSolution(iCurrentSolution))) { 628 if (iCurrentSolution.getModel().variables().size() == iCurrentSolution.getAssignment().nrAssignedVariables()) 629 sLogger.info("Complete solution " + ToolBox.dict2string(iCurrentSolution.getExtendedInfo(), 1) + " was found."); 630 iCurrentSolution.saveBest(); 631 } 632 633 if (iCurrentSolution.getModel().variables().isEmpty()) { 634 iProgress.error("Nothing to solve."); 635 iStop = true; 636 } 637 638 // Iterations: until solver can continue 639 while (!iStop && getTerminationCondition().canContinue(iCurrentSolution)) { 640 // Neighbour selection 641 Neighbour<V, T> neighbour = getNeighbourSelection().selectNeighbour(iCurrentSolution); 642 for (SolverListener<V, T> listener : iSolverListeners) { 643 if (!listener.neighbourSelected(iCurrentSolution.getAssignment(), iCurrentSolution.getIteration(), neighbour)) { 644 neighbour = null; 645 continue; 646 } 647 } 648 if (neighbour == null) { 649 sLogger.debug("No neighbour selected."); 650 // still update the solution (increase iteration etc.) 651 iCurrentSolution.update(JProf.currentTimeSec() - startTime, false); 652 continue; 653 } 654 655 // Assign selected value to the selected variable 656 Lock lock = iCurrentSolution.getLock().writeLock(); 657 lock.lock(); 658 try { 659 neighbour.assign(iCurrentSolution.getAssignment(), iCurrentSolution.getIteration()); 660 } finally { 661 lock.unlock(); 662 } 663 double time = JProf.currentTimeSec() - startTime; 664 iCurrentSolution.update(time); 665 666 onAssigned(startTime, iCurrentSolution); 667 668 // Check if the solution is the best ever found one 669 if ((iSaveBestUnassigned < 0 || iSaveBestUnassigned >= iCurrentSolution.getAssignment().nrUnassignedVariables(iCurrentSolution.getModel())) && (iCurrentSolution.getBestInfo() == null || getSolutionComparator().isBetterThanBestSolution(iCurrentSolution))) { 670 if (iCurrentSolution.getModel().variables().size() == iCurrentSolution.getAssignment().nrAssignedVariables()) { 671 iProgress.debug("Complete solution of value " + iCurrentSolution.getModel().getTotalValue(iCurrentSolution.getAssignment()) + " was found."); 672 } 673 iCurrentSolution.saveBest(); 674 } 675 676 // Increment progress bar 677 if (isUpdateProgress()) { 678 if (iCurrentSolution.getBestInfo() != null && iCurrentSolution.getModel().getBestUnassignedVariables() == 0) { 679 if (!"Improving found solution ...".equals(iProgress.getPhase())) 680 iProgress.setPhase("Improving found solution ..."); 681 iProgress.setProgress(Math.min(100, (int)Math.round(100 * time / timeout))); 682 } else if ((iCurrentSolution.getBestInfo() == null || iCurrentSolution.getModel().getBestUnassignedVariables() > 0) && (iCurrentSolution.getAssignment().nrAssignedVariables() > iProgress.getProgress())) { 683 iProgress.setProgress(iCurrentSolution.getAssignment().nrAssignedVariables()); 684 } 685 } 686 687 } 688 689 // Finalization 690 iLastSolution = iCurrentSolution; 691 692 iProgress.setPhase("Done", 1); 693 iProgress.incProgress(); 694 695 if (iStop) { 696 sLogger.debug("Solver stopped."); 697 iProgress.setStatus("Solver stopped."); 698 onStop(); 699 } else { 700 sLogger.debug("Solver done."); 701 iProgress.setStatus("Solver done."); 702 onFinish(); 703 } 704 } catch (Exception ex) { 705 sLogger.error(ex.getMessage(), ex); 706 iProgress.fatal("Solver failed, reason:" + ex.getMessage(), ex); 707 iProgress.setStatus("Solver failed."); 708 onFailure(); 709 } finally { 710 iSolverThread = null; 711 } 712 } 713 } 714 715 /** Return true if {@link Solver#stopSolver()} was called 716 * @return true if the solver is to be stopped 717 **/ 718 public boolean isStop() { 719 return iStop; 720 } 721 722}