private Collection<Pair<List<AbstractState>, List<ARGState>>> computeCartesianProduct( final List<List<ARGState>> pSuccessorsForEdge, final Map<String, Integer> pStateToPos, final List<AbstractState> pInitialStates) throws InterruptedException, CPAException { // compute number of successors int count = 0; for (List<ARGState> successor : pSuccessorsForEdge) { if (successor.size() > 0) { count = count == 0 ? successor.size() : count * successor.size(); } } // no successor in every of the ARGs if (count == 0) { return Collections.emptySet(); } Collection<Pair<List<AbstractState>, List<ARGState>>> result = new ArrayList<>(count); // compute cartesian product int[] indices = new int[pSuccessorsForEdge.size()]; int nextIndex = 0; boolean restart; int lastSize = pSuccessorsForEdge.get(pSuccessorsForEdge.size() - 1).size(); if (lastSize == 0) { lastSize = 1; } while (indices[indices.length - 1] < lastSize) { shutdown.shutdownIfNecessary(); final List<ARGState> argSuccessors = new ArrayList<>(pSuccessorsForEdge.size()); // collect ARG successors for (int index = 0; index < indices.length; index++) { if (pSuccessorsForEdge.get(index).size() > 0) { argSuccessors.add( getUncoveredSuccessor(pSuccessorsForEdge.get(index).get(indices[index]))); } } // combine ARG states to get one cartesian product element, assume top state if no explicit // state information available result.add( Pair.of(combineARGStates(argSuccessors, pStateToPos, pInitialStates), argSuccessors)); // compute indices for elements of next cartesian element indices[nextIndex]++; restart = false; while (indices[nextIndex] >= pSuccessorsForEdge.get(nextIndex).size() && nextIndex < indices.length - 1) { nextIndex++; indices[nextIndex]++; restart = true; } while (restart && nextIndex > 0) { indices[--nextIndex] = 0; } } return result; }
private Pair<Map<String, Integer>, List<AbstractState>> identifyCompositeStateTypesAndTheirInitialInstances(Collection<ARGState> rootNodes) throws InterruptedException, CPAException { logger.log(Level.FINE, "Derive composite state structure of combined ARG"); List<AbstractState> initialState = new ArrayList<>(); Map<String, Integer> stateToPos = new HashMap<>(); List<String> automataStateNames = new ArrayList<>(); String name; int nextId = 0; Iterable<AbstractState> wrapped; logger.log(Level.FINE, "Add non-automaton states"); for (ARGState root : rootNodes) { shutdown.shutdownIfNecessary(); wrapped = getWrappedStates(root); for (AbstractState innerWrapped : wrapped) { shutdown.shutdownIfNecessary(); if (innerWrapped instanceof AssumptionStorageState) { continue; } name = getName(innerWrapped); if (stateToPos.containsKey(name)) { if (!initialState.get(stateToPos.get(name)).equals(innerWrapped)) { logger.log( Level.WARNING, "Abstract state ", innerWrapped.getClass(), " is used by multiple configurations, but cannot check that always start in the same initial state as it is assumed"); } } else { assert (initialState.size() == nextId); if (innerWrapped instanceof AutomatonState) { automataStateNames.add(name); } else { stateToPos.put(name, nextId); initialState.add(innerWrapped); nextId++; } } } } logger.log(Level.FINE, "Add automaton states related to specification"); Collections.sort(automataStateNames); int numRootStates = rootNodes.size(); Set<String> commonAutomataStates = new TreeSet<>(); for (int i = 1, j = 0; i < automataStateNames.size(); i++) { assert (j < i && j >= 0); if (automataStateNames.get(j).equals(automataStateNames.get(i))) { if (j + numRootStates - 1 == i) { // automaton states commonly used commonAutomataStates.add(automataStateNames.get(j)); } } else { j = i; } } // assume root is the root node of the first ARG constructed ARGState root = rootNodes.iterator().next(); if (root.getWrappedState() instanceof AbstractWrapperState) { wrapped = ((AbstractWrapperState) root.getWrappedState()).getWrappedStates(); } else { wrapped = Collections.singleton(root.getWrappedState()); } for (AbstractState innerWrapped : wrapped) { shutdown.shutdownIfNecessary(); name = getName(innerWrapped); if (commonAutomataStates.contains(name)) { assert (initialState.size() == nextId); stateToPos.put(name, nextId); if (!automatonARGBuilderSupport.registerAutomaton((AutomatonState) innerWrapped)) { logger.log( Level.SEVERE, "Property specification, given by automata specification, is ambigous."); throw new CPAException( "Ambigious property specification, automata specification contains automata with same name or same state names"); } initialState.add( automatonARGBuilderSupport.replaceStateByStateInAutomatonOfSameInstance( (AutomatonState) innerWrapped)); nextId++; } } return Pair.of(stateToPos, initialState); }
@Override public Collection<? extends AbstractState> getAbstractSuccessorsForEdge( AbstractState pElement, Precision pPrecision, CFAEdge pCfaEdge) throws CPATransferException { Preconditions.checkArgument(pElement instanceof AutomatonState); if (pElement instanceof AutomatonUnknownState) { // the last CFA edge could not be processed properly // (strengthen was not called on the AutomatonUnknownState or the strengthen operation had not // enough information to determine a new following state.) AutomatonState top = cpa.getTopState(); return Collections.singleton(top); } if (!(pCfaEdge instanceof MultiEdge)) { Collection<? extends AbstractState> result = getAbstractSuccessors0((AutomatonState) pElement, pPrecision, pCfaEdge); automatonSuccessors.setNextValue(result.size()); return result; } final List<CFAEdge> edges = ((MultiEdge) pCfaEdge).getEdges(); checkArgument(!edges.isEmpty()); // As long as each transition produces only 0 or 1 successors, // we can just iterate through the edges. AutomatonState currentState = (AutomatonState) pElement; Collection<AutomatonState> currentSuccessors = null; int edgeIndex; for (edgeIndex = 0; edgeIndex < edges.size(); edgeIndex++) { CFAEdge edge = edges.get(edgeIndex); currentSuccessors = getAbstractSuccessors0(currentState, pPrecision, edge); if (currentSuccessors.isEmpty()) { automatonSuccessors.setNextValue(0); return currentSuccessors; // bottom } else if (currentSuccessors.size() == 1) { automatonSuccessors.setNextValue(1); currentState = Iterables.getOnlyElement(currentSuccessors); } else { // currentSuccessors.size() > 1 break; } } if (edgeIndex == edges.size()) { automatonSuccessors.setNextValue(currentSuccessors.size()); return currentSuccessors; } // If there are two or more successors once, we use a waitlist algorithm. Deque<Pair<AutomatonState, Integer>> queue = new ArrayDeque<>(1); for (AutomatonState successor : currentSuccessors) { queue.addLast(Pair.of(successor, edgeIndex)); } currentSuccessors.clear(); List<AutomatonState> results = new ArrayList<>(); while (!queue.isEmpty()) { Pair<AutomatonState, Integer> entry = queue.pollFirst(); AutomatonState state = entry.getFirst(); edgeIndex = entry.getSecond(); CFAEdge edge = edges.get(edgeIndex); Integer successorIndex = edgeIndex + 1; if (successorIndex == edges.size()) { // last iteration results.addAll(getAbstractSuccessors0(state, pPrecision, edge)); } else { for (AutomatonState successor : getAbstractSuccessors0(state, pPrecision, edge)) { queue.addLast(Pair.of(successor, successorIndex)); } } } automatonSuccessors.setNextValue(results.size()); return results; }
private boolean combineARGs( List<ARGState> roots, ForwardingReachedSet pReceivedReachedSet, HistoryForwardingReachedSet pForwaredReachedSet) throws InterruptedException, CPAException { Pair<Map<String, Integer>, List<AbstractState>> initStates = identifyCompositeStateTypesAndTheirInitialInstances(roots); Map<String, Integer> stateToPos = initStates.getFirst(); List<AbstractState> initialStates = initStates.getSecond(); try { pReceivedReachedSet.setDelegate(new ReachedSetFactory(config, logger).create()); } catch (InvalidConfigurationException e) { logger.log(Level.SEVERE, "Creating reached set which should contain combined ARG fails."); return false; } shutdown.shutdownIfNecessary(); // combined root ARGState combinedRoot = new ARGState(new CompositeState(initialStates), null); CFANode locPred; ARGState composedState, composedSuccessor; Collection<ARGState> components; List<List<ARGState>> successorsForEdge = new ArrayList<>(initialStates.size()); EdgeSuccessor edgeSuccessorIdentifier = new EdgeSuccessor(); Map<Pair<List<AbstractState>, List<ARGState>>, ARGState> constructedCombinedStates = Maps.newHashMap(); Deque<Pair<List<ARGState>, ARGState>> toVisit = new ArrayDeque<>(); toVisit.add(Pair.of(roots, combinedRoot)); // traverse through ARGs and construct combined ARG // assume that states in initial states are most general, represent top state (except for // automaton CPAs) while (!toVisit.isEmpty()) { shutdown.shutdownIfNecessary(); components = toVisit.peek().getFirst(); composedState = toVisit.poll().getSecond(); // add composed state to reached set pReceivedReachedSet.add(composedState, SingletonPrecision.getInstance()); pReceivedReachedSet.removeOnlyFromWaitlist(composedState); // identify possible successor edges locPred = AbstractStates.extractLocation(composedState); nextEdge: for (CFAEdge succEdge : CFAUtils.allLeavingEdges(locPred)) { shutdown.shutdownIfNecessary(); successorsForEdge.clear(); edgeSuccessorIdentifier.setCFAEdge(succEdge); for (ARGState component : components) { // get the successors of ARG state for this edge succEdge edgeSuccessorIdentifier.setPredecessor(component); successorsForEdge.add( Lists.newArrayList( Iterables.filter(component.getChildren(), edgeSuccessorIdentifier))); // check if stopped because no concrete successors exists, then do not if (successorsForEdge.get(successorsForEdge.size() - 1).isEmpty() && noConcreteSuccessorExist(component, succEdge, pForwaredReachedSet)) { continue nextEdge; } } // construct successors for each identified combination for (Pair<List<AbstractState>, List<ARGState>> combinedSuccessor : computeCartesianProduct(successorsForEdge, stateToPos, initialStates)) { if (constructedCombinedStates.containsKey(combinedSuccessor)) { // handle coverage constructedCombinedStates.get(combinedSuccessor).addParent(composedState); } else { // construct and register composed successor composedSuccessor = new ARGState(new CompositeState(combinedSuccessor.getFirst()), composedState); constructedCombinedStates.put(combinedSuccessor, composedSuccessor); // add successor for further exploration toVisit.add(Pair.of(combinedSuccessor.getSecond(), composedSuccessor)); } } } } return true; }
/** * Returns the <code>AutomatonStates</code> that follow this State in the ControlAutomatonCPA. If * the passed <code>AutomatonExpressionArguments</code> are not sufficient to determine the * following state this method returns a <code>AutomatonUnknownState</code> that contains this as * previous State. The strengthen method of the <code>AutomatonUnknownState</code> should be used * once enough Information is available to determine the correct following State. * * <p>If the state is a NonDet-State multiple following states may be returned. If the only * following state is BOTTOM an empty set is returned. * * @throws CPATransferException */ private Collection<AutomatonState> getFollowStates( AutomatonState state, List<AbstractState> otherElements, CFAEdge edge, boolean failOnUnknownMatch) throws CPATransferException { Preconditions.checkArgument(!(state instanceof AutomatonUnknownState)); if (state == cpa.getBottomState()) { return Collections.emptySet(); } if (collectTokenInformation) { SourceLocationMapper.getKnownToEdge(edge); } if (state.getInternalState().getTransitions().isEmpty()) { // shortcut return Collections.singleton(state); } Collection<AutomatonState> lSuccessors = Sets.newHashSetWithExpectedSize(2); AutomatonExpressionArguments exprArgs = new AutomatonExpressionArguments(state, state.getVars(), otherElements, edge, logger); boolean edgeMatched = false; int failedMatches = 0; boolean nonDetState = state.getInternalState().isNonDetState(); // these transitions cannot be evaluated until last, because they might have sideeffects on // other CPAs (dont want to execute them twice) // the transitionVariables have to be cached (produced during the match operation) // the list holds a Transition and the TransitionVariables generated during its match List<Pair<AutomatonTransition, Map<Integer, String>>> transitionsToBeTaken = new ArrayList<>(2); for (AutomatonTransition t : state.getInternalState().getTransitions()) { exprArgs.clearTransitionVariables(); matchTime.start(); ResultValue<Boolean> match = t.match(exprArgs); matchTime.stop(); // System.out.println("----------------------"); // System.out.println(t.getTrigger()); // System.out.println(t.getFollowState().getName()); // System.out.println(edge.getPredecessor().getNodeNumber()); // System.out.println(edge.getCode()); // System.out.println(match.getValue()); if (match.canNotEvaluate()) { if (failOnUnknownMatch) { throw new CPATransferException( "Automaton transition condition could not be evaluated: " + match.getFailureMessage()); } // if one transition cannot be evaluated the evaluation must be postponed until enough // information is available return Collections.<AutomatonState>singleton(new AutomatonUnknownState(state)); } else { if (match.getValue()) { edgeMatched = true; assertionsTime.start(); ResultValue<Boolean> assertionsHold = t.assertionsHold(exprArgs); assertionsTime.stop(); if (assertionsHold.canNotEvaluate()) { if (failOnUnknownMatch) { throw new CPATransferException( "Automaton transition assertions could not be evaluated: " + assertionsHold.getFailureMessage()); } // cannot yet be evaluated return Collections.<AutomatonState>singleton(new AutomatonUnknownState(state)); } else if (assertionsHold.getValue()) { if (!t.canExecuteActionsOn(exprArgs)) { if (failOnUnknownMatch) { throw new CPATransferException("Automaton transition action could not be executed"); } // cannot yet execute, goto UnknownState return Collections.<AutomatonState>singleton(new AutomatonUnknownState(state)); } // delay execution as described above Map<Integer, String> transitionVariables = ImmutableMap.copyOf(exprArgs.getTransitionVariables()); transitionsToBeTaken.add(Pair.of(t, transitionVariables)); } else { // matching transitions, but unfulfilled assertions: goto error state AutomatonState errorState = AutomatonState.automatonStateFactory( Collections.<String, AutomatonVariable>emptyMap(), AutomatonInternalState.ERROR, cpa, 0, 0, ""); logger.log( Level.INFO, "Automaton going to ErrorState on edge \"" + edge.getDescription() + "\""); lSuccessors.add(errorState); } if (!nonDetState) { // not a nondet State, break on the first matching edge break; } } else { // do nothing if the edge did not match failedMatches++; } } } if (edgeMatched) { // execute Transitions for (Pair<AutomatonTransition, Map<Integer, String>> pair : transitionsToBeTaken) { // this transition will be taken. copy the variables AutomatonTransition t = pair.getFirst(); Map<Integer, String> transitionVariables = pair.getSecond(); actionTime.start(); Map<String, AutomatonVariable> newVars = deepCloneVars(state.getVars()); exprArgs.setAutomatonVariables(newVars); exprArgs.putTransitionVariables(transitionVariables); t.executeActions(exprArgs); actionTime.stop(); String violatedPropertyDescription = null; if (t.getFollowState().isTarget()) { violatedPropertyDescription = t.getViolatedPropertyDescription(exprArgs); } AutomatonState lSuccessor = AutomatonState.automatonStateFactory( newVars, t.getFollowState(), cpa, t.getAssumptions(), state.getMatches() + 1, state.getFailedMatches(), violatedPropertyDescription); if (!(lSuccessor instanceof AutomatonState.BOTTOM)) { lSuccessors.add(lSuccessor); } else { // add nothing } } return lSuccessors; } else { // stay in same state, no transitions to be executed here (no transition matched) AutomatonState stateNewCounters = AutomatonState.automatonStateFactory( state.getVars(), state.getInternalState(), cpa, state.getMatches(), state.getFailedMatches() + failedMatches, null); if (collectTokenInformation) { stateNewCounters.addNoMatchTokens(state.getTokensSinceLastMatch()); if (edge.getEdgeType() != CFAEdgeType.DeclarationEdge) { stateNewCounters.addNoMatchTokens( SourceLocationMapper.getAbsoluteTokensFromCFAEdge(edge, true)); } } return Collections.singleton(stateNewCounters); } }