//============================================================================== // // 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_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%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; 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; 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 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 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 0 || yes_vec[i]> 0) for(k=l2; 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 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 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]; for (i=0; i2) continue; count = 0; for(k=0; 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 0) { constraints_ints[x] = map_var[i+1]; arr_reals[x++] = 1.0; } } } else if(relops[0] == 0) { for(i=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 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; } //------------------------------------------------------------------------------