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.
471 lines
13 KiB
471 lines
13 KiB
package simulator;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
|
|
import param.Function;
|
|
import param.FunctionFactory;
|
|
import param.ModelBuilder;
|
|
import param.SymbolicEngine;
|
|
import parser.State;
|
|
import parser.Values;
|
|
import parser.VarList;
|
|
import parser.ast.ConstantList;
|
|
import parser.ast.Expression;
|
|
import parser.ast.LabelList;
|
|
import parser.ast.ModulesFile;
|
|
import parser.ast.RewardStruct;
|
|
import parser.type.Type;
|
|
import prism.DefaultModelGenerator;
|
|
import prism.ModelGeneratorSymbolic;
|
|
import prism.ModelType;
|
|
import prism.PrismComponent;
|
|
import prism.PrismException;
|
|
import prism.PrismLangException;
|
|
|
|
public class ModulesFileModelGeneratorSymbolic extends DefaultModelGenerator implements ModelGeneratorSymbolic
|
|
{
|
|
// Parent PrismComponent (logs, settings etc.)
|
|
protected PrismComponent parent;
|
|
|
|
// PRISM model info
|
|
/** The original modules file (might have unresolved constants) */
|
|
private ModulesFile originalModulesFile;
|
|
/** The modules file used for generating (has no unresolved constants after {@code initialise}) */
|
|
private ModulesFile modulesFile;
|
|
private ModelType modelType;
|
|
private Values mfConstants;
|
|
private VarList varList;
|
|
private LabelList labelList;
|
|
private List<String> labelNames;
|
|
|
|
// Model exploration info
|
|
|
|
// State currently being explored
|
|
private State exploreState;
|
|
// Updater object for model
|
|
//protected Updater updater;
|
|
protected SymbolicEngine engine;
|
|
// List of currently available transitions
|
|
protected param.TransitionList transitionList;
|
|
// Has the transition list been built?
|
|
protected boolean transitionListBuilt;
|
|
|
|
// Symbolic stuff
|
|
boolean symbolic = false;
|
|
protected ModelBuilder modelBuilder;
|
|
protected FunctionFactory functionFactory;
|
|
|
|
/**
|
|
* Build a ModulesFileModelGenerator for a particular PRISM model, represented by a ModuleFile instance.
|
|
* @param modulesFile The PRISM model
|
|
*/
|
|
public ModulesFileModelGeneratorSymbolic(ModulesFile modulesFile) throws PrismException
|
|
{
|
|
this(modulesFile, null);
|
|
}
|
|
|
|
/**
|
|
* Build a ModulesFileModelGenerator for a particular PRISM model, represented by a ModuleFile instance.
|
|
* @param modulesFile The PRISM model
|
|
*/
|
|
public ModulesFileModelGeneratorSymbolic(ModulesFile modulesFile, PrismComponent parent) throws PrismException
|
|
{
|
|
this.parent = parent;
|
|
|
|
// No support for PTAs yet
|
|
if (modulesFile.getModelType() == ModelType.PTA) {
|
|
throw new PrismException("Sorry - the simulator does not currently support PTAs");
|
|
}
|
|
// No support for system...endsystem yet
|
|
if (modulesFile.getSystemDefn() != null) {
|
|
throw new PrismException("Sorry - the simulator does not currently handle the system...endsystem construct");
|
|
}
|
|
|
|
// Store basic model info
|
|
this.modulesFile = modulesFile;
|
|
this.originalModulesFile = modulesFile;
|
|
modelType = modulesFile.getModelType();
|
|
|
|
// If there are no constants to define, go ahead and initialise;
|
|
// Otherwise, setSomeUndefinedConstants needs to be called when the values are available
|
|
mfConstants = modulesFile.getConstantValues();
|
|
if (mfConstants != null) {
|
|
initialise();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* (Re-)Initialise the class ready for model exploration
|
|
* (can only be done once any constants needed have been provided)
|
|
*/
|
|
private void initialise() throws PrismLangException
|
|
{
|
|
// Evaluate constants on (a copy) of the modules file, insert constant values
|
|
// Note that we don't optimise expressions since this can create some round-off issues
|
|
modulesFile = (ModulesFile) modulesFile.deepCopy().replaceConstants(mfConstants);
|
|
|
|
// Get info
|
|
varList = modulesFile.createVarList();
|
|
labelList = modulesFile.getLabelList();
|
|
labelNames = labelList.getLabelNames();
|
|
|
|
// Create data structures for exploring model
|
|
//updater = new Updater(modulesFile, varList, parent);
|
|
//transitionList = new TransitionList();
|
|
engine = new SymbolicEngine(modulesFile, modelBuilder, functionFactory);
|
|
transitionListBuilt = false;
|
|
}
|
|
|
|
@Override
|
|
public void setSymbolic(ModelBuilder modelBuilder, FunctionFactory functionFactory)
|
|
{
|
|
symbolic = true;
|
|
this.modelBuilder = modelBuilder;
|
|
this.functionFactory = functionFactory;
|
|
//updater.setSymbolic(modelBuilder, functionFactory);
|
|
// TODO: created twice
|
|
engine = new SymbolicEngine(modulesFile, modelBuilder, functionFactory);
|
|
}
|
|
|
|
// Methods for ModelInfo interface
|
|
|
|
@Override
|
|
public ModelType getModelType()
|
|
{
|
|
return modelType;
|
|
}
|
|
|
|
@Override
|
|
public void setSomeUndefinedConstants(Values someValues) throws PrismException
|
|
{
|
|
// We start again with a copy of the original modules file
|
|
// and set the constants in the copy.
|
|
// As {@code initialise()} can replace references to constants
|
|
// with the concrete values in modulesFile, this ensures that we
|
|
// start again at a place where references to constants have not
|
|
// yet been replaced.
|
|
modulesFile = (ModulesFile) originalModulesFile.deepCopy();
|
|
modulesFile.setSomeUndefinedConstants(someValues);
|
|
mfConstants = modulesFile.getConstantValues();
|
|
initialise();
|
|
}
|
|
|
|
@Override
|
|
public Values getConstantValues()
|
|
{
|
|
return mfConstants;
|
|
}
|
|
|
|
@Override
|
|
public boolean containsUnboundedVariables()
|
|
{
|
|
return modulesFile.containsUnboundedVariables();
|
|
}
|
|
|
|
@Override
|
|
public int getNumVars()
|
|
{
|
|
return modulesFile.getNumVars();
|
|
}
|
|
|
|
@Override
|
|
public List<String> getVarNames()
|
|
{
|
|
return modulesFile.getVarNames();
|
|
}
|
|
|
|
@Override
|
|
public List<Type> getVarTypes()
|
|
{
|
|
return modulesFile.getVarTypes();
|
|
}
|
|
|
|
@Override
|
|
public int getNumLabels()
|
|
{
|
|
return labelList.size();
|
|
}
|
|
|
|
@Override
|
|
public List<String> getLabelNames()
|
|
{
|
|
return labelNames;
|
|
}
|
|
|
|
@Override
|
|
public String getLabelName(int i) throws PrismException
|
|
{
|
|
return labelList.getLabelName(i);
|
|
}
|
|
|
|
@Override
|
|
public int getLabelIndex(String label)
|
|
{
|
|
return labelList.getLabelIndex(label);
|
|
}
|
|
|
|
@Override
|
|
public int getNumRewardStructs()
|
|
{
|
|
return modulesFile.getNumRewardStructs();
|
|
}
|
|
|
|
@Override
|
|
public List<String> getRewardStructNames()
|
|
{
|
|
return modulesFile.getRewardStructNames();
|
|
}
|
|
|
|
@Override
|
|
public int getRewardStructIndex(String name)
|
|
{
|
|
return modulesFile.getRewardStructIndex(name);
|
|
}
|
|
|
|
@Override
|
|
public RewardStruct getRewardStruct(int i)
|
|
{
|
|
return modulesFile.getRewardStruct(i);
|
|
}
|
|
|
|
// Methods for ModelGenerator interface
|
|
|
|
@Override
|
|
public boolean hasSingleInitialState() throws PrismException
|
|
{
|
|
return modulesFile.getInitialStates() == null;
|
|
}
|
|
|
|
@Override
|
|
public State getInitialState() throws PrismException
|
|
{
|
|
if (modulesFile.getInitialStates() == null) {
|
|
return modulesFile.getDefaultInitialState();
|
|
} else {
|
|
// Inefficient but probably won't be called
|
|
return getInitialStates().get(0);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public List<State> getInitialStates() throws PrismException
|
|
{
|
|
List<State> initStates = new ArrayList<State>();
|
|
// Easy (normal) case: just one initial state
|
|
if (modulesFile.getInitialStates() == null) {
|
|
State state = modulesFile.getDefaultInitialState();
|
|
initStates.add(state);
|
|
}
|
|
// Otherwise, there may be multiple initial states
|
|
// For now, we handle this is in a very inefficient way
|
|
else {
|
|
Expression init = modulesFile.getInitialStates();
|
|
List<State> allPossStates = varList.getAllStates();
|
|
for (State possState : allPossStates) {
|
|
if (init.evaluateBoolean(modulesFile.getConstantValues(), possState)) {
|
|
initStates.add(possState);
|
|
}
|
|
}
|
|
}
|
|
return initStates;
|
|
}
|
|
|
|
@Override
|
|
public void exploreState(State exploreState) throws PrismException
|
|
{
|
|
this.exploreState = exploreState;
|
|
transitionListBuilt = false;
|
|
}
|
|
|
|
@Override
|
|
public State getExploreState()
|
|
{
|
|
return exploreState;
|
|
}
|
|
|
|
@Override
|
|
public int getNumChoices() throws PrismException
|
|
{
|
|
return getTransitionList().getNumChoices();
|
|
}
|
|
|
|
@Override
|
|
public int getNumTransitions() throws PrismException
|
|
{
|
|
return getTransitionList().getNumTransitions();
|
|
}
|
|
|
|
@Override
|
|
public int getNumTransitions(int index) throws PrismException
|
|
{
|
|
return getTransitionList().getChoice(index).size();
|
|
}
|
|
|
|
@Override
|
|
public String getTransitionAction(int index) throws PrismException
|
|
{
|
|
int a = getTransitionList().getTransitionModuleOrActionIndex(index);
|
|
return a < 0 ? null : modulesFile.getSynch(a - 1);
|
|
}
|
|
|
|
@Override
|
|
public String getTransitionAction(int index, int offset) throws PrismException
|
|
{
|
|
param.TransitionList transitions = getTransitionList();
|
|
int a = transitions.getTransitionModuleOrActionIndex(transitions.getTotalIndexOfTransition(index, offset));
|
|
return a < 0 ? null : modulesFile.getSynch(a - 1);
|
|
}
|
|
|
|
@Override
|
|
public String getChoiceAction(int index) throws PrismException
|
|
{
|
|
param.TransitionList transitions = getTransitionList();
|
|
int a = transitions.getChoiceModuleOrActionIndex(index);
|
|
return a < 0 ? null : modulesFile.getSynch(a - 1);
|
|
}
|
|
|
|
@Override
|
|
public double getTransitionProbability(int index, int offset) throws PrismException
|
|
{
|
|
throw new UnsupportedOperationException();
|
|
/*param.TransitionList transitions = getTransitionList();
|
|
return transitions.getChoice(index).getProbability(offset);*/
|
|
}
|
|
|
|
//@Override
|
|
public double getTransitionProbability(int index) throws PrismException
|
|
{
|
|
throw new UnsupportedOperationException();
|
|
/*param.TransitionList transitions = getTransitionList();
|
|
return transitions.getTransitionProbability(index);*/
|
|
}
|
|
|
|
@Override
|
|
public Function getTransitionProbabilityFunction(int index, int offset) throws PrismException
|
|
{
|
|
param.TransitionList transitions = getTransitionList();
|
|
return transitions.getChoice(index).getProbability(offset);
|
|
}
|
|
|
|
@Override
|
|
public State computeTransitionTarget(int index, int offset) throws PrismException
|
|
{
|
|
return getTransitionList().getChoice(index).computeTarget(offset, exploreState);
|
|
}
|
|
|
|
//@Override
|
|
public State computeTransitionTarget(int index) throws PrismException
|
|
{
|
|
return getTransitionList().computeTransitionTarget(index, exploreState);
|
|
}
|
|
|
|
@Override
|
|
public boolean isLabelTrue(int i) throws PrismException
|
|
{
|
|
Expression expr = labelList.getLabel(i);
|
|
return expr.evaluateBoolean(exploreState);
|
|
}
|
|
|
|
@Override
|
|
public double getStateReward(int r, State state) throws PrismException
|
|
{
|
|
RewardStruct rewStr = modulesFile.getRewardStruct(r);
|
|
int n = rewStr.getNumItems();
|
|
double d = 0;
|
|
for (int i = 0; i < n; i++) {
|
|
if (!rewStr.getRewardStructItem(i).isTransitionReward()) {
|
|
Expression guard = rewStr.getStates(i);
|
|
if (guard.evaluateBoolean(modulesFile.getConstantValues(), state)) {
|
|
double rew = rewStr.getReward(i).evaluateDouble(modulesFile.getConstantValues(), state);
|
|
if (Double.isNaN(rew))
|
|
throw new PrismLangException("Reward structure evaluates to NaN at state " + state, rewStr.getReward(i));
|
|
d += rew;
|
|
}
|
|
}
|
|
}
|
|
return d;
|
|
}
|
|
|
|
@Override
|
|
public double getStateActionReward(int r, State state, Object action) throws PrismException
|
|
{
|
|
RewardStruct rewStr = modulesFile.getRewardStruct(r);
|
|
int n = rewStr.getNumItems();
|
|
double d = 0;
|
|
for (int i = 0; i < n; i++) {
|
|
if (rewStr.getRewardStructItem(i).isTransitionReward()) {
|
|
Expression guard = rewStr.getStates(i);
|
|
String cmdAction = rewStr.getSynch(i);
|
|
if (action == null ? (cmdAction.isEmpty()) : action.equals(cmdAction)) {
|
|
if (guard.evaluateBoolean(modulesFile.getConstantValues(), state)) {
|
|
double rew = rewStr.getReward(i).evaluateDouble(modulesFile.getConstantValues(), state);
|
|
if (Double.isNaN(rew))
|
|
throw new PrismLangException("Reward structure evaluates to NaN at state " + state, rewStr.getReward(i));
|
|
d += rew;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return d;
|
|
}
|
|
|
|
//@Override
|
|
public void calculateStateRewards(State state, double[] store) throws PrismLangException
|
|
{
|
|
// TODO updater.calculateStateRewards(state, store);
|
|
}
|
|
|
|
@Override
|
|
public VarList createVarList()
|
|
{
|
|
return varList;
|
|
}
|
|
|
|
// Miscellaneous (unused?) methods
|
|
|
|
//@Override
|
|
public void getRandomInitialState(RandomNumberGenerator rng, State initialState) throws PrismException
|
|
{
|
|
if (modulesFile.getInitialStates() == null) {
|
|
initialState.copy(modulesFile.getDefaultInitialState());
|
|
} else {
|
|
throw new PrismException("Random choice of multiple initial states not yet supported");
|
|
}
|
|
}
|
|
|
|
// Local utility methods
|
|
|
|
/**
|
|
* Returns the current list of available transitions, generating it first if this has not yet been done.
|
|
*/
|
|
private param.TransitionList getTransitionList() throws PrismException
|
|
{
|
|
// Compute the current transition list, if required
|
|
if (!transitionListBuilt) {
|
|
//updater.calculateTransitions(exploreState, transitionList);
|
|
transitionList = engine.calculateTransitions(exploreState, true);
|
|
transitionListBuilt = true;
|
|
}
|
|
return transitionList;
|
|
}
|
|
|
|
// ModelGeneratorSymbolic
|
|
|
|
@Override
|
|
public Expression getUnknownConstantDefinition(String name) throws PrismException
|
|
{
|
|
ConstantList constantList = modulesFile.getConstantList();
|
|
int i = constantList.getConstantIndex(name);
|
|
if (i == -1) {
|
|
throw new PrismException("Unknown constant " + name);
|
|
}
|
|
return constantList.getConstant(i);
|
|
}
|
|
|
|
@Override
|
|
public boolean rewardStructHasTransitionRewards(int i)
|
|
{
|
|
return modulesFile.rewardStructHasTransitionRewards(i);
|
|
}
|
|
}
|