/* * Copyright (C) 2020 Jacob Maxam (jacob.maxam@googlemail.com) * Copyright (C) 2020 University of Freiburg * * This file is part of the ULTIMATE Automata Library. * * The ULTIMATE Automata Library is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * The ULTIMATE Automata Library 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 the ULTIMATE Automata Library. If not, see . * * Additional permission under GNU GPL version 3 section 7: * If you modify the ULTIMATE Automata Library, or any covered work, by linking * or combining it with Eclipse RCP (or a modified version of Eclipse RCP), * containing parts covered by the terms of the Eclipse Public License, the * licensors of the ULTIMATE Automata Library grant you additional permission * to convey the resulting work. */ package de.uni_freiburg.informatik.ultimate.automata.counting; import java.math.BigInteger; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import de.uni_freiburg.informatik.ultimate.automata.AutomataLibraryException; import de.uni_freiburg.informatik.ultimate.automata.AutomataLibraryServices; import de.uni_freiburg.informatik.ultimate.automata.IOperation; import de.uni_freiburg.informatik.ultimate.automata.nestedword.NestedWord; import de.uni_freiburg.informatik.ultimate.automata.statefactory.IIntersectionStateFactory; import de.uni_freiburg.informatik.ultimate.automata.statefactory.IStateFactory; import de.uni_freiburg.informatik.ultimate.core.model.services.ILogger; import de.uni_freiburg.informatik.ultimate.core.model.services.ILogger.LogLevel; import de.uni_freiburg.informatik.ultimate.lib.smtlibutils.SmtSortUtils; import de.uni_freiburg.informatik.ultimate.lib.smtlibutils.binaryrelation.RelationSymbol; import de.uni_freiburg.informatik.ultimate.logic.Logics; import de.uni_freiburg.informatik.ultimate.logic.Script; import de.uni_freiburg.informatik.ultimate.logic.Script.LBool; import de.uni_freiburg.informatik.ultimate.logic.Term; import de.uni_freiburg.informatik.ultimate.smtinterpol.smtlib2.SMTInterpol; /** * Acceptance method for Counting Automata * * @author Jacob Maxam */ public class Acceptance> implements IOperation { private final AutomataLibraryServices mServices; private final Script mScript; private final ILogger mLogger; private final CountingAutomaton mOperand; private final List mWord; private final LBool mResult; public Acceptance(final AutomataLibraryServices services, final CountingAutomaton operand, final NestedWord word) throws AutomataLibraryException { mServices = services; mLogger = mServices.getLoggingService().getLogger(this.getClass()); mOperand = operand; mWord = word.asList(); mScript = new SMTInterpol(); mScript.setLogic(Logics.LIA); if (mLogger.isInfoEnabled()) { mLogger.info(startMessage()); } mResult = computeResult(); if (mLogger.isInfoEnabled()) { mLogger.info(exitMessage()); } } private LBool computeResult() { ArrayList> preConditions = new ArrayList>(); preConditions.add(new ArrayList()); return iterativeAcceptance(mOperand, preConditions); } private LBool iterativeAcceptance(CountingAutomaton ca, ArrayList> preConditions) { int wordLength = mWord.size(); List states = new ArrayList(); states.addAll(ca.getStates()); LBool result = LBool.UNSAT; int step = 0; Term[] conditions = new Term[wordLength + 2]; ArrayList stateVisited = new ArrayList(); for (int i = 0; i < wordLength + 2; i++) { stateVisited.add((STATE) new Object()); } int[] pathTaken = new int[wordLength + 2]; conditions[0] = dnfToFormula(preConditions, 0); mLogger.log(LogLevel.INFO, wordLength); mLogger.log(LogLevel.INFO, stateVisited.size()); while(step > 0 || pathTaken[step] < states.size()){ mLogger.log(LogLevel.INFO, "entering loop with step = " + step +", trying path #" + (pathTaken[step] + 1)); if (step > wordLength) { mLogger.log(LogLevel.INFO, "end of word reached. checking term satisfiability:"); conditions[step] = mScript.term("and", conditions[step], dnfToFormula(ca.getFinalConditions().get(stateVisited.get(step)).getCondition(), step)); Term conditionsQuantified = mScript.quantifier(mScript.EXISTS, conditions[step].getFreeVars(), conditions[step], null); mLogger.log(LogLevel.INFO, conditionsQuantified); mScript.assertTerm(conditionsQuantified); LBool pathResult = mScript.checkSat(); mScript.resetAssertions(); if (pathResult == LBool.SAT) { return LBool.SAT; } else if(pathResult == LBool.UNKNOWN) {result = LBool.UNKNOWN;} mLogger.log(LogLevel.INFO, "term unsatisfiable"); step--; pathTaken[step]++; } else if (step > 0 && pathTaken[step] >= ca.getTransitions().get(stateVisited.get(step)).size()){ mLogger.log(LogLevel.INFO, "only " + ca.getTransitions().get(stateVisited.get(step)).size() + " possible transitions. going back"); step--; pathTaken[step]++; } else if (step == 0){ mLogger.log(LogLevel.INFO, "choosing initial state: " + states.get(pathTaken[step])); conditions[step + 1] = mScript.term("and", conditions[step], dnfToFormula(ca.getInitialConditions().get(states.get(pathTaken[step])).getCondition(), step)); conditions[step + 1] = mScript.term("and", conditions[step + 1], updateToFormula(new ArrayList(), ca.getCounter(), step)); stateVisited.set(step + 1, states.get(pathTaken[step])); pathTaken[step + 1] = 0; step++; } else { mLogger.log(LogLevel.INFO, "state: " + stateVisited.get(step)); Transition t = ca.getTransitions().get(stateVisited.get(step)).get(pathTaken[step]); mLogger.log(LogLevel.INFO, "checking transition to " + t.getSucState()); LETTER a = mWord.get(step-1); LETTER b = (LETTER) t.getLetter(); mLogger.log(LogLevel.INFO, "required letter: '" + a + "', letter in transition: '" + b + "'"); if (a.equals(b)){ mLogger.log(LogLevel.INFO, "found transition. going on"); conditions[step + 1] = mScript.term("and", conditions[step], dnfToFormula(t.getGuards(), step), updateToFormula(t.getUpdates(), ca.getCounter(), step)); stateVisited.set(step + 1, (STATE) t.getSucState()); pathTaken[step + 1] = 0; step++; } else { mLogger.log(LogLevel.INFO, "transition unavailable"); pathTaken[step]++; } } } mLogger.log(LogLevel.INFO, "no more states left"); return result; } private Term dnfToFormula(ArrayList> dnf, int step) { String counterSuffix = "#" + String.valueOf(step); Term dnfFormula, conjunctionFormula, atomicGuardFormula, leftCounterVariable, rightSide, rightCounterVariable, constant; dnfFormula = null; for (List guardList : dnf) { conjunctionFormula = null; for (Guard guard : guardList) { atomicGuardFormula = null; leftCounterVariable = null; rightSide = null; switch (guard.getTermType()) { case TRUE: atomicGuardFormula = mScript.term("true"); break; case FALSE: atomicGuardFormula = mScript.term("false"); break; case CONSTANT: leftCounterVariable = mScript.variable(guard.getCounterLeft().getCounterName() + counterSuffix, SmtSortUtils.getIntSort(mScript)); rightSide = mScript.numeral(BigInteger.valueOf(guard.getConstant())); break; case COUNTER: leftCounterVariable = mScript.variable(guard.getCounterLeft().getCounterName() + counterSuffix, SmtSortUtils.getIntSort(mScript)); rightSide = mScript.variable(guard.getCounterRight().getCounterName() + counterSuffix, SmtSortUtils.getIntSort(mScript)); break; case SUM: leftCounterVariable = mScript.variable(guard.getCounterLeft().getCounterName() + counterSuffix, SmtSortUtils.getIntSort(mScript)); rightCounterVariable = mScript.variable(guard.getCounterRight().getCounterName() + counterSuffix, SmtSortUtils.getIntSort(mScript)); constant = mScript.numeral(BigInteger.valueOf(guard.getConstant())); rightSide = mScript.term("+", rightCounterVariable, constant); break; } if (atomicGuardFormula == null) { switch (guard.getRelationSymbol()) { case EQ: atomicGuardFormula = mScript.term("=", leftCounterVariable, rightSide); break; case DISTINCT: atomicGuardFormula = mScript.term("distinct", leftCounterVariable, rightSide); break; case GREATER: atomicGuardFormula = mScript.term(">", leftCounterVariable, rightSide); break; case LESS: atomicGuardFormula = mScript.term("<", leftCounterVariable, rightSide); break; case GEQ: atomicGuardFormula = mScript.term(">=", leftCounterVariable, rightSide); break; case LEQ: atomicGuardFormula = mScript.term("<=", leftCounterVariable, rightSide); break; } } if (conjunctionFormula == null) { conjunctionFormula = atomicGuardFormula; } else { conjunctionFormula = mScript.term("and", conjunctionFormula, atomicGuardFormula); } } if (conjunctionFormula == null) { conjunctionFormula = mScript.term("true"); } if (dnfFormula == null) { dnfFormula = conjunctionFormula; } else { dnfFormula = mScript.term("or", dnfFormula, conjunctionFormula); } } if (dnfFormula == null) { dnfFormula = mScript.term("false"); } return dnfFormula; } private Term updateToFormula(List updates, List allCount, int step) { List allCounters = new ArrayList(); allCounters.addAll(allCount); Term updateFormula, atomicUpdateFormula, leftCounterVariable, rightSide, constant, rightCounterVariable; updateFormula = null; for (Update update : updates) { atomicUpdateFormula = null; leftCounterVariable = mScript.variable( update.getCounterLeft().getCounterName() + "#" + String.valueOf(step + 1), SmtSortUtils.getIntSort(mScript)); allCounters.remove(update.getCounterLeft()); switch (update.getTermType()) { case CONSTANT: constant = mScript.numeral(BigInteger.valueOf(update.getConstant())); atomicUpdateFormula = mScript.term("=", leftCounterVariable, constant); break; case COUNTER: rightCounterVariable = mScript.variable( update.getCounterRight().getCounterName() + "#" + String.valueOf(step), SmtSortUtils.getIntSort(mScript)); atomicUpdateFormula = mScript.term("=", leftCounterVariable, rightCounterVariable); break; case SUM: constant = mScript.numeral(BigInteger.valueOf(update.getConstant())); rightCounterVariable = mScript.variable( update.getCounterRight().getCounterName() + "#" + String.valueOf(step), SmtSortUtils.getIntSort(mScript)); rightSide = mScript.term("+", constant, rightCounterVariable); atomicUpdateFormula = mScript.term("=", leftCounterVariable, rightSide); break; } if (updateFormula == null) { updateFormula = atomicUpdateFormula; } else { updateFormula = mScript.term("and", updateFormula, atomicUpdateFormula); } } for (Counter notUpdatedCounter : allCounters) { leftCounterVariable = mScript.variable( notUpdatedCounter.getCounterName() + "#" + String.valueOf(step + 1), SmtSortUtils.getIntSort(mScript)); rightCounterVariable = mScript.variable( notUpdatedCounter.getCounterName() + "#" + String.valueOf(step), SmtSortUtils.getIntSort(mScript)); atomicUpdateFormula = mScript.term("=", leftCounterVariable, rightCounterVariable); if (updateFormula == null) { updateFormula = atomicUpdateFormula; } else { updateFormula = mScript.term("and", updateFormula, atomicUpdateFormula); } } return updateFormula; } @Override public Object getResult() { return mResult; } @Override public boolean checkResult(CRSF stateFactory) throws AutomataLibraryException { // TODO: Check the result return true; } }