001package org.cpsolver.ifs.model; 002 003import java.util.ArrayList; 004import java.util.Collection; 005import java.util.Collections; 006import java.util.Comparator; 007import java.util.HashSet; 008import java.util.HashMap; 009import java.util.List; 010import java.util.Locale; 011import java.util.Map; 012import java.util.Set; 013import java.util.TreeSet; 014import java.util.concurrent.locks.ReentrantReadWriteLock; 015 016import org.cpsolver.coursett.criteria.TimetablingCriterion; 017import org.cpsolver.ifs.assignment.Assignment; 018import org.cpsolver.ifs.assignment.DefaultInheritedAssignment; 019import org.cpsolver.ifs.assignment.DefaultSingleAssignment; 020import org.cpsolver.ifs.assignment.EmptyAssignment; 021import org.cpsolver.ifs.assignment.InheritedAssignment; 022import org.cpsolver.ifs.assignment.context.AssignmentContext; 023import org.cpsolver.ifs.assignment.context.AssignmentContextReference; 024import org.cpsolver.ifs.assignment.context.HasAssignmentContext; 025import org.cpsolver.ifs.criteria.Criterion; 026import org.cpsolver.ifs.solution.Solution; 027import org.cpsolver.ifs.solver.Solver; 028import org.cpsolver.ifs.util.ToolBox; 029 030 031/** 032 * Generic model (definition of a problem). <br> 033 * <br> 034 * It consists of variables and constraints. It has also capability of 035 * memorizing the current and the best ever found assignment. <br> 036 * <br> 037 * Example usage:<br> 038 * <pre> 039 * <code> 040 * MyModel model = new MyModel(); 041 * Variable a = new MyVariable("A"); 042 * model.addVariable(a); 043 * Variable b = new MyVariable("B"); 044 * model.addVariable(b); 045 * Variable c = new MyVariable("C"); 046 * model.addVariable(c); 047 * Constraint constr = MyConstraint("all-different"); 048 * model.addConstraint(constr); 049 * constr.addVariable(a); 050 * constr.addVariable(b); 051 * constr.addVariable(c); 052 * solver.setInitialSolution(model); 053 * </code> 054 * </pre> 055 * 056 * @see Variable 057 * @see Constraint 058 * @see org.cpsolver.ifs.solution.Solution 059 * @see org.cpsolver.ifs.solver.Solver 060 * 061 * @author Tomáš Müller 062 * @version IFS 1.3 (Iterative Forward Search)<br> 063 * Copyright (C) 2006 - 2014 Tomáš Müller<br> 064 * <a href="mailto:muller@unitime.org">muller@unitime.org</a><br> 065 * <a href="http://muller.unitime.org">http://muller.unitime.org</a><br> 066 * <br> 067 * This library is free software; you can redistribute it and/or modify 068 * it under the terms of the GNU Lesser General Public License as 069 * published by the Free Software Foundation; either version 3 of the 070 * License, or (at your option) any later version. <br> 071 * <br> 072 * This library is distributed in the hope that it will be useful, but 073 * WITHOUT ANY WARRANTY; without even the implied warranty of 074 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 075 * Lesser General Public License for more details. <br> 076 * <br> 077 * You should have received a copy of the GNU Lesser General Public 078 * License along with this library; if not see 079 * <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>. 080 * 081 * @param <V> Variable 082 * @param <T> Value 083 */ 084public class Model<V extends Variable<V, T>, T extends Value<V, T>> { 085 private static org.apache.logging.log4j.Logger sLogger = org.apache.logging.log4j.LogManager.getLogger(Model.class); 086 protected static java.text.DecimalFormat sTimeFormat = new java.text.DecimalFormat("0.00", 087 new java.text.DecimalFormatSymbols(Locale.US)); 088 protected static java.text.DecimalFormat sDoubleFormat = new java.text.DecimalFormat("0.00", 089 new java.text.DecimalFormatSymbols(Locale.US)); 090 protected static java.text.DecimalFormat sPercentageFormat = new java.text.DecimalFormat("0.00", 091 new java.text.DecimalFormatSymbols(Locale.US)); 092 093 private List<V> iVariables = new ArrayList<V>(); 094 private List<Constraint<V, T>> iConstraints = new ArrayList<Constraint<V, T>>(); 095 private List<GlobalConstraint<V, T>> iGlobalConstraints = new ArrayList<GlobalConstraint<V, T>>(); 096 private Collection<V> iVariablesWithInitialValueCache = null; 097 private final ReentrantReadWriteLock iVariablesWithInitialValueLock = new ReentrantReadWriteLock(); 098 099 private List<ModelListener<V, T>> iModelListeners = new ArrayList<ModelListener<V, T>>(); 100 private List<InfoProvider<V, T>> iInfoProviders = new ArrayList<InfoProvider<V, T>>(); 101 private HashMap<String, Criterion<V, T>> iCriteria = new HashMap<String, Criterion<V,T>>(); 102 103 private int iBestUnassignedVariables = -1; 104 private int iBestPerturbations = 0; 105 private double iBestValue = 0.0; 106 private int iNextReferenceId = 0; 107 private int iNextVariableIndex = 0; 108 @Deprecated 109 private Assignment<V, T> iAssignment = null; 110 private Assignment<V, T> iEmptyAssignment = null; 111 private Map<Integer, AssignmentContextReference<V, T, ? extends AssignmentContext>> iAssignmentContextReferences = new HashMap<Integer, AssignmentContextReference<V, T, ? extends AssignmentContext>>(); 112 113 /** Constructor */ 114 public Model() { 115 } 116 117 /** The list of variables in the model 118 * @return list of variables in the model 119 **/ 120 public List<V> variables() { 121 return iVariables; 122 } 123 124 /** The number of variables in the model 125 * @return number of variables in the model 126 **/ 127 public int countVariables() { 128 return iVariables.size(); 129 } 130 131 /** Adds a variable to the model 132 * @param variable a variable 133 **/ 134 @SuppressWarnings("unchecked") 135 public void addVariable(V variable) { 136 variable.setModel(this); 137 variable.setIndex(iNextVariableIndex++); 138 iVariables.add(variable); 139 if (variable instanceof InfoProvider<?, ?>) 140 iInfoProviders.add((InfoProvider<V, T>) variable); 141 for (ModelListener<V, T> listener : iModelListeners) 142 listener.variableAdded(variable); 143 invalidateVariablesWithInitialValueCache(); 144 } 145 146 /** Removes a variable from the model 147 * @param variable a variable 148 **/ 149 @SuppressWarnings("unchecked") 150 public void removeVariable(V variable) { 151 variable.setModel(null); 152 iVariables.remove(variable); 153 if (variable instanceof InfoProvider<?, ?>) 154 iInfoProviders.remove((InfoProvider<?, ?>)variable); 155 for (ModelListener<V, T> listener : iModelListeners) 156 listener.variableRemoved(variable); 157 invalidateVariablesWithInitialValueCache(); 158 if (variable instanceof HasAssignmentContext) 159 removeReference((HasAssignmentContext<V, T, ?>)variable); 160 } 161 162 /** The list of constraints in the model 163 * @return list of constraints in the model 164 **/ 165 public List<Constraint<V, T>> constraints() { 166 return iConstraints; 167 } 168 169 /** The number of constraints in the model 170 * @return number of constraints in the model 171 **/ 172 public int countConstraints() { 173 return iConstraints.size(); 174 } 175 176 /** Adds a constraint to the model 177 * @param constraint a constraint 178 **/ 179 @SuppressWarnings("unchecked") 180 public void addConstraint(Constraint<V, T> constraint) { 181 constraint.setModel(this); 182 iConstraints.add(constraint); 183 if (constraint instanceof InfoProvider<?, ?>) 184 iInfoProviders.add((InfoProvider<V, T>) constraint); 185 for (ModelListener<V, T> listener : iModelListeners) 186 listener.constraintAdded(constraint); 187 } 188 189 /** Removes a constraint from the model 190 * @param constraint a constraint 191 **/ 192 @SuppressWarnings("unchecked") 193 public void removeConstraint(Constraint<V, T> constraint) { 194 constraint.setModel(null); 195 iConstraints.remove(constraint); 196 if (constraint instanceof InfoProvider<?, ?>) 197 iInfoProviders.remove((InfoProvider<?, ?>)constraint); 198 for (ModelListener<V, T> listener : iModelListeners) 199 listener.constraintRemoved(constraint); 200 if (constraint instanceof HasAssignmentContext) 201 removeReference((HasAssignmentContext<V, T, ?>)constraint); 202 } 203 204 /** The list of global constraints in the model 205 * @return list of global constraints in the model 206 **/ 207 public List<GlobalConstraint<V, T>> globalConstraints() { 208 return iGlobalConstraints; 209 } 210 211 /** The number of global constraints in the model 212 * @return number of global constraints in the model 213 **/ 214 public int countGlobalConstraints() { 215 return iGlobalConstraints.size(); 216 } 217 218 /** Adds a global constraint to the model 219 * @param constraint a global constraint 220 **/ 221 @SuppressWarnings("unchecked") 222 public void addGlobalConstraint(GlobalConstraint<V, T> constraint) { 223 constraint.setModel(this); 224 iGlobalConstraints.add(constraint); 225 if (constraint instanceof InfoProvider<?, ?>) 226 iInfoProviders.add((InfoProvider<V, T>) constraint); 227 for (ModelListener<V, T> listener : iModelListeners) 228 listener.constraintAdded(constraint); 229 if (constraint instanceof ModelListener<?, ?>) 230 iModelListeners.add((ModelListener<V, T>) constraint); 231 } 232 233 /** Removes a global constraint from the model 234 * @param constraint a global constraint 235 **/ 236 @SuppressWarnings("unchecked") 237 public void removeGlobalConstraint(GlobalConstraint<V, T> constraint) { 238 constraint.setModel(null); 239 iGlobalConstraints.remove(constraint); 240 if (constraint instanceof InfoProvider<?, ?>) 241 iInfoProviders.remove((InfoProvider<?, ?>) constraint); 242 if (constraint instanceof ModelListener<?, ?>) 243 iModelListeners.remove((ModelListener<V, T>) constraint); 244 for (ModelListener<V, T> listener : iModelListeners) 245 listener.constraintRemoved(constraint); 246 if (constraint instanceof HasAssignmentContext) 247 removeReference((HasAssignmentContext<V, T, ?>)constraint); 248 } 249 250 /** 251 * The list of unassigned variables in the model. 252 * Use {@link Model#unassignedVariables(Assignment)} or {@link Assignment#unassignedVariables(Model)} instead. 253 * @return list of unassigned variables in the model 254 **/ 255 @Deprecated 256 public Collection<V> unassignedVariables() { 257 return unassignedVariables(getDefaultAssignment()); 258 } 259 260 /** The list of unassigned variables in the model 261 * @param assignment current assignment 262 * @return list of unassigned variables in the model 263 **/ 264 public Collection<V> unassignedVariables(Assignment<V, T> assignment) { 265 return assignment.unassignedVariables(this); 266 } 267 268 /** 269 * Number of unassigned variables. 270 * Use {@link Model#nrUnassignedVariables(Assignment)} or {@link Assignment#nrUnassignedVariables(Model)} instead. 271 * @return number of unassigned variables in the model 272 **/ 273 @Deprecated 274 public int nrUnassignedVariables() { 275 return nrUnassignedVariables(getDefaultAssignment()); 276 } 277 278 /** Number of unassigned variables 279 * @param assignment current assignment 280 * @return number of unassigned variables in the model 281 **/ 282 public int nrUnassignedVariables(Assignment<V, T> assignment) { 283 return assignment.nrUnassignedVariables(this); 284 } 285 286 /** 287 * The list of assigned variables in the model. 288 * Use {@link Model#assignedVariables(Assignment)} or {@link Assignment#assignedVariables()} instead. 289 * @return list of assigned variables in the model 290 **/ 291 @Deprecated 292 public Collection<V> assignedVariables() { 293 return assignedVariables(getDefaultAssignment()); 294 } 295 296 /** The list of assigned variables in the model 297 * @param assignment current assignment 298 * @return list of assigned variables in the model 299 **/ 300 public Collection<V> assignedVariables(Assignment<V, T> assignment) { 301 return assignment.assignedVariables(); 302 } 303 304 /** 305 * Number of assigned variables. 306 * Use {@link Model#nrAssignedVariables(Assignment)} or {@link Assignment#nrAssignedVariables()} instead. 307 * @return number of assigned variables in the model 308 **/ 309 @Deprecated 310 public int nrAssignedVariables() { 311 return nrAssignedVariables(getDefaultAssignment()); 312 } 313 314 /** Number of assigned variables 315 * @param assignment current assignment 316 * @return number of assigned variables in the model 317 **/ 318 public int nrAssignedVariables(Assignment<V, T> assignment) { 319 return assignment.nrAssignedVariables(); 320 } 321 322 /** 323 * The list of perturbation variables in the model, i.e., the variables 324 * which has an initial value but which are not assigned with this value. 325 * Use {@link Model#perturbVariables(Assignment)} instead. 326 * @return list of perturbation variables in the model 327 */ 328 @Deprecated 329 public Collection<V> perturbVariables() { 330 return perturbVariables(getDefaultAssignment(), variablesWithInitialValue()); 331 } 332 333 /** 334 * The list of perturbation variables in the model, i.e., the variables 335 * which has an initial value but which are not assigned with this value. 336 * @param assignment current assignment 337 * @return list of perturbation variables in the model 338 */ 339 public Collection<V> perturbVariables(Assignment<V, T> assignment) { 340 return perturbVariables(assignment, variablesWithInitialValue()); 341 } 342 343 /** 344 * The list of perturbation variables in the model, i.e., the variables 345 * which has an initial value but which are not assigned with this value. 346 * Only variables from the given set are considered. 347 * Use {@link Model#perturbVariables(Assignment, Collection)} instead. 348 * @param variables sub-problem 349 * @return list of perturbation variables in the sub-problem 350 */ 351 @Deprecated 352 public List<V> perturbVariables(Collection<V> variables) { 353 return perturbVariables(getDefaultAssignment(), variables); 354 } 355 356 /** 357 * The list of perturbation variables in the model, i.e., the variables 358 * which has an initial value but which are not assigned with this value. 359 * Only variables from the given set are considered. 360 * @param assignment current assignment 361 * @param variables sub-problem 362 * @return list of perturbation variables in the sub-problem 363 */ 364 public List<V> perturbVariables(Assignment<V, T> assignment, Collection<V> variables) { 365 return perturbVariables(assignment, variables, true); 366 } 367 368 /** 369 * The list of perturbation variables in the model, i.e., the variables 370 * which has an initial value but which are not assigned with this value. 371 * Only variables from the given set are considered. 372 * @param assignment current assignment 373 * @param variables sub-problem 374 * @param includeNotAssigned when true, include not assigned variables with a hard conflict (that cannot be assigned) 375 * @return list of perturbation variables in the sub-problem 376 */ 377 public List<V> perturbVariables(Assignment<V, T> assignment, Collection<V> variables, boolean includeNotAssigned) { 378 List<V> perturbances = new ArrayList<V>(); 379 for (V variable : variables) { 380 if (variable.getInitialAssignment() == null) 381 continue; 382 T value = assignment.getValue(variable); 383 if (value != null) { 384 if (!variable.getInitialAssignment().equals(value)) 385 perturbances.add(variable); 386 } else if (includeNotAssigned) { 387 boolean hasPerturbance = false; 388 for (Constraint<V, T> constraint : variable.hardConstraints()) { 389 if (constraint.inConflict(assignment, variable.getInitialAssignment())) { 390 hasPerturbance = true; 391 break; 392 } 393 } 394 if (!hasPerturbance) 395 for (GlobalConstraint<V, T> constraint : globalConstraints()) { 396 if (constraint.inConflict(assignment, variable.getInitialAssignment())) { 397 hasPerturbance = true; 398 break; 399 } 400 } 401 if (hasPerturbance) 402 perturbances.add(variable); 403 } 404 } 405 return perturbances; 406 } 407 408 /** 409 * Returns the set of conflicting variables with this value, if it is 410 * assigned to its variable 411 * Use {@link Model#conflictValues(Assignment, Value)} instead. 412 * @param value a value to be assigned 413 * @return a set of conflicting values, i.e., values that would have to be unassigned if the given value is assigned to its variable 414 */ 415 @Deprecated 416 public Set<T> conflictValues(T value) { 417 return conflictValues(getDefaultAssignment(), value); 418 } 419 420 /** 421 * Returns the set of conflicting variables with this value, if it is 422 * assigned to its variable 423 * @param assignment current assignment 424 * @param value a value to be assigned 425 * @return a set of conflicting values, i.e., values that would have to be unassigned if the given value is assigned to its variable 426 */ 427 public Set<T> conflictValues(Assignment<V, T> assignment, T value) { 428 Set<T> conflictValues = new HashSet<T>(); 429 for (Constraint<V, T> constraint : value.variable().hardConstraints()) 430 constraint.computeConflicts(assignment, value, conflictValues); 431 for (GlobalConstraint<V, T> constraint : globalConstraints()) 432 constraint.computeConflicts(assignment, value, conflictValues); 433 return conflictValues; 434 } 435 436 /** 437 * Return true if the given value is in conflict with a hard constraint 438 * Use {@link Model#inConflict(Assignment, Value)} instead. 439 * @param value a value in question 440 * @return true if there is a conflict, i.e., there is at least one value that would have to be unassigned if the given value is assigned to its variable 441 **/ 442 @Deprecated 443 public boolean inConflict(T value) { 444 return inConflict(getDefaultAssignment(), value); 445 } 446 447 /** Return true if the given value is in conflict with a hard constraint 448 * @param assignment current assignment 449 * @param value a value in question 450 * @return true if there is a conflict, i.e., there is at least one value that would have to be unassigned if the given value is assigned to its variable 451 **/ 452 public boolean inConflict(Assignment<V, T> assignment, T value) { 453 for (Constraint<V, T> constraint : value.variable().hardConstraints()) 454 if (constraint.inConflict(assignment, value)) 455 return true; 456 for (GlobalConstraint<V, T> constraint : globalConstraints()) 457 if (constraint.inConflict(assignment, value)) 458 return true; 459 return false; 460 } 461 462 /** The list of variables with an initial value (i.e., variables with {@link Variable#getInitialAssignment()} not null) 463 * @return list of variables with an initial value 464 **/ 465 public Collection<V> variablesWithInitialValue() { 466 iVariablesWithInitialValueLock.readLock().lock(); 467 try { 468 if (iVariablesWithInitialValueCache != null) 469 return iVariablesWithInitialValueCache; 470 } finally { 471 iVariablesWithInitialValueLock.readLock().unlock(); 472 } 473 iVariablesWithInitialValueLock.writeLock().lock(); 474 try { 475 if (iVariablesWithInitialValueCache != null) 476 return iVariablesWithInitialValueCache; 477 iVariablesWithInitialValueCache = new ArrayList<V>(); 478 for (V variable : iVariables) { 479 if (variable.getInitialAssignment() != null) 480 iVariablesWithInitialValueCache.add(variable); 481 } 482 return iVariablesWithInitialValueCache; 483 } finally { 484 iVariablesWithInitialValueLock.writeLock().unlock(); 485 } 486 } 487 488 /** Invalidates cache containing all variables that possess an initial value */ 489 protected void invalidateVariablesWithInitialValueCache() { 490 iVariablesWithInitialValueLock.writeLock().lock(); 491 iVariablesWithInitialValueCache = null; 492 iVariablesWithInitialValueLock.writeLock().unlock(); 493 } 494 495 /** Called before a value is assigned to its variable 496 * @param iteration current iteration 497 * @param value a value to be assigned 498 **/ 499 @Deprecated 500 public void beforeAssigned(long iteration, T value) { 501 } 502 503 /** Called before a value is assigned to its variable 504 * @param assignment current assignment 505 * @param iteration current iteration 506 * @param value a value to be assigned 507 **/ 508 public void beforeAssigned(Assignment<V, T> assignment, long iteration, T value) { 509 beforeAssigned(iteration, value); 510 for (ModelListener<V, T> listener : iModelListeners) 511 listener.beforeAssigned(assignment, iteration, value); 512 } 513 514 /** Called before a value is unassigned from its variable 515 * @param iteration current iteration 516 * @param value a value to be unassigned 517 **/ 518 @Deprecated 519 public void beforeUnassigned(long iteration, T value) { 520 } 521 522 /** Called before a value is unassigned from its variable 523 * @param assignment current assignment 524 * @param iteration current iteration 525 * @param value a value to be unassigned 526 **/ 527 public void beforeUnassigned(Assignment<V, T> assignment, long iteration, T value) { 528 beforeUnassigned(iteration, value); 529 for (ModelListener<V, T> listener : iModelListeners) 530 listener.beforeUnassigned(assignment, iteration, value); 531 } 532 533 /** Called after a value is assigned to its variable 534 * @param iteration current iteration 535 * @param value a value that was assigned 536 **/ 537 @Deprecated 538 public void afterAssigned(long iteration, T value) { 539 } 540 541 /** Called after a value is assigned to its variable 542 * @param assignment current assignment 543 * @param iteration current iteration 544 * @param value a value that was assigned 545 **/ 546 public void afterAssigned(Assignment<V, T> assignment, long iteration, T value) { 547 afterAssigned(iteration, value); 548 for (ModelListener<V, T> listener : iModelListeners) 549 listener.afterAssigned(assignment, iteration, value); 550 } 551 552 /** Called after a value is unassigned from its variable 553 * @param iteration current iteration 554 * @param value a value that was unassigned 555 **/ 556 @Deprecated 557 public void afterUnassigned(long iteration, T value) { 558 } 559 560 /** Called after a value is unassigned from its variable 561 * @param assignment current assignment 562 * @param iteration current iteration 563 * @param value a value that was unassigned 564 **/ 565 public void afterUnassigned(Assignment<V, T> assignment, long iteration, T value) { 566 afterUnassigned(iteration, value); 567 for (ModelListener<V, T> listener : iModelListeners) 568 listener.afterUnassigned(assignment, iteration, value); 569 } 570 571 @Override 572 public String toString() { 573 return "Model{\n variables=" + ToolBox.col2string(variables(), 2) + ",\n constraints=" + ToolBox.col2string(constraints(), 2) + ",\n }"; 574 } 575 576 /** 577 * String representation -- returns a list of values of objective criteria 578 * @param assignment current assignment 579 * @return comma separated string of {@link TimetablingCriterion#toString(Assignment)} 580 */ 581 public String toString(Assignment<V, T> assignment) { 582 List<Criterion<V, T>> criteria = new ArrayList<Criterion<V,T>>(getCriteria()); 583 Collections.sort(criteria, new Comparator<Criterion<V, T>>() { 584 @Override 585 public int compare(Criterion<V, T> c1, Criterion<V, T> c2) { 586 int cmp = -Double.compare(c1.getWeight(), c2.getWeight()); 587 if (cmp != 0) return cmp; 588 return c1.getName().compareTo(c2.getName()); 589 } 590 }); 591 String ret = ""; 592 for (Criterion<V, T> criterion: criteria) { 593 String val = criterion.toString(assignment); 594 if (val != null && !val.isEmpty()) 595 ret += ", " + val; 596 } 597 return (nrUnassignedVariables(assignment) == 0 ? "" : "V:" + nrAssignedVariables(assignment) + "/" + variables().size() + ", ") + "T:" + sDoubleFormat.format(getTotalValue(assignment)) + ret; 598 } 599 600 protected String getPerc(double value, double min, double max) { 601 if (max == min) 602 return sPercentageFormat.format(100.0); 603 return sPercentageFormat.format(100.0 - 100.0 * (value - min) / (max - min)); 604 } 605 606 protected String getPercRev(double value, double min, double max) { 607 if (max == min) 608 return sPercentageFormat.format(0.0); 609 return sPercentageFormat.format(100.0 * (value - min) / (max - min)); 610 } 611 612 /** 613 * Returns information about the current solution. Information from all 614 * model listeners and constraints is also included. 615 * Use {@link Model#getInfo(Assignment)} instead. 616 * @return info table 617 */ 618 @Deprecated 619 public Map<String, String> getInfo() { 620 return getInfo(getDefaultAssignment()); 621 } 622 623 /** 624 * Returns information about the current solution. Information from all 625 * model listeners and constraints is also included. 626 * @param assignment current assignment 627 * @return info table 628 */ 629 public Map<String, String> getInfo(Assignment<V, T> assignment) { 630 Map<String, String> ret = new HashMap<String, String>(); 631 ret.put("Assigned variables", getPercRev(assignment.nrAssignedVariables(), 0, variables().size()) + "% (" + assignment.nrAssignedVariables() + "/" + variables().size() + ")"); 632 Collection<V> varsWithInitialValue = variablesWithInitialValue(); 633 int nrVarsWithInitialValue = varsWithInitialValue.size(); 634 if (nrVarsWithInitialValue > 0) { 635 Collection<V> pv = perturbVariables(assignment, varsWithInitialValue, false); 636 ret.put("Perturbation variables", getPercRev(pv.size(), 0, nrVarsWithInitialValue) + "% (" + pv.size() + " + " + (variables().size() - nrVarsWithInitialValue) + ")"); 637 } 638 ret.put("Overall solution value", sDoubleFormat.format(getTotalValue(assignment))); 639 for (InfoProvider<V, T> provider : iInfoProviders) 640 provider.getInfo(assignment, ret); 641 return ret; 642 } 643 644 /** 645 * Extended information about current solution. Similar to 646 * {@link Model#getInfo(Assignment)}, but some more information (that is more 647 * expensive to compute) might be added. 648 * Use {@link Model#getExtendedInfo(Assignment)} instead. 649 * @return extended info table 650 */ 651 @Deprecated 652 public Map<String, String> getExtendedInfo() { 653 return getExtendedInfo(getDefaultAssignment()); 654 } 655 656 /** 657 * Extended information about current solution. Similar to 658 * {@link Model#getInfo(Assignment)}, but some more information (that is more 659 * expensive to compute) might be added. 660 * @param assignment current assignment 661 * @return extended info table 662 */ 663 public Map<String, String> getExtendedInfo(Assignment<V, T> assignment) { 664 Map<String, String> ret = getInfo(assignment); 665 for (InfoProvider<V, T> provider : iInfoProviders) 666 if (provider instanceof ExtendedInfoProvider) 667 ((ExtendedInfoProvider<V, T>)provider).getExtendedInfo(assignment, ret); 668 return ret; 669 } 670 671 /** 672 * Returns information about the current solution. Information from all 673 * model listeners and constraints is also included. Only variables from the 674 * given set are considered. 675 * Use {@link Model#getInfo(Assignment, Collection)} instead. 676 * @param variables sub-problem 677 * @return info table 678 **/ 679 @Deprecated 680 public Map<String, String> getInfo(Collection<V> variables) { 681 return getInfo(getDefaultAssignment(), variables); 682 } 683 684 /** 685 * Returns information about the current solution. Information from all 686 * model listeners and constraints is also included. Only variables from the 687 * given set are considered. 688 * @param assignment current assignment 689 * @param variables sub-problem 690 * @return info table 691 */ 692 public Map<String, String> getInfo(Assignment<V, T> assignment, Collection<V> variables) { 693 Map<String, String> ret = new HashMap<String, String>(); 694 int assigned = 0, perturb = 0, nrVarsWithInitialValue = 0; 695 for (V variable : variables) { 696 T value = assignment.getValue(variable); 697 if (value != null) 698 assigned++; 699 if (variable.getInitialAssignment() != null) { 700 nrVarsWithInitialValue++; 701 if (value != null) { 702 if (!variable.getInitialAssignment().equals(value)) 703 perturb++; 704 } else { 705 boolean hasPerturbance = false; 706 for (Constraint<V, T> constraint : variable.hardConstraints()) { 707 if (constraint.inConflict(assignment, variable.getInitialAssignment())) { 708 hasPerturbance = true; 709 break; 710 } 711 } 712 if (!hasPerturbance) 713 for (GlobalConstraint<V, T> constraint : globalConstraints()) { 714 if (constraint.inConflict(assignment, variable.getInitialAssignment())) { 715 hasPerturbance = true; 716 break; 717 } 718 } 719 if (hasPerturbance) 720 perturb++; 721 } 722 } 723 } 724 ret.put("Assigned variables", getPercRev(assigned, 0, variables.size()) + "% (" + assigned + "/" + variables.size() + ")"); 725 if (nrVarsWithInitialValue > 0) { 726 ret.put("Perturbation variables", getPercRev(perturb, 0, nrVarsWithInitialValue) + "% (" + perturb + " + " + (variables.size() - nrVarsWithInitialValue) + ")"); 727 } 728 ret.put("Overall solution value", sDoubleFormat.format(getTotalValue(assignment, variables))); 729 for (InfoProvider<V, T> provider : iInfoProviders) 730 provider.getInfo(assignment, ret, variables); 731 return ret; 732 } 733 734 /** 735 * Returns the number of unassigned variables in the best ever found 736 * solution 737 * @return number of unassigned variables in the best solution 738 */ 739 public int getBestUnassignedVariables() { 740 return iBestUnassignedVariables; 741 } 742 743 /** 744 * Returns the number of perturbation variables in the best ever found 745 * solution 746 * @return number of perturbation variables in the best solution 747 */ 748 public int getBestPerturbations() { 749 return iBestPerturbations; 750 } 751 752 /** 753 * Total value of the best ever found solution -- sum of all assigned values 754 * (see {@link Value#toDouble(Assignment)}). 755 * @return value of the best solution 756 */ 757 public double getBestValue() { 758 return iBestValue; 759 } 760 761 /** Set total value of the best ever found solution 762 * @param bestValue value of the best solution 763 **/ 764 public void setBestValue(double bestValue) { 765 iBestValue = bestValue; 766 } 767 768 /** 769 * Save the current assignment as the best ever found assignment 770 * Use {@link Model#saveBest(Assignment)} instead. 771 **/ 772 @Deprecated 773 public void saveBest() { 774 saveBest(getDefaultAssignment()); 775 } 776 777 /** Save the current assignment as the best ever found assignment 778 * @param assignment current assignment 779 **/ 780 public void saveBest(Assignment<V, T> assignment) { 781 iBestUnassignedVariables = iVariables.size() - assignment.nrAssignedVariables(); 782 iBestPerturbations = perturbVariables(assignment).size(); 783 iBestValue = getTotalValue(assignment); 784 for (V variable : iVariables) { 785 variable.setBestAssignment(assignment.getValue(variable), assignment.getIteration(variable)); 786 } 787 for (Criterion<V, T> criterion: getCriteria()) { 788 criterion.bestSaved(assignment); 789 } 790 } 791 792 /** Clear the best ever found assignment */ 793 public void clearBest() { 794 iBestUnassignedVariables = -1; 795 iBestPerturbations = 0; 796 iBestValue = 0; 797 for (V variable : iVariables) { 798 variable.setBestAssignment(null, 0); 799 } 800 } 801 802 /** 803 * Restore the best ever found assignment into the current assignment 804 * Use {@link Model#restoreBest(Assignment)} instead. 805 **/ 806 @Deprecated 807 protected void restoreBest() { 808 restoreBest(getDefaultAssignment()); 809 } 810 811 /** Restore the best ever found assignment into the current assignment 812 * @param assignment current assignment 813 * @param assignmentOrder assignment order of the variables 814 **/ 815 @SuppressWarnings("unchecked") 816 protected void restoreBest(Assignment<V, T> assignment, Comparator<V> assignmentOrder) { 817 TreeSet<V> sortedVariables = new TreeSet<V>(assignmentOrder); 818 for (V variable : iVariables) { 819 T value = assignment.getValue(variable); 820 if (value == null) { 821 if (variable.getBestAssignment() != null) 822 sortedVariables.add(variable); 823 } else if (!value.equals(variable.getBestAssignment())) { 824 assignment.unassign(0, variable); 825 if (variable.getBestAssignment() != null) 826 sortedVariables.add(variable); 827 } 828 } 829 Set<T> problems = new HashSet<T>(); 830 for (V variable : sortedVariables) { 831 Set<T> confs = conflictValues(assignment, variable.getBestAssignment()); 832 if (!confs.isEmpty()) { 833 sLogger.error("restore best problem: assignment " + variable.getName() + " = " + variable.getBestAssignment().getName()); 834 boolean weakened = false; 835 for (Constraint<V, T> c : variable.hardConstraints()) { 836 Set<T> x = new HashSet<T>(); 837 c.computeConflicts(assignment, variable.getBestAssignment(), x); 838 if (!x.isEmpty()) { 839 if (c instanceof WeakeningConstraint) { 840 ((WeakeningConstraint<V, T>)c).weaken(assignment, variable.getBestAssignment()); 841 sLogger.info(" constraint " + c.getClass().getSimpleName() + " " + c.getName() + " had to be weakened"); 842 weakened = true; 843 } else { 844 sLogger.error(" constraint " + c.getClass().getSimpleName() + " " + c.getName() + " causes the following conflicts " + x); 845 } 846 } 847 } 848 for (GlobalConstraint<V, T> c : globalConstraints()) { 849 Set<T> x = new HashSet<T>(); 850 c.computeConflicts(assignment, variable.getBestAssignment(), x); 851 if (!x.isEmpty()) { 852 if (c instanceof WeakeningConstraint) { 853 ((WeakeningConstraint<V, T>)c).weaken(assignment, variable.getBestAssignment()); 854 sLogger.info(" constraint " + c.getClass().getSimpleName() + " " + c.getName() + " had to be weakened"); 855 weakened = true; 856 } else { 857 sLogger.error(" global constraint " + c.getClass().getSimpleName() + " " + c.getName() + " causes the following conflicts " + x); 858 } 859 } 860 } 861 if (weakened && conflictValues(assignment, variable.getBestAssignment()).isEmpty()) 862 assignment.assign(0, variable.getBestAssignment()); 863 else 864 problems.add(variable.getBestAssignment()); 865 } else 866 assignment.assign(0, variable.getBestAssignment()); 867 } 868 int attempt = 0, maxAttempts = 3 * problems.size(); 869 while (!problems.isEmpty() && attempt <= maxAttempts) { 870 attempt++; 871 T value = ToolBox.random(problems); 872 problems.remove(value); 873 V variable = value.variable(); 874 Set<T> confs = conflictValues(assignment, value); 875 if (!confs.isEmpty()) { 876 sLogger.error("restore best problem (again, att=" + attempt + "): assignment " + variable.getName() + " = " + value.getName()); 877 for (Constraint<V, T> c : variable.hardConstraints()) { 878 Set<T> x = new HashSet<T>(); 879 c.computeConflicts(assignment, value, x); 880 if (!x.isEmpty()) 881 sLogger.error(" constraint " + c.getClass().getSimpleName() + " " + c.getName() + " causes the following conflicts " + x); 882 } 883 for (GlobalConstraint<V, T> c : globalConstraints()) { 884 Set<T> x = new HashSet<T>(); 885 c.computeConflicts(assignment, value, x); 886 if (!x.isEmpty()) 887 sLogger.error(" constraint " + c.getClass().getSimpleName() + " " + c.getName() + " causes the following conflicts " + x); 888 } 889 for (T conf : confs) 890 assignment.unassign(0, conf.variable()); 891 problems.addAll(confs); 892 } 893 assignment.assign(0, value); 894 } 895 for (Criterion<V, T> criterion: getCriteria()) { 896 criterion.bestRestored(assignment); 897 } 898 } 899 900 /** Restore the best ever found assignment into the current assignment 901 * @param assignment current assignment 902 **/ 903 public void restoreBest(Assignment<V, T> assignment) { 904 restoreBest(assignment, new Comparator<V>() { 905 @Override 906 public int compare(V v1, V v2) { 907 if (v1.getBestAssignmentIteration() < v2.getBestAssignmentIteration()) return -1; 908 if (v1.getBestAssignmentIteration() > v2.getBestAssignmentIteration()) return 1; 909 return v1.compareTo(v2); 910 } 911 }); 912 } 913 914 /** 915 * The list of unassigned variables in the best ever found solution. 916 * Use {@link Model#bestUnassignedVariables(Assignment)} instead. 917 * @return variables list of unassigned variables in the best solution 918 **/ 919 @Deprecated 920 public Collection<V> bestUnassignedVariables() { 921 return bestUnassignedVariables(getDefaultAssignment()); 922 } 923 924 /** The list of unassigned variables in the best ever found solution 925 * @param assignment current assignment 926 * @return variables list of unassigned variables in the best solution 927 **/ 928 public Collection<V> bestUnassignedVariables(Assignment<V, T> assignment) { 929 Collection<V> ret = new ArrayList<V>(variables().size()); 930 if (iBestUnassignedVariables < 0) { 931 for (V variable : variables()) { 932 if (assignment.getValue(variable) == null) 933 ret.add(variable); 934 } 935 } else { 936 for (V variable : variables()) { 937 if (variable.getBestAssignment() == null) 938 ret.add(variable); 939 } 940 } 941 return ret; 942 } 943 944 /** 945 * Value of the current solution. It is the sum of all assigned values, 946 * i.e., {@link Value#toDouble(Assignment)}. 947 * Use {@link Model#getTotalValue(Assignment)} instead. 948 * @return solution value 949 */ 950 @Deprecated 951 public double getTotalValue() { 952 return getTotalValue(getDefaultAssignment()); 953 } 954 955 /** 956 * Value of the current solution. It is the sum of all assigned values, 957 * i.e., {@link Value#toDouble(Assignment)}. 958 * @param assignment current assignment 959 * @return solution value 960 */ 961 public double getTotalValue(Assignment<V, T> assignment) { 962 double ret = 0.0; 963 if (getCriteria().isEmpty()) 964 for (T t: assignment.assignedValues()) 965 ret += t.toDouble(assignment); 966 else 967 for (Criterion<V, T> c: getCriteria()) 968 ret += c.getWeightedValue(assignment); 969 return ret; 970 } 971 972 /** 973 * Value of the current solution. It is the sum of all assigned values, 974 * i.e., {@link Value#toDouble(Assignment)}. Only variables from the given set are 975 * considered. 976 * Use {@link Model#getTotalValue(Assignment, Collection)} instead. 977 * @param variables sub-problem 978 * @return solution value 979 **/ 980 @Deprecated 981 public double getTotalValue(Collection<V> variables) { 982 return getTotalValue(getDefaultAssignment(), variables); 983 } 984 985 /** 986 * Value of the current solution. It is the sum of all assigned values, 987 * i.e., {@link Value#toDouble(Assignment)}. Only variables from the given set are 988 * considered. 989 * @param assignment current assignment 990 * @param variables sub-problem 991 * @return solution value 992 **/ 993 public double getTotalValue(Assignment<V, T> assignment, Collection<V> variables) { 994 double ret = 0.0; 995 for (V v: variables) { 996 T t = assignment.getValue(v); 997 if (t != null) 998 ret += t.toDouble(assignment); 999 } 1000 return ret; 1001 } 1002 1003 /** Adds a model listener 1004 * @param listener a model listener 1005 **/ 1006 @SuppressWarnings("unchecked") 1007 public void addModelListener(ModelListener<V, T> listener) { 1008 iModelListeners.add(listener); 1009 if (listener instanceof InfoProvider<?, ?>) 1010 iInfoProviders.add((InfoProvider<V, T>) listener); 1011 for (Constraint<V, T> constraint : iConstraints) 1012 listener.constraintAdded(constraint); 1013 for (Constraint<V, T> constraint : iGlobalConstraints) 1014 listener.constraintAdded(constraint); 1015 for (V variable : iVariables) 1016 listener.variableAdded(variable); 1017 } 1018 1019 /** Removes a model listener 1020 * @param listener a model listener 1021 **/ 1022 public void removeModelListener(ModelListener<V, T> listener) { 1023 if (listener instanceof InfoProvider<?, ?>) 1024 iInfoProviders.remove((InfoProvider<?, ?>)listener); 1025 for (V variable : iVariables) 1026 listener.variableRemoved(variable); 1027 for (Constraint<V, T> constraint : iConstraints) 1028 listener.constraintRemoved(constraint); 1029 for (Constraint<V, T> constraint : iGlobalConstraints) 1030 listener.constraintRemoved(constraint); 1031 iModelListeners.remove(listener); 1032 } 1033 1034 /** Model initialization 1035 * @param solver current solver 1036 * @return true if successfully initialized 1037 **/ 1038 public boolean init(Solver<V, T> solver) { 1039 for (ModelListener<V, T> listener : new ArrayList<ModelListener<V, T>>(iModelListeners)) { 1040 if (!listener.init(solver)) 1041 return false; 1042 } 1043 return true; 1044 } 1045 1046 /** The list of model listeners 1047 * @return list of model listeners 1048 **/ 1049 public List<ModelListener<V, T>> getModelListeners() { 1050 return iModelListeners; 1051 } 1052 1053 /** The list of model listeners that are of the given class 1054 * @param type model listener type 1055 * @return list of model listeners that are of the given class 1056 **/ 1057 public ModelListener<V, T> modelListenerOfType(Class<ModelListener<V, T>> type) { 1058 for (ModelListener<V, T> listener : iModelListeners) { 1059 if (listener.getClass() == type) 1060 return listener; 1061 } 1062 return null; 1063 } 1064 1065 /** 1066 * The list of constraints which are in a conflict with the given value if 1067 * it is assigned to its variable. This means the constraints, which adds a 1068 * value into the set of conflicting values in 1069 * {@link Constraint#computeConflicts(Assignment, Value, Set)}. 1070 * @param assignment current assignment 1071 * @param value given value 1072 * @return hard constraints and their conflicts that are conflicting with the given value 1073 */ 1074 public Map<Constraint<V, T>, Set<T>> conflictConstraints(Assignment<V, T> assignment, T value) { 1075 Map<Constraint<V, T>, Set<T>> conflictConstraints = new HashMap<Constraint<V, T>, Set<T>>(); 1076 for (Constraint<V, T> constraint : value.variable().hardConstraints()) { 1077 Set<T> conflicts = new HashSet<T>(); 1078 constraint.computeConflicts(assignment, value, conflicts); 1079 if (!conflicts.isEmpty()) { 1080 conflictConstraints.put(constraint, conflicts); 1081 } 1082 } 1083 for (GlobalConstraint<V, T> constraint : globalConstraints()) { 1084 Set<T> conflicts = new HashSet<T>(); 1085 constraint.computeConflicts(assignment, value, conflicts); 1086 if (!conflicts.isEmpty()) { 1087 conflictConstraints.put(constraint, conflicts); 1088 } 1089 } 1090 return conflictConstraints; 1091 } 1092 1093 /** 1094 * The list of hard constraints which contain at least one variable that is 1095 * not assigned. 1096 * @param assignment current assignment 1097 * @return list of hard constraints which contain at least one variable that is not assigned 1098 */ 1099 public List<Constraint<V, T>> unassignedHardConstraints(Assignment<V, T> assignment) { 1100 List<Constraint<V, T>> ret = new ArrayList<Constraint<V, T>>(); 1101 constraints: for (Constraint<V, T> constraint : constraints()) { 1102 if (!constraint.isHard()) 1103 continue; 1104 for (V v : constraint.variables()) { 1105 if (assignment.getValue(v) == null) { 1106 ret.add(constraint); 1107 continue constraints; 1108 } 1109 } 1110 } 1111 if (iVariables.size() > assignment.nrAssignedVariables()) 1112 ret.addAll(globalConstraints()); 1113 return ret; 1114 } 1115 1116 /** Registered info providers (see {@link InfoProvider}) 1117 * @return list of registered info providers 1118 **/ 1119 protected List<InfoProvider<V, T>> getInfoProviders() { 1120 return iInfoProviders; 1121 } 1122 1123 /** Register a new criterion 1124 * @param criterion a criterion 1125 **/ 1126 public void addCriterion(Criterion<V,T> criterion) { 1127 iCriteria.put(criterion.getClass().getName(), criterion); 1128 criterion.setModel(this); 1129 addModelListener(criterion); 1130 } 1131 1132 /** Unregister an existing criterion 1133 * @param criterion a criterion 1134 **/ 1135 public void removeCriterion(Criterion<V,T> criterion) { 1136 iCriteria.remove(criterion.getClass().getName()); 1137 criterion.setModel(null); 1138 removeModelListener(criterion); 1139 } 1140 1141 /** Unregister an existing criterion 1142 * @param criterion a criterion 1143 **/ 1144 public void removeCriterion(Class<? extends Criterion<V, T>> criterion) { 1145 Criterion<V,T> c = iCriteria.remove(criterion.getName()); 1146 if (c != null) 1147 removeModelListener(c); 1148 } 1149 1150 /** Return a registered criterion of the given type. 1151 * @param criterion criterion type 1152 * @return registered criterion of the given type 1153 **/ 1154 public Criterion<V, T> getCriterion(Class<? extends Criterion<V, T>> criterion) { 1155 return iCriteria.get(criterion.getName()); 1156 } 1157 1158 /** List all registered criteria 1159 * @return list all registered criteria 1160 **/ 1161 public Collection<Criterion<V, T>> getCriteria() { 1162 return iCriteria.values(); 1163 } 1164 1165 /** 1166 * Weaken all weakening constraint so that the given value can be assigned without 1167 * them creating a conflict using {@link WeakeningConstraint#weaken(Assignment, Value)}. 1168 * This method is handy for instance when an existing solution is being loaded 1169 * into the solver. 1170 * @param assignment current assignment 1171 * @param value given value 1172 */ 1173 @SuppressWarnings("unchecked") 1174 public void weaken(Assignment<V, T> assignment, T value) { 1175 for (Constraint<V, T> constraint : value.variable().hardConstraints()) { 1176 if (constraint instanceof WeakeningConstraint) 1177 ((WeakeningConstraint<V,T>)constraint).weaken(assignment, value); 1178 } 1179 for (GlobalConstraint<V, T> constraint : globalConstraints()) { 1180 if (constraint instanceof WeakeningConstraint) 1181 ((WeakeningConstraint<V,T>)constraint).weaken(assignment, value); 1182 } 1183 } 1184 1185 /** 1186 * Create a reference to an assignment context for a class that is in a need of one. Through this 1187 * reference an assignment context (see {@link AssignmentContext}) can be accessed using 1188 * {@link Assignment#getAssignmentContext(AssignmentContextReference)}. 1189 * @param parent class needing an assignment context 1190 * @param <C> assignment context type 1191 * @return reference to an assignment context 1192 */ 1193 public synchronized <C extends AssignmentContext> AssignmentContextReference<V,T,C> createReference(HasAssignmentContext<V, T, C> parent) { 1194 AssignmentContextReference<V, T, C> ref = new AssignmentContextReference<V, T, C>(parent, iNextReferenceId); 1195 iAssignmentContextReferences.put(iNextReferenceId, ref); 1196 iNextReferenceId++; 1197 return ref; 1198 } 1199 1200 /** 1201 * Clear all assignment contexts for the given assignment 1202 * @param assignment given {@link Assignment} 1203 */ 1204 public synchronized void clearAssignmentContexts(Assignment<V, T> assignment) { 1205 for (AssignmentContextReference<V,T,? extends AssignmentContext> ref: iAssignmentContextReferences.values()) 1206 assignment.clearContext(ref); 1207 } 1208 1209 /** 1210 * Remove a reference to an assignment context for the model 1211 * @param parent class with an assignment context 1212 * @param <C> assignment context type 1213 * @return reference to an assignment context that was removed from the model (if any) 1214 */ 1215 @SuppressWarnings("unchecked") 1216 public synchronized <C extends AssignmentContext> AssignmentContextReference<V,T,C> removeReference(HasAssignmentContext<V, T, C> parent) { 1217 AssignmentContextReference<V,T,C> reference = parent.getAssignmentContextReference(); 1218 if (reference != null) 1219 return (AssignmentContextReference<V,T,C>) iAssignmentContextReferences.remove(reference.getIndex()); 1220 return null; 1221 } 1222 1223 /** 1224 * Create all assignment contexts for the given assignment 1225 * @param assignment given {@link Assignment} 1226 * @param clear if true {@link Assignment#clearContext(AssignmentContextReference)} is called first 1227 */ 1228 public synchronized void createAssignmentContexts(Assignment<V, T> assignment, boolean clear) { 1229 for (AssignmentContextReference<V,T,? extends AssignmentContext> ref: iAssignmentContextReferences.values()) { 1230 if (clear) assignment.clearContext(ref); 1231 assignment.getAssignmentContext(ref); 1232 } 1233 } 1234 1235 /** 1236 * Return default assignment that is using the old {@link Variable#getAssignment()} assignments. 1237 * @return as instance of {@link DefaultSingleAssignment} 1238 */ 1239 @Deprecated 1240 public Assignment<V, T> getDefaultAssignment() { 1241 if (iAssignment == null) 1242 iAssignment = new DefaultSingleAssignment<V, T>(); 1243 return iAssignment; 1244 } 1245 1246 /** 1247 * Set default assignment 1248 * @param assignment current assignment to become default 1249 */ 1250 @Deprecated 1251 public void setDefaultAssignment(Assignment<V, T> assignment) { 1252 iAssignment = assignment; 1253 } 1254 1255 /** 1256 * Returns an instance of an empty assignment (using {@link EmptyAssignment}) 1257 * @return an empty assignment 1258 */ 1259 public Assignment<V, T> getEmptyAssignment() { 1260 if (iEmptyAssignment == null) 1261 iEmptyAssignment = new EmptyAssignment<V, T>(); 1262 return iEmptyAssignment; 1263 } 1264 1265 1266 /** 1267 * Create a new inherited assignment from the given solution 1268 * @param solution a solution that is using this model 1269 * @param index thread index of the new assignment 1270 * @return a new inherited assignment 1271 */ 1272 public InheritedAssignment<V, T> createInheritedAssignment(Solution<V, T> solution, int index) { 1273 return new DefaultInheritedAssignment<V, T>(solution, index); 1274 } 1275}