// $Id: Solution_Backtrack.java,v 1.18 2010/09/23 08:26:53 olga Exp $
// $Log: Solution_Backtrack.java,v $
// Revision 1.18 2010/09/23 08:26:53 olga
// tuning
//
// Revision 1.17 2010/06/09 10:24:31 olga
// tuning
//
// Revision 1.16 2010/03/08 15:50:34 olga
// code optimizing
//
// Revision 1.15 2010/02/22 14:43:23 olga
// code optimizing
//
// Revision 1.14 2009/05/12 10:36:53 olga
// CPA: bug fixed
// Applicability of Rule Seq. : bug fixed
//
// Revision 1.13 2009/02/23 09:01:57 olga
// Convert Atomic Graph Constraints to Post Application Condition of Rule - bug fixed, reworked and improved error messaging
// Graph copy - bug fixed
// Code tuning
//
// Revision 1.12 2008/07/30 06:27:14 olga
// Applicability of RS , concurrent rule - handling of attributes improved
//
// Revision 1.11 2008/04/07 09:36:55 olga
// Code tuning: refactoring + profiling
// Extension: CPA - two new options added
//
// Revision 1.10 2007/12/03 08:35:12 olga
// - Some bugs fixed in visualization of morphism mappings after deleting and creating
// nodes, edges
// - implemented: matching with non-injective NAC and Match morphism
//
// Revision 1.9 2007/11/19 08:48:41 olga
// Some GUI usability mistakes fixed.
// Default values in node/edge of a type graph implemented.
// Code tuning.
//
// Revision 1.8 2007/11/05 09:18:19 olga
// code tuning
//
// Revision 1.7 2007/11/01 09:58:19 olga
// Code refactoring: generic types- done
//
// Revision 1.6 2007/09/10 13:05:09 olga
// In this update:
// - package xerces2.5.0 is not used anymore;
// - class com.objectspace.jgl.Pair is replaced by the agg own generic class agg.util.Pair;
// - bugs fixed in: usage of PACs in rules; match completion;
// usage of static method calls in attr. conditions
// - graph editing: added some new features
//
// Revision 1.5 2007/05/07 07:59:35 olga
// CSP: extentions of CSP variables concept
//
// Revision 1.4 2007/01/11 10:21:17 olga
// Optimized Version 1.5.1beta , free for tests
//
// Revision 1.3 2006/12/13 13:33:04 enrico
// reimplemented code
//
// Revision 1.2 2006/11/01 11:17:29 olga
// Optimized agg sources of CSP algorithm, match usability,
// graph isomorphic copy,
// node/edge type multiplicity check for injective rule and match
//
// Revision 1.1 2005/08/25 11:56:55 enrico
// *** empty log message ***
//
// Revision 1.1 2005/05/30 12:58:01 olga
// Version with Eclipse
//
// Revision 1.5 2004/12/20 14:53:48 olga
// Changes because of matching optimisation.
//
// Revision 1.4 2004/01/15 16:43:06 olga
// Korrektur an transformation
//
// Revision 1.3 2003/12/18 16:27:25 olga
// .
//
// Revision 1.2 2003/03/05 18:24:25 komm
// sorted/optimized import statements
//
// Revision 1.1.1.1 2002/07/11 12:17:26 olga
// Imported sources
//
// Revision 1.5 1999/07/14 09:09:20 olga
// Umstellung auf JAVA2.0
// Zum Testen oeffnet sich nur in
// Transform/Step der InputParameterEditor
// Verbesserung von GUI und AttrEditor
//
// Revision 1.4 1999/06/28 16:36:10 shultzke
// Hoffentlich erzeigen wir eine uebersetzungsfaehige Version
//
// Revision 1.3 1998/09/03 14:31:36 mich
// Updated for use with JGL V3.1.
//
// Revision 1.2 1998/07/15 22:14:33 mich
// *** empty log message ***
//
// Revision 1.1 1998/07/15 22:13:00 mich
// Initial revision
//
// This is basically the same as Solution_Backjump rev 1.1,
// slightly hacked up so it does
// no backjumps, because backjumping may lose some solutions as long as
// attributes don't provide information about their interdependencies
// caused by the use of variables.
// It is a hack because it still does all the work needed to
// prepare backjumping. This should really be optimized away before
// doing efficiency comparisons BJ vs. BT!
//
// (see sec. 7.1.5. (page 89) of my master's thesis about the dis-
// advantages of BJ which should be optimized away here)
package agg.util.csp;
import java.util.Dictionary;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;
import agg.xt_basis.GraphObject;
import agg.xt_basis.csp.Query_Type;
/** A CSP solution strategy NOT using the back jumping technique. */
public class Solution_Backtrack implements SolutionStrategy {
private CSP itsCSP;
private boolean parallel;
private boolean startParallelbyFirst;
private Vector<Query> itsQueries = new Vector<Query>();
final private Dictionary<Variable, Integer> itsVarIndexMap = new Hashtable<Variable, Integer>();
final private Dictionary<Object, Variable> itsInstanceVarMap = new Hashtable<Object, Variable>();
private boolean itsInjectiveFlag;
// the map of other solution solver
private Dictionary<Object, Variable> otherInstanceVarMap;
private SearchStrategy itsSearcher = new Search_BreadthFirst();
/**
* Vector elements are of type <code>OrderedSet</code> of
* <code>Variable</code>. Elements are of type <code>Variable</code>.
*/
// final private HashSet itsBackjumpTargets = new HashSet();
/**
* Value is either <code>NEXT</code> or <code>BACK</code> according to
* the recent traversal direction of the search tree.
*/
// private int itsDirection;
/** Index into <code>itsVariables</code>. */
private int itsCurrentIndex;
/**
* Index into <code>itsVariables</code>. Variables below this index are
* considered to be pre-instantiated and will not be touched by the solution
* algorithm.
*/
private Variable itsCurrentVar;
private Query itsCurrentQuery;
private int itsState;
@SuppressWarnings("unused")
private int itsInstantiationCounter;
@SuppressWarnings("unused")
private int itsBackstepCounter;
private boolean solutionFound;
// constants for csp solution state machine:
private final static int START = 1;
private final static int NEXT = 2;
private final static int INSTANTIATE = 3;
private final static int BACK = 4;
private final static int SUCCESS = 5;
private final static int NO_MORE_SOLUTIONS = 6;
private final static int BACKJUMP = 7;
public Solution_Backtrack() {
this.itsInjectiveFlag = false;
}
public Solution_Backtrack(boolean injective) {
this.itsInjectiveFlag = injective;
}
public void clear() {
this.itsQueries.clear();
((Hashtable<Variable, Integer>) this.itsVarIndexMap).clear();
((Hashtable<Object, Variable>) this.itsInstanceVarMap).clear();
}
public void setRelatedInstanceVarMap(
Dictionary<Object, Variable> relatedVarIndexMap) {
this.otherInstanceVarMap = relatedVarIndexMap;
}
public Dictionary<Object, Variable> getInstanceVarMap() {
return this.itsInstanceVarMap;
}
/**
* Compute the search plan (variable order) and do some other initialization
* stuff.
*
* @return <code>false</code> iff some pre-instantiated variables are
* violating some constraint.
*/
private final boolean initialize(CSP csp) {
this.itsCSP = csp;
this.itsQueries.removeAllElements();
this.itsQueries = this.itsSearcher.execute(this.itsCSP);
this.itsQueries.trimToSize();
for (int i = 0; i < this.itsQueries.size(); i++) {
this.itsVarIndexMap.put(this.itsQueries.elementAt(i).getTarget(),
Integer.valueOf(i));
}
Enumeration<Variable> anEnum = this.itsCSP.getVariables();
Variable aVar;
while (anEnum.hasMoreElements()) {
aVar = anEnum.nextElement();
if (aVar.getInstance() != null) {
if (aVar.checkConstraints().hasMoreElements())
return false;
this.itsVarIndexMap.put(aVar, Integer.valueOf(-1));
this.itsInstanceVarMap.put(aVar.getInstance(), aVar);
}
}
return true;
}
public final boolean reinitialize(boolean doUpdateQueries) {
if (doUpdateQueries) {
this.itsQueries.removeAllElements();
this.itsQueries = this.itsSearcher.execute(this.itsCSP);
this.itsQueries.trimToSize();
}
((Hashtable<Variable, Integer>) this.itsVarIndexMap).clear();
((Hashtable<Object, Variable>) this.itsInstanceVarMap).clear();
for (int i = 0; i < this.itsQueries.size(); i++) {
Query q = this.itsQueries.elementAt(i);
this.itsVarIndexMap.put(q.getTarget(), Integer.valueOf(i));
if (q instanceof Query_Type) {
((Query_Type)q).resetObjects();
}
}
Enumeration<Variable> anEnum = this.itsCSP.getVariables();
while (anEnum.hasMoreElements()) {
Variable aVar = anEnum.nextElement();
if (aVar.getInstance() != null) {
if (aVar.checkConstraints().hasMoreElements())
return false;
this.itsVarIndexMap.put(aVar, Integer.valueOf(-1));
this.itsInstanceVarMap.put(aVar.getInstance(), aVar);
}
}
this.itsState = START;
return true;
}
public void reinitialize(Variable var) {
boolean queryExists = false;
Query q = null;
for (int i = 0; i < this.itsQueries.size(); i++) {
q = this.itsQueries.elementAt(i);
Variable v = this.itsQueries.elementAt(i).getTarget();
if (v == var) {
queryExists = true;
if (var.getInstance() != null)
this.itsInstanceVarMap.remove(var.getInstance());
this.itsVarIndexMap.put(v, Integer.valueOf(i));
this.itsState = START;
break;
}
}
if (!queryExists) {
if (var.getInstance() != null)
this.itsInstanceVarMap.remove(var.getInstance());
q = var.getTypeQuery();
if (q != null) {
this.itsQueries.add(0, q);
for (int i = 0; i < this.itsQueries.size(); i++) {
q = this.itsQueries.elementAt(i);
Variable v = this.itsQueries.elementAt(i).getTarget();
this.itsVarIndexMap.put(v, Integer.valueOf(i));
}
this.itsState = START;
}
}
}
public final void reset() {
this.itsState = START;
}
public void resetQuery_Type() {
for (int i = 0; i < this.itsQueries.size(); i++) {
if (this.itsQueries.elementAt(i) instanceof Query_Type) {
((Query_Type)this.itsQueries.elementAt(i)).resetObjects();
}
}
}
public synchronized final boolean next(CSP csp) {
this.solutionFound = false;
if (!csp.equals(this.itsCSP)) {
if (!initialize(csp))
return false;
this.itsState = START;
}
if (this.itsState == SUCCESS) {
this.itsState = BACK;
// we want to continue where we left off, instead of actually
// making a back step:
this.itsCurrentIndex++;
}
while (true) {
switch (this.itsState) {
case START:
this.itsInstantiationCounter = 0;
this.itsBackstepCounter = 0;
this.itsCurrentIndex = -1;
this.itsState = NEXT;
break;
case NEXT:
if (this.itsCurrentIndex >= this.itsQueries.size() - 1) {
this.itsState = SUCCESS;
} else {
// itsBackjumpTargets.clear();
this.itsCurrentQuery = this.itsQueries.elementAt(++this.itsCurrentIndex);
this.itsCurrentVar = this.itsCurrentQuery.getTarget();
if (this.itsCurrentQuery.isApplicable()) {
this.itsCurrentVar.setDomainEnum(this.itsCurrentQuery.execute());
// addToBackjumpTargets(itsCurrentQuery.getSources());
this.itsState = INSTANTIATE;
} else {
this.itsState = NO_MORE_SOLUTIONS;
}
}
// itsDirection = NEXT;
break;
case INSTANTIATE:
// HACK to never use backjumping at all:
this.itsState = BACK;
// deactivate correspondent constraint before checking // pablo
this.itsCurrentQuery.deactivateCorrespondent();
while (this.itsCurrentVar.hasNext()) {
this.itsInstantiationCounter++;
this.itsCurrentVar.setInstance(this.itsCurrentVar.getNext());
Variable aConflictVar = checkInjection(this.itsCurrentVar);
if (aConflictVar != null) {
this.itsCurrentVar.setInstance(null);
// if (this.itsVarIndexMap.get(aConflictVar) != null)
// itsBackjumpTargets.add(aConflictVar);
continue;
}
Enumeration<?> allConflictVars = this.itsCurrentVar.checkConstraints();
if (!allConflictVars.hasMoreElements()) {
this.itsState = NEXT;
addInjection(this.itsCurrentVar);
break;
}
// else {
// if (itsState == BACKJUMP)
// itsBackjumpTargets.add(getFirstOf(allConflictVars));
// }
}
// re-activate correspondent constraint after checking // pablo
this.itsCurrentQuery.activateCorrespondent();
break;
case BACK:
this.itsBackstepCounter++;
if (this.itsCurrentIndex == 0) {
if (this.itsCurrentVar != null
&& this.itsCurrentVar.hasNext()) {
removeInjection(this.itsCurrentVar);
this.itsCurrentVar.setInstance(null);
this.itsState = INSTANTIATE;
// this.itsDirection = NEXT;
} else {
this.itsState = NO_MORE_SOLUTIONS;
// this.itsDirection = BACK;
}
} else {
removeInjection(this.itsCurrentVar);
this.itsCurrentVar.setInstance(null);
this.itsCurrentVar = this.itsQueries.elementAt(--this.itsCurrentIndex)
.getTarget();
removeInjection(this.itsCurrentVar);
this.itsState = INSTANTIATE;
// itsDirection = BACK;
}
break;
// BACKJUMP is not used here
case BACKJUMP:
this.itsBackstepCounter++;
// int aStepCounter = 0;
this.itsState = NO_MORE_SOLUTIONS;
while (this.itsCurrentIndex > 0) {
// aStepCounter++;
removeInjection(this.itsCurrentVar);
this.itsCurrentVar.setInstance(null);
this.itsCurrentVar = this.itsQueries.elementAt(--this.itsCurrentIndex)
.getTarget();
removeInjection(this.itsCurrentVar);
this.itsState = INSTANTIATE;
// if (!itsBackjumpTargets.find(itsCurrentVar).equals(
// itsBackjumpTargets.end())) {
// break;
// }
}
// itsDirection = BACK;
break;
case SUCCESS:
if (this.parallel && this.startParallelbyFirst) {
removeUsedObjectFromDomain();
}
this.solutionFound = true;
return true;
case NO_MORE_SOLUTIONS:
return false;
default:
System.out.println("Should have never come here..." + this.itsState);
}
}
}
private void removeUsedObjectFromDomain() {
for (int i=0; i<this.itsQueries.size(); i++) {
Query q = this.itsQueries.get(i);
if (q instanceof Query_Type) {
((Query_Type)q).removeObject((GraphObject) q.getTarget().getInstance());
}
}
((Hashtable<Object, Variable>) this.itsInstanceVarMap).clear();
// itsBackjumpTargets.clear();
}
public boolean hasSolution() {
return this.solutionFound;
}
public boolean hasQueries() {
return (!this.itsQueries.isEmpty());
}
/*
* Return the Variable with the smallest index of <code>vars</code>.
* <p>
* <b>Pre:</b> <code>vars.hasMoreElements()</code>.
*/
/*
private final Variable getFirstOf(Enumeration<Variable> vars) {
Variable aFirstVar = vars.nextElement();
int aFirstIndex = itsQueries.size();
Variable aCurrentVar;
int aCurrentIndex;
while (vars.hasMoreElements()) {
aCurrentVar = vars.nextElement();
aCurrentIndex = itsVarIndexMap.get(aCurrentVar).intValue();
if (aCurrentIndex < aFirstIndex) {
aFirstIndex = aCurrentIndex;
aFirstVar = aCurrentVar;
}
}
return aFirstVar;
}
*/
private final void addInjection(Variable var) {
if (this.itsInjectiveFlag)
this.itsInstanceVarMap.put(var.getInstance(), var);
}
private final void removeInjection(Variable var) {
if (this.itsInjectiveFlag && (var.getInstance() != null))
this.itsInstanceVarMap.remove(var.getInstance());
}
private final Variable checkInjection(Variable var) {
if (var.getInstance() != null) {
if (this.otherInstanceVarMap != null) {
// this check is always injective
Variable other = this.otherInstanceVarMap.get(var.getInstance());
if (other != null) {
return other;
}
}
if (this.itsInjectiveFlag)
return this.itsInstanceVarMap.get(var.getInstance());
return null;
}
return null;
}
// private final void addToBackjumpTargets(Enumeration<?> en) {
// while (en.hasMoreElements())
// itsBackjumpTargets.add(en.nextElement());
// }
/**
* @see agg.util.csp.SolutionStrategy#parallelSearch()
*/
public boolean parallelSearch() {
return this.parallel;
}
/**
* @see agg.util.csp.SolutionStrategy#enableParallelSearch(boolean)
*/
public void enableParallelSearch(boolean b) {
this.parallel = b;
}
/**
* @see agg.util.csp.SolutionStrategy#setStartParallelSearchByFirst(boolean)
*/
public void setStartParallelSearchByFirst(boolean b) {
this.startParallelbyFirst = b;
}
/* (non-Javadoc)
* @see agg.util.csp.SolutionStrategy#getStartVariable()
*/
public Variable getStartVariable() {
return this.itsQueries.get(0).itsTarget;
}
/* (non-Javadoc)
* @see agg.util.csp.SolutionStrategy#getQuery(agg.util.csp.Variable)
*/
public Query getQuery(Variable var) {
return this.itsQueries.get(this.itsVarIndexMap.get(var).intValue());
}
}