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.
1243 lines
34 KiB
1243 lines
34 KiB
//==============================================================================
|
|
//
|
|
// Copyright (c) 2004-2005, Andrew Hinton
|
|
//
|
|
// 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
|
|
//
|
|
//==============================================================================
|
|
|
|
#include "simreasoning.h"
|
|
#include "simutil.h"
|
|
#include "simstate.h"
|
|
#include "simmodel.h"
|
|
#include "simrandom.h"
|
|
#include <vector>
|
|
#include <cstdio>
|
|
#include <string>
|
|
|
|
using std::string;
|
|
using std::cout;
|
|
using std::endl;
|
|
using std::vector;
|
|
|
|
|
|
//=============================================================================
|
|
// Description
|
|
//=============================================================================
|
|
|
|
/*
|
|
* This interface allows reasoning to be performed on the model for any
|
|
* valid configuration of the current state. It allows the calculation of
|
|
* updates to a state and also the calculation of rewards of
|
|
* the a state.
|
|
*
|
|
* The results can be queryed by a series of access methods.
|
|
*/
|
|
|
|
//=============================================================================
|
|
// Local Function Declarations
|
|
//=============================================================================
|
|
|
|
/*
|
|
* Returns the calculated update at the given index. Because these updates
|
|
* are preallocated, if it is required that more are needed than have been
|
|
* allocated, then this method doubles the allocation of potential update
|
|
* storage.
|
|
*
|
|
* throws an exception if out of memory
|
|
*/
|
|
//inline CFinalUpdate* Get_Update(int index);
|
|
CFinalUpdate* Get_Update(int index); // Mac OS X doesn't like this being inline for some reason
|
|
|
|
/*
|
|
* Finds all of the commands that are true in the command table for the given
|
|
* variables and populates the true_commands array with pointers to them
|
|
* also sets num_distinct_actions
|
|
*/
|
|
inline void Find_True_Commands(int* variables);
|
|
|
|
/*
|
|
* Forms the product of what is in the sorted_slot ([no_modules][synch_index]
|
|
* and what is in the given slot [synch_index][module_index]
|
|
*/
|
|
inline void Product_Commands(int synch_index, int module_index);
|
|
|
|
/*
|
|
* Forms the product of the two CCommand parameters. i.e. the guards
|
|
* are ignored, but the product of the updates is created.
|
|
*/
|
|
inline CCommand* Product_Two_Commands(CCommand* comm1, CCommand* comm2);
|
|
|
|
/*
|
|
* Forms the product of two updates. The probability or rate, is
|
|
* simply the product of the two and the assignment set is the
|
|
* union of the two product sets.
|
|
*/
|
|
inline CUpdate* Merge_Two_Updates(CUpdate* upd1, CUpdate* upd2);
|
|
|
|
/*
|
|
* Outputs the contents of true_commands to the command line .
|
|
*/
|
|
inline void Print_True_Commands();
|
|
|
|
|
|
//=============================================================================
|
|
// Class Definitions
|
|
//=============================================================================
|
|
|
|
/*
|
|
* CFinalUpdate
|
|
* ============
|
|
*
|
|
* The CFinalUpdate class is responsible for storing the updates calculated
|
|
* as potential transitions of the current state. Each update has a value for
|
|
* probability (rate), module index, action index and a list of pointers
|
|
* to the assignments in the model data structures.
|
|
*
|
|
*/
|
|
|
|
/*
|
|
* Constructor: populates an empty update
|
|
*/
|
|
CFinalUpdate::CFinalUpdate(int max_assignments)
|
|
{
|
|
this->action_index = -1;
|
|
this->module_index = -1;
|
|
this->max_assignments = max_assignments;
|
|
if(&assignments == NULL)
|
|
{
|
|
Report_Error("Out of memory when allocating reasoning data structures");
|
|
throw string("Out of memory error: simreasoning.cc 008");
|
|
}
|
|
assignments.reserve(max_assignments);
|
|
|
|
this->no_assignments = 0;
|
|
this->all_defined = false;
|
|
|
|
this->probability = UNDEFINED_DOUBLE;
|
|
this->probability_distribution = -1;
|
|
}
|
|
|
|
/*
|
|
* Destructor for a CFinalUpdate object. This only
|
|
* needs to remove all of the pointers to assignments
|
|
* from the assignments vector. It does not need
|
|
* to delete the assingments themselves.
|
|
*/
|
|
CFinalUpdate::~CFinalUpdate()
|
|
{
|
|
//only delete the array not the whole thing
|
|
if(&assignments != NULL)
|
|
assignments.clear();
|
|
}
|
|
|
|
/*
|
|
* Populates this CFinalUpdate object with all of the relevant
|
|
* information to make it a valid update. This method
|
|
* extracts all of the assignments from upd.
|
|
*/
|
|
void CFinalUpdate::Set(double probability, int distribution_index,
|
|
int action_index, int module_index,
|
|
CUpdate * upd)
|
|
{
|
|
this->probability = probability;
|
|
this->probability_distribution = distribution_index;
|
|
this->action_index = action_index;
|
|
this->module_index = module_index;
|
|
|
|
|
|
int curr_assign_index = 0;
|
|
|
|
if(upd != NULL)
|
|
{
|
|
assignments.clear();
|
|
this->no_assignments = upd->no_assignments;
|
|
for(int j = 0; j < upd->no_assignments; j++)
|
|
assignments.push_back(upd->assignments[j]);
|
|
all_defined = true;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* For DTMCs, it is necessary to normalise the probabilities if
|
|
* there is more than one probability distribution. This method
|
|
* is responsible for doing this.
|
|
*/
|
|
void CFinalUpdate::Normalise(int no_distributions)
|
|
{
|
|
probability /= (double)no_distributions;
|
|
probability_distribution = 0; //when normalised, we only have
|
|
}
|
|
|
|
/*
|
|
* Prints a representation of this CFinalUpdate object to the command line.
|
|
*/
|
|
void CFinalUpdate::Print_Update()
|
|
{
|
|
cout
|
|
<< probability_distribution
|
|
<< "\t["
|
|
<< action_index
|
|
<< "]\t"
|
|
<< module_index
|
|
<< "\t"
|
|
<< probability
|
|
<< "\t";
|
|
for(int i = 0; i < no_assignments; i++)
|
|
{
|
|
cout << assignments[i]->To_String();
|
|
if(i != no_assignments-1) cout << ",";
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Executes the assignments pointed to by the assignments vector for this
|
|
* CFinalUpdate object. The assignments are performed on the variable
|
|
* array parameter. The temporary results for the current state are
|
|
* first calculates and then they are applied together.
|
|
*/
|
|
void CFinalUpdate::Perform_Update(int*variables)
|
|
{
|
|
for(int i = 0; i < no_assignments; i++)
|
|
assignments[i]->Do_Assign(variables);
|
|
for(int i = 0; i < no_assignments; i++)
|
|
assignments[i]->Complete_Assign(variables);
|
|
}
|
|
|
|
//=============================================================================
|
|
// Local Data
|
|
//=============================================================================
|
|
|
|
vector<CFinalUpdate*> the_updates; //The update set
|
|
int no_updates = 0; //The size of the update set
|
|
int max_updates = 0; //The current maximum size of the_updates
|
|
int max_assignments = 0; //The current maximum number of assignments per update
|
|
int* update_variables = NULL; //The variables that this update set corresponds to
|
|
int no_distributions = 0; //Used for DTMCs to normalise the update set
|
|
|
|
//this can be indexed [moduleIndex][synchIndex][index] returns a CCommand*
|
|
CCommand**** true_commands = NULL; //Used for Expression evaluation
|
|
//this can be indexed [moduleIndex][synchIndex]
|
|
int** no_true_commands = NULL;
|
|
|
|
//TODO make this a vector too
|
|
CCommand** temp_commands = NULL; //pointers to temporary commands used for synchronisation
|
|
int temp_commands_count = 0;
|
|
|
|
int* count_synchs = NULL; //used for fast updates
|
|
int no_used_synchs = 0;
|
|
|
|
//Rewards
|
|
double *state_reward = NULL; //The rewards of being in the state update_variables
|
|
|
|
|
|
//=============================================================================
|
|
// Functions
|
|
//=============================================================================
|
|
|
|
/*
|
|
* Delete the data structures required to reason about a state of the model.
|
|
* Important: deallocate this before the model is deallocated
|
|
*/
|
|
void Deallocate_Reasoning()
|
|
{
|
|
if(&the_updates != NULL)
|
|
{
|
|
CFinalUpdate* upd = NULL;
|
|
for(int i = 0; i < the_updates.size(); i++)
|
|
{
|
|
upd = Get_Update(i);
|
|
if(upd != NULL) delete upd;
|
|
}
|
|
the_updates.clear();
|
|
}
|
|
|
|
if(true_commands != NULL)
|
|
{
|
|
for(int i = 0; i < no_modules+1; i++)
|
|
{
|
|
|
|
for(int j = 0; j < no_actions+1; j++)
|
|
{
|
|
//cout << "deleting " << i << " " << j << endl;
|
|
delete[] true_commands[i][j];
|
|
}
|
|
delete[] true_commands[i];
|
|
//cout << "deleting " << i << endl;
|
|
}
|
|
|
|
delete[] true_commands;
|
|
}
|
|
true_commands = NULL;
|
|
|
|
if(no_true_commands != NULL)
|
|
{
|
|
for(int i = 0; i < no_modules+1; i++)
|
|
{
|
|
|
|
delete[] no_true_commands[i];
|
|
}
|
|
delete[] no_true_commands;
|
|
}
|
|
|
|
no_true_commands = NULL;
|
|
|
|
if(count_synchs != NULL) delete[] count_synchs;
|
|
|
|
count_synchs = NULL;
|
|
|
|
if(temp_commands != NULL)
|
|
{
|
|
//Delete any old synchronisation data
|
|
for(int i = 0; i < temp_commands_count; i++)
|
|
{
|
|
if(temp_commands[i] != NULL)
|
|
delete temp_commands[i];
|
|
}
|
|
temp_commands_count = 0;
|
|
delete[] temp_commands;
|
|
temp_commands = NULL;
|
|
}
|
|
|
|
// deallocate rewards info
|
|
if(state_reward == NULL) delete[] state_reward;
|
|
state_reward = NULL;
|
|
}
|
|
|
|
/*
|
|
* Allocate the resources for reasoning about the current state of the model.
|
|
* Some of the data allocated is dependent upon the current loaded model,
|
|
* and it is a prerequisite that the model has been successfully loaded.
|
|
*/
|
|
void Allocate_Reasoning()
|
|
{
|
|
max_assignments = DEFAULT_MAX_NO_ASSIGNMENTS;
|
|
|
|
if(&the_updates == NULL)
|
|
{
|
|
Report_Error("Out of memory when allocating reasoning data structures");
|
|
throw string("Out of memory error: simreasoning.cc 001");
|
|
}
|
|
|
|
the_updates.reserve(DEFAULT_MAX_NO_UPDATES);
|
|
|
|
//Allocate holders for CFinalUpdate objects and their pointers
|
|
for(int i = 0; i < DEFAULT_MAX_NO_UPDATES; i++)
|
|
{
|
|
CFinalUpdate* upd = new CFinalUpdate(max_assignments);
|
|
if(upd == NULL)
|
|
{
|
|
Report_Error("Out of memory when allocating reasoning data structures");
|
|
throw string("Out of memory error: simreasoning.cc 002");
|
|
}
|
|
the_updates.push_back(upd);
|
|
}
|
|
|
|
no_updates = 0;
|
|
|
|
//setup storage for true command pointers
|
|
true_commands = new CCommand***[no_modules+1]; //access to true_commands[no_modules] gives
|
|
if(true_commands == NULL)
|
|
{
|
|
Report_Error("Out of memory when allocating reasoning data structures");
|
|
throw string("Out of memory error: simreasoning.cc 003");
|
|
}
|
|
no_true_commands = new int*[no_modules+1]; //sorted out synchronised commands
|
|
if(no_true_commands == NULL)
|
|
{
|
|
Report_Error("Out of memory when allocating reasoning data structures");
|
|
throw string("Out of memory error: simreasoning.cc 004");
|
|
}
|
|
for(int i = 0; i < no_modules+1; i++)
|
|
{
|
|
true_commands[i] = new CCommand**[no_actions+1]; //access to true_commands[][nosynchs] gives
|
|
if(true_commands[i] == NULL)
|
|
{
|
|
Report_Error("Out of memory when allocating reasoning data structures");
|
|
throw string("Out of memory error: simreasoning.cc 005");
|
|
}
|
|
no_true_commands[i] = new int[no_actions+1]; //module commands
|
|
if(no_true_commands[i] == NULL)
|
|
{
|
|
Report_Error("Out of memory when allocating reasoning data structures");
|
|
throw string("Out of memory error: simreasoning.cc 006");
|
|
}
|
|
for(int j = 0; j < no_actions+1; j++)
|
|
{
|
|
true_commands[i][j] = new CCommand*[no_commands]; //no_transitions to be on the safe side
|
|
if(true_commands[i][j] == NULL)
|
|
{
|
|
Report_Error("Out of memory when allocating reasoning data structures");
|
|
throw string("Out of memory error: simreasoning.cc 007");
|
|
}
|
|
no_true_commands[i][j] = 0;
|
|
for(int k = 0; k < no_commands; k++)
|
|
true_commands[i][j][k] = NULL;
|
|
}
|
|
}
|
|
no_distributions = 1;
|
|
|
|
|
|
//setup storage for fast updating
|
|
|
|
count_synchs = new int[no_actions];
|
|
for(int i = 0; i < no_actions; i++)
|
|
count_synchs[i] = -1;
|
|
no_used_synchs = 0;
|
|
|
|
temp_commands_count = 0;
|
|
|
|
//allocate space for array of pointers to temporary
|
|
//commands used for synchronisation
|
|
//TODO Work out the actual maximum possible number of synchronised transitions
|
|
temp_commands = new CCommand*[no_commands*1000];
|
|
|
|
// allocate rewards info
|
|
state_reward = new double[no_reward_structs];
|
|
if(state_reward == NULL)
|
|
{
|
|
Report_Error("Out of memory when allocating reasoning data structures");
|
|
throw string("Out of memory error: simreasoning.cc");
|
|
}
|
|
}
|
|
|
|
|
|
//==============================================
|
|
//Functions to reason about a particular state
|
|
//==============================================
|
|
|
|
/*
|
|
* Populates the_updates with the_updates appropriate for the given state
|
|
*/
|
|
void Calculate_Updates(int* variables)
|
|
{
|
|
Find_True_Commands(variables);
|
|
|
|
//Print_True_Commands();
|
|
|
|
//Delete any old synchronisation data
|
|
for(int i = 0; i < temp_commands_count; i++)
|
|
{
|
|
if(temp_commands[i] != NULL)
|
|
delete temp_commands[i];
|
|
}
|
|
temp_commands_count = 0;
|
|
|
|
//Form products of synchronous updates
|
|
for(int i = 0; i < no_actions; i++)
|
|
{
|
|
for(int j = 0; j < no_modules; j++)
|
|
{
|
|
Product_Commands(i,j);
|
|
}
|
|
}
|
|
|
|
//Populate the_updates
|
|
int count_distributions = 0; //count probability distributions
|
|
no_updates = 0;
|
|
|
|
CCommand* comm;
|
|
CUpdate* upd;
|
|
//do asynchronous first
|
|
for(int i = 0; i < no_modules; i++)
|
|
{
|
|
//cout << "looking at the " << i << "th module" << endl;
|
|
for(int j = 0; j < no_true_commands[i][no_actions]; j++)
|
|
{
|
|
//cout << "in here for the " << j <<"th command" << endl;
|
|
count_distributions++;
|
|
comm = true_commands[i][no_actions][j];
|
|
for(int k = 0; k < comm->no_updates; k++)
|
|
{
|
|
//cout << "should be actually adding it" << endl;
|
|
upd = comm->updates[k];
|
|
|
|
Get_Update(no_updates++)->Set
|
|
(upd->probability->EvaluateDouble(variables),(count_distributions-1),-1,i,upd);
|
|
}
|
|
}
|
|
}
|
|
|
|
//now do synchronous
|
|
for(int i = 0; i < no_actions; i++)
|
|
{
|
|
for(int j = 0; j < no_true_commands[no_modules][i]; j++)
|
|
{
|
|
count_distributions++;
|
|
comm = true_commands[no_modules][i][j];
|
|
for(int k = 0; k < comm->no_updates; k++)
|
|
{
|
|
//cout << "should be adding a synchronous update" << endl;
|
|
upd = comm->updates[k];
|
|
Get_Update(no_updates++)->Set
|
|
(upd->probability->EvaluateDouble(variables),(count_distributions-1),i,-1,upd);
|
|
}
|
|
}
|
|
}
|
|
|
|
no_distributions = count_distributions;
|
|
|
|
//For DTMCs, we need to normalise the updates
|
|
if(model_type == PROBABILISTIC)
|
|
{
|
|
if(no_distributions > 1) //no need to divide by 1
|
|
{
|
|
for(int i = 0; i < no_updates; i++)
|
|
{
|
|
Get_Update(i)->Normalise(no_distributions);
|
|
}
|
|
no_distributions = 1;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
update_variables = variables;
|
|
}
|
|
|
|
/*
|
|
* Calculates the reward of being in the state describes by the variables parameter.
|
|
*/
|
|
void Calculate_State_Reward(int* variables)
|
|
{
|
|
for(int i = 0; i < no_reward_structs; i++) {
|
|
state_reward[i] = 0.0;
|
|
for(int j = 0; j < no_state_rewards[i]; j++)
|
|
{
|
|
CStateReward* rew = state_rewards_table[i][j];
|
|
state_reward[i] += rew->Get_Reward_For_State(variables);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* This function makes a random choice from the update set
|
|
* so long as the model is continuous time and sum_rates
|
|
* is indeed the sum of the rates in the update set.
|
|
* Returns -1 if there were no updates to choose from (i.e. deadlock).
|
|
*/
|
|
int Choose_Continuous_Update(double sum_rates)
|
|
{
|
|
//because the probability of an update being selected
|
|
//is rate / all rates
|
|
//we can just sample from the sum of all rates
|
|
//and work out which was selected
|
|
double sample = sum_rates * Random_Uniform();
|
|
|
|
double counter = 0.0;
|
|
int selected_index = -1;
|
|
|
|
for(int i = 0; i < Get_No_Updates(); i++)
|
|
{
|
|
CFinalUpdate* upd = Get_Update(i);
|
|
counter += upd->probability;
|
|
if(sample <= counter)
|
|
{
|
|
selected_index = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return selected_index;
|
|
}
|
|
|
|
/*
|
|
* This function makes a random selection according
|
|
* to the current state (state_variables) for discrete
|
|
* time markov chains and markov decision processes.
|
|
* It does not need to complete the entire update set.
|
|
* Returns NULL if there were no updates to choose from (i.e. deadlock).
|
|
*/
|
|
CUpdate* Choose_Discrete_Update_On_The_Fly(bool& up_determ, double& selected_prob)
|
|
{
|
|
Find_True_Commands(state_variables);
|
|
//Print_True_Commands();
|
|
|
|
//Find number of asynchronous commands
|
|
int count_asynch = Count_Asynchronous_Commands();
|
|
|
|
//Find number of synchronous commands
|
|
no_used_synchs = 0;
|
|
for(int i = 0; i < no_actions; i++)
|
|
{
|
|
count_synchs[i] = Count_Synchronous_Commands(i);
|
|
no_used_synchs += count_synchs[i];
|
|
}
|
|
|
|
if(count_asynch+no_used_synchs == 0) return NULL; //if there is a deadlock
|
|
|
|
up_determ = Are_Updates_Deterministic_On_The_Fly(state_variables, count_asynch, no_used_synchs);
|
|
|
|
//remove any products that were created
|
|
for(int i = 0; i < no_actions; i++)
|
|
no_true_commands[no_modules][i] = 0;
|
|
|
|
//randomly select the probability distribution
|
|
//this is modelling the non-deterministic choice
|
|
//by simply uniformly choosing between probability distributions
|
|
int selection = Random_Uniform_From_Range(0, count_asynch+no_used_synchs);
|
|
|
|
CCommand* selected_command = NULL;
|
|
if(selection < count_asynch) //synchronous update selected
|
|
{
|
|
//search for the selected command
|
|
int counter = 0;
|
|
for(int i = 0; i < no_modules; i++)
|
|
for(int j = 0; j < no_true_commands[i][no_actions]; j++)
|
|
if(counter++ == selection)
|
|
selected_command = true_commands[i][no_actions][j];
|
|
}
|
|
else //must be synchronous
|
|
{
|
|
selection -= count_asynch; //ignore asynchronous commands
|
|
int synch_index = -1;
|
|
int synch_sub_index = -1;
|
|
int so_far = 0;
|
|
|
|
//find the synch and sub_synch index of the command
|
|
for(int i = 0; i < no_actions; i++)
|
|
{
|
|
if(selection < so_far+count_synchs[i])
|
|
{
|
|
synch_index = i;
|
|
synch_sub_index = selection-so_far;
|
|
break;
|
|
}
|
|
so_far+=count_synchs[i];
|
|
}
|
|
|
|
//Do the necessary product to get this command
|
|
//(this could be optimised further)
|
|
for(int i = 0; i < no_modules; i++)
|
|
Product_Commands(synch_index, i);
|
|
|
|
selected_command =
|
|
true_commands[no_modules][synch_index][synch_sub_index];
|
|
}
|
|
|
|
//Declare storage for the probability distribution
|
|
double*distribution;
|
|
int size_distribution=0;
|
|
|
|
if(selected_command == NULL)
|
|
{
|
|
return NULL;;
|
|
}
|
|
|
|
//create probability distribution
|
|
size_distribution = selected_command->no_updates;
|
|
|
|
distribution = new double[size_distribution];
|
|
|
|
for(int i = 0; i < size_distribution; i++)
|
|
distribution[i] =
|
|
selected_command->updates[i]->probability->EvaluateDouble();
|
|
|
|
int answer = Random_From_Prob_Distribution(distribution, size_distribution);
|
|
|
|
selected_prob = distribution[answer];
|
|
delete[] distribution;
|
|
|
|
return selected_command->updates[answer];
|
|
}
|
|
|
|
/*
|
|
* Counts the number of synchronous commands in the true_commands data structure
|
|
* for a particular action parameterised by synch_index
|
|
*/
|
|
int Count_Synchronous_Commands(int synch_index)
|
|
{
|
|
if(synch_index == no_actions) return Count_Asynchronous_Commands();
|
|
int number_sorted = 0;
|
|
for(int module_index = 0; module_index < no_modules; module_index++)
|
|
{
|
|
if(no_true_commands[module_index][synch_index] == 0)
|
|
continue; // nothing to do
|
|
else if(number_sorted == 0) //if nothing in sorted yet
|
|
number_sorted = no_true_commands[module_index][synch_index];
|
|
else
|
|
number_sorted = number_sorted * no_true_commands[module_index][synch_index];
|
|
}
|
|
return number_sorted;
|
|
}
|
|
|
|
/*
|
|
* Counts the number of asynchronous command in the true_commands data structure.
|
|
*/
|
|
int Count_Asynchronous_Commands()
|
|
{
|
|
int counter = 0;
|
|
for(int module_index = 0; module_index < no_modules; module_index++)
|
|
counter += no_true_commands[module_index][no_actions];
|
|
return counter;
|
|
|
|
}
|
|
|
|
//=====================================
|
|
//Actually carry out one of the updates
|
|
//=====================================
|
|
|
|
/*
|
|
* Actually perform the calculated update at updates[index] on
|
|
* the given variable set.
|
|
*/
|
|
void Execute_Update(int index, int* variables)
|
|
{
|
|
//cout << "executing update" << endl;
|
|
Get_Update(index)->Perform_Update(variables);
|
|
}
|
|
|
|
//=====================================
|
|
//Functions to query calculated results
|
|
//=====================================
|
|
|
|
/*
|
|
* Reasons about the calculated update set to say whether it
|
|
* is deterministic.
|
|
*/
|
|
bool Are_Updates_Deterministic()
|
|
{
|
|
if(no_updates <= 1) return true;
|
|
else
|
|
{
|
|
//cout << "Are_Updates_Deterministic 1" << endl;
|
|
int* compare_state = new int[no_state_variables];
|
|
int* new_state = new int[no_state_variables];
|
|
//create a new place to put the state
|
|
Copy_Int_Array(state_variables, new_state, no_state_variables);
|
|
|
|
//cout << "Are_Updates_Deterministic 2" << endl;
|
|
CFinalUpdate* first = Get_Update(0);
|
|
//look at what the first update would do (compare_state)
|
|
for(int i = 0; i < first->no_assignments; i++)
|
|
{
|
|
first->assignments[i]->Do_Assign(new_state);
|
|
}
|
|
for(int i = 0; i < first->no_assignments; i++)
|
|
{
|
|
first->assignments[i]->Complete_Assign(new_state);
|
|
}
|
|
//cout << "Are_Updates_Deterministic 3" << endl;
|
|
//do each of the other updates match
|
|
for(int i = 1; i < no_updates; i++)
|
|
{
|
|
//cout << "Are_Updates_Deterministic 4" << endl;
|
|
Copy_Int_Array(state_variables, compare_state, no_state_variables);
|
|
for(int j = 0; j < Get_Update(i)->no_assignments; j++)
|
|
{
|
|
Get_Update(i)->assignments[j]->Do_Assign(compare_state);
|
|
}
|
|
for(int j = 0; j < Get_Update(i)->no_assignments; j++)
|
|
{
|
|
Get_Update(i)->assignments[j]->Complete_Assign(compare_state);
|
|
}
|
|
//if any are not the same we are not moving deterministically, return false
|
|
if(!Int_Arrays_Equals(compare_state, new_state, no_state_variables))
|
|
{
|
|
delete[]compare_state;
|
|
delete[]new_state;
|
|
return false;
|
|
}
|
|
}
|
|
//cout << "Are_Updates_Deterministic 4" << endl;
|
|
delete[] compare_state;
|
|
delete[] new_state;
|
|
return true;
|
|
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Determines whether the updates to the given variable set will be determninistic without
|
|
* having to first calculate the whole update set.
|
|
*/
|
|
bool Are_Updates_Deterministic_On_The_Fly(int* variables, int num_asynch, int num_synch)
|
|
{
|
|
int* state1 = new int[no_state_variables];
|
|
int* state2 = new int[no_state_variables];
|
|
bool assigned_one = false;
|
|
int curr_state = 1; //false = state1 true = state2
|
|
CCommand* comm;
|
|
CUpdate* upd;
|
|
|
|
Copy_Int_Array(variables, state2, no_state_variables);
|
|
|
|
// check asynchronous first (if necessary)
|
|
if(num_asynch != 0)
|
|
{
|
|
for(int i = 0; i < no_modules; i++)
|
|
{
|
|
for(int j = 0; j < no_true_commands[i][no_actions]; j++)
|
|
{
|
|
comm = true_commands[i][no_actions][j];
|
|
for(int k = 0; k < comm->no_updates; k++)
|
|
{
|
|
upd = comm->updates[k];
|
|
if(!assigned_one)//curr_state must be 1
|
|
{
|
|
Copy_Int_Array(state_variables, state1, no_state_variables);
|
|
upd->Do_Update(state1);
|
|
curr_state = 2;
|
|
assigned_one = true;
|
|
}
|
|
else
|
|
{
|
|
if(curr_state == 1)//if in the state1
|
|
{
|
|
Copy_Int_Array(state_variables, state1, no_state_variables);
|
|
upd->Do_Update(state1);
|
|
curr_state = 2;
|
|
}
|
|
else //if in state1
|
|
{
|
|
Copy_Int_Array(state_variables, state2, no_state_variables);
|
|
upd->Do_Update(state2);
|
|
curr_state = 1;
|
|
}
|
|
//if the last two things are not the same return false
|
|
if(!Int_Arrays_Equals(state1, state2, no_state_variables))
|
|
{
|
|
delete[] state1;
|
|
delete[] state2;
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//if we are here all of the asynchronous ones are deterministic, now
|
|
//go through the (slow) process of finding out about the synchronous
|
|
//ones.
|
|
if(num_synch != 0)
|
|
{
|
|
//algorithm: for each synch, work out whether the product command
|
|
//produces the same result as all of the rest
|
|
for(int i = 0; i < no_actions; i++)
|
|
{
|
|
//tidy up any old temporary commands
|
|
for(int j = 0; j < temp_commands_count; j++)
|
|
{
|
|
if(temp_commands[j] != NULL)
|
|
delete temp_commands[j];
|
|
}
|
|
temp_commands_count = 0;
|
|
//form the product for this synch
|
|
for(int j = 0; j < no_modules; j++)
|
|
Product_Commands(i,j);
|
|
|
|
//now check this product for equality with other previous updates
|
|
for(int j = 0; j < no_true_commands[no_modules][i]; j++)
|
|
{
|
|
comm = true_commands[no_modules][i][j];
|
|
for(int k = 0; k < comm->no_updates; k++)
|
|
{
|
|
upd = comm->updates[k];
|
|
if(!assigned_one)//curr_state must be 1
|
|
{
|
|
Copy_Int_Array(state_variables, state1, no_state_variables);
|
|
upd->Do_Update(state1);
|
|
curr_state = 2;
|
|
assigned_one = true;
|
|
}
|
|
else
|
|
{
|
|
if(curr_state == 1)//if in the state1
|
|
{
|
|
Copy_Int_Array(state_variables, state1, no_state_variables);
|
|
upd->Do_Update(state1);
|
|
curr_state = 2;
|
|
}
|
|
else //if in state1
|
|
{
|
|
Copy_Int_Array(state_variables, state2, no_state_variables);
|
|
upd->Do_Update(state2);
|
|
curr_state = 1;
|
|
}
|
|
//if the last two things are not the same return false
|
|
if(!Int_Arrays_Equals(state1, state2, no_state_variables))
|
|
{
|
|
delete[] state1;
|
|
delete[] state2;
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
delete[] state1;
|
|
delete[] state2;
|
|
|
|
return true;
|
|
}
|
|
|
|
double Calculate_Sum_Rates()
|
|
{
|
|
//find the sum of all the rates
|
|
double sum_rates = 0;
|
|
|
|
for(int i = 0; i < Get_No_Updates(); i++)
|
|
{
|
|
CFinalUpdate* upd = Get_Update(i);
|
|
sum_rates += upd->probability;
|
|
}
|
|
|
|
return sum_rates;
|
|
}
|
|
|
|
|
|
//Methods for accessing the CUpdates objects theUpdates
|
|
|
|
/*
|
|
* Returns the reward calcualated by calling Calculate_State_Reward()
|
|
*/
|
|
double Get_State_Reward(int i)
|
|
{
|
|
return state_reward[i];
|
|
}
|
|
|
|
//=======================================================
|
|
// Methods for accessing the CUpdates objects theUpdates
|
|
//=======================================================
|
|
|
|
/*
|
|
* Returns the current size of the update set.
|
|
*/
|
|
int Get_No_Updates()
|
|
{
|
|
return no_updates;
|
|
}
|
|
|
|
/*
|
|
* Returns the probability of the update at the given index
|
|
*/
|
|
double Get_Probability_Of_Update(int update_index)
|
|
{
|
|
return Get_Update(update_index)->probability;
|
|
}
|
|
|
|
/*
|
|
* Returns the probability distribution index of the update at the given index
|
|
*/
|
|
int Get_Distribution_Index_Of_Update(int update_index)
|
|
{
|
|
return Get_Update(update_index)->probability_distribution;
|
|
}
|
|
|
|
/*
|
|
* Returns the action index of the update at the given index
|
|
*/
|
|
int Get_Action_Index_Of_Update(int update_index)
|
|
{
|
|
return Get_Update(update_index)->action_index;
|
|
}
|
|
|
|
/*
|
|
* Returns the module index of the update at the given index
|
|
*/
|
|
int Get_Module_Of_Update(int update_index)
|
|
{
|
|
return Get_Update(update_index)->module_index;
|
|
}
|
|
|
|
//maybe move this
|
|
int Get_Result_Of_Update(int update_index, int var_index)
|
|
{
|
|
int* copy = new int[no_state_variables];
|
|
Copy_Int_Array(update_variables, copy, no_state_variables);
|
|
Get_Update(update_index)->Perform_Update(copy);
|
|
int result = copy[var_index];
|
|
delete[]copy;
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* Returns the number of assignments of the updates at the given index.
|
|
*/
|
|
int Get_Num_Assignments_Of_Update(int update_index)
|
|
{
|
|
return Get_Update(update_index)->no_assignments;
|
|
}
|
|
|
|
/*
|
|
* Returns the state index of the update_indexth updates assignment_indexth assignment.
|
|
*/
|
|
int Get_Assignment_Variable_Index_Of_Update(int update_index, int assignment_index)
|
|
{
|
|
return Get_Update(update_index)->assignments[assignment_index]->Get_Variable_Index();
|
|
}
|
|
|
|
//maybe move this
|
|
int Get_Assignment_Value_Of_Update(int update_index, int assignment_index)
|
|
{
|
|
return Get_Result_Of_Update
|
|
(update_index,
|
|
Get_Assignment_Variable_Index_Of_Update(update_index, assignment_index));
|
|
}
|
|
|
|
/*
|
|
* Returns the calculated update at the given index. Because these updates
|
|
* are preallocated, if it is required that more are needed than have been
|
|
* allocated, then this method doubles the allocation of potential update
|
|
* storage.
|
|
*
|
|
* throws an exception if out of memory
|
|
*/
|
|
CFinalUpdate* Get_Update(int index)
|
|
{
|
|
int upd_size = the_updates.size();
|
|
if(index >= upd_size)
|
|
{
|
|
for(int i = 0; i < upd_size; i++)
|
|
{
|
|
CFinalUpdate* fupd = new CFinalUpdate(max_assignments);
|
|
if(fupd == NULL)
|
|
{
|
|
Report_Error("Out of memory when allocating reasoning data structures");
|
|
throw string("Out of memory error: simreasoning.cc 010");
|
|
}
|
|
the_updates.push_back(fupd);
|
|
}
|
|
}
|
|
return the_updates[index];
|
|
}
|
|
|
|
|
|
//=============================================================================
|
|
// Local Functions
|
|
//=============================================================================
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
* Finds all of the commands that are true in the command table for the given
|
|
* variables and populates the true_commands array with pointers to them
|
|
* also sets num_distinct_actions
|
|
*/
|
|
inline void Find_True_Commands(int* variables)
|
|
{
|
|
//cout << "Find_True_Commands 1" << endl;
|
|
//Print_Array(variables, no_state_variables);
|
|
for(int i = 0; i < no_modules+1; i++)
|
|
{
|
|
for(int j = 0; j < no_actions+1; j++)
|
|
{
|
|
no_true_commands[i][j] = 0;
|
|
}
|
|
}
|
|
//cout << "Find_True_Commands 2" << endl;
|
|
int mod_index;
|
|
int synch_index;
|
|
|
|
//check each command in the command_table and see whether it evaluates to true
|
|
|
|
for(int i = 0; i < no_commands; i++)
|
|
{
|
|
|
|
CCommand* comm = command_table[i];
|
|
|
|
if(comm->guard->Evaluate(variables))
|
|
{
|
|
|
|
mod_index = comm->module_index;
|
|
|
|
if(comm->action_index == -1) //asynchronous
|
|
synch_index = no_actions;
|
|
else //synchronous
|
|
synch_index = comm->action_index;
|
|
|
|
true_commands[mod_index][synch_index][no_true_commands[mod_index][synch_index]++] = comm;
|
|
|
|
}
|
|
}
|
|
//cout << "Find_True_Commands 3" << endl;
|
|
//sort out blocking
|
|
//a command can block the whole synch if no other commands
|
|
//are true in the same module as the blocker
|
|
|
|
//for each synch - note that synchs are i and modules are j (usually the other way round)
|
|
for(int i = 0; i < no_actions; i++)
|
|
{
|
|
//check that there is at least one in each module (if the module has that synch in its alphabet)
|
|
for(int j = 0; j < no_modules; j++)
|
|
if(alphabet[j][i]) //it is possible that this module could block the action
|
|
if(no_true_commands[j][i] == 0) //if this module has no synchs but should
|
|
{
|
|
for(int k = 0; k < no_modules; k++)//block it
|
|
no_true_commands[k][i] =0; //by setting num_true_commands for all modules to 0 for this synch
|
|
break; //next synch as this has been blocked
|
|
}
|
|
}
|
|
|
|
//cout << "Find_True_Commands 4" << endl;
|
|
}
|
|
|
|
/*
|
|
* Forms the product of what is in the sorted_slot ([no_modules][synch_index]
|
|
* and what is in the given slot [synch_index][module_index]
|
|
*/
|
|
inline void Product_Commands(int synch_index, int module_index)
|
|
{
|
|
if(no_true_commands[module_index][synch_index] == 0) return; // nothing to do
|
|
else if(no_true_commands[no_modules][synch_index] == 0) //if nothing in sorted_slot
|
|
for(int i = 0; i < no_true_commands[module_index][synch_index]; i++)//just copy (create a pointer) the contents to the sorted_slot
|
|
true_commands[no_modules][synch_index][no_true_commands[no_modules][synch_index]++] =
|
|
true_commands[module_index][synch_index][i];
|
|
else //we have to form a product
|
|
{
|
|
//storage for product
|
|
CCommand** products =
|
|
new CCommand*[no_true_commands[no_modules][synch_index] * no_true_commands[module_index][synch_index]];
|
|
if(products == NULL)
|
|
{
|
|
Report_Error("Out of memory when allocating reasoning data structures");
|
|
throw string("Out of memory error: simreasoning.cc 011");
|
|
}
|
|
int products_done = 0;
|
|
|
|
for(int i = 0; i < no_true_commands[no_modules][synch_index]; i++) //for each command in sorted_slot
|
|
for(int j = 0; j < no_true_commands[module_index][synch_index]; j++) //for each command in given slot
|
|
{
|
|
products[products_done++] =
|
|
Product_Two_Commands(
|
|
true_commands[no_modules][synch_index][i],
|
|
true_commands[module_index][synch_index][j]); //do the product
|
|
}
|
|
//remove old pointers
|
|
delete [] true_commands[no_modules][synch_index];
|
|
|
|
//set the slot to the new pointers
|
|
true_commands[no_modules][synch_index] = products;
|
|
no_true_commands[no_modules][synch_index] = products_done;
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
* Forms the product of the two CCommand parameters. i.e. the guards
|
|
* are ignored, but the product of the updates is created.
|
|
*/
|
|
inline CCommand* Product_Two_Commands(CCommand* comm1, CCommand* comm2)
|
|
{
|
|
CCommand* new_command = new CCommand(comm1->guard, comm1->action_index, no_modules, comm1->no_updates*comm2->no_updates);
|
|
if(new_command == NULL)
|
|
{
|
|
Report_Error("Out of memory when allocating reasoning data structures");
|
|
throw string("Out of memory error: simreasoning.cc 012");
|
|
}
|
|
temp_commands[temp_commands_count++] = new_command;
|
|
|
|
for(int i = 0; i < comm1->no_updates; i++)
|
|
{
|
|
for(int j = 0; j < comm2->no_updates; j++)
|
|
{
|
|
new_command->Add_Update(Merge_Two_Updates(comm1->updates[i], comm2->updates[j]));
|
|
}
|
|
}
|
|
new_command->Set_Owns_Guard(false); //so it doesn't delete its children
|
|
return new_command;
|
|
}
|
|
|
|
/*
|
|
* Forms the product of two updates. The probability or rate, is
|
|
* simply the product of the two and the assignment set is the
|
|
* union of the two product sets.
|
|
*/
|
|
inline CUpdate* Merge_Two_Updates(CUpdate* upd1, CUpdate* upd2)
|
|
{
|
|
CRealTimes* new_prob = new CRealTimes(upd1->probability, upd2->probability);
|
|
if(new_prob == NULL)
|
|
{
|
|
Report_Error("Out of memory when allocating reasoning data structures");
|
|
throw string("Out of memory error: simreasoning.cc 013");
|
|
}
|
|
new_prob->Set_Owns_Children(false);
|
|
|
|
CUpdate* new_update = new CUpdate(new_prob,
|
|
(upd1->no_assignments + upd2->no_assignments));
|
|
for(int i = 0; i < upd1->no_assignments; i++)
|
|
new_update->Add_Assignment(upd1->assignments[i]);
|
|
for(int i = 0; i < upd2->no_assignments; i++)
|
|
new_update->Add_Assignment(upd2->assignments[i]);
|
|
new_update->Set_Owns_Assignments(false);
|
|
return new_update;
|
|
|
|
}
|
|
|
|
|
|
//=============================================================================
|
|
// Helper Functions
|
|
//=============================================================================
|
|
/*
|
|
* Outputs the contents of updates to the command line.
|
|
*/
|
|
void Print_Updates()
|
|
{
|
|
cout << "Current update set:\n\n" ;
|
|
|
|
cout
|
|
<< "no."
|
|
<< "\t"
|
|
<< "dist"
|
|
<< "\t"
|
|
<< "synch"
|
|
<< "\t"
|
|
<< "module"
|
|
<< "\t"
|
|
<< "prob"
|
|
<< "\t"
|
|
<< "assigns\t\n";
|
|
int curr_dist = -1;
|
|
for(int i = 0; i < no_updates; i++)
|
|
{
|
|
if(Get_Update(i)->probability_distribution != curr_dist)
|
|
{
|
|
curr_dist = Get_Update(i)->probability_distribution;
|
|
cout << "------------------------------------------------------------" << endl;
|
|
}
|
|
cout << i << ":\t";
|
|
Get_Update(i)->Print_Update();
|
|
cout << endl;
|
|
}
|
|
cout << "------------------------------------------------------------" << endl;
|
|
cout << "Number of probability distributions: " << no_distributions << endl;
|
|
cout << "------------------------------------------------------------" << endl;
|
|
}
|
|
|
|
|
|
/*
|
|
* Outputs the contents of true_commands to the command line .
|
|
*/
|
|
inline void Print_True_Commands()
|
|
{
|
|
|
|
cout << "True commands for current state: "<< endl << endl;
|
|
for(int i = 0; i < no_modules+1; i++)
|
|
for(int j = 0; j < no_actions+1; j++)
|
|
for(int k = 0; k < no_true_commands[i][j]; k++)
|
|
{
|
|
cout << (true_commands[i][j][k]->To_String()) << endl;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|