/*
* (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.ioautomata.testing;
import java.util.ArrayList;
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.ioautomata.IOAlphabetType;
import rationals.ioautomata.IOAutomaton;
import rationals.ioautomata.IOStateMachine;
import rationals.ioautomata.IOTransition;
import rationals.transformations.TransformationsToolBox;
/**
* An IOAutomata tester based on the alphab-beta evaluation algorithm for two
* players games.
* <p>
* The specification is expected to be in normal form, which means that set of
* states may be partitionned in two : player states from which only inputs are
* possible and opponent states from which only outputs are possible.
*
* @author nono
* @version $Id: AlphaBetaIOTester.java 2 2006-08-24 14:41:48Z oqube $
*/
public class AlphaBetaIOTester extends AbstractIOTester {
private IOAutomaton spec;
private IOStateMachine impl;
/* depth of search */
private int depth = 2;
/* precomputed initial states */
private Set q0;
/* the evaluator */
private Evaluator evaluator;
/*
* (non-Javadoc)
*
* @see rationals.ioautomata.testing.IOAutomataTester#test(rationals.ioautomata.IOStateMachine,
* rationals.ioautomata.IOAutomaton)
*/
public void test(IOStateMachine impl, IOAutomaton spec)
throws IOTestFailure {
this.spec = spec;
this.impl = impl;
this.evaluator.setTester(this);
List trace = new ArrayList();
/* the set of test executed so far */
Set /* < List < Object > > */traces = new HashSet();
traces.add(trace);
/* the current state - assume non deterministic automata */
Set state = q0 = TransformationsToolBox.epsilonClosure(spec.initials(),
spec);
while (true) {
/* select a move */
Move mv = select(trace, traces, state, depth);
if (mv == Move.terminate) {
return;
} else if (mv == Move.reset) {
impl.reset();
evaluator.reset();
traces.add(trace);
trace = new ArrayList();
state = TransformationsToolBox.epsilonClosure(spec.initials(),
spec);
System.err.println("Resetting");
continue;
} else if (mv == Move.out) {
System.err.println("Waiting for output");
/* get output from impl */
Object o = impl.output();
/* transform as output for spec */
IOTransition.IOLetter lt = new IOTransition.IOLetter(o,
IOAlphabetType.OUTPUT);
/* find next state */
Set next = spec.step(state, lt);
if (!next.isEmpty()) {
trace.add(lt);
evaluator.step(state, next);
state = next;
} else {
/* failure */
IOTestFailure fail = new IOTestFailure("unexpected output : "+lt);
fail.setFailureTrace(trace);
throw fail;
}
} else {
System.err.println("Selecting input : "+mv.move);
/* send input to impl */
impl.input(mv.move);
IOTransition.IOLetter lt = new IOTransition.IOLetter(mv.move,
IOAlphabetType.INPUT);
Set next = spec.step(state, lt);
evaluator.step(state, next);
state = next;
trace.add(lt);
}
}
}
/**
* @param trace
* the current trace
* @param traces
* the set of test traces
* @param state
* the current state
* @param depth2
* the search depth
* @return
*/
private Move select(List trace, Set traces, Set state, int depth2) {
/* split transitions into input/output sets */
Set itrs = spec.delta(state);
Set otrs = new HashSet(itrs);
filterInputs(itrs);
filterOutputs(otrs);
/* basic tests */
if ((itrs.size() == 0 && otrs.size() == 0))
return Move.reset;
/* check eta-coverage */
if (evaluator.isEnough(traces))
return Move.terminate;
/* check possible outputs */
if (!otrs.isEmpty())
return Move.out;
Map /* < Transition, Double > */desirability = new HashMap();
/* inputs evaluation */
double maxi = -Double.MAX_VALUE;
IOTransition maxt = null;
for (Iterator i = itrs.iterator(); i.hasNext();) {
IOTransition tr = (IOTransition) i.next();
Set end = spec.step(state, tr.label());
double e;
e = eval(end, tr, traces, Double.MAX_VALUE, -Double.MAX_VALUE,
depth, false);
if (e >= maxi) {
maxi = e;
maxt = tr;
}
}
System.err.println(" Maximum value of input "+maxi);
/* reset evaluation */
if (!state.equals(q0)) {
Set q0trs = spec.delta(q0);
List qtr = new ArrayList();
for (Iterator i = q0trs.iterator(); i.hasNext();) {
IOTransition tr = (IOTransition) i.next();
double e;
Set end = spec.step(q0, tr.label());
e = -eval(end, tr, traces, Double.MAX_VALUE, -Double.MAX_VALUE,
depth, false);
if (e > maxi) {
System.err.println("Found reset move "+tr +"with value"+e);
return Move.reset;
}
}
}
return new Move(((IOTransition.IOLetter) maxt.label()).label);
}
/**
* Alpha-beta evaluation.
*
* @param st
* the state reached by trans
* @param trans
* the transition to eval
* @param traces
* the set of test traces
* @param alpha
* the alpha threshold
* @param beta
* the beta threshold
* @param curdepth
* the current depth of search
* @param in
* true if state is a player state, false otherwise
* @return an evaluation for state st
*/
private double eval(Set st, IOTransition trans, Set traces, double alpha,
double beta, int curdepth, boolean in) {
/* end of recursion */
if (curdepth == 0) {
/* return result of evaluation function */
/*
* evaluate min distance of trace to traces double min =
* Double.MAX_VALUE; for (Iterator i = traces.iterator();
* i.hasNext();) { List l = (List) i.next(); double d =
* distance.distance(trace, l); if (d < min) min = d; }
*/
return evaluator.eval(traces, trans, st);
}
Set itrs = spec.delta(st);
/* basic tests */
if ((itrs.size() == 0))
return evaluator.eval(traces, trans, st);
/* inputs evaluation */
double maxi = alpha;
IOTransition maxt = null;
for (Iterator i = itrs.iterator(); i.hasNext();) {
IOTransition tr = (IOTransition) i.next();
Set end = spec.step(st, tr.label());
/* update trace and recurse */
double e = -eval(end, tr, traces, -beta, -alpha, curdepth - 1, true);
if (e > beta) {
beta = e;
}
if (beta >= alpha)
return beta;
}
return beta;
}
/**
* @return Returns the depth.
*/
public int getDepth() {
return depth;
}
/**
* @param depth
* The depth to set.
*/
public void setDepth(int depth) {
this.depth = depth;
}
/**
* @return Returns the evaluator.
*/
public Evaluator getEvaluator() {
return evaluator;
}
/**
* @param evaluator
* The evaluator to set.
*/
public void setEvaluator(Evaluator evaluator) {
this.evaluator = evaluator;
}
/**
* @return Returns the impl.
*/
public IOStateMachine getImpl() {
return impl;
}
/**
* @return Returns the q0.
*/
public Set getQ0() {
return q0;
}
/**
* @return Returns the spec.
*/
public IOAutomaton getSpec() {
return spec;
}
}