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

de.learnlib.oracle.equivalence.SampleSetEQOracle Maven / Gradle / Ivy

There is a newer version: 0.17.0
Show newest version
/* Copyright (C) 2013-2018 TU Dortmund
 * This file is part of LearnLib, http://www.learnlib.de/.
 *
 * 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 de.learnlib.oracle.equivalence;

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 de.learnlib.api.oracle.EquivalenceOracle;
import de.learnlib.api.oracle.MembershipOracle;
import de.learnlib.api.query.DefaultQuery;
import de.learnlib.api.query.Query;
import net.automatalib.automata.concepts.SuffixOutput;
import net.automatalib.words.Word;

/**
 * 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. * * @param * input symbol type * @param * output domain type * * @author Malte Isberner */ 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 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; } /** * 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; } @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 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; } /** * 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()); } }