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.
549 lines
14 KiB
549 lines
14 KiB
//==============================================================================
|
|
//
|
|
// 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;
|
|
|
|
import java.util.*;
|
|
|
|
import prism.*;
|
|
import parser.ast.*;
|
|
import parser.type.*;
|
|
|
|
/**
|
|
* Class to store information about the set of variables in a PRISM model.
|
|
* Assumes that any constants in the model have been given fixed values.
|
|
* Thus, initial/min/max values for all variables are known.
|
|
* VarList also takes care of how each variable will be encoded to an integer
|
|
* (e.g. for (MT)BDD representation).
|
|
*/
|
|
public class VarList
|
|
{
|
|
// List of variables
|
|
private List<Var> vars;
|
|
// Mapping from names to indices
|
|
private Map<String, Integer> nameMap;
|
|
// Total number of bits needed to encode
|
|
private int totalNumBits;
|
|
|
|
/**
|
|
* Construct empty variable list.
|
|
*/
|
|
public VarList()
|
|
{
|
|
vars = new ArrayList<Var>();
|
|
nameMap = new HashMap<String, Integer>();
|
|
totalNumBits = 0;
|
|
}
|
|
|
|
/**
|
|
* Construct variable list for a ModulesFile.
|
|
*/
|
|
public VarList(ModulesFile modulesFile) throws PrismLangException
|
|
{
|
|
this();
|
|
|
|
int i, j, n, n2;
|
|
parser.ast.Module module;
|
|
Declaration decl;
|
|
|
|
// First add all globals to the list
|
|
n = modulesFile.getNumGlobals();
|
|
for (i = 0; i < n; i++) {
|
|
decl = modulesFile.getGlobal(i);
|
|
addVar(decl, -1, modulesFile.getConstantValues());
|
|
}
|
|
|
|
// Then add all module variables to the list
|
|
n = modulesFile.getNumModules();
|
|
for (i = 0; i < n; i++) {
|
|
module = modulesFile.getModule(i);
|
|
n2 = module.getNumDeclarations();
|
|
for (j = 0; j < n2; j++) {
|
|
decl = module.getDeclaration(j);
|
|
addVar(decl, i, modulesFile.getConstantValues());
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Add a new variable to the end of the VarList.
|
|
* @param decl Declaration defining the variable
|
|
* @param module Index of module containing variable
|
|
* @param constantValues Values of constants needed to evaluate low/high/etc.
|
|
*/
|
|
public void addVar(Declaration decl, int module, Values constantValues) throws PrismLangException
|
|
{
|
|
Var var = createVar(decl, module, constantValues);
|
|
vars.add(var);
|
|
totalNumBits += getRangeLogTwo(vars.size() - 1);
|
|
nameMap.put(decl.getName(), vars.size() - 1);
|
|
}
|
|
|
|
/**
|
|
* Add a new variable at position i in the VarList.
|
|
* @param decl Declaration defining the variable
|
|
* @param module Index of module containing variable
|
|
* @param constantValues Values of constants needed to evaluate low/high/etc.
|
|
*/
|
|
public void addVar(int i, Declaration decl, int module, Values constantValues) throws PrismLangException
|
|
{
|
|
Var var = createVar(decl, module, constantValues);
|
|
vars.add(i, var);
|
|
totalNumBits += getRangeLogTwo(i);
|
|
// Recompute name map
|
|
int j, n;
|
|
n = getNumVars();
|
|
nameMap = new HashMap<String, Integer>(n);
|
|
for (j = 0; j < n; j++) {
|
|
nameMap.put(getName(j), j);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create a new variable object to the store in the list.
|
|
* @param decl Declaration defining the variable
|
|
* @param module Index of module containing variable
|
|
* @param constantValues Values of constants needed to evaluate low/high/etc.
|
|
*/
|
|
private Var createVar(Declaration decl, int module, Values constantValues) throws PrismLangException
|
|
{
|
|
Var var;
|
|
int low, high, start;
|
|
DeclarationType declType;
|
|
|
|
// Create new Var object
|
|
var = new Var();
|
|
|
|
// Store name/type/module
|
|
var.decl = decl;
|
|
var.module = module;
|
|
|
|
declType = decl.getDeclType();
|
|
// Variable is a bounded integer
|
|
if (declType instanceof DeclarationInt) {
|
|
DeclarationInt intdecl = (DeclarationInt) declType;
|
|
low = intdecl.getLow().evaluateInt(constantValues);
|
|
high = intdecl.getHigh().evaluateInt(constantValues);
|
|
start = decl.getStartOrDefault().evaluateInt(constantValues);
|
|
// Check range is valid
|
|
if (high - low <= 0) {
|
|
String s = "Invalid range (" + low + "-" + high + ") for variable \"" + decl.getName() + "\"";
|
|
throw new PrismLangException(s, decl);
|
|
}
|
|
if ((long) high - (long) low >= Integer.MAX_VALUE) {
|
|
String s = "Range for variable \"" + decl.getName() + "\" (" + low + "-" + high + ") is too big";
|
|
throw new PrismLangException(s, decl);
|
|
}
|
|
// Check start is valid
|
|
if (start < low || start > high) {
|
|
String s = "Invalid initial value (" + start + ") for variable \"" + decl.getName() + "\"";
|
|
throw new PrismLangException(s, decl);
|
|
}
|
|
}
|
|
// Variable is a Boolean
|
|
else if (declType instanceof DeclarationBool) {
|
|
low = 0;
|
|
high = 1;
|
|
start = (decl.getStartOrDefault().evaluateBoolean(constantValues)) ? 1 : 0;
|
|
}
|
|
// Variable is a clock
|
|
else if (declType instanceof DeclarationClock) {
|
|
// Create dummy info
|
|
low = 0;
|
|
high = 1;
|
|
start = 0;
|
|
}
|
|
// Variable is an (unbounded) integer
|
|
else if (declType instanceof DeclarationIntUnbounded) {
|
|
// Create dummy range info
|
|
low = 0;
|
|
high = 1;
|
|
start = decl.getStartOrDefault().evaluateInt(constantValues);
|
|
}
|
|
else {
|
|
throw new PrismLangException("Unknown variable type \"" + declType + "\" in declaration", decl);
|
|
}
|
|
|
|
// Store low/high/start and return
|
|
var.low = low;
|
|
var.high = high;
|
|
var.start = start;
|
|
|
|
return var;
|
|
}
|
|
|
|
/**
|
|
* Get the number of variables stored in this list.
|
|
*/
|
|
public int getNumVars()
|
|
{
|
|
return vars.size();
|
|
}
|
|
|
|
/**
|
|
* Look up the index of a variable, as stored in this list, by name.
|
|
* Returns -1 if there is no such variable.
|
|
*/
|
|
public int getIndex(String name)
|
|
{
|
|
Integer i = nameMap.get(name);
|
|
return (i == null) ? -1 : i;
|
|
}
|
|
|
|
/**
|
|
* Check if there is a variable of a given name in this list.
|
|
*/
|
|
public boolean exists(String name)
|
|
{
|
|
return getIndex(name) != -1;
|
|
}
|
|
|
|
/**
|
|
* Get the declaration of the ith variable in this list.
|
|
*/
|
|
public Declaration getDeclaration(int i)
|
|
{
|
|
return vars.get(i).decl;
|
|
}
|
|
|
|
/**
|
|
* Get the index in this VarList for a given declaration.
|
|
*/
|
|
public int getIndexFromDeclaration(Declaration d)
|
|
{
|
|
for (int i=0;i<vars.size();i++) {
|
|
if (vars.get(i).decl == d) {
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/**
|
|
* Get the name of the ith variable in this list.
|
|
*/
|
|
public String getName(int i)
|
|
{
|
|
return vars.get(i).decl.getName();
|
|
}
|
|
|
|
/**
|
|
* Get the type of the ith variable in this list.
|
|
*/
|
|
public Type getType(int i)
|
|
{
|
|
return vars.get(i).decl.getDeclType().getType();
|
|
}
|
|
|
|
/**
|
|
* Get the index of the module of the ith variable in this list (-1 denotes global variable).
|
|
*/
|
|
public int getModule(int i)
|
|
{
|
|
return vars.get(i).module;
|
|
}
|
|
|
|
/**
|
|
* Get the low value of the ith variable in this list (when encoded as an integer).
|
|
*/
|
|
public int getLow(int i)
|
|
{
|
|
return vars.get(i).low;
|
|
}
|
|
|
|
/**
|
|
* Get the high value of the ith variable in this list (when encoded as an integer).
|
|
*/
|
|
public int getHigh(int i)
|
|
{
|
|
return vars.get(i).high;
|
|
}
|
|
|
|
/**
|
|
* Get the range of the ith variable in this list (when encoded as an integer).
|
|
*/
|
|
public int getRange(int i)
|
|
{
|
|
return vars.get(i).high - vars.get(i).low + 1;
|
|
}
|
|
|
|
/**
|
|
* Get the number of bits required to store the ith variable in this list (when encoded as an integer).
|
|
*/
|
|
public int getRangeLogTwo(int i)
|
|
{
|
|
return (int) Math.ceil(PrismUtils.log2(getRange(i)));
|
|
}
|
|
|
|
/**
|
|
* Get the total number of bits required to store all variables in this list (when encoded as integers).
|
|
*/
|
|
public int getTotalNumBits()
|
|
{
|
|
return totalNumBits;
|
|
}
|
|
|
|
/**
|
|
* Get the initial value of the ith variable in this list (when encoded as an integer).
|
|
*/
|
|
public int getStart(int i)
|
|
{
|
|
return vars.get(i).start;
|
|
}
|
|
|
|
/**
|
|
* Get the value (as an Object) of a variable, from the value encoded as an integer.
|
|
*/
|
|
public Object decodeFromInt(int var, int val)
|
|
{
|
|
Type type = getType(var);
|
|
// Integer type
|
|
if (type instanceof TypeInt) {
|
|
return new Integer(val + getLow(var));
|
|
}
|
|
// Boolean type
|
|
else if (type instanceof TypeBool) {
|
|
return new Boolean(val != 0);
|
|
}
|
|
// Anything else
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Get the integer encoding of a value for a variable, specified as an Object.
|
|
* The Object is assumed to be of correct type (e.g. Integer, Boolean).
|
|
* Throws an exception if Object is of the wrong type.
|
|
*/
|
|
public int encodeToInt(int var, Object val) throws PrismLangException
|
|
{
|
|
Type type = getType(var);
|
|
try {
|
|
// Integer type
|
|
if (type instanceof TypeInt) {
|
|
return ((Integer) val).intValue() - getLow(var);
|
|
}
|
|
// Boolean type
|
|
else if (type instanceof TypeBool) {
|
|
return ((Boolean) val).booleanValue() ? 1 : 0;
|
|
}
|
|
// Anything else
|
|
else {
|
|
throw new PrismLangException("Unknown type " + type + " for variable " + getName(var));
|
|
}
|
|
} catch (ClassCastException e) {
|
|
throw new PrismLangException("Value " + val + " is wrong type for variable " + getName(var));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get the integer encoding of a value for a variable, specified as a string.
|
|
*/
|
|
public int encodeToIntFromString(int var, String s) throws PrismLangException
|
|
{
|
|
Type type = getType(var);
|
|
// Integer type
|
|
if (type instanceof TypeInt) {
|
|
try {
|
|
int i = Integer.parseInt(s);
|
|
return i - getLow(var);
|
|
} catch (NumberFormatException e) {
|
|
throw new PrismLangException("\"" + s + "\" is not a valid integer value");
|
|
}
|
|
}
|
|
// Boolean type
|
|
else if (type instanceof TypeBool) {
|
|
if (s.equals("true"))
|
|
return 1;
|
|
else if (s.equals("false"))
|
|
return 0;
|
|
else
|
|
throw new PrismLangException("\"" + s + "\" is not a valid Boolean value");
|
|
|
|
}
|
|
// Anything else
|
|
else {
|
|
throw new PrismLangException("Unknown type " + type + " for variable " + getName(var));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get a list of all possible values for a subset of the variables in this list.
|
|
* @param vars The subset of variables
|
|
*/
|
|
public List<Values> getAllValues(List<String> vars) throws PrismLangException
|
|
{
|
|
int i, j, k, n, lo, hi;
|
|
Vector<Values> allValues;
|
|
Values vals, valsNew;
|
|
|
|
allValues = new Vector<Values>();
|
|
allValues.add(new Values());
|
|
for (String var : vars) {
|
|
i = getIndex(var);
|
|
if (getType(i) instanceof TypeBool) {
|
|
n = allValues.size();
|
|
for (j = 0; j < n; j++) {
|
|
vals = allValues.get(j);
|
|
valsNew = new Values(vals);
|
|
valsNew.setValue(var, true);
|
|
allValues.add(valsNew);
|
|
vals.addValue(var, false);
|
|
}
|
|
} else if (getType(i) instanceof TypeInt) {
|
|
lo = getLow(i);
|
|
hi = getHigh(i);
|
|
n = allValues.size();
|
|
for (j = 0; j < n; j++) {
|
|
vals = allValues.get(j);
|
|
for (k = lo + 1; k < hi + 1; k++) {
|
|
valsNew = new Values(vals);
|
|
valsNew.setValue(var, k);
|
|
allValues.add(valsNew);
|
|
}
|
|
vals.addValue(var, lo);
|
|
}
|
|
} else {
|
|
throw new PrismLangException("Cannot determine all values for a variable of type " + getType(i));
|
|
}
|
|
}
|
|
|
|
return allValues;
|
|
}
|
|
|
|
/**
|
|
* Get a list of all possible states over the variables in this list. Use with care!
|
|
*/
|
|
public List<State> getAllStates() throws PrismLangException
|
|
{
|
|
List<State> allStates;
|
|
State state, stateNew;
|
|
|
|
int numVars = getNumVars();
|
|
allStates = new ArrayList<State>();
|
|
allStates.add(new State(numVars));
|
|
for (int i = 0; i < numVars; i++) {
|
|
if (getType(i) instanceof TypeBool) {
|
|
int n = allStates.size();
|
|
for (int j = 0; j < n; j++) {
|
|
state = allStates.get(j);
|
|
stateNew = new State(state);
|
|
stateNew.setValue(i, true);
|
|
state.setValue(i, false);
|
|
allStates.add(stateNew);
|
|
}
|
|
} else if (getType(i) instanceof TypeInt) {
|
|
int lo = getLow(i);
|
|
int hi = getHigh(i);
|
|
int n = allStates.size();
|
|
for (int j = 0; j < n; j++) {
|
|
state = allStates.get(j);
|
|
for (int k = lo + 1; k < hi + 1; k++) {
|
|
stateNew = new State(state);
|
|
stateNew.setValue(i, k);
|
|
allStates.add(stateNew);
|
|
}
|
|
state.setValue(i, lo);
|
|
}
|
|
} else {
|
|
throw new PrismLangException("Cannot determine all values for a variable of type " + getType(i));
|
|
}
|
|
}
|
|
|
|
return allStates;
|
|
}
|
|
|
|
/**
|
|
* Convert a bit vector representing a single state to a State object.
|
|
*/
|
|
public State convertBitSetToState(BitSet bits)
|
|
{
|
|
int i, n, j, var, val;
|
|
State state;
|
|
state = new State(getNumVars());
|
|
var = val = j = 0;
|
|
n = totalNumBits;
|
|
for (i = 0; i < n; i++) {
|
|
if (bits.get(i))
|
|
val += (1 << (getRangeLogTwo(var) - j - 1));
|
|
if (j >= getRangeLogTwo(var) - 1) {
|
|
state.setValue(var, decodeFromInt(var, val));
|
|
var++;
|
|
val = 0;
|
|
j = 0;
|
|
} else {
|
|
j++;
|
|
}
|
|
}
|
|
return state;
|
|
}
|
|
|
|
/**
|
|
* Clone this list.
|
|
*/
|
|
public Object clone()
|
|
{
|
|
int i, n;
|
|
n = getNumVars();
|
|
VarList rv = new VarList();
|
|
rv.vars = new ArrayList<Var>(n);
|
|
rv.nameMap = new HashMap<String, Integer>(n);
|
|
for (i = 0; i < n; i++) {
|
|
rv.vars.add(new Var(vars.get(i)));
|
|
rv.nameMap.put(getName(i), i);
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
/**
|
|
* Class to store information about a single variable.
|
|
*/
|
|
class Var
|
|
{
|
|
// Basic info (name/type/etc.) stored as Declaration
|
|
public Declaration decl;
|
|
// Index of containing module (-1 for a global)
|
|
public int module;
|
|
// Info about how variable is encoded as an integer
|
|
public int low;
|
|
public int high;
|
|
public int start;
|
|
|
|
// Default constructor
|
|
public Var()
|
|
{
|
|
}
|
|
|
|
// Copy constructor
|
|
public Var(Var var)
|
|
{
|
|
decl = (Declaration) var.decl.deepCopy();
|
|
module = var.module;
|
|
low = var.low;
|
|
high = var.high;
|
|
start = var.start;
|
|
}
|
|
}
|
|
}
|