Vcsn  2.3a
Be Rational
random-automaton.hh
Go to the documentation of this file.
1 #pragma once
2 
4 #include <vcsn/ctx/traits.hh>
5 #include <vcsn/dyn/automaton.hh>
6 #include <vcsn/dyn/context.hh>
7 
8 namespace vcsn
9 {
10  /*--------------------.
11  | random_automaton. |
12  `--------------------*/
13 
39  template <typename Ctx>
40  mutable_automaton<Ctx>
41  random_automaton(const Ctx& ctx,
42  unsigned num_states, float density = 0.1,
43  unsigned num_initial = 1, unsigned num_final = 1,
44  boost::optional<unsigned> max_labels = {},
45  float loop_chance = 0.0)
46  {
47  require(0 <= density && density <= 1,
48  "random_automaton: density must be in [0,1]");
49  require(0 <= loop_chance && loop_chance <= 1,
50  "random_automaton: loop chance must be in [0,1]");
51 
52  using detail::irange;
53  using automaton_t = mutable_automaton<Ctx>;
54  using state_t = state_t_of<automaton_t>;
55  auto res = make_shared_ptr<automaton_t>(ctx);
56 
57  auto& gen = make_random_engine();
58 
59  // Labels.
60  const auto& ls = *ctx.labelset();
61  if (max_labels)
62  {
63  require(0 < *max_labels,
64  "random_automaton: max number of labels cannot be null");
65 
66  require(*max_labels <= boost::distance(ls.generators()) + ls.has_one(),
67  "random_automaton: max number of labels cannot be greater "
68  "than the number of generators");
69  }
70  else
71  {
72  max_labels = (boost::distance(ls.generators()) + ls.has_one());
73  require(0 < *max_labels,
74  "random_automaton: empty labelset: ", ls);
75  }
76  auto num_labels = std::uniform_int_distribution<>(1, *max_labels);
77 
78  auto states = std::vector<state_t>{};
79  states.reserve(num_states);
80  // Indirect access to states[] to help random selection of successors.
81  auto state_randomizer = std::vector<int>{};
82  state_randomizer.reserve(num_states);
83 
84  // Using Sgi::hash_set instead of std::set for these sets is 3
85  // times slower (tested on a 50000 states example). These are
86  // indexes in states[].
87  using state_set = std::set<int>;
88  state_set worklist;
89  // Reachability from state[0] (_not_ from pre()).
90  state_set unreachables;
91 
92  for (unsigned i: detail::irange(num_states))
93  {
94  states.emplace_back(res->new_state());
95  state_randomizer.emplace_back(i);
96  // State 0 is "reachable" from 0.
97  if (i)
98  unreachables.emplace(i);
99  if (i < num_initial)
100  res->set_initial(states[i]);
101  }
102  worklist.insert(0);
103 
104  // Select the final states.
105  for (unsigned i: detail::irange(num_final))
106  {
107  auto dis = std::uniform_int_distribution<>(i, num_states - 1);
108  int index = dis(gen);
109  res->set_final(states[state_randomizer[index]]);
110  // Swap it at the beginning of state_randomizer, so we cannot
111  // pick it again.
112  std::swap(state_randomizer[index], state_randomizer[i]);
113  }
114 
115  // We want to connect each state to a number of successors between
116  // 1 and n. If the probability to connect to each successor is d,
117  // the number of connected successors follows a binomial distribution.
118  auto bin = std::binomial_distribution<>(num_states - 1, density);
119 
120  // Pick a member of a container following a uniform distribution.
121  auto pick = make_random_selector(gen);
122 
123  while (!worklist.empty())
124  {
125  auto src = states[*worklist.begin()];
126  worklist.erase(worklist.begin());
127 
128  // Choose a random number of successors (at least one), using
129  // a binomial distribution.
130  unsigned nsucc = 1 + bin(gen);
131 
132  // Connect to NSUCC randomly chosen successors. We want at
133  // least one unreachable successors among these if there are
134  // some.
135  bool saw_unreachable = false;
136  auto possibilities = num_states;
137  while (nsucc--)
138  {
139  // The index (in states[]) of the destination.
140  unsigned dst = -1;
141  // No connection to unreachable successors so far. This is
142  // our last chance, so force it now.
143  if (nsucc == 0
144  && !saw_unreachable
145  && !unreachables.empty())
146  {
147  // Pick a random unreachable state.
148  dst = pick.pop(unreachables);
149  worklist.insert(dst);
150  }
151  else
152  {
153  // Pick the index of a random state.
154  auto dis
155  = std::uniform_int_distribution<>(0, possibilities - 1);
156  int index = dis(gen);
157  possibilities--;
158 
159  dst = state_randomizer[index];
160 
161  // Permute it with state_randomizer[possibilities], so
162  // we cannot pick it again.
163  std::swap(state_randomizer[index],
164  state_randomizer[possibilities]);
165 
166  state_set::iterator j = unreachables.find(dst);
167  if (j != unreachables.end())
168  {
169  worklist.insert(dst);
170  unreachables.erase(j);
171  saw_unreachable = true;
172  }
173  }
174  auto n = num_labels(gen);
175  for (auto _: detail::irange(n))
176  res->add_transition(src, states[dst],
177  random_label(ls, gen));
178  }
179  }
180 
181  // Add loops.
182  if (0 < loop_chance)
183  {
184  auto dis = std::bernoulli_distribution(loop_chance);
185  for (auto s : res->states())
186  if (dis(gen))
187  res->add_transition(s, s, random_label(*ctx.labelset(), gen));
188  }
189 
190  return res;
191  }
192 
193  namespace dyn
194  {
195  namespace detail
196  {
198  template <typename Ctx, typename NumStates, typename Density,
199  typename NumInitial, typename NumFinal,
200  typename MaxLabels, typename LoopChance>
201  automaton
203  unsigned num_states, float density,
204  unsigned num_initial, unsigned num_final,
205  boost::optional<unsigned> max_labels,
206  float loop_chance)
207  {
208  const auto& c = ctx->as<Ctx>();
209  return vcsn::random_automaton(c, num_states, density,
210  num_initial, num_final,
211  max_labels,
212  loop_chance);
213  }
214  }
215  }
216 
217 
218  /*----------------------------------.
219  | random_automaton_deterministic. |
220  `----------------------------------*/
221 
222  template <typename Ctx>
223  mutable_automaton<Ctx>
224  random_automaton_deterministic(const Ctx& ctx, unsigned num_states)
225  {
226  require(0 < num_states, "num_states must be > 0");
227 
228  using automaton_t = mutable_automaton<Ctx>;
229  using state_t = state_t_of<automaton_t>;
230  automaton_t res = make_shared_ptr<automaton_t>(ctx);
231 
232  auto& gen = make_random_engine();
233  auto dis = std::uniform_int_distribution<int>(0, num_states - 1);
234 
235  auto states = std::vector<state_t>{};
236  states.reserve(num_states);
237 
238  for (auto _: detail::irange(num_states))
239  states.emplace_back(res->new_state());
240 
241  for (auto i: detail::irange(num_states))
242  for (auto l : ctx.labelset()->generators())
243  res->add_transition(states[i], states[dis(gen)], l,
244  ctx.weightset()->one());
245 
246  res->set_initial(states[dis(gen)]);
247  res->set_final(states[dis(gen)]);
248 
249  return res;
250  }
251 
252  namespace dyn
253  {
254  namespace detail
255  {
257  template <typename Ctx, typename>
258  automaton
259  random_automaton_deterministic(const context& ctx, unsigned num_states)
260  {
261  const auto& c = ctx->as<Ctx>();
262  return vcsn::random_automaton_deterministic(c, num_states);
263  }
264  }
265  }
266 }
Definition: a-star.hh:8
automaton random_automaton_deterministic(const context &ctx, unsigned num_states)
Bridge.
return res
Definition: multiply.hh:398
void require(Bool b, Args &&...args)
If b is not verified, raise an error with args as message.
Definition: raise.hh:91
automaton random_automaton(const context &ctx, unsigned num_states, float density, unsigned num_initial, unsigned num_final, boost::optional< unsigned > max_labels, float loop_chance)
Bridge.
typename detail::state_t_of_impl< base_t< ValueSet >>::type state_t_of
Definition: traits.hh:64
auto & as()
Downcast to the exact type.
Definition: context.hh:36
random_selector< RandomGenerator > make_random_selector(RandomGenerator &g)
Definition: random.hh:102
Template-less root for contexts.
Definition: context.hh:16
std::mt19937 & make_random_engine()
Generate a unique random device.
Definition: random.cc:6
mutable_automaton< Ctx > random_automaton_deterministic(const Ctx &ctx, unsigned num_states)
std::shared_ptr< detail::mutable_automaton_impl< Context >> mutable_automaton
Definition: fwd.hh:25
expressionset< Context >::value_t random_label(const expressionset< Context > &rs, RandomGenerator &gen=RandomGenerator())
Random label from expressionset: limited to a single label.
auto irange(Integer last)
Generate an integer range.
Definition: irange.hh:11
mutable_automaton< Ctx > random_automaton(const Ctx &ctx, unsigned num_states, float density=0.1, unsigned num_initial=1, unsigned num_final=1, boost::optional< unsigned > max_labels={}, float loop_chance=0.0)
Produce a random automaton.