package rationals;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
import java.util.Set;
/**
* This class implements an algorithm for finding a synchronizing
* word given a target letter.
*
* @author bailly
* @version $Id: MixPlay.java 2 2006-08-24 14:41:48Z oqube $
*/
public class MixPlay implements AutomatonRunner {
class MixException extends Exception {
List word;
List states;
MixException(List w, List st) {
this.word = w;
this.states = st;
}
}
private Set explored;
private final static Random random = new Random();
private final static Transition[] trmodel = new Transition[0];
private int upperBound = 1;
private Object target;
private List autos;
private Synchronization sync;
private Set syncAlphabet;
private Set listeners = new HashSet();
/*
* current set of states
*/
private StatesTuple current;
/**
* Construct a mix with the given list of automata.
*
* @param autos a List of automaton objects
*/
public MixPlay(List autos) {
this.autos = autos;
this.sync = new DefaultSynchronization();
}
/**
* Construct an empty mix.
*
*/
public MixPlay() {
this.autos = new ArrayList();
this.sync = new DefaultSynchronization();
}
/**
* Adds a new automaton to this mix.
*
* @param a
*/
public void addAutomaton(Automaton a) {
this.autos.add(a);
}
/**
* Reset the state of this mix.
* The current state is set to the start states of the
* mixed automata.
*/
public void reset() {
this.explored = new HashSet();
this.target = null;
Set[] states = new Set[autos.size()];
int i = 0;
Set synalph = new HashSet();
List alphl = new ArrayList();
for (Iterator it = autos.iterator(); it.hasNext();) {
Automaton a = (Automaton) it.next();
upperBound *= a.states().size();
states[i++] = a.initials();
Set alph = a.alphabet();
alphl.add(alph);
}
/* make synalph */
this.syncAlphabet = sync.synchronizable(alphl);
this.current = new StatesTuple(states);
}
/**
* Try to play for given target with given start states in each automaton.
*
* @param target
* the targeted letter
* @return a list of letters ending in <code>target</code>
*/
public List play(Object target) throws Exception {
this.target = target;
List word = new ArrayList();
List tuples = new ArrayList();
/* initial states */
try {
doPlay(word, tuples, current);
} catch (MixException mex) {
/* notify listeners of synchronization */
notify(mex.word, mex.states);
return mex.word;
}
return new ArrayList();
}
/**
* Notify each listener of the fired transitions when a word is found.
*
* @param word
* @param states
*/
private void notify(List word, List states) {
if (listeners.isEmpty() || word.isEmpty() || states.isEmpty())
return;
Iterator wit = word.iterator();
Iterator sit = states.iterator();
for (; sit.hasNext();) {
StatesTuple tup = (StatesTuple) sit.next();
Object lt = wit.next();
int ln = tup.sets.length;
/* fire event */
for (int i = 0; i < ln; i++) {
Automaton a = (Automaton) autos.get(i);
Set trans = new HashSet();
for (Iterator stit = tup.sets[i].iterator(); stit.hasNext();)
trans.addAll(a.delta((State) stit.next(), lt));
for (Iterator lit = listeners.iterator(); lit.hasNext();)
((AutomatonRunListener) lit.next()).fire(a, trans, lt);
}
}
}
/**
* Recursive play function
*
* @param word current accumulated word
* @param tuples current accumulated list of states tuples
* @param states current states tuple
*/
private void doPlay(List word, List tuples, StatesTuple states)
throws MixException {
/* set current states*/
current = states;
if (!word.isEmpty() && word.get(word.size() - 1).equals(target))
throw new MixException(word, tuples);
/* stop exploring on loop */
if (explored.contains(states))
return;
else
explored.add(states);
/* contains already tested transitions */
Set s = new HashSet();
/* list of transitions */
for (int i = 0; i < states.sets.length; i++) {
Transition[] trs = (Transition[]) ((Automaton) autos.get(i)).delta(
states.sets[i]).toArray(trmodel);
int ln = trs.length;
int k = random.nextInt(ln);
for (int j = 0; j < ln; j++) {
Transition tr = trs[(k + j) % ln];
if (s.contains(tr))
continue;
s.add(tr);
/* check synchronization */
if (!checkSynchronizableWith(tr.label(), states))
continue;
/* check early rejection */
if (!checkAccessibleWith(tr.label(), states))
continue;
/* ok - try this transition */
StatesTuple tup = advanceWith(tr.label(), states);
/* recurse - an exception is thrown if a match is found */
word.add(tr.label());
tuples.add(states);
// System.err.println("Trying " + word);
doPlay(word, tuples, tup);
// System.err.println("No way for " + word);
word.remove(word.size() - 1);
tuples.remove(tuples.size() - 1);
}
}
}
/**
* Checks synchronization of automaton on this letter
*
* @param object
* @param states
* @return
*/
private boolean checkSynchronizableWith(Object object, StatesTuple states) {
if (!syncAlphabet.contains(object))
return true;
for (int i = 0; i < states.sets.length; i++) {
Automaton auto = (Automaton) autos.get(i);
if (!sync.synchronizeWith(object, auto.alphabet()))
continue;
/*
* compute synchronizable transitions
*/
Set s = auto.delta(states.sets[i]);
Set adv = auto.getStateFactory().stateSet();
for (Iterator j = s.iterator(); j.hasNext();) {
Transition tr = (Transition) j.next();
Object lbl = tr.label();
if (sync.synchronize(lbl, object) != null)
adv.add(tr.end());
}
if (adv.isEmpty())
return false;
}
return true;
}
/**
* Checks that, if object is in the alphabet of an automaton, firing of
* transation does not preclude access of target
*
* @param object
* @param states
* @return
*/
private boolean checkAccessibleWith(Object object, StatesTuple states) {
return true;
}
/**
* @param object
* @param states
* @return
*/
private StatesTuple advanceWith(Object object, StatesTuple states) {
Set[] nstates = new Set[autos.size()];
for (int i = 0; i < states.sets.length; i++) {
Automaton auto = (Automaton) autos.get(i);
/*
* compute synchronizable transitions
*/
Set s = auto.delta(states.sets[i]);
Set adv = auto.getStateFactory().stateSet();
for (Iterator j = s.iterator(); j.hasNext();) {
Transition tr = (Transition) j.next();
Object lbl = tr.label();
if (sync.synchronize(lbl, object) != null)
adv.add(tr.end());
}
nstates[i] = adv.isEmpty() ? states.sets[i] : adv;
}
return new StatesTuple(nstates);
}
/*
* (non-Javadoc)
* @see rationals.AutomatonRunner#addRunListener(rationals.AutomatonRunListener)
*/
public void addRunListener(AutomatonRunListener l) {
listeners.add(l);
}
/*
* (non-Javadoc)
* @see rationals.AutomatonRunner#removeRunListener(rationals.AutomatonRunListener)
*/
public void removeRunListener(AutomatonRunListener l) {
listeners.remove(l);
}
/**
*
* @return
*/
public Synchronization getSynchronization() {
return sync;
}
/**
*
* @param sync
*/
public void setSynchronization(Synchronization sync) {
this.sync = sync;
}
}
/*
* Created on Apr 9, 2004
*
* $Log: MixPlay.java,v $ Revision 1.7 2004/08/31 14:16:22 bailly *** empty log
* message ***
*
* Revision 1.6 2004/04/15 11:51:00 bailly added randomization of MixPlay TODO:
* check accessibility of synchronization letter
*
* Revision 1.5 2004/04/14 10:02:14 bailly *** empty log message ***
*
* Revision 1.4 2004/04/14 07:33:43 bailly correct version of synchronization on
* the fly
*
* Revision 1.3 2004/04/13 07:08:38 bailly *** empty log message ***
*
* Revision 1.2 2004/04/12 16:37:59 bailly worked on synchronization algorithm :
* begins to work but there are still problems with proper implementation of
* backtracking
*
* Revision 1.1 2004/04/09 15:51:50 bailly Added algorithm for computing a mixed
* word from several automata (to be verified)
*
*/