// -*- coding: utf-8 -*- // Copyright (C) 2008, 2009, 2010, 2012, 2013, 2014, 2015 Laboratoire de // Recherche et Développement de l'Epita (LRDE). // Copyright (C) 2004, 2005, 2007 Laboratoire d'Informatique de // Paris 6 (LIP6), département Systèmes Répartis Coopératifs (SRC), // Université Pierre et Marie Curie. // // This file is part of Spot, a model checking library. // // Spot 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 3 of the License, or // (at your option) any later version. // // Spot 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 this program. If not, see . #include "randomgraph.hh" #include "tgba/tgbagraph.hh" #include "misc/random.hh" #include "misc/bddlt.hh" #include "ltlast/atomic_prop.hh" #include #include #include #include #include namespace spot { namespace { unsigned random_deterministic_labels_rec(std::vector& labels, int *props, int props_n, bdd current, unsigned n) { if (n > 1 && props_n >= 1) { bdd ap = bdd_ithvar(*props); ++props; --props_n; // There are m labels generated from "current & ap" // and n - m labels generated from "current & !ap" unsigned m = rrand(1, n - 1); if (2 * m < n) { m = n - m; ap = !ap; } unsigned res = random_deterministic_labels_rec(labels, props, props_n, current & ap, m); res += random_deterministic_labels_rec(labels, props, props_n, current & !ap, n - res); return res; } else { labels.push_back(current); return 1; } } std::vector random_deterministic_labels(int *props, int props_n, unsigned n) { std::vector bddvec; random_deterministic_labels_rec(bddvec, props, props_n, bddtrue, n); return bddvec; } acc_cond::mark_t random_acc_cond(twa_graph_ptr aut, unsigned n_accs, float a) { acc_cond::mark_t m = 0U; for (unsigned i = 0U; i < n_accs; ++i) if (drand() < a) m |= aut->acc().mark(i); return m; } bdd random_labels(int* props, int props_n, float t) { int val = 0; int size = 0; bdd p = bddtrue; while (props_n) { if (size == 8 * sizeof(int)) { p &= bdd_ibuildcube(val, size, props); props += size; val = 0; size = 0; } val <<= 1; val |= (drand() < t); ++size; --props_n; } if (size > 0) p &= bdd_ibuildcube(val, size, props); return p; } } twa_graph_ptr random_graph(int n, float d, const ltl::atomic_prop_set* ap, const bdd_dict_ptr& dict, unsigned n_accs, float a, float t, bool deterministic, bool state_acc) { assert(n > 0); auto res = make_twa_graph(dict); if (deterministic) res->prop_deterministic(); if (state_acc) res->prop_state_based_acc(); int props_n = ap->size(); int* props = new int[props_n]; int pi = 0; for (auto i: *ap) props[pi++] = dict->register_proposition(i, res); res->set_generalized_buchi(n_accs); // Using std::unordered_set instead of std::set for these sets is 3 // times slower (tested on a 50000 nodes example). typedef std::set node_set; node_set nodes_to_process; node_set unreachable_nodes; res->new_states(n); std::vector state_randomizer(n); state_randomizer[0] = 0; nodes_to_process.insert(0); for (int i = 1; i < n; ++i) { state_randomizer[i] = i; unreachable_nodes.insert(i); } // We want to connect each node to a number of successors between // 1 and n. If the probability to connect to each successor is d, // the number of connected successors follows a binomial distribution. barand bin(n - 1, d); while (!nodes_to_process.empty()) { auto src = *nodes_to_process.begin(); nodes_to_process.erase(nodes_to_process.begin()); // Choose a random number of successors (at least one), using // a binomial distribution. unsigned nsucc = 1 + bin.rand(); bool saw_unreachable = false; // Create NSUCC random labels. std::vector labels; if (deterministic) { labels = random_deterministic_labels(props, props_n, nsucc); // if nsucc > 2^props_n, we cannot produce nsucc deterministic // transitions so we set it to labels.size() nsucc = labels.size(); } else for (unsigned i = 0; i < nsucc; ++i) labels.push_back(random_labels(props, props_n, t)); int possibilities = n; unsigned dst; acc_cond::mark_t m = 0U; if (state_acc) m = random_acc_cond(res, n_accs, a); for (auto& l: labels) { if (!state_acc) m = random_acc_cond(res, n_accs, a); // No connection to unreachable successors so far. This // is our last chance, so force it now. if (--nsucc == 0 && !unreachable_nodes.empty() && !saw_unreachable) { // Pick a random unreachable node. int index = mrand(unreachable_nodes.size()); node_set::const_iterator i = unreachable_nodes.begin(); std::advance(i, index); // Link it from src. res->new_transition(src, *i, l, m); nodes_to_process.insert(*i); unreachable_nodes.erase(*i); break; } // Pick the index of a random node. int index = mrand(possibilities--); // Permute it with state_randomizer[possibilities], so // we cannot pick it again. dst = state_randomizer[index]; state_randomizer[index] = state_randomizer[possibilities]; state_randomizer[possibilities] = dst; res->new_transition(src, dst, l, m); auto j = unreachable_nodes.find(dst); if (j != unreachable_nodes.end()) { nodes_to_process.insert(dst); unreachable_nodes.erase(j); saw_unreachable = true; } } // The node must have at least one successor. assert(res->get_graph().state_storage(src).succ); } // All nodes must be reachable. assert(unreachable_nodes.empty()); delete[] props; return res; } acc_cond::acc_code random_acceptance(unsigned n_accs) { // With 0 acceptance sets, we always generate the true acceptance. // (Working with false is somehow pointless, and the formulas we // generate for n_accs>0 are always satisfiable, so it makes sense // that it should be satisfiable for n_accs=0 as well.) if (n_accs == 0) return {}; acc_cond acc(n_accs); std::vector codes; codes.reserve(n_accs); for (unsigned i = 0; i < n_accs; ++i) if (drand() < 0.5) codes.push_back(acc.inf(acc.mark(i))); else codes.push_back(acc.fin(acc.mark(i))); int s = codes.size(); while (s > 1) { // Pick a random code and put it at the end int p1 = mrand(s--); std::swap(codes[p1], codes[s]); // and another one int p2 = mrand(s); if (drand() < 0.5) codes[p2].append_or(std::move(codes.back())); else codes[p2].append_and(std::move(codes.back())); codes.pop_back(); } return codes[0]; } }