committed by
Sascha Wunderlich
24 changed files with 1982 additions and 13 deletions
-
53prism/src/explicit/AccumulationModelChecker.java
-
121prism/src/explicit/AccumulationProduct.java
-
301prism/src/explicit/AccumulationProductCounting.java
-
345prism/src/explicit/AccumulationProductRegular.java
-
104prism/src/explicit/AccumulationState.java
-
83prism/src/explicit/AccumulationTrack.java
-
68prism/src/explicit/AccumulationTracker.java
-
162prism/src/explicit/AccumulationTransformation.java
-
13prism/src/explicit/DTMCModelChecker.java
-
9prism/src/explicit/MDPModelChecker.java
-
10prism/src/explicit/ProbModelChecker.java
-
45prism/src/explicit/StoragePool.java
-
136prism/src/parser/PrismParser.jj
-
67prism/src/parser/ast/AccumulationConstraint.java
-
59prism/src/parser/ast/AccumulationFactor.java
-
36prism/src/parser/ast/AccumulationFunction.java
-
38prism/src/parser/ast/AccumulationSymbol.java
-
44prism/src/parser/ast/Expression.java
-
142prism/src/parser/ast/ExpressionAccumulation.java
-
36prism/src/parser/visitor/ASTTraverse.java
-
36prism/src/parser/visitor/ASTTraverseModify.java
-
3prism/src/parser/visitor/ASTVisitor.java
-
67prism/src/parser/visitor/ReplaceAccumulationExpression.java
-
17prism/src/prism/IntegerBound.java
@ -0,0 +1,53 @@ |
|||||
|
package explicit; |
||||
|
|
||||
|
import java.util.ArrayList; |
||||
|
import java.util.BitSet; |
||||
|
|
||||
|
import parser.ast.Expression; |
||||
|
import parser.ast.ExpressionLabel; |
||||
|
import parser.ast.ExpressionRegular; |
||||
|
import parser.type.TypeBool; |
||||
|
import prism.PrismComponent; |
||||
|
import prism.PrismException; |
||||
|
|
||||
|
public class AccumulationModelChecker extends PrismComponent { |
||||
|
public Expression checkMaximalStateFormulas(ProbModelChecker mc, Model model, Expression expr, ArrayList<BitSet> labelBS) |
||||
|
throws PrismException |
||||
|
{ |
||||
|
// This is basically copied from DM |
||||
|
// A state formula |
||||
|
if (expr.getType() instanceof TypeBool) { |
||||
|
// Model check |
||||
|
StateValues sv = mc.checkExpression(model, expr, null); |
||||
|
BitSet bs = sv.getBitSet(); |
||||
|
// Detect special cases (true, false) for optimisation |
||||
|
if (bs.isEmpty()) { |
||||
|
return Expression.False(); |
||||
|
} |
||||
|
if (bs.cardinality() == model.getNumStates()) { |
||||
|
return Expression.True(); |
||||
|
} |
||||
|
// See if we already have an identical result |
||||
|
// (in which case, reuse it) |
||||
|
int i = labelBS.indexOf(bs); |
||||
|
if (i != -1) { |
||||
|
sv.clear(); |
||||
|
return new ExpressionLabel("L" + i); |
||||
|
} |
||||
|
// Otherwise, add result to list, return new label |
||||
|
labelBS.add(bs); |
||||
|
return new ExpressionLabel("L" + (labelBS.size() - 1)); |
||||
|
} |
||||
|
// A path formula (recurse, modify, return) |
||||
|
else if (expr instanceof ExpressionRegular) { |
||||
|
ExpressionRegular reg = (ExpressionRegular)expr; |
||||
|
if(reg.getOperand1() != null) { |
||||
|
reg.setOperand1(checkMaximalStateFormulas(mc, model, reg.getOperand1(), labelBS)); |
||||
|
} |
||||
|
if(reg.getOperand2() != null) { |
||||
|
reg.setOperand2(checkMaximalStateFormulas(mc, model, reg.getOperand2(), labelBS)); |
||||
|
} |
||||
|
} |
||||
|
return expr; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,121 @@ |
|||||
|
package explicit; |
||||
|
|
||||
|
import java.util.ArrayList; |
||||
|
import java.util.BitSet; |
||||
|
import java.util.Iterator; |
||||
|
import java.util.Map; |
||||
|
|
||||
|
import prism.PrismException; |
||||
|
import prism.PrismFileLog; |
||||
|
import prism.PrismLog; |
||||
|
|
||||
|
/** |
||||
|
* An AccumulationProduct has ProductStates, where the first component is the |
||||
|
* stateId in the original model, and the second component is the index of an |
||||
|
* AccumulationTracker. |
||||
|
* |
||||
|
* @author Sascha Wunderlich |
||||
|
* |
||||
|
* @param <M> |
||||
|
*/ |
||||
|
|
||||
|
public abstract class AccumulationProduct<M extends Model,Component> extends ProductWithProductStates<M> |
||||
|
{ |
||||
|
final StoragePool<AccumulationTracker<Component>> trackers; |
||||
|
final StoragePool<AccumulationState> accStates; |
||||
|
|
||||
|
final BitSet goodStates; |
||||
|
|
||||
|
final ArrayList<BitSet> labels; |
||||
|
|
||||
|
int numberOfTracks; |
||||
|
int numberOfWeights; |
||||
|
|
||||
|
public AccumulationProduct(M originalModel) { |
||||
|
super(originalModel); |
||||
|
trackers = new StoragePool<>(); |
||||
|
accStates = new StoragePool<>(); |
||||
|
|
||||
|
goodStates = new BitSet(); |
||||
|
labels = new ArrayList<BitSet>(); |
||||
|
} |
||||
|
|
||||
|
public BitSet getGoodStates() { |
||||
|
return goodStates; |
||||
|
} |
||||
|
|
||||
|
public int getNumberOfTracks() { |
||||
|
return numberOfTracks; |
||||
|
} |
||||
|
|
||||
|
public void exportToDotFile(String filename) throws PrismException { |
||||
|
try (PrismFileLog log = PrismFileLog.create(filename)) { |
||||
|
exportToDotFile(log); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public void exportToDotFile(PrismLog out) { |
||||
|
out.print(toDot()); |
||||
|
} |
||||
|
|
||||
|
public String quoteForDot(String original) { |
||||
|
String result = original; |
||||
|
result = result.replaceAll("&", "&"); |
||||
|
result = result.replaceAll("<", "<"); |
||||
|
result = result.replaceAll(">", ">"); |
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
public String toDot() { |
||||
|
StringBuffer result = new StringBuffer(); |
||||
|
|
||||
|
result.append("digraph " + originalModel.getModelType() + " {\n"); |
||||
|
|
||||
|
for(int i = 0; i < prod_states.size(); i++) { |
||||
|
ProductState fromState = prod_states.get(i); |
||||
|
AccumulationState accState = accStates.getById(fromState.getSecondState()); |
||||
|
AccumulationTracker<Component> tracker = trackers.getById(accState.getTrackerId()); |
||||
|
result.append("" |
||||
|
+ i |
||||
|
+ "[shape=box, color=black" |
||||
|
+ " label= < <TABLE BO8RDER=\"0\">" |
||||
|
+ "<TR>" |
||||
|
+ "<TD>" + i + "=" + fromState + "</TD>" |
||||
|
+ "</TR><TR>" |
||||
|
+ "<TD>" + accState + "</TD>" |
||||
|
+ "</TR><TR>" |
||||
|
+ "<TD>\"" + quoteForDot(tracker.toString()) + "\"</TD>" |
||||
|
+ "</TR>" |
||||
|
+ " </TABLE> >]\n"); |
||||
|
|
||||
|
switch(productModel.getModelType()) { |
||||
|
case DTMC: |
||||
|
DTMCExplicit castDTMC = (DTMCExplicit)productModel; |
||||
|
Iterator<Map.Entry<Integer, Double>> dtmcIter = castDTMC.getTransitionsIterator(i); |
||||
|
while (dtmcIter.hasNext()) { |
||||
|
Map.Entry<Integer, Double> e = dtmcIter.next(); |
||||
|
result.append(i + " -> " + e.getKey() + " [ label=\""); |
||||
|
result.append(e.getValue() + "\" ];\n"); |
||||
|
} |
||||
|
break; |
||||
|
case MDP: |
||||
|
MDPExplicit castMDP = (MDPExplicit)productModel; |
||||
|
for(int c = 0; c < castMDP.getNumChoices(i); c++) { |
||||
|
Iterator<Map.Entry<Integer, Double>> mdpIter = castMDP.getTransitionsIterator(i, c); |
||||
|
while (mdpIter.hasNext()) { |
||||
|
Map.Entry<Integer, Double> e = mdpIter.next(); |
||||
|
result.append(i + " -> " + e.getKey() + " [ label=\""); |
||||
|
result.append(c + "," + e.getValue() + "\" ];\n"); |
||||
|
} |
||||
|
} |
||||
|
break; |
||||
|
default: |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
result.append("}"); |
||||
|
|
||||
|
return result.toString(); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,301 @@ |
|||||
|
package explicit; |
||||
|
|
||||
|
import java.util.ArrayList; |
||||
|
import java.util.BitSet; |
||||
|
import java.util.Vector; |
||||
|
|
||||
|
import explicit.rewards.MCRewards; |
||||
|
import explicit.rewards.MDPRewards; |
||||
|
import explicit.rewards.Rewards; |
||||
|
import parser.ast.AccumulationFactor; |
||||
|
import parser.ast.ExpressionAccumulation; |
||||
|
import prism.IntegerBound; |
||||
|
import prism.PrismException; |
||||
|
|
||||
|
/** |
||||
|
* An AccumulationProduct has ProductStates, where the first component is the |
||||
|
* stateId in the original model, and the second component is the index of an |
||||
|
* AccumulationTracker. |
||||
|
* |
||||
|
* @author Sascha Wunderlich |
||||
|
* |
||||
|
* @param <M> |
||||
|
*/ |
||||
|
|
||||
|
public class AccumulationProductCounting<M extends Model> extends AccumulationProduct<M,Integer> |
||||
|
{ |
||||
|
|
||||
|
public AccumulationProductCounting(M originalModel) { |
||||
|
super(originalModel); |
||||
|
} |
||||
|
|
||||
|
public static AccumulationProductCounting<DTMC> generate(final DTMC graph, final ExpressionAccumulation accexp, final Vector<MCRewards> rewards, final ProbModelChecker mc, BitSet statesOfInterest) throws PrismException { |
||||
|
final AccumulationProductCounting<DTMC> result = new AccumulationProductCounting<DTMC>(graph); |
||||
|
// Create auxiliary data |
||||
|
result.createAuxData(graph, accexp, rewards, mc); |
||||
|
|
||||
|
// Build an operator |
||||
|
class AccumulationDTMCProductOperator implements DTMCProductOperator |
||||
|
{ |
||||
|
@Override |
||||
|
public ProductState getInitialState(Integer dtmc_state) |
||||
|
throws PrismException { |
||||
|
int initialAccStateId = result.createInitialStateId(rewards.size()); |
||||
|
return new ProductState(dtmc_state, initialAccStateId); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public ProductState getSuccessor(ProductState from_state, |
||||
|
Integer dtmc_to_state) throws PrismException { |
||||
|
// Get the current accumulation state |
||||
|
AccumulationState from_accstate = result.accStates.getById(from_state.getSecondState()); |
||||
|
|
||||
|
// Get step weights |
||||
|
double[] weights = new double[rewards.size()]; |
||||
|
|
||||
|
for (int i=0; i < rewards.size(); i++) { |
||||
|
weights[i] = rewards.get(i).getStateReward(from_state.getFirstState()); |
||||
|
} |
||||
|
|
||||
|
// Update accumulation product state, store it and get its ID. |
||||
|
AccumulationState to_accproduct = result.updateAccumulationState(from_state.getFirstState(), from_accstate, accexp, weights, mc); |
||||
|
int to_accproduct_id = result.accStates.findOrAdd(to_accproduct); |
||||
|
|
||||
|
return new ProductState(dtmc_to_state, to_accproduct_id); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void notify(ProductState state, Integer index) |
||||
|
throws PrismException { |
||||
|
AccumulationState accState = result.accStates.getById(state.getSecondState()); |
||||
|
if (result.isGoodAccState(accState, accexp, mc)) { |
||||
|
result.goodStates.set(index); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void finish() throws PrismException { |
||||
|
// Do nothing |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public DTMC getGraph() { |
||||
|
return graph; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Apply the operator |
||||
|
AccumulationDTMCProductOperator op = new AccumulationDTMCProductOperator(); |
||||
|
ProductWithProductStates.generate(op, result, statesOfInterest); |
||||
|
|
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
public static AccumulationProductCounting<MDP> generate(final MDP graph, final ExpressionAccumulation accexp, final Vector<MDPRewards> rewards, final ProbModelChecker mc, BitSet statesOfInterest) throws PrismException { |
||||
|
// This is basically the same thing as for DTMCs |
||||
|
final AccumulationProductCounting<MDP> result = new AccumulationProductCounting<MDP>(graph); |
||||
|
|
||||
|
// Create auxiliary data |
||||
|
result.createAuxData(graph, accexp, rewards, mc); |
||||
|
|
||||
|
class AccumulationMDPProductOperator implements MDPProductOperator |
||||
|
{ |
||||
|
|
||||
|
@Override |
||||
|
public ProductState getInitialState(final Integer MDP_state) |
||||
|
throws PrismException { |
||||
|
int initialAccStateId = result.createInitialStateId(rewards.size()); |
||||
|
return new ProductState(MDP_state, initialAccStateId); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public ProductState getSuccessor(final ProductState from_state, |
||||
|
final int choice_i, final Integer mdp_to_state) throws PrismException { |
||||
|
// Get the current accumulation state |
||||
|
AccumulationState from_accstate = result.accStates.getById(from_state.getSecondState()); |
||||
|
|
||||
|
// Get step weights |
||||
|
// THIS IS DIFFERENT FROM ABOVE! |
||||
|
double[] weights = new double[rewards.size()]; |
||||
|
|
||||
|
for (int i=0; i < rewards.size(); i++) { |
||||
|
double currentWeight = rewards.get(i).getStateReward(from_state.getFirstState()); |
||||
|
currentWeight += rewards.get(i).getTransitionReward(from_state.getFirstState(), choice_i); |
||||
|
weights[i] = currentWeight; |
||||
|
} |
||||
|
|
||||
|
// Update accumulation product state, store it and get its ID. |
||||
|
AccumulationState to_accproduct = result.updateAccumulationState(from_state.getFirstState(), from_accstate, accexp, weights, mc); |
||||
|
int to_accproduct_id = result.accStates.findOrAdd(to_accproduct); |
||||
|
return new ProductState(mdp_to_state, to_accproduct_id); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void notify(final ProductState state, final Integer index) |
||||
|
throws PrismException { |
||||
|
AccumulationState accState = result.accStates.getById(state.getSecondState()); |
||||
|
if (result.isGoodAccState(accState, accexp, mc)) { |
||||
|
result.goodStates.set(index); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void finish() throws PrismException { |
||||
|
// Do nothing |
||||
|
mc.getLog().println("."); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public MDP getGraph() { |
||||
|
return graph; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
AccumulationMDPProductOperator op = new AccumulationMDPProductOperator(); |
||||
|
ProductWithProductStates.generate(op, result, statesOfInterest); |
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
private boolean isFinalTrack(final AccumulationTrack<Integer> track, final ExpressionAccumulation accexp, final ProbModelChecker mc) |
||||
|
throws PrismException { |
||||
|
boolean isFinal = false; |
||||
|
if ( track != null ) { |
||||
|
IntegerBound stepBound = IntegerBound.fromTemporalOperatorBound(accexp.getBoundExpression(), mc.getConstantValues(), true); |
||||
|
isFinal = stepBound.isInBounds(track.getComponent()); |
||||
|
} |
||||
|
return isFinal; |
||||
|
} |
||||
|
|
||||
|
private boolean isGoodAccState(final AccumulationState state, final ExpressionAccumulation accexp, final ProbModelChecker mc) |
||||
|
throws PrismException { |
||||
|
return state.hasGoodTrack(); |
||||
|
} |
||||
|
|
||||
|
private boolean isGoodTrack(final AccumulationTrack<Integer> track, final ExpressionAccumulation accexp, final ProbModelChecker mc) |
||||
|
throws PrismException { |
||||
|
// Only final tracks can be good |
||||
|
if (!isFinalTrack(track,accexp,mc)) { return false; } |
||||
|
boolean isGood = false; |
||||
|
|
||||
|
// Collect the weight linear combination, factor*weight+... |
||||
|
int lhs = 0; |
||||
|
int factorNr = 0; |
||||
|
for (AccumulationFactor factor : accexp.getConstraint().getFactors()) { |
||||
|
lhs += factor.getFactor().evaluateInt(mc.getConstantValues()) |
||||
|
* track.getWeight(factorNr); |
||||
|
} |
||||
|
|
||||
|
// Check the bound |
||||
|
IntegerBound rhs = IntegerBound.fromTemporalOperatorBound(accexp.getConstraint().getBound(), mc.getConstantValues(), true); |
||||
|
|
||||
|
// For DIA operators, we just check the bound. |
||||
|
// For BOX operators, we check the INVERTED bound. |
||||
|
switch(accexp.getSymbol()) { |
||||
|
case ACCBOXMINUS: |
||||
|
case ACCBOXPLUS: |
||||
|
if (!rhs.isInBounds(lhs)) { |
||||
|
isGood = true; |
||||
|
} |
||||
|
break; |
||||
|
case ACCDIAMINUS: |
||||
|
case ACCDIAPLUS: |
||||
|
if (rhs.isInBounds(lhs)) { |
||||
|
isGood = true; |
||||
|
} |
||||
|
break; |
||||
|
default: |
||||
|
throw new RuntimeException("Oh boy!"); |
||||
|
} |
||||
|
if(isGood) {mc.getLog().print("+");} else {mc.getLog().print("-");} |
||||
|
return isGood; |
||||
|
} |
||||
|
|
||||
|
private AccumulationState updateAccumulationState(final int modelFromStateId, |
||||
|
final AccumulationState accstate, final ExpressionAccumulation accexp, |
||||
|
final double[] weights, final ProbModelChecker mc) throws PrismException { |
||||
|
// We have the current accumulation state, the current model id and the accumulation expression. |
||||
|
|
||||
|
// Get the old tracker and tracks. |
||||
|
AccumulationTracker<Integer> oldTracker = trackers.getById(accstate.getTrackerId()); |
||||
|
ArrayList<AccumulationTrack<Integer>> oldTracks = oldTracker.getTracks(); |
||||
|
|
||||
|
BitSet oldGoodTracks = accstate.getGoodTracks(); |
||||
|
BitSet newGoodTracks = (BitSet) oldGoodTracks.clone(); |
||||
|
|
||||
|
// This restart will be... |
||||
|
int newLastRestartNr = accstate.getNextRestartNr(); |
||||
|
mc.getLog().print(newLastRestartNr); |
||||
|
|
||||
|
// Build the new tracks. |
||||
|
ArrayList<AccumulationTrack<Integer>> newTracks = new ArrayList<>(); |
||||
|
|
||||
|
int trackNr = 0; |
||||
|
for(AccumulationTrack<Integer> oldTrack : oldTracks) { |
||||
|
AccumulationTrack<Integer> newTrack; |
||||
|
|
||||
|
// restart or advance |
||||
|
if(trackNr == newLastRestartNr) { |
||||
|
//assert oldTrack == null : "Track " + newLastRestartNr + " is not null!"; |
||||
|
newTrack = new AccumulationTrack<Integer>(numberOfWeights, 0); //TODO: off-by-one? |
||||
|
newGoodTracks.clear(trackNr); |
||||
|
} else if (oldTrack == null) { |
||||
|
newTrack = null; |
||||
|
} else { |
||||
|
assert oldTrack != null; |
||||
|
newTrack = updateTrackBounds(oldTrack, accexp, weights, mc); |
||||
|
} |
||||
|
|
||||
|
// check whether the track is good |
||||
|
if(!newGoodTracks.get(trackNr)) { |
||||
|
newGoodTracks.set(trackNr, isGoodTrack(newTrack, accexp, mc)); |
||||
|
} |
||||
|
|
||||
|
newTracks.add(newTrack); |
||||
|
trackNr++; |
||||
|
} |
||||
|
|
||||
|
AccumulationTracker<Integer> newTracker = new AccumulationTracker<>(newTracks); |
||||
|
|
||||
|
|
||||
|
int newTrackerId = trackers.findOrAdd(newTracker); |
||||
|
|
||||
|
return new AccumulationState(newTrackerId, newLastRestartNr, numberOfTracks, newGoodTracks); |
||||
|
} |
||||
|
|
||||
|
private AccumulationTrack<Integer> updateTrackBounds(final AccumulationTrack<Integer> track, |
||||
|
final ExpressionAccumulation accexp, final double[] weights, final StateModelChecker mc) throws PrismException { |
||||
|
int currentStep = track.getComponent(); |
||||
|
int maxStep = IntegerBound.fromTemporalOperatorBound(accexp.getBoundExpression(), mc.getConstantValues(), true).getHighestInteger(); |
||||
|
|
||||
|
// If we are done, return null-Track |
||||
|
if (currentStep >= maxStep) { return null; } |
||||
|
|
||||
|
// Otherwise, we update the weights and increase the step. |
||||
|
double[] newweights = new double[weights.length]; |
||||
|
for (int i = 0; i < weights.length; i++) { |
||||
|
newweights[i] = weights[i] + track.getWeights()[i]; |
||||
|
} |
||||
|
|
||||
|
return new AccumulationTrack<Integer>(newweights, currentStep+1); |
||||
|
} |
||||
|
|
||||
|
protected int createInitialStateId(final int numberOfRewards) { |
||||
|
// The initial active track is the first one, all tracks are non-good by default |
||||
|
int initialActiveTrack = 0; |
||||
|
BitSet initialGoodTracks = new BitSet(); |
||||
|
|
||||
|
// Generate the initial tracker and product state |
||||
|
AccumulationTracker<Integer> initialTracker = new AccumulationTracker<>(numberOfTracks, numberOfRewards, 0); |
||||
|
int initialTrackerId = trackers.findOrAdd(initialTracker); |
||||
|
AccumulationState initialAccState = new AccumulationState(initialTrackerId, initialActiveTrack, numberOfTracks, initialGoodTracks); |
||||
|
int initialAccStateId = accStates.findOrAdd(initialAccState); |
||||
|
|
||||
|
return initialAccStateId; |
||||
|
} |
||||
|
|
||||
|
protected void createAuxData(final Model graph, final ExpressionAccumulation accexp, |
||||
|
final Vector<? extends Rewards> rewards, final ProbModelChecker mc) throws PrismException { |
||||
|
numberOfTracks = IntegerBound.fromTemporalOperatorBound(accexp.getBoundExpression(), mc.getConstantValues(), true).getHighestInteger()+1; |
||||
|
numberOfWeights = rewards.size(); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,345 @@ |
|||||
|
package explicit; |
||||
|
|
||||
|
import java.util.ArrayList; |
||||
|
import java.util.BitSet; |
||||
|
import java.util.Vector; |
||||
|
|
||||
|
import automata.finite.DeterministicFiniteAutomaton; |
||||
|
import automata.finite.EdgeLabel; |
||||
|
import automata.finite.NondeterministicFiniteAutomaton; |
||||
|
import automata.finite.State; |
||||
|
import explicit.rewards.MCRewards; |
||||
|
import explicit.rewards.MDPRewards; |
||||
|
import parser.ast.AccumulationFactor; |
||||
|
import parser.ast.ExpressionAccumulation; |
||||
|
import parser.ast.ExpressionRegular; |
||||
|
import prism.IntegerBound; |
||||
|
import prism.PrismException; |
||||
|
|
||||
|
/** |
||||
|
* An AccumulationProduct has ProductStates, where the first component is the |
||||
|
* stateId in the original model, and the second component is the index of an |
||||
|
* AccumulationTracker. |
||||
|
* |
||||
|
* @author Sascha Wunderlich |
||||
|
* |
||||
|
* @param <M> |
||||
|
*/ |
||||
|
|
||||
|
public class AccumulationProductRegular<M extends Model> extends AccumulationProduct<M,State> |
||||
|
{ |
||||
|
DeterministicFiniteAutomaton<String> automaton; |
||||
|
|
||||
|
public AccumulationProductRegular(M originalModel) { |
||||
|
super(originalModel); |
||||
|
} |
||||
|
|
||||
|
public static AccumulationProductRegular<DTMC> generate(final DTMC graph, final ExpressionAccumulation accexp, final Vector<MCRewards> rewards, final ProbModelChecker mc, BitSet statesOfInterest) throws PrismException { |
||||
|
final AccumulationProductRegular<DTMC> result = new AccumulationProductRegular<DTMC>(graph); |
||||
|
// Create auxiliary data |
||||
|
result.createAuxData(graph, accexp, mc); |
||||
|
|
||||
|
// Build an operator |
||||
|
class AccumulationDTMCProductOperator implements DTMCProductOperator |
||||
|
{ |
||||
|
@Override |
||||
|
public ProductState getInitialState(Integer dtmc_state) |
||||
|
throws PrismException { |
||||
|
int initialAccStateId = result.createInitialStateId(rewards.size()); |
||||
|
return new ProductState(dtmc_state, initialAccStateId); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public ProductState getSuccessor(ProductState from_state, |
||||
|
Integer dtmc_to_state) throws PrismException { |
||||
|
// Get the current accumulation state |
||||
|
AccumulationState from_accstate = result.accStates.getById(from_state.getSecondState()); |
||||
|
|
||||
|
// Get step weights |
||||
|
double[] weights = new double[rewards.size()]; |
||||
|
|
||||
|
for (int i=0; i < rewards.size(); i++) { |
||||
|
weights[i] = rewards.get(i).getStateReward(from_state.getFirstState()); |
||||
|
} |
||||
|
|
||||
|
// Update accumulation product state, store it and get its ID. |
||||
|
AccumulationState to_accproduct = result.updateAccumulationState(from_state.getFirstState(), from_accstate, accexp, weights, mc); |
||||
|
int to_accproduct_id = result.accStates.findOrAdd(to_accproduct); |
||||
|
|
||||
|
return new ProductState(dtmc_to_state, to_accproduct_id); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void notify(ProductState state, Integer index) |
||||
|
throws PrismException { |
||||
|
AccumulationState accState = result.accStates.getById(state.getSecondState()); |
||||
|
if (result.isGoodAccState(accState, accexp, mc)) { |
||||
|
result.goodStates.set(index); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void finish() throws PrismException { |
||||
|
// Do nothing |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public DTMC getGraph() { |
||||
|
return graph; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Apply the operator |
||||
|
AccumulationDTMCProductOperator op = new AccumulationDTMCProductOperator(); |
||||
|
ProductWithProductStates.generate(op, result, statesOfInterest); |
||||
|
|
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
public static AccumulationProductRegular<MDP> generate(final MDP graph, final ExpressionAccumulation accexp, final Vector<MDPRewards> rewards, final ProbModelChecker mc, BitSet statesOfInterest) throws PrismException { |
||||
|
// This is basically the same thing as for DTMCs |
||||
|
final AccumulationProductRegular<MDP> result = new AccumulationProductRegular<MDP>(graph); |
||||
|
|
||||
|
// Create auxiliary data |
||||
|
result.createAuxData(graph, accexp, mc); |
||||
|
|
||||
|
class AccumulationMDPProductOperator implements MDPProductOperator |
||||
|
{ |
||||
|
|
||||
|
@Override |
||||
|
public ProductState getInitialState(Integer MDP_state) |
||||
|
throws PrismException { |
||||
|
int initialAccStateId = result.createInitialStateId(rewards.size()); |
||||
|
return new ProductState(MDP_state, initialAccStateId); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public ProductState getSuccessor(ProductState from_state, |
||||
|
int choice_i, Integer mdp_to_state) throws PrismException { |
||||
|
// Get the current accumulation state |
||||
|
AccumulationState from_accstate = result.accStates.getById(from_state.getSecondState()); |
||||
|
|
||||
|
// Get step weights |
||||
|
// THIS IS DIFFERENT FROM ABOVE! |
||||
|
double[] weights = new double[rewards.size()]; |
||||
|
|
||||
|
for (int i=0; i < rewards.size(); i++) { |
||||
|
double currentWeight = rewards.get(i).getStateReward(from_state.getFirstState()); |
||||
|
currentWeight += rewards.get(i).getTransitionReward(from_state.getFirstState(), choice_i); |
||||
|
weights[i] = currentWeight; |
||||
|
} |
||||
|
|
||||
|
// Update accumulation product state, store it and get its ID. |
||||
|
AccumulationState to_accproduct = result.updateAccumulationState(from_state.getFirstState(), from_accstate, accexp, weights, mc); |
||||
|
int to_accproduct_id = result.accStates.findOrAdd(to_accproduct); |
||||
|
|
||||
|
return new ProductState(mdp_to_state, to_accproduct_id); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void notify(ProductState state, Integer index) |
||||
|
throws PrismException { |
||||
|
AccumulationState accState = result.accStates.getById(state.getSecondState()); |
||||
|
if (result.isGoodAccState(accState, accexp, mc)) { |
||||
|
result.goodStates.set(index); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void finish() throws PrismException { |
||||
|
// Do nothing |
||||
|
|
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public MDP getGraph() { |
||||
|
return graph; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
AccumulationMDPProductOperator op = new AccumulationMDPProductOperator(); |
||||
|
ProductWithProductStates.generate(op, result, statesOfInterest); |
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
private boolean isFinalTrack(AccumulationTrack<State> track, ExpressionAccumulation accexp, ProbModelChecker mc) throws PrismException { |
||||
|
boolean isFinal = false; |
||||
|
if ( track != null ) { |
||||
|
isFinal = automaton.isAcceptingState(track.getComponent()); |
||||
|
} |
||||
|
|
||||
|
return isFinal; |
||||
|
} |
||||
|
|
||||
|
private boolean isGoodAccState(AccumulationState state, ExpressionAccumulation accexp, ProbModelChecker mc) throws PrismException { |
||||
|
//TODO: fill me! |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
private boolean isGoodTrack(AccumulationTrack<State> track, ExpressionAccumulation accexp, ProbModelChecker mc) throws PrismException { |
||||
|
//System.out.println("Checking " + track + " for goodness..."); |
||||
|
// Only final tracks can be good |
||||
|
if (!isFinalTrack(track,accexp,mc)) { return false; } |
||||
|
boolean isGood = false; |
||||
|
|
||||
|
//System.out.println("Final: " + track); |
||||
|
|
||||
|
// Collect the weight linear combination, factor*weight+... |
||||
|
int lhs = 0; |
||||
|
int factorNr = 0; |
||||
|
for (AccumulationFactor factor : accexp.getConstraint().getFactors()) { |
||||
|
lhs += factor.getFactor().evaluateInt(mc.getConstantValues()) |
||||
|
* track.getWeight(factorNr); |
||||
|
} |
||||
|
|
||||
|
// Check the bound |
||||
|
IntegerBound rhs = IntegerBound.fromTemporalOperatorBound(accexp.getConstraint().getBound(), mc.getConstantValues(), true); |
||||
|
|
||||
|
// For DIA operators, we just check the bound. |
||||
|
// For BOX operators, we check the INVERTED bound. |
||||
|
switch(accexp.getSymbol()) { |
||||
|
case ACCBOXMINUS: |
||||
|
case ACCBOXPLUS: |
||||
|
if (!rhs.isInBounds(lhs)) { |
||||
|
isGood = true; |
||||
|
} |
||||
|
break; |
||||
|
case ACCDIAMINUS: |
||||
|
case ACCDIAPLUS: |
||||
|
if (rhs.isInBounds(lhs)) { |
||||
|
isGood = true; |
||||
|
} |
||||
|
break; |
||||
|
default: |
||||
|
throw new RuntimeException("Oh boy!"); |
||||
|
} |
||||
|
|
||||
|
//System.out.println("Good? " + isGood); |
||||
|
|
||||
|
return isGood; |
||||
|
} |
||||
|
|
||||
|
private AccumulationState updateAccumulationState(final int modelFromStateId, |
||||
|
final AccumulationState accstate, final ExpressionAccumulation accexp, |
||||
|
final double[] weights, final ProbModelChecker mc) throws PrismException { |
||||
|
// We have the current accumulation state, the current model id and the accumulation expression. |
||||
|
|
||||
|
// Get the old tracker and tracks. |
||||
|
AccumulationTracker<State> oldTracker = trackers.getById(accstate.getTrackerId()); |
||||
|
ArrayList<AccumulationTrack<State>> oldTracks = oldTracker.getTracks(); |
||||
|
|
||||
|
BitSet oldGoodTracks = accstate.getGoodTracks(); |
||||
|
BitSet newGoodTracks = (BitSet) oldGoodTracks.clone(); |
||||
|
|
||||
|
// This restart will be... |
||||
|
int newLastRestartNr = accstate.getNextRestartNr(); |
||||
|
mc.getLog().print(newLastRestartNr); |
||||
|
|
||||
|
// Build the new tracks. |
||||
|
ArrayList<AccumulationTrack<State>> newTracks = new ArrayList<>(); |
||||
|
|
||||
|
int trackNr = 0; |
||||
|
for(AccumulationTrack<State> oldTrack : oldTracks) { |
||||
|
AccumulationTrack<State> newTrack; |
||||
|
|
||||
|
// restart or advance |
||||
|
if(trackNr == newLastRestartNr) { |
||||
|
//assert oldTrack == null : "Track " + newLastRestartNr + " is not null!"; |
||||
|
newTrack = new AccumulationTrack<State>(numberOfWeights, automaton.getInitialState()); //TODO: off-by-one? |
||||
|
newGoodTracks.clear(trackNr); |
||||
|
} else if (oldTrack == null) { |
||||
|
newTrack = null; |
||||
|
} else { |
||||
|
assert oldTrack != null; |
||||
|
newTrack = updateTrackRegular(modelFromStateId, oldTrack, accexp, weights, mc); |
||||
|
} |
||||
|
|
||||
|
// check whether the track is good |
||||
|
if(!newGoodTracks.get(trackNr)) { |
||||
|
newGoodTracks.set(trackNr, isGoodTrack(newTrack, accexp, mc)); |
||||
|
} |
||||
|
|
||||
|
newTracks.add(newTrack); |
||||
|
trackNr++; |
||||
|
} |
||||
|
|
||||
|
AccumulationTracker<State> newTracker = new AccumulationTracker<>(newTracks); |
||||
|
|
||||
|
|
||||
|
int newTrackerId = trackers.findOrAdd(newTracker); |
||||
|
|
||||
|
return new AccumulationState(newTrackerId, newLastRestartNr, numberOfTracks, newGoodTracks); |
||||
|
} |
||||
|
private AccumulationTrack<State> updateTrackRegular(Integer modelFromStateId, AccumulationTrack<State> track, ExpressionAccumulation accexp, double[] weights, StateModelChecker mc) { |
||||
|
State currentState = track.getComponent(); |
||||
|
|
||||
|
// Build EdgeLabel from labels. |
||||
|
// labels is a BitSet with labels L0,...,Ln |
||||
|
|
||||
|
ArrayList<String> edgeSymbols = new ArrayList<>(); |
||||
|
BitSet edgeValues = new BitSet(); |
||||
|
|
||||
|
for (int i=0; i < labels.size(); i++) { |
||||
|
// Each bitLabel contains one L |
||||
|
edgeSymbols.add(i, "L"+i); |
||||
|
if(labels.get(i).get(modelFromStateId)) { |
||||
|
edgeValues.set(i); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
EdgeLabel<String> edgeLabel = new EdgeLabel<>(edgeSymbols, edgeValues); |
||||
|
|
||||
|
State nextState = automaton.getSuccessor(currentState,edgeLabel); |
||||
|
//System.out.println("Successor of " + currentState + " via " + edgeLabel + ":" + nextState); |
||||
|
|
||||
|
// Undefined behavior, return the null-Track |
||||
|
if (nextState == null) { return null; } |
||||
|
|
||||
|
// If we have defined behavior, update the weights and get the new stateId. |
||||
|
double[] newweights = new double[weights.length]; |
||||
|
for (int i = 0; i < weights.length; i++) { |
||||
|
newweights[i] = weights[i] + track.getWeights()[i]; |
||||
|
} |
||||
|
|
||||
|
return new AccumulationTrack<State>(newweights, nextState); |
||||
|
} |
||||
|
|
||||
|
protected int createInitialStateId(int numberOfRewards) { |
||||
|
// Find the initial state id (0 or automaton state) |
||||
|
State initialState = automaton.getInitialState(); |
||||
|
|
||||
|
// The initial active track is the first one, all tracks are non-good by default |
||||
|
int initialActiveTrack = 0; |
||||
|
BitSet initialGoodTracks = new BitSet(); |
||||
|
|
||||
|
// Generate the initial track and product state |
||||
|
AccumulationTracker<State> initialTracker = new AccumulationTracker<>(numberOfTracks, numberOfRewards, initialState); |
||||
|
int initialTrackerId = trackers.findOrAdd(initialTracker); |
||||
|
AccumulationState initialAccState = new AccumulationState(initialTrackerId, initialActiveTrack, numberOfTracks, initialGoodTracks); |
||||
|
int initialAccStateId = accStates.findOrAdd(initialAccState); |
||||
|
|
||||
|
return initialAccStateId; |
||||
|
} |
||||
|
|
||||
|
public void createAuxData(final Model graph, final ExpressionAccumulation accexp, final ProbModelChecker mc) throws PrismException { |
||||
|
mc.getLog().println(" [AP] generating aux data for " + graph + "\n and " + accexp); |
||||
|
// Build labels and DFA |
||||
|
AccumulationModelChecker accMc = new AccumulationModelChecker(); |
||||
|
ExpressionRegular reg = (ExpressionRegular)accMc.checkMaximalStateFormulas(mc, graph, accexp.getRegularExpression(), labels); |
||||
|
|
||||
|
// Create and store the actual DFA |
||||
|
NondeterministicFiniteAutomaton<String> nfa = NondeterministicFiniteAutomaton.fromExpressionRegular(reg); |
||||
|
//System.out.println(nfa.toDot()); |
||||
|
|
||||
|
automaton = nfa.determinize(); |
||||
|
automaton.trim(); // This should remove cycles. |
||||
|
|
||||
|
System.out.println(nfa.toDot()); |
||||
|
|
||||
|
if (!automaton.isAcyclic()) { |
||||
|
throw new PrismException("Cannot handle cyclic automata!"); |
||||
|
} |
||||
|
|
||||
|
numberOfTracks = automaton.getLongestPathLength(); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,104 @@ |
|||||
|
package explicit; |
||||
|
|
||||
|
import java.util.BitSet; |
||||
|
|
||||
|
/** |
||||
|
* An AccumulationState contains a reference to an AccumulationTracker, a BitSet |
||||
|
* of successful tracks goodTracks, a pointer to the latest restart and the |
||||
|
* numberOfTracks in the tracker. |
||||
|
* |
||||
|
* @author Sascha Wunderlich |
||||
|
* |
||||
|
*/ |
||||
|
public class AccumulationState { |
||||
|
final int trackerId; |
||||
|
|
||||
|
final BitSet goodTracks; |
||||
|
|
||||
|
int lastRestartNr; |
||||
|
int numberOfTracks; |
||||
|
|
||||
|
public AccumulationState(int trackerId, int lastRestartNr, int numberOfTracks, |
||||
|
BitSet goodTracks) { |
||||
|
super(); |
||||
|
this.trackerId = trackerId; |
||||
|
this.lastRestartNr = lastRestartNr; |
||||
|
this.numberOfTracks = numberOfTracks; |
||||
|
this.goodTracks = goodTracks; |
||||
|
} |
||||
|
|
||||
|
public int getTrackerId() { |
||||
|
return trackerId; |
||||
|
} |
||||
|
|
||||
|
public int getLastRestartNr() { |
||||
|
return lastRestartNr; |
||||
|
} |
||||
|
|
||||
|
public void setLastRestartNr(int lastRestartNr) { |
||||
|
this.lastRestartNr = lastRestartNr; |
||||
|
} |
||||
|
|
||||
|
public BitSet getGoodTracks() { |
||||
|
return goodTracks; |
||||
|
} |
||||
|
|
||||
|
public boolean isGoodTrack(int trackNr) { |
||||
|
return goodTracks.get(trackNr); |
||||
|
} |
||||
|
|
||||
|
public boolean hasGoodTrack() { |
||||
|
return !goodTracks.isEmpty(); |
||||
|
} |
||||
|
|
||||
|
public int getNextRestartNr() { |
||||
|
return (lastRestartNr + 1) % numberOfTracks; |
||||
|
} |
||||
|
|
||||
|
public void advance() { |
||||
|
lastRestartNr = getNextRestartNr(); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public int hashCode() { |
||||
|
final int prime = 31; |
||||
|
int result = 1; |
||||
|
result = prime * result + ((goodTracks == null) ? 0 : goodTracks.hashCode()); |
||||
|
result = prime * result + lastRestartNr; |
||||
|
result = prime * result + trackerId; |
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public boolean equals(Object obj) { |
||||
|
if (this == obj) |
||||
|
return true; |
||||
|
if (obj == null) |
||||
|
return false; |
||||
|
if (getClass() != obj.getClass()) |
||||
|
return false; |
||||
|
AccumulationState other = (AccumulationState) obj; |
||||
|
if (goodTracks == null) { |
||||
|
if (other.goodTracks != null) |
||||
|
return false; |
||||
|
} else if (!goodTracks.equals(other.goodTracks)) |
||||
|
return false; |
||||
|
if (lastRestartNr != other.lastRestartNr) |
||||
|
return false; |
||||
|
if (trackerId != other.trackerId) |
||||
|
return false; |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public String toString() { |
||||
|
StringBuffer result = new StringBuffer(); |
||||
|
result.append("S"); |
||||
|
|
||||
|
result.append(trackerId); |
||||
|
result.append(goodTracks); |
||||
|
|
||||
|
return result.toString(); |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,83 @@ |
|||||
|
package explicit; |
||||
|
|
||||
|
import java.util.Arrays; |
||||
|
|
||||
|
/** |
||||
|
* An AccumulationTrack contains a double array of weights |
||||
|
* and a Component representing the current progress of the accumulation window. |
||||
|
* @author Sascha Wunderlich |
||||
|
* |
||||
|
* @param <Component> |
||||
|
*/ |
||||
|
public class AccumulationTrack<Component> { |
||||
|
final private double[] weights; |
||||
|
final private Component component; |
||||
|
|
||||
|
public AccumulationTrack(int numberOfWeights, Component component) { |
||||
|
weights = new double[numberOfWeights]; |
||||
|
for (int i=0; i<numberOfWeights; i++) { |
||||
|
weights[i] = 0.0; |
||||
|
} |
||||
|
|
||||
|
this.component = component; |
||||
|
} |
||||
|
|
||||
|
public AccumulationTrack(double[] weights, Component component) { |
||||
|
super(); |
||||
|
this.weights = weights; |
||||
|
this.component = component; |
||||
|
} |
||||
|
|
||||
|
public double[] getWeights() { |
||||
|
return weights; |
||||
|
} |
||||
|
|
||||
|
public Component getComponent() { |
||||
|
return component; |
||||
|
} |
||||
|
|
||||
|
public double getWeight(int index) { |
||||
|
return weights[index]; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public int hashCode() { |
||||
|
final int prime = 31; |
||||
|
int result = 1; |
||||
|
result = prime * result + ((component == null) ? 0 : component.hashCode()); |
||||
|
result = prime * result + Arrays.hashCode(weights); |
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
@SuppressWarnings("rawtypes") |
||||
|
@Override |
||||
|
public boolean equals(Object obj) { |
||||
|
if (this == obj) |
||||
|
return true; |
||||
|
if (obj == null) |
||||
|
return false; |
||||
|
if (getClass() != obj.getClass()) |
||||
|
return false; |
||||
|
AccumulationTrack other = (AccumulationTrack) obj; |
||||
|
if (component == null) { |
||||
|
if (other.component != null) |
||||
|
return false; |
||||
|
} else if (!component.equals(other.component)) |
||||
|
return false; |
||||
|
if (!Arrays.equals(weights, other.weights)) |
||||
|
return false; |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public String toString() { |
||||
|
StringBuffer result = new StringBuffer(); |
||||
|
result.append("T"); |
||||
|
|
||||
|
result.append(Arrays.toString(weights)); |
||||
|
result.append("@"); |
||||
|
result.append(component); |
||||
|
|
||||
|
return result.toString(); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,68 @@ |
|||||
|
package explicit; |
||||
|
|
||||
|
import java.util.ArrayList; |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* An AccumulationTracker contains a Vector of AccumulationTracks. |
||||
|
* @author Sascha Wunderlich |
||||
|
* |
||||
|
* @param <Component> |
||||
|
*/ |
||||
|
public class AccumulationTracker<Component> { |
||||
|
final private ArrayList<AccumulationTrack<Component>> tracks; |
||||
|
|
||||
|
public AccumulationTracker(int numberOfTracks, int numberOfWeights, Component component) { |
||||
|
super(); |
||||
|
this.tracks = new ArrayList<AccumulationTrack<Component>>(numberOfTracks); |
||||
|
this.tracks.add(new AccumulationTrack<>(numberOfWeights, component)); |
||||
|
while(this.tracks.size() < numberOfTracks) { |
||||
|
this.tracks.add(null); |
||||
|
} |
||||
|
assert(this.tracks.size() == numberOfTracks); |
||||
|
} |
||||
|
|
||||
|
public AccumulationTracker(ArrayList<AccumulationTrack<Component>> tracks) { |
||||
|
super(); |
||||
|
this.tracks = new ArrayList<AccumulationTrack<Component>>(tracks.size()); |
||||
|
this.tracks.addAll(tracks); |
||||
|
} |
||||
|
|
||||
|
public ArrayList<AccumulationTrack<Component>> getTracks() { |
||||
|
return tracks; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public int hashCode() { |
||||
|
final int prime = 31; |
||||
|
int result = 1; |
||||
|
result = prime * result + ((tracks == null) ? 0 : tracks.hashCode()); |
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
@SuppressWarnings("rawtypes") |
||||
|
@Override |
||||
|
public boolean equals(Object obj) { |
||||
|
if (this == obj) |
||||
|
return true; |
||||
|
if (obj == null) |
||||
|
return false; |
||||
|
if (getClass() != obj.getClass()) |
||||
|
return false; |
||||
|
AccumulationTracker other = (AccumulationTracker) obj; |
||||
|
if (tracks == null) { |
||||
|
if (other.tracks != null) |
||||
|
return false; |
||||
|
} else if (!tracks.equals(other.tracks)) |
||||
|
return false; |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
@Override |
||||
|
public String toString() { |
||||
|
StringBuffer result = new StringBuffer(); |
||||
|
result.append(tracks); |
||||
|
return result.toString(); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,162 @@ |
|||||
|
package explicit; |
||||
|
|
||||
|
import java.util.BitSet; |
||||
|
import java.util.Vector; |
||||
|
|
||||
|
import explicit.rewards.ConstructRewards; |
||||
|
import explicit.rewards.MCRewards; |
||||
|
import explicit.rewards.MDPRewards; |
||||
|
import parser.ast.Expression; |
||||
|
import parser.ast.ExpressionAccumulation; |
||||
|
import parser.ast.ExpressionReward; |
||||
|
import parser.ast.RewardStruct; |
||||
|
import parser.visitor.ReplaceAccumulationExpression; |
||||
|
import prism.PrismException; |
||||
|
|
||||
|
public class AccumulationTransformation<M extends Model> implements ModelExpressionTransformation<M, M> { |
||||
|
final private Expression originalExpression; |
||||
|
final private M originalModel; |
||||
|
final private BitSet statesOfInterest; |
||||
|
final ProbModelChecker mc; |
||||
|
|
||||
|
private Expression transformedExpression; |
||||
|
private AccumulationProduct<M,?> product; |
||||
|
|
||||
|
public AccumulationTransformation( |
||||
|
ProbModelChecker mc, |
||||
|
M originalModel, Expression expr, |
||||
|
BitSet statesOfInterest) throws PrismException{ |
||||
|
super(); |
||||
|
this.originalExpression = expr; |
||||
|
this.originalModel = originalModel; |
||||
|
this.mc = mc; |
||||
|
this.statesOfInterest = statesOfInterest; |
||||
|
doTransformation(); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public M getOriginalModel() { |
||||
|
return originalModel; |
||||
|
} |
||||
|
@Override |
||||
|
public Expression getTransformedExpression() { |
||||
|
return transformedExpression; |
||||
|
} |
||||
|
@Override |
||||
|
public Expression getOriginalExpression() { |
||||
|
return originalExpression; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public BitSet getTransformedStatesOfInterest() { |
||||
|
return product.getTransformedStatesOfInterest(); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public M getTransformedModel() { |
||||
|
return product.getTransformedModel(); |
||||
|
} |
||||
|
@Override |
||||
|
public StateValues projectToOriginalModel(StateValues svTransformedModel) |
||||
|
throws PrismException { |
||||
|
return product.projectToOriginalModel(svTransformedModel); |
||||
|
} |
||||
|
|
||||
|
@SuppressWarnings("unchecked") |
||||
|
private void doTransformation() throws PrismException { |
||||
|
mc.getLog().println("Performing accumulation transformation..."); |
||||
|
// We work on a copy |
||||
|
transformedExpression = originalExpression.deepCopy(); |
||||
|
|
||||
|
// Get the first ExpressionAccumulation |
||||
|
ExpressionAccumulation accexp = transformedExpression.getFirstAccumulationExpression(); |
||||
|
|
||||
|
// Get the rewards and build the product |
||||
|
switch(originalModel.getModelType()) { |
||||
|
case DTMC: |
||||
|
Vector<MCRewards> dtmc_rewards = new Vector<MCRewards>(); |
||||
|
|
||||
|
for (int i=0; i < accexp.getConstraint().getFactors().size(); i++) { |
||||
|
Object rewardIndex = accexp.getConstraint().getFactors().get(i).getFunction().getRewardIndex(); |
||||
|
|
||||
|
RewardStruct rewStruct = ExpressionReward.getRewardStructByIndexObject(rewardIndex, mc.modulesFile, originalModel.getConstantValues()); |
||||
|
ConstructRewards constructRewards = new ConstructRewards(); |
||||
|
constructRewards.allowNegativeRewards(); |
||||
|
|
||||
|
MCRewards dtmc_reward = constructRewards.buildMCRewardStructure((DTMC)originalModel, rewStruct, mc.getConstantValues()); |
||||
|
dtmc_rewards.add(i,dtmc_reward); |
||||
|
} |
||||
|
mc.getLog().println(" [AT] performing product construction..."); |
||||
|
if(accexp.hasRegularExpression()) { |
||||
|
product = (AccumulationProductRegular<M>) AccumulationProductRegular.generate((DTMC)originalModel, accexp, dtmc_rewards, mc, statesOfInterest); |
||||
|
} else if (accexp.hasBoundExpression()) { |
||||
|
product = (AccumulationProductCounting<M>) AccumulationProductCounting.generate((DTMC)originalModel, accexp, dtmc_rewards, mc, statesOfInterest); |
||||
|
} else { |
||||
|
throw new PrismException("Accumulation Expression has no valid monitor!"); |
||||
|
} |
||||
|
break; |
||||
|
case MDP: |
||||
|
Vector<MDPRewards> mdp_rewards = new Vector<MDPRewards>(); |
||||
|
|
||||
|
for (int i=0; i < accexp.getConstraint().getFactors().size(); i++) { |
||||
|
Object rewardIndex = accexp.getConstraint().getFactors().get(i).getFunction().getRewardIndex(); |
||||
|
|
||||
|
RewardStruct rewStruct = ExpressionReward.getRewardStructByIndexObject(rewardIndex, mc.modulesFile, originalModel.getConstantValues()); |
||||
|
ConstructRewards constructRewards = new ConstructRewards(); |
||||
|
constructRewards.allowNegativeRewards(); |
||||
|
|
||||
|
MDPRewards mdp_reward = constructRewards.buildMDPRewardStructure((MDP)originalModel, rewStruct, mc.getConstantValues()); |
||||
|
mdp_rewards.add(i,mdp_reward); |
||||
|
} |
||||
|
mc.getLog().println(" [AT] performing product construction..."); |
||||
|
if(accexp.hasRegularExpression()) { |
||||
|
product = (AccumulationProductRegular<M>) AccumulationProductRegular.generate((MDP)originalModel, accexp, mdp_rewards, mc, statesOfInterest); |
||||
|
} else if (accexp.hasBoundExpression()) { |
||||
|
product = (AccumulationProductCounting<M>) AccumulationProductCounting.generate((MDP)originalModel, accexp, mdp_rewards, mc, statesOfInterest); |
||||
|
} else { |
||||
|
throw new PrismException("Accumulation Expression has no valid monitor!"); |
||||
|
} |
||||
|
|
||||
|
break; |
||||
|
default: |
||||
|
throw new PrismException("Can't handle weight functions for " + originalModel.getModelType()); |
||||
|
} |
||||
|
|
||||
|
// Transform the model |
||||
|
BitSet goodStates = product.getGoodStates(); |
||||
|
String label = gensymLabel("good", product.getTransformedModel()); |
||||
|
|
||||
|
((ModelExplicit)product.getTransformedModel()).addLabel(label, goodStates); |
||||
|
|
||||
|
//System.out.println("Good states " + goodStates); |
||||
|
|
||||
|
// Transform the expression |
||||
|
ReplaceAccumulationExpression replace = new ReplaceAccumulationExpression(accexp, label, product.getNumberOfTracks()); |
||||
|
transformedExpression = (Expression)transformedExpression.accept(replace); |
||||
|
mc.getLog().println("Transformed " + originalExpression.toString() + |
||||
|
"\n into " + transformedExpression.toString()); |
||||
|
//DEBUG: output dotfile |
||||
|
product.exportToDotFile("./out.dot"); |
||||
|
} |
||||
|
|
||||
|
public String gensymLabel(String prefix, Model model) { |
||||
|
int suffix = 0; |
||||
|
String label = prefix + "_" + suffix; |
||||
|
while(product.getTransformedModel().getLabels().contains(label)) { |
||||
|
suffix++; |
||||
|
label = prefix + "_" + suffix; |
||||
|
} |
||||
|
return label; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public Integer mapToTransformedModel(int state) { |
||||
|
return product.mapToTransformedModel(state); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public BitSet mapToTransformedModel(BitSet states) { |
||||
|
return product.mapToTransformedModel(states); |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,45 @@ |
|||||
|
package explicit; |
||||
|
|
||||
|
import java.util.ArrayList; |
||||
|
import java.util.HashMap; |
||||
|
import java.util.Iterator; |
||||
|
|
||||
|
/** |
||||
|
* A generic pool of things of type T, which can only grow. |
||||
|
* Uses an ArrayList and a HashMap internally. |
||||
|
* Provides a findOrAdd method. |
||||
|
* |
||||
|
* @author sw |
||||
|
* |
||||
|
* @param <T> |
||||
|
*/ |
||||
|
public class StoragePool<T> implements Iterable<T> { |
||||
|
final ArrayList<T> things; |
||||
|
final HashMap<T,Integer> thingIds; |
||||
|
|
||||
|
public StoragePool() { |
||||
|
things = new ArrayList<T>(); |
||||
|
thingIds = new HashMap<T,Integer>(); |
||||
|
} |
||||
|
|
||||
|
public T getById(final int thingId) { |
||||
|
return things.get(thingId); |
||||
|
} |
||||
|
|
||||
|
public int findOrAdd(final T thing) { |
||||
|
Integer thingId = thingIds.get(thing); |
||||
|
|
||||
|
if (thingId == null) { |
||||
|
thingId = things.size(); |
||||
|
things.add(thingId, thing); |
||||
|
thingIds.put(thing, thingId); |
||||
|
} |
||||
|
|
||||
|
return thingId; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public Iterator<T> iterator() { |
||||
|
return things.iterator(); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,67 @@ |
|||||
|
package parser.ast; |
||||
|
|
||||
|
import java.util.ArrayList; |
||||
|
|
||||
|
import parser.visitor.ASTVisitor; |
||||
|
import prism.PrismLangException; |
||||
|
|
||||
|
public class AccumulationConstraint extends ASTElement { |
||||
|
|
||||
|
private ArrayList<AccumulationFactor> factors; |
||||
|
private TemporalOperatorBound bound; |
||||
|
|
||||
|
public AccumulationConstraint(ArrayList<AccumulationFactor> factors, |
||||
|
TemporalOperatorBound bound) { |
||||
|
this.factors = factors; |
||||
|
this.bound = bound; |
||||
|
} |
||||
|
|
||||
|
public ArrayList<AccumulationFactor> getFactors() { |
||||
|
return factors; |
||||
|
} |
||||
|
|
||||
|
public void setFactors(ArrayList<AccumulationFactor> factors) { |
||||
|
this.factors = factors; |
||||
|
} |
||||
|
|
||||
|
public TemporalOperatorBound getBound() { |
||||
|
return bound; |
||||
|
} |
||||
|
|
||||
|
public void setBound(TemporalOperatorBound bound) { |
||||
|
this.bound = bound; |
||||
|
} |
||||
|
|
||||
|
public AccumulationConstraint deepCopy() { |
||||
|
ArrayList<AccumulationFactor> factorscopy = new ArrayList<AccumulationFactor>(); |
||||
|
TemporalOperatorBound boundcopy = bound.deepCopy(); |
||||
|
|
||||
|
for (AccumulationFactor factor : factors) { |
||||
|
factorscopy.add(factor.deepCopy()); |
||||
|
} |
||||
|
|
||||
|
return new AccumulationConstraint(factorscopy,boundcopy); |
||||
|
} |
||||
|
|
||||
|
public String toString() |
||||
|
{ |
||||
|
boolean first = true; |
||||
|
String ret = ""; |
||||
|
|
||||
|
for (AccumulationFactor factor : factors) { |
||||
|
if (!first) ret += " + "; |
||||
|
first = false; |
||||
|
|
||||
|
ret += factor.toString(); |
||||
|
} |
||||
|
|
||||
|
ret += bound.toString(); |
||||
|
|
||||
|
return ret; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public Object accept(ASTVisitor v) throws PrismLangException { |
||||
|
return v.visit(this); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,59 @@ |
|||||
|
package parser.ast; |
||||
|
|
||||
|
import parser.visitor.ASTVisitor; |
||||
|
import prism.PrismLangException; |
||||
|
import parser.type.TypeInt; |
||||
|
|
||||
|
public class AccumulationFactor extends ASTElement { |
||||
|
|
||||
|
private Expression factor; |
||||
|
private AccumulationFunction func; |
||||
|
|
||||
|
public AccumulationFactor(AccumulationFunction func) |
||||
|
{ |
||||
|
this(func, null); |
||||
|
} |
||||
|
|
||||
|
public AccumulationFactor(AccumulationFunction func, Expression factor) { |
||||
|
this.func = func; |
||||
|
if (factor != null) { |
||||
|
this.factor = factor; |
||||
|
} else { |
||||
|
this.factor = new ExpressionLiteral(TypeInt.getInstance(), 1); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public Expression getFactor() { |
||||
|
return factor; |
||||
|
} |
||||
|
|
||||
|
public void setFactor(Expression factor) { |
||||
|
this.factor = factor; |
||||
|
} |
||||
|
|
||||
|
public AccumulationFunction getFunction() { |
||||
|
return func; |
||||
|
} |
||||
|
|
||||
|
public void setFunction(AccumulationFunction func) { |
||||
|
this.func = func; |
||||
|
} |
||||
|
|
||||
|
public AccumulationFactor deepCopy() { |
||||
|
return new AccumulationFactor(func,factor.deepCopy()); |
||||
|
} |
||||
|
|
||||
|
public String toString() |
||||
|
{ |
||||
|
if (factor != null) { |
||||
|
return factor.toString() + " * " + func.toString(); |
||||
|
} else { |
||||
|
return func.toString(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public Object accept(ASTVisitor v) throws PrismLangException { |
||||
|
return v.visit(this); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,36 @@ |
|||||
|
package parser.ast; |
||||
|
|
||||
|
public enum AccumulationFunction { |
||||
|
ACC_TIME, ACC_STEPS, ACC_REWARD; |
||||
|
|
||||
|
private Object rewardIndex = null; |
||||
|
|
||||
|
public Object getRewardIndex() { |
||||
|
return rewardIndex; |
||||
|
} |
||||
|
|
||||
|
public void setRewardIndex(Object index) { |
||||
|
rewardIndex = index; |
||||
|
} |
||||
|
|
||||
|
public String toString() { |
||||
|
switch(this) { |
||||
|
case ACC_REWARD: |
||||
|
return "reward" + indexString(); |
||||
|
case ACC_STEPS: |
||||
|
return "steps"; |
||||
|
case ACC_TIME: |
||||
|
return "time"; |
||||
|
default: |
||||
|
throw new RuntimeException("Unknown accumulation function"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private String indexString() { |
||||
|
if (rewardIndex == null) return ""; |
||||
|
|
||||
|
if (rewardIndex instanceof String) return "{\"" + rewardIndex + "\"}"; |
||||
|
else return "{" + rewardIndex.toString() + "}"; |
||||
|
|
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,38 @@ |
|||||
|
package parser.ast; |
||||
|
|
||||
|
public enum AccumulationSymbol { |
||||
|
ACCDIAPLUS, ACCDIAMINUS, ACCBOXPLUS, ACCBOXMINUS; |
||||
|
|
||||
|
public String toString() |
||||
|
{ |
||||
|
switch(this) { |
||||
|
case ACCBOXMINUS: |
||||
|
return "[-]"; |
||||
|
case ACCBOXPLUS: |
||||
|
return "[+]"; |
||||
|
case ACCDIAMINUS: |
||||
|
return "<->"; |
||||
|
case ACCDIAPLUS: |
||||
|
return "<+>"; |
||||
|
default: |
||||
|
throw new RuntimeException("Unknown accumulation symbol"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public boolean isDia() { |
||||
|
return (this == ACCDIAPLUS) || (this == ACCDIAMINUS); |
||||
|
} |
||||
|
|
||||
|
public boolean isBox() { |
||||
|
return (this == ACCBOXPLUS) || (this == ACCBOXMINUS); |
||||
|
} |
||||
|
|
||||
|
public boolean isMinus() { |
||||
|
return (this == ACCDIAMINUS) || (this == ACCBOXMINUS); |
||||
|
} |
||||
|
|
||||
|
public boolean isPlus() { |
||||
|
return (this == ACCDIAPLUS) || (this == ACCBOXPLUS); |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,142 @@ |
|||||
|
//============================================================================== |
||||
|
// |
||||
|
// Copyright (c) 2002- |
||||
|
// Authors: |
||||
|
// * Dave Parker <david.parker@comlab.ox.ac.uk> (University of Oxford, formerly University of Birmingham) |
||||
|
// |
||||
|
//------------------------------------------------------------------------------ |
||||
|
// |
||||
|
// This file is part of PRISM. |
||||
|
// |
||||
|
// PRISM is free software; you can redistribute it and/or modify |
||||
|
// it under the terms of the GNU General Public License as published by |
||||
|
// the Free Software Foundation; either version 2 of the License, or |
||||
|
// (at your option) any later version. |
||||
|
// |
||||
|
// PRISM 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 General Public License for more details. |
||||
|
// |
||||
|
// You should have received a copy of the GNU General Public License |
||||
|
// along with PRISM; if not, write to the Free Software Foundation, |
||||
|
// Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
||||
|
// |
||||
|
//============================================================================== |
||||
|
|
||||
|
package parser.ast; |
||||
|
|
||||
|
import parser.EvaluateContext; |
||||
|
import parser.type.TypePathBool; |
||||
|
import parser.visitor.ASTVisitor; |
||||
|
import prism.PrismLangException; |
||||
|
|
||||
|
public class ExpressionAccumulation extends Expression |
||||
|
{ |
||||
|
AccumulationSymbol symbol; |
||||
|
AccumulationConstraint constraint; |
||||
|
ExpressionRegular regexp; |
||||
|
TemporalOperatorBound bound; |
||||
|
|
||||
|
public ExpressionAccumulation(AccumulationSymbol symbol) { |
||||
|
this.symbol = symbol; |
||||
|
this.setType(TypePathBool.getInstance()); |
||||
|
} |
||||
|
|
||||
|
public boolean hasRegularExpression() { |
||||
|
return regexp != null; |
||||
|
} |
||||
|
|
||||
|
public boolean hasBoundExpression() { |
||||
|
return bound != null; |
||||
|
} |
||||
|
|
||||
|
public AccumulationSymbol getSymbol() { |
||||
|
return symbol; |
||||
|
} |
||||
|
public void setSymbol(AccumulationSymbol symbol) { |
||||
|
this.symbol = symbol; |
||||
|
} |
||||
|
public AccumulationConstraint getConstraint() { |
||||
|
return constraint; |
||||
|
} |
||||
|
public void setConstraint(AccumulationConstraint constraint) { |
||||
|
this.constraint = constraint; |
||||
|
} |
||||
|
public ExpressionRegular getRegularExpression() { |
||||
|
if ( hasRegularExpression() ) { return regexp; } |
||||
|
else throw new RuntimeException("getRegularExpression called without RegularExpression"); |
||||
|
} |
||||
|
public void setRegularExpression(ExpressionRegular regexp) { |
||||
|
this.bound = null; |
||||
|
this.regexp = regexp; |
||||
|
} |
||||
|
|
||||
|
public TemporalOperatorBound getBoundExpression() { |
||||
|
if ( hasBoundExpression() ) { return bound; } |
||||
|
else throw new RuntimeException("getBoundExpression called without BoundExpression"); |
||||
|
} |
||||
|
|
||||
|
public void setBoundExpression(TemporalOperatorBound bound) { |
||||
|
if ( bound.isDefaultBound() && bound.hasUpperBound() ) { |
||||
|
this.regexp= null; |
||||
|
this.bound = bound; |
||||
|
} else { |
||||
|
throw new RuntimeException("Bounds need to be upper and default."); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Add a toString |
||||
|
public String toString() { |
||||
|
String ret = ""; |
||||
|
ret += symbol.toString(); |
||||
|
if ( hasRegularExpression() ) { ret += "( Reg:" + regexp.toString() + ")"; } |
||||
|
else if ( hasBoundExpression() ) { ret += "(" + bound.toString() + ")"; } |
||||
|
else throw new RuntimeException("Cannot stringify AccumulationExpression without fragment bounds."); |
||||
|
ret += "(" + constraint.toString() + ")"; |
||||
|
return ret; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public boolean isConstant() { |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public boolean isProposition() { |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public Object evaluate(EvaluateContext ec) throws PrismLangException { |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public boolean returnsSingleValue() { |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public Expression deepCopy() { |
||||
|
ExpressionAccumulation ret = new ExpressionAccumulation(this.getSymbol()); |
||||
|
|
||||
|
ret.setConstraint(this.getConstraint().deepCopy()); |
||||
|
if ( this.hasBoundExpression() ) { |
||||
|
ret.setBoundExpression(this.getBoundExpression().deepCopy()); |
||||
|
} |
||||
|
if ( this.hasRegularExpression() ) { |
||||
|
ret.setRegularExpression((ExpressionRegular) this.getRegularExpression().deepCopy()); |
||||
|
} |
||||
|
|
||||
|
return ret; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public Object accept(ASTVisitor v) throws PrismLangException { |
||||
|
return v.visit(this); |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
||||
|
//------------------------------------------------------------------------------ |
||||
@ -0,0 +1,67 @@ |
|||||
|
package parser.visitor; |
||||
|
|
||||
|
import parser.ast.AccumulationSymbol; |
||||
|
import parser.ast.ExpressionAccumulation; |
||||
|
import parser.ast.ExpressionLabel; |
||||
|
import parser.ast.ExpressionTemporal; |
||||
|
import parser.ast.ExpressionUnaryOp; |
||||
|
import parser.ast.TemporalOperatorBound; |
||||
|
import parser.ast.TemporalOperatorBounds; |
||||
|
import prism.IntegerBound; |
||||
|
import prism.PrismLangException; |
||||
|
|
||||
|
|
||||
|
public class ReplaceAccumulationExpression extends ASTTraverseModify { |
||||
|
|
||||
|
private ExpressionAccumulation accexp; |
||||
|
private String replacementLabel; |
||||
|
private int replacementPosition; |
||||
|
|
||||
|
|
||||
|
|
||||
|
public ReplaceAccumulationExpression(ExpressionAccumulation accexp, String replacementLabel) { |
||||
|
super(); |
||||
|
this.accexp = accexp; |
||||
|
this.replacementLabel = replacementLabel; |
||||
|
this.replacementPosition = 0; |
||||
|
} |
||||
|
|
||||
|
public ReplaceAccumulationExpression(ExpressionAccumulation accexp, String replacementLabel, int replacementPosition) |
||||
|
{ |
||||
|
this(accexp, replacementLabel); |
||||
|
this.replacementPosition = replacementPosition; |
||||
|
} |
||||
|
|
||||
|
public Object visit(ExpressionAccumulation expr) throws PrismLangException |
||||
|
{ |
||||
|
ExpressionLabel label = new ExpressionLabel(replacementLabel); |
||||
|
//System.out.println("Replacing "+expr.toString()+" with "+label.toString()); |
||||
|
if (expr == accexp) { |
||||
|
AccumulationSymbol sym = expr.getSymbol(); |
||||
|
|
||||
|
if (sym.isPlus()) { |
||||
|
// We are looking into a bright future, wrap it in F=position |
||||
|
ExpressionTemporal fLabel = new ExpressionTemporal(ExpressionTemporal.P_F, null, label); |
||||
|
TemporalOperatorBounds fBounds = new TemporalOperatorBounds(); |
||||
|
TemporalOperatorBound fBound = IntegerBound.fromEqualBounds(replacementPosition).toTemporalOperatorBound(); |
||||
|
fBounds.setDefaultBound(fBound); |
||||
|
fLabel.setBounds(fBounds); |
||||
|
// If its a BOX, negate it |
||||
|
if (sym.isBox()) { |
||||
|
return new ExpressionUnaryOp(ExpressionUnaryOp.NOT, fLabel); |
||||
|
} else { |
||||
|
return fLabel; |
||||
|
} |
||||
|
} else { |
||||
|
// This is a MINUS, just check for negation |
||||
|
if (sym.isBox()) { |
||||
|
return new ExpressionUnaryOp(ExpressionUnaryOp.NOT, label); |
||||
|
} else { |
||||
|
return label; |
||||
|
} |
||||
|
} |
||||
|
} else { |
||||
|
return expr; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue