Awali
Another Weighted Automata library
random.hh
Go to the documentation of this file.
1 // This file is part of Awali.
2 // Copyright 2016-2022 Sylvain Lombardy, Victor Marsault, Jacques Sakarovitch
3 //
4 // Awali is a free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
16 
17 #ifndef AWALI_ALGOS_RANDOM_HH
18 # define AWALI_ALGOS_RANDOM_HH
19 
20 #include <awali/sttc/ctx/traits.hh>
25 #include <awali/sttc/misc/raise.hh>
27 #include <awali/sttc/misc/set.hh>
28 
29 namespace awali { namespace sttc {
30  namespace internal {
31 
32  /* This files contains unevaluated functions to
33  generate random automata
34  */
35 
36 
37  template <typename RandomGenerator = std::default_random_engine>
38  typename oneset::value_t
39  random_label(const oneset& ls,
40  RandomGenerator& = RandomGenerator())
41  {
42  return ls.one();
43  };
44 
45  template <typename... LabelSet,
46  typename RandomGenerator = std::default_random_engine>
47  typename tupleset<LabelSet...>::value_t
49  RandomGenerator& gen = RandomGenerator())
50  {
51  return random_label(ls, gen, ls.indices);
52  };
53 
54  template <typename... LabelSet,
55  size_t... I,
56  typename RandomGenerator = std::default_random_engine>
57  typename tupleset<LabelSet...>::value_t
59  RandomGenerator& gen,
61  {
62  return ls.value(std::make_tuple(random_label(ls.template set<I>(), gen)...));
63  };
64 
65 
66  template <typename LabelSet,
67  typename RandomGenerator = std::default_random_engine>
68  typename LabelSet::value_t
69  random_label(const LabelSet& ls,
70  RandomGenerator& gen = RandomGenerator())
71  {
72  // Pick a member of a container following a uniform distribution.
73  auto pick = make_random_selector(gen);
74  return ls.value(pick(ls.genset()));
75  };
76 
77  template <typename LabelSet,
78  typename RandomGenerator = std::default_random_engine>
81  RandomGenerator& gen = RandomGenerator())
82  {
83  std::uniform_int_distribution<> dis(0, 1);
84  if (dis(gen))
85  return ls.one();
86  else
87  return ls.value(random_label(*ls.labelset(), gen));
88  };
89 
90  template <typename Context,
91  typename RandomGenerator = std::default_random_engine>
94  RandomGenerator& gen = RandomGenerator())
95  {
96  return rs.atom(random_label(*rs.labelset(), gen));
97  };
98 
99  /*--------------------.
100  | random(automaton). |
101  `--------------------*/
102 
103  template <typename Ctx>
105  random(const Ctx& ctx,
106  unsigned num_states, float density = 0.1,
107  unsigned num_initial = 1, unsigned num_final = 1)
108  {
109  require(0 <= density && density <= 1,
110  "random: density must be in [0,1]");
111  using automaton_t = mutable_automaton<Ctx>;
112  automaton_t res = make_shared_ptr<automaton_t>(ctx);
113 
114  // A good source of random integers.
115  std::random_device rd;
116  auto seed = rd();
117  if (getenv("AWALI_SEED"))
118  seed = std::mt19937::default_seed;
119  std::mt19937 gen(seed);
120 
121  std::vector<state_t> states;
122  states.reserve(num_states);
123  // Indirect access to states[] to help random selection of successors.
124  std::vector<int> state_randomizer;
125  state_randomizer.reserve(num_states);
126 
127  // Using Sgi::hash_set instead of std::set for these sets is 3
128  // times slower (tested on a 50000 states example). These are
129  // indexes in states[].
130  using state_set = std::set<int>;
131  state_set worklist;
132  // Reachability from state[0] (_not_ from pre()).
133  state_set unreachables;
134 
135  for (unsigned i = 0; i < num_states; ++i)
136  {
137  states.push_back(res->add_state());
138  state_randomizer.push_back(i);
139  // State 0 is "reachable" from 0.
140  if (i)
141  unreachables.emplace(i);
142  if (i < num_initial)
143  res->set_initial(states[i]);
144  }
145  worklist.insert(0);
146 
147  // Select the final states.
148  for (unsigned i = 0; i < num_final; ++i)
149  {
150  std::uniform_int_distribution<> dis(i, num_states - 1);
151  int index = dis(gen);
152  res->set_final(states[state_randomizer[index]]);
153  // Swap it at the beginning of state_randomizer, so we cannot
154  // pick it again.
155  std::swap(state_randomizer[index], state_randomizer[i]);
156  }
157 
158  // We want to connect each state to a number of successors between
159  // 1 and n. If the probability to connect to each successor is d,
160  // the number of connected successors follows a binomial distribution.
161  std::binomial_distribution<> bin(num_states - 1, density);
162 
163  // Pick a member of a container following a uniform distribution.
165 
166  while (!worklist.empty())
167  {
168  auto src = states[*worklist.begin()];
169  worklist.erase(worklist.begin());
170 
171  // Choose a random number of successors (at least one), using
172  // a binomial distribution.
173  unsigned nsucc = 1 + bin(gen);
174 
175  // Connect to NSUCC randomly chosen successors. We want at
176  // least one unreachable successors among these if there are
177  // some.
178  bool saw_unreachable = false;
179  auto possibilities = num_states;
180  while (nsucc--)
181  {
182  // The index (in states[]) of the destination.
183  unsigned dst = -1;
184  // No connection to unreachable successors so far. This is
185  // our last chance, so force it now.
186  if (nsucc == 0
187  && !saw_unreachable
188  && !unreachables.empty())
189  {
190  // Pick a random unreachable state.
191  dst = pick.pop(unreachables);
192  worklist.insert(dst);
193  }
194  else
195  {
196  // Pick the index of a random state.
197  std::uniform_int_distribution<> dis(0, possibilities - 1);
198  int index = dis(gen);
199  possibilities--;
200 
201  dst = state_randomizer[index];
202 
203  // Permute it with state_randomizer[possibilities], so
204  // we cannot pick it again.
205  std::swap(state_randomizer[index],
206  state_randomizer[possibilities]);
207 
208  state_set::iterator j = unreachables.find(dst);
209  if (j != unreachables.end())
210  {
211  worklist.insert(dst);
212  unreachables.erase(j);
213  saw_unreachable = true;
214  }
215  }
216  res->add_transition(src, states[dst],
217  random_label(*ctx.labelset(), gen));
218  }
219  }
220  return std::move(res);
221  }
222 
223  /*-----------------------.
224  | random_deterministic. |
225  `-----------------------*/
226 
227  template <typename Ctx>
229  random_deterministic(const Ctx& ctx, unsigned num_states)
230  {
231  require(0 < num_states, "num_states must be > 0");
232 
233  using automaton_t = mutable_automaton<Ctx>;
234  automaton_t res = make_shared_ptr<automaton_t>(ctx);
235 
236  std::random_device rd;
237  auto seed = rd();
238  if (getenv("AWALI_SEED"))
239  seed = std::mt19937::default_seed;
240  std::mt19937 gen(seed);
241  std::uniform_int_distribution<int> distrib(0, num_states - 1);
242 
243  std::vector<state_t> states;
244  states.reserve(num_states);
245 
246  for (unsigned i = 0; i < num_states; ++i)
247  states.push_back(res->add_state());
248 
249  for (unsigned i = 0; i < num_states; ++i)
250  for (auto l : ctx.labelset()->genset())
251  res->add_transition(states[i], states[distrib(gen)], l,
252  ctx.weightset()->one());
253 
254  res->set_initial(states[distrib(gen)]);
255  res->set_final(states[distrib(gen)]);
256  res->set_name("random-"+std::to_string(num_states));
257  return res;
258  }
259 
260  }}}//end of ns awali::stc
261 
262 #endif // !AWALI_ALGOS_RANDOM_HH
Implementation of labels are nullables (letter or empty).
Definition: nullableset.hh:189
typename helper_t::value_t value_t
Definition: nullableset.hh:197
static constexpr ATTRIBUTE_PURE value_t one()
Definition: nullableset.hh:257
const labelset_ptr labelset() const
Definition: nullableset.hh:281
value_t value(Args &&... args) const
Definition: nullableset.hh:288
Implementation of labels are ones: there is a single instance of label.
Definition: oneset.hh:38
empty_t value_t
Definition: oneset.hh:41
static empty_t one()
Definition: oneset.hh:133
A ValueSet which is a Cartesian product of ValueSets.
Definition: tupleset.hh:80
static constexpr indices_t indices
Definition: tupleset.hh:84
value_t value(const std::tuple< Args... > &args) const
Construct a value.
Definition: tupleset.hh:174
std::vector< state_t > states(abstract_automaton_t const *aut, bool all)
Definition: tuple.hh:43
mutable_automaton< Ctx > random_deterministic(const Ctx &ctx, unsigned num_states)
Definition: random.hh:229
mutable_automaton< Ctx > random(const Ctx &ctx, unsigned num_states, float density=0.1, unsigned num_initial=1, unsigned num_final=1)
Definition: random.hh:105
struct random_selector< RandomGenerator > make_random_selector(const RandomGenerator &g) ATTRIBUTE_PURE
Definition: random.hh:81
oneset::value_t random_label(const oneset &ls, RandomGenerator &=RandomGenerator())
Definition: random.hh:39
std::string to_string(identities i)
std::shared_ptr< internal::mutable_automaton_impl< Context > > mutable_automaton
Definition: mutable_automaton.hh:45
void require(bool b, Args &&... args)
If b is not verified, raise an error with args as message.
Definition: raise.hh:55
Main namespace of Awali.
Definition: ato.hh:22
auto pop(Container &c) -> typename Container::value_type
A randomly selected member of c. Remove it from c.
Definition: random.hh:61
Provide a variadic mul on top of a binary mul(), and one().
Definition: weightset.hh:38