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