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