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.
 
 
 
 
 
 

581 lines
19 KiB

//==============================================================================
//
// Copyright (c) 2002-
// Authors:
// * Dave Parker <david.parker@comlab.ox.ac.uk> (University of Oxford)
// * Hongyang Qu <hongyang.qu@comlab.ox.ac.uk> (University of Oxford)
//
//------------------------------------------------------------------------------
//
// 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
//
//==============================================================================
// includes
#include "PrismSparse.h"
#include <cmath>
#include <util.h>
#include <cudd.h>
#include <dd.h>
#include <odd.h>
#include <dv.h>
#include "sparse.h"
#include "sparse_adv.h"
#include "prism.h"
#include "PrismSparseGlob.h"
#include "PrismNativeGlob.h"
#include "jnipointer.h"
#include <new>
#include "lp_lib.h"
//------------------------------------------------------------------------------
JNIEXPORT jdouble __jlongpointer JNICALL Java_sparse_PrismSparse_PS_1NondetMultiReach
(
JNIEnv *env,
jclass cls,
jlong __jlongpointer t, // trans matrix
jlong __jlongpointer ta, // trans action labels
jobject synchs,
jlong __jlongpointer od, // odd
jlong __jlongpointer rv, // row vars
jint num_rvars,
jlong __jlongpointer cv, // col vars
jint num_cvars,
jlong __jlongpointer ndv, // nondet vars
jint num_ndvars,
jlongArray _targets, // target state sets
jintArray _relops, // target relops (0:=?, 1:>, 2:>=)
jdoubleArray _bounds, // target probability bounds
jlong __jlongpointer m, // 'maybe' states
jlong __jlongpointer _start // initial state(s)
)
{
// cast function parameters
DdNode *trans = jlong_to_DdNode(t); // trans matrix
DdNode *trans_actions = jlong_to_DdNode(ta); // trans action labels
ODDNode *odd = jlong_to_ODDNode(od); // reachable states
DdNode **rvars = jlong_to_DdNode_array(rv); // row vars
DdNode **cvars = jlong_to_DdNode_array(cv); // col vars
DdNode **ndvars = jlong_to_DdNode_array(ndv); // nondet vars
DdNode *maybe = jlong_to_DdNode(m); // 'maybe' states
DdNode *start = jlong_to_DdNode(_start); // initial state(s)
// target info
jlong *target_ptrs = NULL;
DdNode **targets = NULL;
jint *relops = NULL;
double *bounds = NULL;
// mtbdds
DdNode *a = NULL, **yes = NULL, *maybe_yes = NULL, *loops = NULL, *tmp = NULL;
// model stats
int num_targets, n, nc, nc_r;
long nnz, nnz_r;
// sparse matrix
NDSparseMatrix *ndsm = NULL, *ndsm_r = NULL;
// action info
jstring *action_names_jstrings;
const char** action_names = NULL;
int num_actions;
// vectors
double **yes_vecs, *maybe_vec = NULL/*, *maybe_vec_r = NULL*/;
// timing stuff
long start1, start2, start3, stop, stop2;
double time_taken, time_for_setup, time_for_lp;
// lp stuff
lprec *lp = NULL;
REAL *arr_reals = NULL;// *lp_soln = NULL;
int *arr_ints = NULL;
bool selfloop;
int arr_size, res;
int num_lp_vars;
double *lp_soln;
double lp_result = 0.0;
bool lp_solved = false;
// misc
int i, j, k, k_r, l1, h1, l2, h2, l2_r, h2_r, start_index, count;
double d1, d2, kb, kbt;
bool done, first;
jclass vn_cls;
jmethodID vn_mid;
// exception handling around whole function
try {
// start clocks
start1 = start2 = util_cpu_time();
// Extract arrays of target info from function parameters
num_targets = (int)env->GetArrayLength(_targets);
target_ptrs = env->GetLongArrayElements(_targets, 0);
targets = new DdNode*[num_targets];
for (i = 0; i < num_targets; i++) targets[i] = jlong_to_DdNode(target_ptrs[i]);
relops = env->GetIntArrayElements(_relops, 0);
bounds = env->GetDoubleArrayElements(_bounds, 0);
yes = new DdNode*[num_targets];
yes_vecs = new double*[num_targets];
for(i=0; i<num_targets; i++) yes[i] = targets[i];
// Display some info about the targets
PS_PrintToMainLog(env, "\n%d Targets:\n", num_targets);
for (i = 0; i < num_targets; i++) {
PS_PrintToMainLog(env, "#%d: ", i);
switch (relops[i]) {
case 0: PS_PrintToMainLog(env, "Pmax=?"); break;
case 1: PS_PrintToMainLog(env, "P>%g", bounds[i]); break;
case 2: PS_PrintToMainLog(env, "P>=%g", bounds[i]); break;
}
PS_PrintToMainLog(env, " (%.0f states)\n", DD_GetNumMinterms(ddman, targets[i], num_rvars));
}
// Filter out rows, store in "a"
Cudd_Ref(maybe);
maybe_yes = maybe;
for (i = 0; i < num_targets; i++) {
Cudd_Ref(yes[i]);
maybe_yes = DD_Or(ddman, maybe_yes, yes[i]);
}
Cudd_Ref(trans);
Cudd_Ref(maybe_yes);
a = DD_Apply(ddman, APPLY_TIMES, trans, maybe_yes);
// For efficiency, remove any probability 1 self-loops from the model.
// For multi-objective, we always do maximum reachability, so these do not matter.
Cudd_Ref(a);
loops = DD_And(ddman, DD_Equals(ddman, a, 1.0), DD_Identity(ddman, rvars, cvars, num_rvars));
loops = DD_ThereExists(ddman, loops, cvars, num_rvars);
Cudd_Ref(loops);
a = DD_ITE(ddman, loops, DD_Constant(ddman, 0), a);
// Get number of states
n = odd->eoff + odd->toff;
// Build sparse matrix
PS_PrintToMainLog(env, "\nBuilding sparse matrix... ");
ndsm = build_nd_sparse_matrix(ddman, a, rvars, cvars, num_rvars, ndvars, num_ndvars, odd);
// Get number of transitions/choices
nnz = ndsm->nnz;
nc = ndsm->nc;
kb = ndsm->mem;
kbt = kb;
// print out info
PS_PrintToMainLog(env, "[n=%d, nc=%d, nnz=%ld, k=%d] ", n, nc, nnz, ndsm->k);
PS_PrintMemoryToMainLog(env, "[", kb, "]\n");
// If needed, and if info is available, build a vector of action indices for the MDP
if (export_adv != EXPORT_ADV_NONE) {
if (trans_actions != NULL) {
PS_PrintToMainLog(env, "Building action information... ");
// first need to filter out unwanted rows
Cudd_Ref(trans_actions);
Cudd_Ref(maybe_yes);
tmp = DD_Apply(ddman, APPLY_TIMES, trans_actions, maybe_yes);
Cudd_Ref(loops);
tmp = DD_ITE(ddman, loops, DD_Constant(ddman, 0), tmp);
// then convert to a vector of integer indices
build_nd_action_vector(ddman, a, tmp, ndsm, rvars, cvars, num_rvars, ndvars, num_ndvars, odd);
Cudd_RecursiveDeref(ddman, tmp);
kb = n*4.0/1024.0;
kbt += kb;
PS_PrintMemoryToMainLog(env, "[", kb, "]\n");
// also extract list of action names from 'synchs'
get_string_array_from_java(env, synchs, action_names_jstrings, action_names, num_actions);
} else {
PS_PrintWarningToMainLog(env, "Action labels are not available for adversary generation.");
}
}
// Get vectors for yes/maybe
PS_PrintToMainLog(env, "Creating vectors for yes... ");
for(i=0; i<num_targets; i++) {
yes_vecs[i] = mtbdd_to_double_vector(ddman, yes[i], rvars, num_rvars, odd);
}
kb = n*sizeof(int)/1024.0;
kbt += num_targets * kb;
PS_PrintToMainLog(env, "[%d x ", num_targets);
PS_PrintMemoryToMainLog(env, "", kb, "]\n");
PS_PrintToMainLog(env, "Creating vector for maybe... ");
maybe_vec = mtbdd_to_double_vector(ddman, maybe, rvars, num_rvars, odd);
kb = n*8.0/1024.0;
kbt += kb;
PS_PrintMemoryToMainLog(env, "[", kb, "]\n");
// Get index of single (first) initial state
start_index = get_index_of_first_from_bdd(ddman, start, rvars, num_rvars, odd);
PS_PrintToMainLog(env, "Initial state index: %1d\n", start_index);
// Print total memory usage
PS_PrintMemoryToMainLog(env, "TOTAL: [", kbt, "]\n");
// Store local copies of sparse matrix stuff
double *non_zeros = ndsm->non_zeros;
unsigned char *row_counts = ndsm->row_counts;
int *row_starts = (int *)ndsm->row_counts;
unsigned char *choice_counts = ndsm->choice_counts;
int *choice_starts = (int *)ndsm->choice_counts;
bool use_counts = ndsm->use_counts;
unsigned int *cols = ndsm->cols;
// Set up LP problem...
PS_PrintToMainLog(env, "\nBuilding LP problem...\n");
int *yes_vec;
int *map_var;
yes_vec = new int[n];
map_var = new int[n+1];
arr_ints = new int[n];
int sp;
// Compute the number of LP variables needed
// (one per choice for each 'maybe'/'yes' state + one extra for each yes state)
// Also build map_var (mapping from states to first corresponding LP var)
// Init counters (maybe_nc/yes_nc = num maybe/yes choices, yes_count = num yes, count = num vars so far)
int maybe_nc = 0;
int yes_nc = 0;
int yes_count = 0;
count = 0;
// Traverse sparse matrix to get info
h1 = h2 = 0;
for (i = 0; i < n; i++) {
if (!use_counts) {
l1 = row_starts[i];
h1 = row_starts[i+1];
} else {
l1 = h1;
h1 += row_counts[i];
}
k=0;
for (j = l1; j < h1; j++) {
if (!use_counts) {
l2 = choice_starts[j];
h2 = choice_starts[j+1];
} else {
l2 = h2;
h2 += choice_counts[j];
}
k++;
}
// Store first LP var for state i
map_var[i] = count;
// If a maybe state...
if (maybe_vec[i] > 0) {
maybe_nc += k;
count += k;
}
else {
for (j=0; j<num_targets; j++) {
// If a yes state...
if (yes_vecs[j][i] > 0) {
yes_nc += k;
yes_count ++; // each target state has one extra action
count += k+1;
// Skip any further targets for this state
break;
}
}
}
}
// Compute total var count
num_lp_vars = maybe_nc + yes_nc + yes_count;
// Store first LP var for final state
map_var[n] = num_lp_vars; // maybe need to be modified.
PS_PrintToMainLog(env, "Number of LP variables = %1d\n", num_lp_vars);
for(i=0; i<n; i++)
yes_vec[i] = 0;
for(i=0; i<n; i++) {
for(j=0; j<num_targets; j++) {
if(yes_vecs[j][i]> 0) { // For each state, count the number of targets it belongs to.
yes_vec[i]++;
}
}
}
/*printf("map_var = ");
for(i=0; i<=n; i++)
printf(" %1d", map_var[i]);
printf("\n");
fflush(stdout);*/
h1 = h2 = 0;
for(i=0; i<n; i++)
arr_ints[i] = map_var[i+1]-map_var[i];
for (i = 0; i < n; i++) {
if (!use_counts) {
l1 = row_starts[i];
h1 = row_starts[i+1];
} else {
l1 = h1;
h1 += row_counts[i];
}
for (j = l1; j < h1; j++) { // j: choice index; x+j: variable index
if (!use_counts) {
l2 = choice_starts[j];
h2 = choice_starts[j+1];
} else {
l2 = h2;
h2 += choice_counts[j];
}
if (maybe_vec[i]> 0 || yes_vec[i]> 0)
for(k=l2; k<h2; k++) {
if(maybe_vec[cols[k]]> 0 || yes_vec[cols[k]]> 0)
arr_ints[cols[k]]++;
}
}
}
int *constraints_sp;
int *constraints_ints;
double *constraints_reals;
int **constraints_int_ptr;
double **constraints_real_ptr;
constraints_sp = new int[n];
constraints_int_ptr = new int*[n];
constraints_real_ptr = new double*[n];
for(i=0; i<n; i++)
if(maybe_vec[i]> 0 || yes_vec[i]> 0) {
//printf("i = %1d, ", i);
constraints_ints = new int[arr_ints[i]];
constraints_reals = new REAL[arr_ints[i]];
constraints_sp[i] = map_var[i+1]-map_var[i];
constraints_int_ptr[i] = constraints_ints;
constraints_real_ptr[i] = constraints_reals;
for(j=0; j<map_var[i+1]-map_var[i]; j++) {
constraints_ints[j] = map_var[i] + j + 1; // index starts at 1 in lp_solve
constraints_reals[j] = 1.0;
}
for(j=map_var[i+1]-map_var[i]; j<arr_ints[i]; j++) {
constraints_ints[j] = -1;
constraints_reals[j] = 0;
}
}
arr_reals = new REAL[num_lp_vars];
if ((lp=make_lp(0, num_lp_vars)) == NULL) throw "Could not create LP problem";
// Lower verbosity: Warnings and errors only
set_verbose(lp, IMPORTANT);
// Set mode: will create row by row
set_add_rowmode(lp, true);
// Add constraints to LP problem
int x;
h1 = h2 = 0;
for (x = 0; x < n; x++) { // h2: row index
if(maybe_vec[x]> 0 || yes_vec[x]> 0) {
if (!use_counts) {
l1 = row_starts[x];
h1 = row_starts[x+1];
} else {
l1 = h1;
h1 += row_counts[x];
}
count = 0;
for (j = l1; j < h1; j++) { // j: choice index; x+j: variable index
if (!use_counts) {
l2 = choice_starts[j];
h2 = choice_starts[j+1];
} else {
l2 = h2;
h2 += choice_counts[j];
}
for(k=l2; k<h2; k++) { // k: column index
// get the index of the corresponding variable
i = cols[k];
if(maybe_vec[i]> 0 || yes_vec[i]> 0) {
constraints_ints = constraints_int_ptr[i];
constraints_reals = constraints_real_ptr[i];
for(k_r=0; k_r<constraints_sp[i]; k_r++)
if(constraints_ints[k_r]==map_var[x]+j-l1-count+1)
break;
if(k_r == constraints_sp[i]) {
//count++;
constraints_ints[k_r]=map_var[x]+j-l1-count + 1; // index starts at 1 in lp_solve
constraints_reals[k_r] = -non_zeros[k];
constraints_sp[i]++;
} else {
constraints_reals[k_r] -= non_zeros[k];
if(constraints_reals[k_r] == 0.0) { // In fact, this cannot happen.
//count--;
for(l2_r=k_r; l2_r<constraints_sp[i]-1; l2_r++) {
constraints_reals[l2_r] = constraints_reals[l2_r+1];
constraints_ints[l2_r] = constraints_ints[l2_r+1];
}
constraints_sp[i]--;
}
}
}
}
}
} else if(use_counts) {
l1 = h1;
h1 += row_counts[x];
for (j = l1; j < h1; j++)
h2 += choice_counts[j];
}
}
for (i = 0; i < n; i++)
if (maybe_vec[i]> 0 || yes_vec[i]> 0) {
constraints_ints = constraints_int_ptr[i];
constraints_reals = constraints_real_ptr[i];
add_constraintex(lp, constraints_sp[i], constraints_reals, constraints_ints, EQ, (start_index==i ? 1.0 : 0.0));
delete[] constraints_ints;
delete[] constraints_reals;
}
delete[] constraints_sp;
delete[] constraints_int_ptr;
delete[] constraints_real_ptr;
// Add LP constraints for bounded (non-quantitative) objectives
PS_PrintToMainLog(env, "Adding extra constraints for bounded objectives...\n");
constraints_ints = new int[num_lp_vars];
for (i=0; i<num_targets; i++) {
// Skip quantitative constraint
if (relops[i]==0 || relops[i]>2)
continue;
count = 0;
for(k=0; k<n; k++) {
if(yes_vecs[i][k]> 0) {
constraints_ints[count] = map_var[k+1];
arr_reals[count] = 1.0;
count++;
}
}
add_constraintex(lp, count, arr_reals, constraints_ints, GE, bounds[i]);
}
// Set objective function for LP
PS_PrintToMainLog(env, "Setting objective...\n");
x = 0;
if(relops[0]> 0 && relops[0]<=2) {
for(i=0; i<n; i++) {
if(yes_vec[i]> 0) {
constraints_ints[x] = map_var[i+1];
arr_reals[x++] = 1.0;
}
}
} else if(relops[0] == 0) {
for(i=0; i<n; i++)
if(yes_vecs[0][i]> 0) {
constraints_ints[x] = map_var[i+1];
arr_reals[x++] = 1.0;
}
}
set_maxim(lp);
set_obj_fnex(lp, x, arr_reals, constraints_ints);
delete[] constraints_ints;
// Finished building LP problem
set_add_rowmode(lp, false);
// Get setup time
stop = util_cpu_time();
time_for_setup = (double)(stop - start2)/1000;
start2 = stop;
// Export the MDP to a dot file
//export_model(ndsm, n, yes_vec, start_index);
// Solve the LP, extract result
PS_PrintToMainLog(env, "Solving LP problem...\n");
res = solve(lp);
//Get LP solving time
stop2 = util_cpu_time();
time_for_lp = (double)(stop2 - start2)/1000;
if (res != 0) {
PS_PrintToMainLog(env, "No solution\n");
lp_solved = false;
} else {
lp_solved = true;
lp_result = get_objective(lp);
lp_soln = new double[num_lp_vars];
get_ptr_variables(lp, &lp_soln);
// Generate adversary from the solution, if required
if (export_adv != EXPORT_ADV_NONE) {
// Adversary generation
export_adversary_ltl_tra(export_adv_filename, ndsm, ndsm->actions, action_names, yes_vec, maybe_vec, num_lp_vars, map_var, lp_soln, start_index);
//export_adversary_ltl_dot(env, ndsm, n, nnz, yes_vec, maybe_vec, num_lp_vars, map_var, lp_soln, start_index);
}
/*for (i=0; i<num_lp_vars; i++) {
if(lp_soln[i] != 0) {
PS_PrintToMainLog(env, "X%d = %g ", i, lp_soln[i]);
count++;
}
if(count == 8) {
PS_PrintToMainLog(env, "\n");
count = 0;
}
}
if(count)
PS_PrintToMainLog(env, "\n");*/
}
// Modify result based on type
if (relops[0] > 0) {
// for qualitative queries, return 1/0 for existence of solution or not
PS_PrintToMainLog(env, "LP problem solution %sfound so result is %s\n", lp_solved ? "" : "not ", lp_solved ? "true" : "false");
lp_result = lp_solved ? 1.0 : 0.0;
} else {
// return NaN for quantitative queries that can't be solved
PS_PrintToMainLog(env, "LP problem solution %sfound; result is %f\n", lp_solved ? "" : "not ", lp_result);
if (!lp_solved) lp_result = NAN;
}
// Print timing info
time_taken = time_for_setup + time_for_lp;
PS_PrintToMainLog(env, "\nLP problem solved in %.2f seconds (setup %.2f, lpsolve %.2f)\n", time_taken, time_for_setup, time_for_lp);
delete yes_vec;
delete map_var;
// Catch exceptions: register error
} catch (std::bad_alloc e) {
PS_SetErrorMessage("Out of memory");
lp_result = NAN;
}
// Free memory
if (lp) delete_lp(lp);
if (a) Cudd_RecursiveDeref(ddman, a);
if (maybe_yes) Cudd_RecursiveDeref(ddman, maybe_yes);
if (loops) Cudd_RecursiveDeref(ddman, loops);
if (ndsm) delete ndsm;
if (ndsm_r) delete ndsm_r;
delete[] maybe_vec;
for (i = 0; i < num_targets; i++)
delete[] yes_vecs[i];
delete[] arr_reals;
delete[] arr_ints;
if (target_ptrs) env->ReleaseLongArrayElements(_targets, target_ptrs, 0);
if (relops) env->ReleaseIntArrayElements(_relops, relops, 0);
if (bounds) env->ReleaseDoubleArrayElements(_bounds, bounds, 0);
if (action_names != NULL) {
release_string_array_from_java(env, action_names_jstrings, action_names, num_actions);
}
return lp_result;
}
//------------------------------------------------------------------------------