You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

237 lines
9.3 KiB

#pragma once
#include "storm/automata/APSet.h"
#include "storm/automata/DeterministicAutomaton.h"
#include "storm/automata/HOAConsumerDAHeader.h"
#include "storm/exceptions/OutOfRangeException.h"
#include "storm/exceptions/InvalidOperationException.h"
#include "storm/storage/BitVector.h"
#include "storm/storage/SparseMatrix.h"
#include "storm/storage/expressions/ExpressionManager.h"
#include "storm/solver/SmtSolver.h"
#include "storm/utility/solver.h"
#include "cpphoafparser/consumer/hoa_consumer.hh"
#include "cpphoafparser/util/implicit_edge_helper.hh"
#include <boost/optional.hpp>
#include <exception>
namespace storm {
namespace automata {
class HOAConsumerDA : public HOAConsumerDAHeader {
private:
AcceptanceCondition::ptr acceptance;
DeterministicAutomaton::ptr da;
cpphoafparser::ImplicitEdgeHelper *helper = nullptr;
std::shared_ptr<storm::expressions::ExpressionManager> expressionManager;
std::vector<storm::expressions::Variable> apVariables;
std::unique_ptr<storm::solver::SmtSolver> solver;
storm::storage::BitVector seenEdges;
public:
typedef std::shared_ptr<HOAConsumerDA> ptr;
HOAConsumerDA() : seenEdges(0) {
expressionManager.reset(new storm::expressions::ExpressionManager());
storm::utility::solver::SmtSolverFactory factory;
solver = factory.create(*expressionManager);
}
~HOAConsumerDA() {
delete helper;
}
DeterministicAutomaton::ptr getDA() {
return da;
}
/**
* Called by the parser to notify that the BODY of the automaton has started [mandatory, once].
*/
virtual void notifyBodyStart() {
if (!header.numberOfStates) {
throw std::runtime_error("Parsing deterministic HOA automaton: Missing number-of-states header");
}
acceptance = header.getAcceptanceCondition();
da.reset(new DeterministicAutomaton(header.apSet, *header.numberOfStates, *header.startState, acceptance));
helper = new cpphoafparser::ImplicitEdgeHelper(header.apSet.size());
seenEdges.resize(*header.numberOfStates * helper->getEdgesPerState());
for (const std::string& ap : header.apSet.getAPs()) {
apVariables.push_back(expressionManager->declareBooleanVariable(ap));
}
}
/**
* Called by the parser for each "State: ..." item [multiple].
* @param id the identifier for this state
* @param info an optional string providing additional information about the state (empty pointer if not provided)
* @param labelExpr an optional boolean expression over labels (state-labeled) (empty pointer if not provided)
* @param accSignature an optional list of acceptance set indizes (state-labeled acceptance) (empty pointer if not provided)
*/
virtual void addState(unsigned int id, std::shared_ptr<std::string> info, label_expr::ptr labelExpr, std::shared_ptr<int_list> accSignature) {
if (accSignature) {
for (unsigned int accSet : *accSignature) {
acceptance->getAcceptanceSet(accSet).set(id);
}
}
if (labelExpr) {
throw std::runtime_error("Parsing deterministic HOA automaton: State-labeled automata not supported");
}
helper->startOfState(id);
}
/**
* Called by the parser for each implicit edge definition [multiple], i.e.,
* where the edge label is deduced from the index of the edge.
*
* If the edges are provided in implicit form, after every `addState()` there should be 2^|AP| calls to
* `addEdgeImplicit`. The corresponding boolean expression over labels / BitSet
* can be obtained by calling BooleanExpression.fromImplicit(i-1) for the i-th call of this function per state.
*
* @param stateId the index of the 'from' state
* @param conjSuccessors a list of successor state indizes, interpreted as a conjunction
* @param accSignature an optional list of acceptance set indizes (transition-labeled acceptance) (empty pointer if not provided)
*/
virtual void addEdgeImplicit(unsigned int stateId, const int_list& conjSuccessors, std::shared_ptr<int_list> accSignature) {
std::size_t edgeIndex = helper->nextImplicitEdge();
if (conjSuccessors.size() != 1) {
throw std::runtime_error("Parsing deterministic HOA automaton: Does not support alternation (conjunction of successor states)");
}
if (accSignature) {
throw std::runtime_error("Parsing deterministic HOA automaton: Does not support transition-based acceptance");
}
da->setSuccessor(stateId, edgeIndex, conjSuccessors.at(0));
markEdgeAsSeen(stateId, edgeIndex);
}
/**
* Called by the parser for each explicit edge definition [optional, multiple], i.e.,
* where the label is either specified for the edge or as a state-label.
* <br/>
* @param stateId the index of the 'from' state
* @param labelExpr a boolean expression over labels (empty pointer if none provided, only in case of state-labeled states)
* @param conjSuccessors a list of successors state indizes, interpreted as a conjunction
* @param accSignature an optional list of acceptance set indizes (transition-labeled acceptance) (empty pointer if none provided)
*/
virtual void addEdgeWithLabel(unsigned int stateId, label_expr::ptr labelExpr, const int_list& conjSuccessors, std::shared_ptr<int_list> accSignature) {
if (conjSuccessors.size() != 1) {
throw std::runtime_error("Parsing deterministic HOA automaton: Does not support alternation (conjunction of successor states)");
}
if (accSignature) {
throw std::runtime_error("Parsing deterministic HOA automaton: Does not support transition-based acceptance");
}
std::size_t successor = conjSuccessors.at(0);
solver->reset();
solver->add(labelToStormExpression(labelExpr));
solver->allSat(apVariables,
[this, stateId, successor] (storm::expressions::SimpleValuation& valuation) {
// construct edge index from valuation
APSet::alphabet_element edgeIndex = header.apSet.elementAllFalse();
for (std::size_t i = 0; i < apVariables.size(); i++) {
if (valuation.getBooleanValue(apVariables[i])) {
edgeIndex = header.apSet.elementAddAP(edgeIndex, i);
}
}
// require: edge already exists -> same successor
STORM_LOG_THROW(!alreadyHaveEdge(stateId, edgeIndex) || da->getSuccessor(stateId, edgeIndex) == successor,
storm::exceptions::InvalidOperationException, "HOA automaton: multiple definitions of successor for state " << stateId << " and edge " << edgeIndex);
// std::cout << stateId << " -(" << edgeIndex << ")-> " << successor << std::endl;
da->setSuccessor(stateId, edgeIndex, successor);
markEdgeAsSeen(stateId, edgeIndex);
// continue with next valuation
return true;
});
}
/**
* Called by the parser to notify the consumer that the definition for state `stateId`
* has ended [multiple].
*/
virtual void notifyEndOfState(unsigned int stateId) {
helper->endOfState();
}
/**
* Called by the parser to notify the consumer that the automata definition has ended [mandatory, once].
*/
virtual void notifyEnd() {
// require that we have seen all edges, i.e., that the automaton is complete
STORM_LOG_THROW(seenEdges.full(),
storm::exceptions::InvalidOperationException, "HOA automaton has mismatch in number of edges, not complete?");
}
/**
* Called by the parser to notify the consumer that an "ABORT" message has been encountered
* (at any time, indicating error, the automaton should be discarded).
*/
virtual void notifyAbort() {
throw std::runtime_error("Parsing deterministic automaton: Automaton is incomplete (abort)");
}
/**
* Is called whenever a condition is encountered that merits a (non-fatal) warning.
* The consumer is free to handle this situation as it wishes.
*/
virtual void notifyWarning(const std::string& warning) {
// IGNORE
(void)warning;
}
private:
storm::expressions::Expression labelToStormExpression(label_expr::ptr labelExpr) {
switch (labelExpr->getType()) {
case label_expr::EXP_AND:
return labelToStormExpression(labelExpr->getLeft()) && labelToStormExpression(labelExpr->getRight());
case label_expr::EXP_OR:
return labelToStormExpression(labelExpr->getLeft()) || labelToStormExpression(labelExpr->getRight());
case label_expr::EXP_NOT:
return !labelToStormExpression(labelExpr->getLeft());
case label_expr::EXP_TRUE:
return expressionManager->boolean(true);
case label_expr::EXP_FALSE:
return expressionManager->boolean(false);
case label_expr::EXP_ATOM: {
unsigned int apIndex = labelExpr->getAtom().getAPIndex();
STORM_LOG_THROW(apIndex < apVariables.size(),
storm::exceptions::OutOfRangeException, "HOA automaton refers to non-existing atomic proposition");
return apVariables.at(apIndex).getExpression();
}
}
throw std::runtime_error("Unknown label expression operator");
}
bool alreadyHaveEdge(std::size_t stateId, std::size_t edgeIndex) {
return seenEdges.get(stateId * helper->getEdgesPerState() + edgeIndex);
}
void markEdgeAsSeen(std::size_t stateId, std::size_t edgeIndex) {
seenEdges.set(stateId * helper->getEdgesPerState() + edgeIndex);
}
};
}
}