/*
* (C) Copyright 2005 Arnaud Bailly (arnaud.oqube@gmail.com),
* Yves Roos (yroos@lifl.fr) and others.
*
* Licensed under the Apache License, Version 2.0 (the License);
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an AS IS BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package rationals.transductions;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import rationals.Automaton;
import rationals.NoSuchStateException;
import rationals.State;
import rationals.Transition;
/**
* A deterministic transducer implementation.
*
* @author nono
* @version $Id: DeterministicTransducer.java 8 2007-05-29 19:52:29Z oqube $
*/
public class DeterministicTransducer extends Transducer {
/**
* There may be no more than one starting state in a deterministic
* transducer. <em>Note</em>: this is not really useful as
* determinization can be later applied to this automaton, but it helps.
*
* @see rationals.Automaton#addState(boolean, boolean)
*/
public State addState(boolean initial, boolean terminal) {
if (initial && initials().size() > 0)
throw new IllegalArgumentException(
"Cannot have more than one start state in a deterministic transducer");
return super.addState(initial, terminal);
}
/**
* This method ensures that no nondeterminism is introduced in the
* transducer. For each added transition, it checks that there is no other
* transition with same input label starting from the given start state and
* that the input label is not null.
*
* @see rationals.Automaton#addTransition(rationals.Transition)
*/
public void addTransition(Transition transition)
throws NoSuchStateException {
TransducerRelation rel = (TransducerRelation)transition.label();
if (rel.getIn() == null)
throw new IllegalArgumentException(
"Cannot have epsilon input transition in a deterministic transducer");
Set s = delta(transition.start());
for (Iterator i = s.iterator(); i.hasNext();) {
Transition tr = (Transition) i.next();
TransducerRelation r2 = (TransducerRelation)tr.label();
if (rel.getIn().equals(r2.getIn()))
throw new IllegalArgumentException(
"Cannot duplicate input transition in a deterministic transducer");
}
super.addTransition(transition);
}
/*
* (non-Javadoc)
*
* @see rationals.transductions.Transduction#image(rationals.Automaton)
*/
public Automaton image(Automaton a) {
// TODO Auto-generated method stub
return super.image(a);
}
/*
* (non-Javadoc)
*
* @see rationals.transductions.Transduction#image(java.lang.Object[])
*/
public Automaton image(Object[] word) {
/* set of transitions fired */
Set s = initials();
List /* < Set < Transition > > */trs = new ArrayList(); /*
* list of set
* of
* transitions
*/
/*
* first compute a the list of set of transition that the input word
* crosses
*/
int ln = word.length;
for (int i = 0; i < ln; i++) {
Set tf = new HashSet();
s = step(s, word[i], tf);
if (s.isEmpty())
return new Automaton(); /* language is empty */
trs.add(tf);
}
s.retainAll(terminals());
if (s.isEmpty())
return new Automaton();
Automaton a = new Automaton();
State cur = a.addState(true, false);
Iterator it = trs.iterator();
while (it.hasNext()) {
/* always ONE transition */
Transition tr = (Transition) ((Set) it.next()).iterator().next();
TransducerRelation rel = (TransducerRelation)tr.label();
State ns = a.addState(false, it.hasNext() ? false : true);
try {
a.addTransition(new Transition(cur, rel.getOut(), ns));
} catch (NoSuchStateException e) {
}
cur = ns;
}
return a;
}
/**
* Returns the set of UIO sequences for each state in this transducer (if
* they exist). A set <code>{UIO1,UIO2,... , UIOn} </code>, for
* <code>n</code> the number of states in this automaton is a sequence of
* transitions such that for any two states <code>qi,qj</code> in the
* transducer, there exists <code>UIOi != UIOj</code>. It allows
* distinction of any two states by both input and output sequences.
* <p>
* This method returns a Map from State of this transducer to UIO sequences,
* that is List of {@see rationals.transductions.TransducerRelation}objects. It
* repeteadly breaks a partition of states using transitions and is an
* adaptation of the original UIO algorithm found in
* <p>"<em>A new technique for generating protocol tests</em>", Sabnani
* and Dahbura, 1985
* <p>
* This algorithm assumer that this transducer is minimal.
*
* @return a Map from State to List of TransducerRelation
*/
public Map makeUIOSet() throws TransductionException {
/* return map */
Map ret = new HashMap();
/* map from words to set of states that can read this word */
Map /* < Object , Set < State > > */am = new HashMap();
/* set of states not assigned an UIO */
/* initial mapping from singletons to states */
for (Iterator it = delta().iterator(); it.hasNext();) {
Transition tr = (Transition) it.next();
List l = new ArrayList();
l.add(tr.label());
Set s = (Set) am.get(l);
if (s == null) {
s = getStateFactory().stateSet();
am.put(l, s);
}
s.add(tr.start());
}
do {
Set rm = null;
/* remove singleton sets and clear remaining sets */
do {
/* clean set of states */
if (rm != null && !rm.isEmpty()) {
for (Iterator i = am.values().iterator(); i.hasNext();) {
Set s = (Set) i.next();
s.removeAll(rm);
}
}
rm = getStateFactory().stateSet();
for (Iterator i = am.entrySet().iterator(); i.hasNext();) {
Map.Entry e = (Map.Entry) i.next();
Set s = (Set) e.getValue();
if (s.size() == 1) {
State st = (State) s.iterator().next();
ret.put(st, e.getKey());
i.remove();
rm.add(st);
}
}
} while (!rm.isEmpty());
/* duplicate */
Map nam = new HashMap(am);
/* handle multiple sets */
for (Iterator i = nam.entrySet().iterator(); i.hasNext();) {
Map.Entry e = (Map.Entry) i.next();
List word = (List) e.getKey();
Set states = (Set) e.getValue();
/* partition states set */
Set step = steps(states, word);
Set dt = delta(step);
for (Iterator i3 = dt.iterator(); i3.hasNext();) {
Transition tr = (Transition) i3.next();
List nw = new ArrayList(word);
Set ns = getStateFactory().stateSet();
nw.add(tr.label());
for (Iterator i2 = states.iterator(); i2.hasNext();) {
State st = (State) i2.next();
Set tos = steps(st, nw);
if (tos.size() == 0)
continue;
ns.add(st);
}
/* add word with list of states */
am.put(nw, ns);
}
i.remove();
am.remove(word);
}
} while (!am.isEmpty());
return ret;
}
/**
* Compute - if it exists - a characterizing set for this transducer. A
* characterizing set <code>W</code> is a set of input words
* <code>{w1,w2, ..., wn}</code> such that for any two states
* <code>qi,qj</code> in the transducer, there exists <code>wk</code> in
* <code>W</code> such that <code>out(qi,wk) != out(qj,wk)</code>.
* <p>
* This algorithm is similar to the algorithm for computing UIO sequences.
* It computes a sequence for each word
*
* @return a Set of List of Object - input letters.
*/
public Set makeCharacterizingSet() {
/* return map */
Set ret = new HashSet();
/*
* map from words to equivalence class of states for output with this
* word
*/
Map /* < Object , Set < State > > */am = new HashMap();
/* set of states not assigned an w */
Set /* < State > */sm = getStateFactory().stateSet();
/* initial partition */
List l = new ArrayList();
am.put(l, getStateFactory().stateSet(states()));
do {
/* duplicate */
Map nam = new HashMap(am);
/* handle multiple sets */
for (Iterator i = nam.entrySet().iterator(); i.hasNext();) {
Map.Entry e = (Map.Entry) i.next();
List word = (List) e.getKey();
Set states = (Set) e.getValue();
states.removeAll(sm);
/* local map storing output word equivalent states */
Map /* < List < Object > , Set < State > > */equiv = new HashMap();
/* local map storing output to input */
Map /* < List < Object > , List < Object > > */wwm = new HashMap();
/* partition states set */
for (Iterator i3 = inputAlphabet().iterator(); i3.hasNext();) {
/* append letter */
List nw = new ArrayList(word);
nw.add(i3.next());
for (Iterator i2 = states.iterator(); i2.hasNext();) {
State st = (State) i2.next();
List out = Arrays.asList(image(st, nw.toArray()));
if (out == null)
continue;
Set ns = (Set) equiv.get(out);
if (ns == null) {
ns = getStateFactory().stateSet();
equiv.put(out, ns);
wwm.put(out, nw);
}
ns.add(st);
}
/* remove states in singleton equiv set */
for (Iterator i2 = equiv.entrySet().iterator(); i2.hasNext();) {
Map.Entry me = (Map.Entry) i2.next();
List out = (List) me.getKey();
Set s = (Set) me.getValue();
if (s.size() == 1) {
/* nw is a new characteristic word */
ret.add(wwm.remove(out));
/* remove state from states */
states.removeAll(s);
sm.addAll(s);
} else {
am.put(wwm.remove(out), s);
}
i2.remove();
}
}
am.remove(word);
if (am.isEmpty())
break;
}
} while (!am.isEmpty());
return ret;
}
/**
* Compute output word from given state and given input word. The output is
* a list of objects without <code>null</code>.
*
* @param st
* the starting state
* @param word
* the word to read.
* @return a List of output letters. May be null if input word cannot be
* read from this state.
*/
public Object[] image(State st, Object[] word) {
Set s = new HashSet();
s.add(st);
List /* < Set < Transition > > */trs = new ArrayList(); /*
* list of set
* of
* transitions
*/
/*
* first compute a the list of set of transition that the input word
* crosses
*/
for (int i =0;i<word.length;i++) {
Set tf = new HashSet();
s = step(s, word[i], tf);
if (s.isEmpty())
return null; /* not recognized */
trs.add(tf);
}
List ret = new ArrayList();
Iterator it = trs.iterator();
while (it.hasNext()) {
/* always ONE transition */
Transition tr = (Transition) ((Set) it.next()).iterator().next();
TransducerRelation rel = (TransducerRelation)tr.label();
Object l = rel.getOut();
if (l != null)
ret.add(l);
}
return ret.toArray();
}
/**
* Check that a given set of input sequences is a characterizing set
* for this transducer.
*
* @param s a Set of List of Object input letters.
* @return true if <code>s</code> is a characterizing set for this, false otherwise.
*/
public boolean verifyW(Set s) {
/* construct map from state to set of output words */
State[] sts = (State[])states().toArray(new State[states().size()]);
Set[] words = new Set[sts.length];
final int len = sts.length;
for (Iterator i = s.iterator(); i.hasNext();) {
List in = (List) i.next();
for (int j =0;j<len;j++) {
State st = sts[j];
Object[] out = image(st, in.toArray());
if(out == null)
continue;
Set ss = words[j];
if (ss == null) {
ss = words[j] = new HashSet();
}
ss.add(out);
}
}
/* ensure that no two states hava same set of output words */
for(int i=0;i<len;i++)
for(int j = i+1;j<len;j++) {
if(words[i].equals(words[j]))
return false;
}
return true;
}
/**
* @param m
*/
public boolean verifyUIO(Map m) {
if(m.size() != states().size())
return false;
/* check is sequence is different from any other */
List[] words = new List[m.size()];
words = (List[]) m.values().toArray(words);
final int len = words.length;
for(int i=0;i<len;i++)
for(int j=i+1;j<len;j++)
if(words[i].equals(words[j]))
return false;
return true;
}
/* (non-Javadoc)
* @see rationals.transductions.RationalFunction#imageWord(java.util.List)
*/
public Object[] imageWord(List word) {
return image((State)initials().iterator().next(),word.toArray());
}
/* (non-Javadoc)
* @see rationals.transductions.RationalFunction#imageWord(java.lang.Object[])
*/
public Object[] imageWord(Object[] word) {
return image((State)initials().iterator().next(),word);
}
}