All Downloads are FREE. Search and download functionalities are using the official Maven repository.

de.learnlib.eqtests.basic.SampleSetEQOracle Maven / Gradle / Ivy

There is a newer version: 0.12.0
Show newest version
/* Copyright (C) 2014 TU Dortmund
 * This file is part of LearnLib, http://www.learnlib.de/.
 * 
 * LearnLib is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License version 3.0 as published by the Free Software Foundation.
 * 
 * LearnLib is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with LearnLib; if not, see
 * .
 */
package de.learnlib.eqtests.basic;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;

import net.automatalib.automata.concepts.SuffixOutput;
import net.automatalib.words.Word;
import de.learnlib.api.EquivalenceOracle;
import de.learnlib.api.MembershipOracle;
import de.learnlib.api.Query;
import de.learnlib.oracles.DefaultQuery;


/**
 * An equivalence oracle that tests a hypothesis against a fixed set of sample queries.
 * 

* Sample queries are provided through one of the {@code add(...)} or {@code addAll(...)} * methods of this class. A query consists of an input word (split into a prefix * and a suffix), and an expected output. During an equivalence query, for each * of those queries if the respective actual suffix output of the hypothesis equals the expected * output. *

* This oracle will always repeatedly test queries from the sample set if they turned out * to be counterexamples. However, the oracle can be configured to remove queries from the * sample set if they did not serve as counterexamples. * * @author Malte Isberner * * @param input symbol type * @param output domain type */ public class SampleSetEQOracle implements EquivalenceOracle, I, D>{ private final boolean removeUnsuccessful; private final List> testQueries; /** * Constructor. Initializes the oracle with an empty sample set. * * @param removeUnsuccessful if set to {@code true}, queries will be removed * from the sample set if they did not reveal a counterexample. Otherwise, * all queries from the sample set will always be tested upon each invocation * of {@link #findCounterExample(SuffixOutput, Collection)}. */ public SampleSetEQOracle(boolean removeUnsuccessful) { this.removeUnsuccessful = removeUnsuccessful; if(!removeUnsuccessful) { testQueries = new ArrayList<>(); } else { testQueries = new LinkedList<>(); // for O(1) removal of elements } } /** * Adds a query word along with its expected output to the sample set. * * @param input the input word * @param expectedOutput the expected output for this word * @return {@code this}, to enable chained {@code add} or {@code addAll} calls */ public SampleSetEQOracle add(Word input, D expectedOutput) { testQueries.add(new DefaultQuery<>(input, expectedOutput)); return this; } /** * Adds several query words to the sample set. The expected output is determined by * means of the specified membership oracle. * * @param oracle the membership oracle used to determine expected outputs * @param words the words to be added to the sample set * @return {@code this}, to enable chained {@code add} or {@code addAll} calls */ @SafeVarargs public final SampleSetEQOracle addAll(MembershipOracle oracle, Word ...words) { return addAll(oracle, Arrays.asList(words)); } /** * Adds queries to the sample set. These must be {@link DefaultQuery}s, which allow * for retrieving the corresponding (expected) output. * * @param newTestQueries the queries to add to the sample set * @return {@code this}, to enable chained {@code add} or {@code addAll} calls */ @SafeVarargs public final SampleSetEQOracle addAll(DefaultQuery... newTestQueries) { return addAll(Arrays.asList(newTestQueries)); } /** * Adds queries to the sample set. These must be {@link DefaultQuery}s, which allow * for retrieving the corresponding (expected) output. * * @param newTestQueries the queries to add to the sample set * @return {@code this}, to enable chained {@code add} or {@code addAll} calls */ public SampleSetEQOracle addAll(Collection> newTestQueries) { testQueries.addAll(newTestQueries); return this; } /** * Adds words to the sample set. The expected output is determined by means * of the specified membership oracle. * * @param oracle the membership oracle used to determine the expected output * @param words the words to add * @return {@code this}, to enable chained {@code add} or {@code addAll} calls */ public SampleSetEQOracle addAll(MembershipOracle oracle, Collection> words) { if(words.isEmpty()) { return this; } List> newQueries = new ArrayList<>(words.size()); for(Word w : words) { newQueries.add(new DefaultQuery(w)); } oracle.processQueries(newQueries); testQueries.addAll(newQueries); return this; } /* * (non-Javadoc) * @see de.learnlib.api.EquivalenceOracle#findCounterExample(java.lang.Object, java.util.Collection) */ @Override public DefaultQuery findCounterExample(SuffixOutput hypothesis, Collection inputs) { Iterator> queryIt = testQueries.iterator(); while(queryIt.hasNext()) { DefaultQuery query = queryIt.next(); if(checkInputs(query, inputs)) { if(!test(query, hypothesis)) { return query; } else if(removeUnsuccessful) { queryIt.remove(); } } } return null; } /** * Tests if the suffix output of the given hypothesis matches the expected output stored * in the query. * @param query the query, containing the expected output * @param hypOutput the suffix output portion of the hypothesis * @return {@code true} if the suffix output by {@code hypOutput} matches the expected * output stored in {@code query}, {@code false} otherwise. */ private static boolean test(DefaultQuery query, SuffixOutput hypOutput) { D hypOut = hypOutput.computeSuffixOutput(query.getPrefix(), query.getSuffix()); return Objects.equals(hypOut, query.getOutput()); } /** * Tests if the input word of the given {@link Query} consists entirely of symbols * in {@code inputs}. * @param query the query to test * @param inputs the set of allowed inputs * @return {@code true} if the input word of {@code query} consists entirely of symbols * in {@code inputs}, {@code false} otherwise */ private static boolean checkInputs(Query query, Collection inputs) { for(I sym : query.getPrefix()) { if(!inputs.contains(sym)) { return false; } } for(I sym : query.getSuffix()) { if(!inputs.contains(sym)) { return false; } } return true; } }