// $Id: Solution_Backjump.java,v 1.38 2010/09/23 08:26:52 olga Exp $
// $Log: Solution_Backjump.java,v $
// Revision 1.38 2010/09/23 08:26:52 olga
// tuning
//
// Revision 1.37 2010/06/23 13:44:14 olga
// tuning
//
// Revision 1.36 2010/06/22 11:10:21 olga
// tuning
//
// Revision 1.35 2010/06/09 10:24:32 olga
// tuning
//
// Revision 1.34 2010/03/08 15:50:35 olga
// code optimizing
//
// Revision 1.33 2010/03/04 14:13:42 olga
// code optimizing
//
// Revision 1.32 2010/02/22 14:43:23 olga
// code optimizing
//
// Revision 1.31 2009/11/23 09:02:45 olga
// tests
//
// Revision 1.30 2009/10/15 13:53:48 olga
// tuning
//
// Revision 1.29 2009/10/05 08:53:25 olga
// RSA check - bug fixed
//
// Revision 1.28 2009/05/28 13:18:29 olga
// Amalgamated graph transformation - development stage
//
// Revision 1.27 2009/05/12 10:36:53 olga
// CPA: bug fixed
// Applicability of Rule Seq. : bug fixed
//
// Revision 1.26 2009/02/12 13:03:38 olga
// Some optimization of match searching
//
// Revision 1.25 2008/07/30 14:42:59 olga
// Graph layouting tuning
//
// Revision 1.24 2008/07/30 14:03:37 olga
// Graph Transformation - bug fixed
//
// Revision 1.23 2008/07/30 06:27:14 olga
// Applicability of RS , concurrent rule - handling of attributes improved
//
// Revision 1.22 2008/04/21 09:32:19 olga
// Visualization of inheritance edge - bugs fixed
// Graph layout tuning
//
// Revision 1.21 2008/04/07 09:36:55 olga
// Code tuning: refactoring + profiling
// Extension: CPA - two new options added
//
// Revision 1.20 2007/12/10 08:42:58 olga
// CPA of grammar with node type inheritance for attributed graphs - bug fixed
//
// Revision 1.19 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.18 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.17 2007/11/05 09:18:19 olga
// code tuning
//
// Revision 1.16 2007/11/01 09:58:19 olga
// Code refactoring: generic types- done
//
// Revision 1.15 2007/10/17 14:48:02 olga
// EdRule: bug fixed
// Code tuning
//
// Revision 1.14 2007/10/11 15:03:38 olga
// code tuning
//
// Revision 1.13 2007/09/17 10:50:01 olga
// Bug fixed in graph transformation by rules with NACs and PACs .
// AGG help docus extended by new implemented features.
//
// Revision 1.12 2007/09/10 13:05:10 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.11 2007/05/07 07:59:35 olga
// CSP: extentions of CSP variables concept
//
// Revision 1.10 2007/04/24 07:08:45 olga
// undo/redo match mappings
//
// Revision 1.9 2007/01/31 09:19:28 olga
// Bug fixed in case of transformating attributed grammar with inheritance and non-injective match
//
// Revision 1.8 2007/01/11 10:21:17 olga
// Optimized Version 1.5.1beta , free for tests
//
// Revision 1.7 2006/12/13 13:33:04 enrico
// reimplemented code
//
// Revision 1.6 2006/11/15 09:00:32 olga
// Transform with input parameter : bug fixed
//
// Revision 1.5 2006/11/06 10:09:36 olga
// Type editor GUI tuning
//
// Revision 1.4 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.3 2006/05/08 08:24:12 olga
// Some extentions of GUI: - Undo Delete button of tool bar to undo deletions
// if grammar elements like rule, NAC, graph constraints;
// - the possibility to add a new graph to a grammar or a copy of the current
// host graph;
// - to set one or more layer for consistency constraints.
// Also some bugs fixed of matching and some optimizations of CSP algorithmus done.
//
// Revision 1.2 2006/04/20 11:58:39 olga
// Attr type check: Bug fixed
//
// Revision 1.1 2005/08/25 11:56:55 enrico
// *** empty log message ***
//
// Revision 1.2 2005/06/20 13:37:04 olga
// Up to now the version 1.2.8 will be prepared.
//
// Revision 1.1 2005/05/30 12:58:01 olga
// Version with Eclipse
//
// Revision 1.10 2004/12/20 14:53:48 olga
// Changes because of matching optimisation.
//
// Revision 1.9 2004/05/06 17:23:27 olga
// graph matching OK
//
// Revision 1.8 2004/04/28 12:46:38 olga
// test CSP
//
// Revision 1.7 2004/01/28 17:58:38 olga
// Errors suche
//
// Revision 1.6 2004/01/22 17:50:08 olga
// test
//
// Revision 1.5 2004/01/15 16:43:06 olga
// Korrektur an transformation
//
// Revision 1.4 2003/12/18 16:27:25 olga
// .
//
// Revision 1.3 2003/03/05 18:24:25 komm
// sorted/optimized import statements
//
// Revision 1.2 2002/12/04 14:00:19 komm
// code newly formated for debuging
//
// Revision 1.1.1.1 2002/07/11 12:17:26 olga
// Imported sources
//
// Revision 1.5 2000/05/17 11:58:38 olga
// Testversion fuer Gabi mit diversen Aenderungen. Fehler sind moeglich!!
//
// Revision 1.4 1999/07/14 09:08:51 olga
// Umstellung auf JAVA2.0
// Zum Testen oeffnet sich nur in
// Transform/Step der InputParameterEditor
// Verbesserung von GUI und AttrEditor
//
// Revision 1.3 1999/06/28 16:36:05 shultzke
// Hoffentlich erzeigen wir eine uebersetzungsfaehige Version
//
// Revision 1.2 1998/04/07 14:21:00 mich
// Updated for use with JGL V3.1.
//
// Revision 1.1 1997/12/26 21:17:13 mich
// Initial revision
//
// ********************************************
// *** moved from util.csp.impl to util.csp ***
// ********************************************
//
// Revision 1.5 1997/10/15 07:37:50 mich
// + Now checks preinstantiated Variables for consistency.
// Tested.
//
// Revision 1.4 1997/09/23 14:14:42 mich
// + option for injective solutions coded into the state machine.
// Tested.
//
// Revision 1.3 1997/09/22 17:08:53 mich
// + Additional Backstep counter for performance measurement.
//
// Revision 1.2 1997/09/22 05:13:30 mich
// First working version after conversion from
// VariableOrderingStrategy to SearchStrategy.
//
// Revision 1.1 1997/09/20 03:49:55 mich
// Initial revision
//
package agg.util.csp;
import java.util.Dictionary;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.List;
import java.util.Vector;
import agg.xt_basis.GraphObject;
import agg.xt_basis.csp.Query_Type;
/** A CSP solution strategy using the backjumping technique. */
public class Solution_Backjump implements SolutionStrategy {
private CSP itsCSP;
private boolean itsInjectiveFlag;
private boolean parallel;
private boolean startParallelbyFirst;
final 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>();
// the map of other solution solver
private Dictionary<Object, Variable> otherInstanceVarMap;
// final private BinaryPredicate itsVariableOrder = new SimpleVariableOrder();
final 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<Variable> itsBackjumpTargets = new HashSet<Variable>();
/**
* 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 preinstantiated and will not be touched by the solution
* algorithm.
*/
private Variable itsCurrentVar;
// private Variable itsStartVar;
private Query itsCurrentQuery;
private int itsState;
// private int itsInstantiationCounter;
// private int itsBackstepCounter;
@SuppressWarnings("unused")
private int allInstances = 0;
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_Backjump(boolean injective) {
this.itsInjectiveFlag = injective;
}
public void setRelatedInstanceVarMap(
Dictionary<Object, Variable> relatedVarIndexMap) {
this.otherInstanceVarMap = relatedVarIndexMap;
}
public Dictionary<Object, Variable> getInstanceVarMap() {
return this.itsInstanceVarMap;
}
/**
*
*/
public void clear() {
this.itsQueries.clear();
((Hashtable<Object, Variable>) this.itsInstanceVarMap).clear();
((Hashtable<Variable, Integer>) this.itsVarIndexMap).clear();
this.itsBackjumpTargets.clear();
}
/**
* Compute the search plan (variable order) and do some other initialization
* stuff.
*
* @return <code>false</code> iff some preinstantiated variables are
* violating some constraint.
*/
private synchronized final boolean initialize(CSP csp) {
this.itsCSP = csp;
clear();
this.itsQueries.addAll(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));
}
final Enumeration<Variable> anEnum = this.itsCSP.getVariables();
while (anEnum.hasMoreElements()) {
final 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);
}
}
return true;
}
public final boolean reinitialize(boolean doUpdateQueries) {
if (doUpdateQueries) {
this.itsQueries.clear();
this.itsQueries.addAll(this.itsSearcher.execute(this.itsCSP));
this.itsQueries.trimToSize();
}
((Hashtable<Variable, Integer>) this.itsVarIndexMap).clear();
((Hashtable<Object, Variable>) this.itsInstanceVarMap).clear();
this.itsBackjumpTargets.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();
}
}
if (this.itsCSP != null) {
final Enumeration<Variable> anEnum = this.itsCSP.getVariables();
while (anEnum.hasMoreElements()) {
final 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(final Variable var) {
if (var.getInstance() != null) {
this.itsInstanceVarMap.remove(var.getInstance());
}
boolean queryExists = false;
for (int i = 0; i < this.itsQueries.size(); i++) {
if (var == this.itsQueries.elementAt(i).getTarget()) {
this.itsVarIndexMap.put(var, Integer.valueOf(i));
this.itsState = START;
queryExists = true;
break;
}
}
if (!queryExists) {
if (var.getTypeQuery() != null) {
this.itsQueries.add(0, var.getTypeQuery());
for (int i = 0; i < this.itsQueries.size(); i++) {
final Variable v = this.itsQueries.elementAt(i).getTarget();
this.itsVarIndexMap.put(v, Integer.valueOf(i));
}
this.itsState = START;
}
}
// showQueries(itsQueries);
}
public Variable getStartVariable() {
return this.itsQueries.get(0).itsTarget;
}
public Query getQuery(final Variable var) {
return (!this.itsQueries.isEmpty())? this.itsQueries.get(this.itsVarIndexMap.get(var).intValue()): null;
}
/**
* Set my state to <code>START</code>.
*/
public final void reset() {
this.itsState = START;
}
/**
* Reset the object domain of the query <code>Query_Type</code>.
*/
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();
}
}
}
/**
* Search for next solution.
*/
public synchronized final boolean next(CSP csp) {
// long t = System.nanoTime();
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++;
}
// showQueries(itsQueries);
while (true) {
switch (this.itsState) {
case START:
// itsInstantiationCounter = 0;
// itsBackstepCounter = 0;
this.itsCurrentIndex = -1;
this.itsState = NEXT;
break;
case NEXT:
if (this.itsCurrentIndex >= this.itsQueries.size() - 1) {
this.itsState = SUCCESS;
} else {
this.itsCurrentQuery = this.itsQueries.elementAt(++this.itsCurrentIndex);
this.itsCurrentVar = this.itsCurrentQuery.getTarget();
if (this.itsCurrentQuery.isApplicable()
&& !this.itsCurrentQuery.isDomainEmpty()
) {
this.itsCurrentVar.setDomainEnum(this.itsCurrentQuery.execute());
addToBackjumpTargets(this.itsCurrentQuery.getSources());
this.itsState = INSTANTIATE;
} else {
this.itsState = NO_MORE_SOLUTIONS;
}
}
this.itsDirection = NEXT;
break;
case INSTANTIATE:
if (this.itsDirection == NEXT) {
// we will use backjumping if instantiation fails
this.itsState = BACKJUMP;
} else {
// we must not use backjumping because there was
// a consistent instantiation found for the current
// variable previously
this.itsState = BACK;
}
// deactivate correspondent constraint before checking // pablo
this.itsCurrentQuery.deactivateCorrespondent();
while (this.itsCurrentVar.hasNext()) {
Object obj = this.itsCurrentVar.getNext();
// Node/Edge with graph context == null was destroyed,
// but it remains in domain of CSP variable during parallel matching,
// so it must be exclude as instance of a CSP variable
if (//obj == null ||
((GraphObject)obj).getContext() == null) {
continue;
}
// itsInstantiationCounter++;
this.itsCurrentVar.setInstance(obj);
this.allInstances++;
Variable aConflictVar = checkInjection(this.itsCurrentVar);
if (aConflictVar != null) {
this.itsCurrentVar.setInstance(null);
this.allInstances--;
if (this.itsVarIndexMap.get(aConflictVar) != null) {
this.itsBackjumpTargets.add(aConflictVar);
}
continue;
}
// else if (obj instanceof Node && !this.checkDangling(itsCurrentVar)) {
// itsCurrentVar.setInstance(null);
// allInstances--;
// continue;
// }
Enumeration<Variable> allConflictVars = this.itsCurrentVar.checkConstraints();
if (!allConflictVars.hasMoreElements()) {
this.itsState = NEXT;
addInjection(this.itsCurrentVar);
break;
}
if (this.itsState == BACKJUMP) {
Variable conflictVar1 = getFirstOf(allConflictVars);
this.itsBackjumpTargets.add(conflictVar1);
}
}
// re-activate correspondent constraint after checking // pablo
this.itsCurrentQuery.activateCorrespondent();
break;
case BACK:
// System.out.println("BACK");
// itsBackstepCounter++;
if (this.itsCurrentIndex == 0) {
if ((this.itsCurrentVar != null)
&& this.itsCurrentVar.hasNext()) {
removeInjection(this.itsCurrentVar);
this.itsCurrentVar.setInstance(null);
this.allInstances--;
this.itsState = INSTANTIATE;
this.itsDirection = NEXT;
} else {
this.itsState = NO_MORE_SOLUTIONS;
this.itsDirection = BACK;
}
} else if (this.itsCurrentIndex > 0) {
removeInjection(this.itsCurrentVar);
this.itsCurrentVar.setInstance(null);
this.allInstances--;
this.itsCurrentVar = this.itsQueries.get(--this.itsCurrentIndex).getTarget();
removeInjection(this.itsCurrentVar);
this.itsState = INSTANTIATE;
this.itsDirection = BACK;
} else {
this.itsState = NO_MORE_SOLUTIONS;
this.itsDirection = BACK;
}
break;
case BACKJUMP:
// System.out.println("BACKJUMP");
// itsBackstepCounter++;
// int aStepCounter = 0;
this.itsState = NO_MORE_SOLUTIONS;
while (this.itsCurrentIndex > 0) {
// aStepCounter++;
removeInjection(this.itsCurrentVar);
this.itsCurrentVar.setInstance(null);
this.allInstances--;
this.itsCurrentVar = this.itsQueries.elementAt(--this.itsCurrentIndex)
.getTarget();
removeInjection(this.itsCurrentVar);
this.itsState = INSTANTIATE;
if (this.itsBackjumpTargets.contains(this.itsCurrentVar)) {
this.itsBackjumpTargets.remove(this.itsCurrentVar);
break;
}
}
this.itsDirection = BACK;
break;
case SUCCESS:
// System.out.println("SUCCESS");
if (this.parallel && this.startParallelbyFirst) {
removeUsedObjectFromDomain();
}
this.solutionFound = true;
// System.out.println("solution found in : "+(System.nanoTime()-t)+" nano");
return true;
case NO_MORE_SOLUTIONS:
// System.out.println("NO_MO_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();
this.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 = this.itsQueries.size();
Variable aCurrentVar;
int aCurrentIndex;
while (vars.hasMoreElements()) {
aCurrentVar = vars.nextElement();
if (this.itsVarIndexMap.get(aCurrentVar) != null) {
aCurrentIndex = this.itsVarIndexMap.get(aCurrentVar).intValue();
if (aCurrentIndex < aFirstIndex) {
aFirstIndex = aCurrentIndex;
aFirstVar = aCurrentVar;
}
}
}
return aFirstVar;
}
private final void addInjection(Variable var) {
if (this.itsInjectiveFlag && (var.getInstance() != null)) {
this.itsInstanceVarMap.put(var.getInstance(), var);
}
}
/*
private final void removeInjection() {
for (int i=1; i<itsQueries.size(); i++) {
this.itsCurrentVar = itsQueries.get(i).getTarget();
if (itsInjectiveFlag && (this.itsCurrentVar.getInstance() != null)) {
itsInstanceVarMap.remove(this.itsCurrentVar.getInstance());
this.itsCurrentVar.setInstance(null);
}
}
}
*/
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) {
// in case of NAC / PAC
// 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;
}
/*
private final boolean checkDangling(Variable var) {
if (this.itsCSP.getRequester() instanceof Match) {
return !MatchHelper.isDanglingPoint((Match)this.itsCSP.getRequester(),
(GraphObject) var.getGraphObject(),
(GraphObject) var.getInstance());
}
return true;
}
*/
private final void addToBackjumpTargets(List<?> en) {
for (int i=0; i<en.size(); i++)
this.itsBackjumpTargets.add((Variable)en.get(i));
}
/* (non-Javadoc)
* @see agg.util.csp.SolutionStrategy#parallelSearch()
*/
public boolean parallelSearch() {
return this.parallel;
}
public void enableParallelSearch(boolean b) {
this.parallel = b;
}
/* (non-Javadoc)
* @see agg.util.csp.SolutionStrategy#setStartParallelSearchByFirst(boolean)
*/
public void setStartParallelSearchByFirst(boolean b) {
this.startParallelbyFirst = b;
}
}