Vcsn  2.1
Be Rational
random.hh
Go to the documentation of this file.
1 #pragma once
2 
3 #include <vcsn/ctx/traits.hh>
5 #include <vcsn/dyn/automaton.hh>
6 #include <vcsn/dyn/context.hh>
10 #include <vcsn/misc/raise.hh>
11 #include <vcsn/misc/random.hh>
12 #include <vcsn/misc/set.hh>
13 
14 namespace vcsn
15 {
16 
17  template <typename RandomGenerator = std::default_random_engine>
18  typename oneset::value_t
19  random_label(const oneset& ls,
20  RandomGenerator& = RandomGenerator())
21  {
22  return ls.one();
23  };
24 
25  template <typename... LabelSet,
26  typename RandomGenerator = std::default_random_engine>
27  typename tupleset<LabelSet...>::value_t
29  RandomGenerator& gen = RandomGenerator())
30  {
31  return random_label(ls, gen, ls.indices);
32  };
33 
34  template <typename... LabelSet,
35  size_t... I,
36  typename RandomGenerator = std::default_random_engine>
37  typename tupleset<LabelSet...>::value_t
39  RandomGenerator& gen,
41  {
42  return ls.value(std::make_tuple(random_label(ls.template set<I>(), gen)...));
43  };
44 
45  template <typename GenSet,
46  typename RandomGenerator = std::default_random_engine>
47  typename wordset<GenSet>::value_t
49  RandomGenerator& gen = RandomGenerator())
50  {
51  std::uniform_int_distribution<> dis(0, 5);
52  auto res_label = ls.one();
53  auto pick = make_random_selector(gen);
54  for (auto i = 0; i < dis(gen); ++i)
55  res_label = ls.mul(res_label, ls.value(pick(ls.genset())));
56  return res_label;
57  };
58 
59  template <typename LabelSet,
60  typename RandomGenerator = std::default_random_engine>
61  typename LabelSet::value_t
62  random_label(const LabelSet& ls,
63  RandomGenerator& gen = RandomGenerator())
64  {
65  // Pick a member of a container following a uniform distribution.
66  auto pick = make_random_selector(gen);
67  return ls.value(pick(ls.genset()));
68  };
69 
70  template <typename LabelSet,
71  typename RandomGenerator = std::default_random_engine>
72  typename nullableset<LabelSet>::value_t
74  RandomGenerator& gen = RandomGenerator())
75  {
76  std::bernoulli_distribution dis(0.5);
77  if (dis(gen))
78  return ls.one();
79  else
80  return ls.value(random_label(*ls.labelset(), gen));
81  };
82 
83 
84  template <typename Context,
85  typename RandomGenerator = std::default_random_engine>
86  typename expressionset<Context>::value_t
88  RandomGenerator& gen = RandomGenerator())
89  {
90  return rs.atom(random_label(*rs.labelset(), gen));
91  };
92 
93  /*--------------------.
94  | random_automaton. |
95  `--------------------*/
96 
97  template <typename Ctx>
98  mutable_automaton<Ctx>
99  random_automaton(const Ctx& ctx,
100  unsigned num_states, float density = 0.1,
101  unsigned num_initial = 1, unsigned num_final = 1,
102  float loop_chance = 0.0)
103  {
104  require(0 <= density && density <= 1,
105  "random: density must be in [0,1]");
106  require(0 <= loop_chance && loop_chance <= 1,
107  "random: loop chance must be in [0,1]");
108  using automaton_t = mutable_automaton<Ctx>;
109  using state_t = state_t_of<automaton_t>;
110  automaton_t res = make_shared_ptr<automaton_t>(ctx);
111 
112  // A good source of random integers.
113  std::random_device rd;
114  auto seed = rd();
115  if (getenv("VCSN_SEED"))
116  seed = std::mt19937::default_seed;
117  std::mt19937 gen(seed);
118 
119  std::vector<state_t> states;
120  states.reserve(num_states);
121  // Indirect access to states[] to help random selection of successors.
122  std::vector<int> state_randomizer;
123  state_randomizer.reserve(num_states);
124 
125  // Using Sgi::hash_set instead of std::set for these sets is 3
126  // times slower (tested on a 50000 states example). These are
127  // indexes in states[].
128  using state_set = std::set<int>;
129  state_set worklist;
130  // Reachability from state[0] (_not_ from pre()).
131  state_set unreachables;
132 
133  for (unsigned i = 0; i < num_states; ++i)
134  {
135  states.push_back(res->new_state());
136  state_randomizer.push_back(i);
137  // State 0 is "reachable" from 0.
138  if (i)
139  unreachables.emplace(i);
140  if (i < num_initial)
141  res->set_initial(states[i]);
142  }
143  worklist.insert(0);
144 
145  // Select the final states.
146  for (unsigned i = 0; i < num_final; ++i)
147  {
148  std::uniform_int_distribution<> dis(i, num_states - 1);
149  int index = dis(gen);
150  res->set_final(states[state_randomizer[index]]);
151  // Swap it at the beginning of state_randomizer, so we cannot
152  // pick it again.
153  std::swap(state_randomizer[index], state_randomizer[i]);
154  }
155 
156  // We want to connect each state to a number of successors between
157  // 1 and n. If the probability to connect to each successor is d,
158  // the number of connected successors follows a binomial distribution.
159  std::binomial_distribution<> bin(num_states - 1, density);
160 
161  // Pick a member of a container following a uniform distribution.
163 
164  while (!worklist.empty())
165  {
166  auto src = states[*worklist.begin()];
167  worklist.erase(worklist.begin());
168 
169  // Choose a random number of successors (at least one), using
170  // a binomial distribution.
171  unsigned nsucc = 1 + bin(gen);
172 
173  // Connect to NSUCC randomly chosen successors. We want at
174  // least one unreachable successors among these if there are
175  // some.
176  bool saw_unreachable = false;
177  auto possibilities = num_states;
178  while (nsucc--)
179  {
180  // The index (in states[]) of the destination.
181  unsigned dst = -1;
182  // No connection to unreachable successors so far. This is
183  // our last chance, so force it now.
184  if (nsucc == 0
185  && !saw_unreachable
186  && !unreachables.empty())
187  {
188  // Pick a random unreachable state.
189  dst = pick.pop(unreachables);
190  worklist.insert(dst);
191  }
192  else
193  {
194  // Pick the index of a random state.
195  std::uniform_int_distribution<> dis(0, possibilities - 1);
196  int index = dis(gen);
197  possibilities--;
198 
199  dst = state_randomizer[index];
200 
201  // Permute it with state_randomizer[possibilities], so
202  // we cannot pick it again.
203  std::swap(state_randomizer[index],
204  state_randomizer[possibilities]);
205 
206  state_set::iterator j = unreachables.find(dst);
207  if (j != unreachables.end())
208  {
209  worklist.insert(dst);
210  unreachables.erase(j);
211  saw_unreachable = true;
212  }
213  }
214  res->add_transition(src, states[dst],
215  random_label(*ctx.labelset(), gen));
216  }
217  }
218 
219 
220  // Add loops
221  if (loop_chance > 0)
222  {
223  std::bernoulli_distribution bern(0.5);
224  for (auto s : res->states())
225  if (bern(gen))
226  res->add_transition(s, s, random_label(*ctx.labelset(), gen));
227  }
228 
229  return res;
230  }
231 
232  namespace dyn
233  {
234  namespace detail
235  {
237  template <typename Ctx, typename, typename, typename, typename, typename>
238  automaton
240  unsigned num_states, float density,
241  unsigned num_initial, unsigned num_final,
242  float loop_chance)
243  {
244  const auto& c = ctx->as<Ctx>();
245  return make_automaton(vcsn::random_automaton(c, num_states, density,
246  num_initial, num_final,
247  loop_chance));
248  }
249  }
250  }
251 
252 
253  /*----------------------------------.
254  | random_automaton_deterministic. |
255  `----------------------------------*/
256 
257  template <typename Ctx>
258  mutable_automaton<Ctx>
259  random_automaton_deterministic(const Ctx& ctx, unsigned num_states)
260  {
261  require(0 < num_states, "num_states must be > 0");
262 
263  using automaton_t = mutable_automaton<Ctx>;
264  using state_t = state_t_of<automaton_t>;
265  automaton_t res = make_shared_ptr<automaton_t>(ctx);
266 
267  std::random_device rd;
268  auto seed = rd();
269  if (getenv("VCSN_SEED"))
270  seed = std::mt19937::default_seed;
271  std::mt19937 gen(seed);
272  std::uniform_int_distribution<int> distrib(0, num_states - 1);
273 
274  std::vector<state_t> states;
275  states.reserve(num_states);
276 
277  for (unsigned i = 0; i < num_states; ++i)
278  states.push_back(res->new_state());
279 
280  for (unsigned i = 0; i < num_states; ++i)
281  for (auto l : ctx.labelset()->genset())
282  res->add_transition(states[i], states[distrib(gen)], l,
283  ctx.weightset()->one());
284 
285  res->set_initial(states[distrib(gen)]);
286  res->set_final(states[distrib(gen)]);
287 
288  return res;
289  }
290 
291  namespace dyn
292  {
293  namespace detail
294  {
296  template <typename Ctx, typename>
297  automaton
298  random_automaton_deterministic(const context& ctx, unsigned num_states)
299  {
300  const auto& c = ctx->as<Ctx>();
302  }
303 
304  }
305  }
306 } // vcsn::
struct random_selector< RandomGenerator > make_random_selector(const RandomGenerator &g) ATTRIBUTE_PURE
Definition: random.hh:64
std::shared_ptr< const detail::context_base > context
A dyn::context.
Definition: fwd.hh:41
mutable_automaton< Ctx > random_automaton(const Ctx &ctx, unsigned num_states, float density=0.1, unsigned num_initial=1, unsigned num_final=1, float loop_chance=0.0)
Definition: random.hh:99
typename detail::state_t_of_impl< base_t< ValueSet >>::type state_t_of
Definition: traits.hh:48
static empty_t one()
Definition: oneset.hh:104
vcsn::empty_t value_t
Definition: oneset.hh:21
value_t value(Args &&...args) const
Definition: nullableset.hh:290
mutable_automaton< Ctx > random_automaton_deterministic(const Ctx &ctx, unsigned num_states)
Definition: random.hh:259
automaton make_automaton(const Aut &aut)
Build a dyn::automaton.
Definition: automaton.hh:75
value_t value(Args &&...args) const
Value constructor.
Definition: wordset.hh:83
static dyn::context ctx(const driver &d)
Get the context of the driver.
Definition: parse.cc:80
const genset_t & genset() const
Provide a variadic mul on top of a binary mul(), and one().
Definition: fwd.hh:46
Implementation of labels are ones: there is a single instance of label.
Definition: oneset.hh:17
std::shared_ptr< detail::automaton_base > automaton
Definition: automaton.hh:69
static ATTRIBUTE_PURE constexpr value_t one()
Definition: nullableset.hh:235
void require(bool b, Args &&...args)
If b is not verified, raise an error with args as message.
Definition: raise.hh:75
auto pop(Container &c) -> typename Container::value_type
A randomly selected member of c. Remove it from c.
Definition: random.hh:44
const labelset_ptr labelset() const
Definition: nullableset.hh:283
automaton random_automaton(const context &ctx, unsigned num_states, float density, unsigned num_initial, unsigned num_final, float loop_chance)
Bridge.
Definition: random.hh:239
automaton random_automaton_deterministic(const context &ctx, unsigned num_states)
Bridge.
Definition: random.hh:298
auto rs
Definition: lift.hh:151
static value_t one()
Definition: wordset.hh:170
weightset_mixin< detail::tupleset_impl< LabelSets...>> tupleset
Definition: fwd.hh:27
oneset::value_t random_label(const oneset &ls, RandomGenerator &=RandomGenerator())
Definition: random.hh:19
auto mul(Args &&...args) const -> decltype(this->genset().mul(std::forward< Args >(args)...))
Implementation of labels are words.
Definition: fwd.hh:31
Implementation of labels are nullables (letter or empty).
Definition: fwd.hh:14
std::shared_ptr< detail::mutable_automaton_impl< Context >> mutable_automaton
Definition: fwd.hh:24