//============================================================================== // // Copyright (c) 2002- // Authors: // * Dave Parker (University of Oxford) // * Hongyang Qu (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 #include #include #include #include #include #include "sparse.h" #include "sparse_adv.h" #include "prism.h" #include "PrismSparseGlob.h" #include "PrismNativeGlob.h" #include "jnipointer.h" #include #include "lp_lib.h" //------------------------------------------------------------------------------ JNIEXPORT jdouble __jlongpointer JNICALL Java_sparse_PrismSparse_PS_1NondetMultiReachReward1 ( 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 jlongArray _combinations, // combinations of target state sets jintArray _combinationIDs, // combinations' ID jintArray _relopsProb, // target relops (0:=?, 1:>, 2:>=) jdoubleArray _boundsProb, // target probability bounds jintArray _relopsReward, // target relops (0:=?, 1:>, 2:>=) jdoubleArray _boundsReward, // target probability bounds jlong __jlongpointer m, // 'maybe' states jlong __jlongpointer _start, // initial state(s) //jlong __jlongpointer m_r, // 'maybe' states for the reward formula jlongArray _trrs, // transition rewards jlong _becs // bottom end components in no states // Reward paramters for more complicated cases //jlongArray _rewards, // sets of rewards for reward formulas //jlongArray _targets_r, // target state sets for reward formulas //jdoubleArray _bounds_r, // target probability bounds for reward formulas ) { // 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) DdNode *bottomec = jlong_to_DdNode(_becs); // bottom end compoment states // target info jlong *target_ptrs = NULL; DdNode **targets = NULL; jlong *combination_ptrs = NULL; DdNode **combinations = NULL; jint *relops = NULL, *combinationIDs = NULL; double *bounds = NULL; // mtbdds DdNode *a = NULL, **yes = NULL, *maybe_yes = NULL, *loops = NULL, *tmp = NULL; // model stats int num_targets, num_combinations, 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, **combination_vecs, *maybe_vec = NULL, *bottomec_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; REAL *back_arr_reals = 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; // Reward info jlong *reward_ptrs = NULL; DdNode **rewards = NULL; jint *relopsReward = NULL; double *boundsReward = NULL; int num_rewards; // 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(_relopsProb, 0); bounds = env->GetDoubleArrayElements(_boundsProb, 0); num_combinations = (int)env->GetArrayLength(_combinations); combination_ptrs = env->GetLongArrayElements(_combinations, 0); combinations = new DdNode*[num_combinations]; combination_vecs = new double*[num_combinations]; for (i = 0; i < num_combinations; i++) { combinations[i] = jlong_to_DdNode(combination_ptrs[i]); } combinationIDs = env->GetIntArrayElements(_combinationIDs, 0); yes = new DdNode*[num_targets]; yes_vecs = new double*[num_targets]; for(i=0; iGetArrayLength(_trrs); reward_ptrs = env->GetLongArrayElements(_trrs, 0); rewards = new DdNode*[num_rewards]; for (i = 0; i < num_rewards; i++) rewards[i] = jlong_to_DdNode(reward_ptrs[i]); relopsReward = env->GetIntArrayElements(_relopsReward, 0); boundsReward = env->GetDoubleArrayElements(_boundsReward, 0); // Display some info about the targets/combinations 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)); } PS_PrintToMainLog(env, "%d Target combinations:\n", num_combinations); for (i = 0; i < num_combinations; i++) { PS_PrintToMainLog(env, "#%d: ", i); PS_PrintToMainLog(env, "%d ", combinationIDs[i]); PS_PrintToMainLog(env, " (%.0f states)\n", DD_GetNumMinterms(ddman, combinations[i], num_rvars)); } // Display some info about the rewards PS_PrintToMainLog(env, "\n%d Rewards:\n", num_rewards); bool disable_selfloop = true; for (i = 0; i < num_rewards; i++) { PS_PrintToMainLog(env, "#%d: ", i); switch (relopsReward[i]) { case 3: PS_PrintToMainLog(env, "Rmax=?\n"); disable_selfloop = false; break; case 8: PS_PrintToMainLog(env, "Rmin=?\n"); break; case 4: PS_PrintToMainLog(env, "R>=%g\n", boundsReward[i]); disable_selfloop = false; break; case 9: PS_PrintToMainLog(env, "R<=%g\n", boundsReward[i]); break; } } // 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]); } for (i = 0; i < num_combinations; i++) { Cudd_Ref(combinations[i]); maybe_yes = DD_Or(ddman, maybe_yes, combinations[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. if(!disable_selfloop) { 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=%d, 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.", export_adv_filename); } } // Get vectors for yes/maybe PS_PrintToMainLog(env, "Creating vectors for yes "); for(i=0; inon_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; int bottomec_nc = 0; count = 0; int yc = 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(bottomec_vec[i] > 0) { // each state in a bottom end component has an extra action bottomec_nc += k+1; count += k+1; } else if (maybe_vec[i] > 0) { // If a maybe state... maybe_nc += k; count += k; } else { yc = 0; for (j=0; j 0) yc++; for (j=0; j 0) yc++; yes_nc += k; // each target state may have more than one extra action yes_count += yc; count += k+yc; //printf("i=%d, yes_nc=%d, yes_count=%d, count=%d\n", i, yes_nc, yes_count, count); } } // Compute total var count num_lp_vars = maybe_nc + yes_nc + yes_count + bottomec_nc; // 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 0) { // For each state, count the number of targets it belongs to. yes_vec[i]++; } } for(j=0; j 0) { // For each state, count the number of targets it belongs to. yes_vec[i]++; } } } h1 = h2 = 0; for(i=0; i 0 || yes_vec[i]> 0) for(k=l2; k 0 || yes_vec[i]> 0) { 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 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 0 || yes_vec[i]> 0) { constraints_ints = constraints_int_ptr[i]; constraints_reals = constraints_real_ptr[i]; for(k_r=0; k_r 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]; int varsp, index; for (i=0; i2) continue; count = 0; for(k=0; k 0) { if(j == i) { constraints_ints[count] = map_var[k+1]-yes_vec[k]+varsp + 1; arr_reals[count] = 1.0; count++; } varsp++; } } for(j=0; j 0) { index = 1; index = index << i; if((index & combinationIDs[j]) > 0) { constraints_ints[count] = map_var[k+1]-yes_vec[k]+varsp + 1; arr_reals[count] = 1.0; count++; } varsp++; } } } add_constraintex(lp, count, arr_reals, constraints_ints, GE, bounds[i]); } /***************************************************************** * Reward *****************************************************************/ PS_PrintToMainLog(env, "computing max function for rewards ...\n"); double *non_zeros_r; unsigned char *choice_counts_r; int *choice_starts_r; bool use_counts_r; unsigned int *cols_r; bool isMax; DdNode *trans_rewards; for (int ii=num_rewards-1; ii>=0; ii--) { isMax = true; // Build array for constraint (1 iff is an extra transition for the i_th target) for(k=0; knnz; nc_r = ndsm_r->nc; non_zeros_r = ndsm_r->non_zeros; choice_counts_r = ndsm_r->choice_counts; choice_starts_r = (int *)ndsm_r->choice_counts; use_counts_r = ndsm_r->use_counts; cols_r = ndsm_r->cols; h1 = h2 = l2_r = h2_r = x = 0; for(i=0; i 0 || yes_vec[i]> 0) { if (!use_counts) { // assume we always do not use counts l1 = row_starts[i]; h1 = row_starts[i+1]; } else { // Problematic l1 = h1; h1 += row_counts[i]; } for (j = l1; j < h1; j++) {// \Sigma_{v\in V/F} y_{(v, \gamma)}*c_{(v, \gamma)} if (!use_counts) { l2 = choice_starts[j]; h2 = choice_starts[j+1]; } else { l2 = h2; h2 += choice_counts[j]; } if (!use_counts_r) { l2_r = choice_starts_r[j]; h2_r = choice_starts_r[j+1]; } else { l2_r = h2_r; h2_r += choice_counts_r[j]; } k_r = l2_r; while (k_r < h2_r && cols_r[k_r] != cols[l2]) k_r++; // if there is one, add reward * prob to reward value if (k_r < h2_r) { constraints_ints[x] = map_var[i]+j-l1 + 1; arr_reals[x++] = non_zeros_r[k_r]; } } } else if(use_counts) { // count the number of choices for non maybe nodes. Is it necessary? l1 = h1; h1 += row_counts[i]; for (j = l1; j < h1; j++) { h2 += choice_counts[j]; h2_r += choice_counts_r[j]; } } if (export_adv != EXPORT_ADV_NONE) { back_arr_reals = new REAL[num_lp_vars]; memcpy (back_arr_reals, arr_reals, sizeof(REAL)*num_lp_vars); } // Skip quantitative constraint if (relopsReward[ii]==3 || relopsReward[ii]==8) break; else if(relopsReward[ii]==9) isMax = false; if(isMax) add_constraintex(lp, x, arr_reals, constraints_ints, GE, boundsReward[ii]); else add_constraintex(lp, x, arr_reals, constraints_ints, LE, boundsReward[ii]); } // Set objective function for LP PS_PrintToMainLog(env, "Setting objective...\n"); isMax = true; if(num_targets > 0 && relops[0] == 0) { x = 0; for(i=0; i 0) { constraints_ints[x] = map_var[i+1] - yes_vec[i] + 1; arr_reals[x++] = 1.0; varsp++; } for(j=1; j 0) varsp++; for(j=0; j 0) { if((combinationIDs[j] & 1) > 0) { constraints_ints[x] = map_var[i+1] - yes_vec[i] + varsp + 1; arr_reals[x++] = 1.0; } varsp++; } } } } else if(relopsReward[0] == 3) { // Rmax } else if(relopsReward[0] == 8) { // Rmin isMax = false; } else if(relops[0]==2) { x = 0; for(i=0; iactions, 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; iReleaseLongArrayElements(_targets, target_ptrs, 0); if (relops) env->ReleaseIntArrayElements(_relopsProb, relops, 0); if (bounds) env->ReleaseDoubleArrayElements(_boundsProb, bounds, 0); if (reward_ptrs) env->ReleaseLongArrayElements(_trrs, reward_ptrs, 0); if (relopsReward) env->ReleaseIntArrayElements(_relopsReward, relopsReward, 0); if (boundsReward) env->ReleaseDoubleArrayElements(_boundsReward, boundsReward, 0); //if (rewards) // for(i = 0; i