Synthia
Generic and flexible data structure generator
MarkovChain.java
Go to the documentation of this file.
1 /*
2  Synthia, a data structure generator
3  Copyright (C) 2019-2020 Laboratoire d'informatique formelle
4  Université du Québec à Chicoutimi, Canada
5 
6  This program is free software: you can redistribute it and/or modify
7  it under the terms of the GNU Lesser General Public License as published
8  by the Free Software Foundation, either version 3 of the License, or
9  (at your option) any later version.
10 
11  This program is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  GNU Lesser General Public License for more details.
15 
16  You should have received a copy of the GNU Lesser General Public License
17  along with this program. If not, see <http://www.gnu.org/licenses/>.
18  */
19 package ca.uqac.lif.synthia.sequence;
20 
21 import java.io.PrintStream;
22 import java.util.ArrayList;
23 import java.util.HashMap;
24 import java.util.List;
25 import java.util.Map;
26 
27 import ca.uqac.lif.synthia.Picker;
29 
30 /**
31  * Generates a sequence of objects by a random walk in a Markov chain.
32  * A <a href="https://en.wikipedia.org/wiki/Markov_chain">Markov chain</a>
33  * is a stochastic model describing a sequence of possible events. Each state
34  * of the chain is associated to a {@link Picker}; transitions between states
35  * A &rarr; B are labelled with the probability of going to B when the
36  * current state ia A. The following picture shows an example of a Markov
37  * chain:
38  * <p>
39  * <img src="./doc-files/Markov.png" alt="Markov chain">
40  * <p>
41  *
42  * @param <T> The type of objects returned by the Markov chain. The pickers
43  * associated to each state must return objects of type <tt>T</tt> or its
44  * descendants.
45  * @ingroup API
46  */
47 public class MarkovChain<T> implements Picker<T>
48 {
49  /**
50  * The index of the current state of the machine
51  */
52  protected int m_currentState;
53 
54  /**
55  * The pickers associated to each state
56  */
57  protected Map<Integer,Picker<? extends T>> m_pickers;
58 
59  /**
60  * A map associating state numbers with the list of their outgoing
61  * transitions
62  */
63  protected Map<Integer,List<Transition>> m_transitions;
64 
65  /**
66  * A source of numbers between 0 and 1. This source is used to pick the
67  * next transition to take.
68  */
70 
71  /**
72  * Whether to exhaust each state before moving to another
73  */
74  protected boolean m_exhaust;
75 
76  /**
77  * Creates a new empty Markov chain.
78  * @param float_source A source of numbers between 0 and 1. This source is
79  * used to pick the next transition to take.
80  */
81  public MarkovChain(Picker<Float> float_source)
82  {
83  super();
84  m_transitions = new HashMap<Integer,List<Transition>>();
85  m_pickers = new HashMap<Integer,Picker<? extends T>>();
86  m_floatSource = float_source;
87  m_exhaust = false;
88  }
89 
90  /**
91  * Adds a new transition to the machine
92  * @param source The source state
93  * @param destination The destination state
94  * @param probability The probability of taking this transition (between 0 and 1)
95  * @return This machine
96  */
97  public MarkovChain<T> add(int source, int destination, Number probability)
98  {
99  Transition t = new Transition(destination, probability.floatValue());
100  List<Transition> trans = m_transitions.get(source);
101  if (trans == null)
102  {
103  trans = new ArrayList<Transition>();
104  m_transitions.put(source, trans);
105  }
106  trans.add(t);
107  return this;
108  }
109 
110  /**
111  * Associates a picker to a state
112  * @param state The state
113  * @param p The picker
114  * @return This machine
115  */
116  public MarkovChain<T> add(int state, Picker<T> p)
117  {
118  m_pickers.put(state, p);
119  return this;
120  }
121 
122  /**
123  * Sets whether to exhaust the picker associated to a state before
124  * transitioning to another state
125  * @param b Set to <tt>true</tt> to exhaust each picker, <tt>false</tt>
126  * otherwise
127  * @return This Markov chain
128  */
129  public MarkovChain<T> exhaust(boolean b)
130  {
131  m_exhaust = b;
132  return this;
133  }
134 
135  @Override
136  public T pick()
137  {
138  if (m_exhaust)
139  {
140  T t = m_pickers.get(m_currentState).pick();
141  if (t != null)
142  {
143  return t;
144  }
145  }
146  float p = m_floatSource.pick();
147  int new_state = selectTransition(p);
148  if (new_state < 0)
149  {
150  return null;
151  }
152  Picker<? extends T> lp = m_pickers.get(new_state);
153  if (lp == null)
154  {
155  throw new PickerException("State " + new_state + " does not have a picker");
156  }
157  m_currentState = new_state;
158  if (m_exhaust)
159  {
160  lp.reset();
161  }
162  return lp.pick();
163  }
164 
165  /**
166  * From the current state, randomly selects a destination state based on
167  * the probabilities associated to each outgoing transition
168  * @param p A random value between 0 and 1, used to pick the next state
169  * @return The number of the state, or -1 if no destination was
170  * chosen
171  */
172  protected int selectTransition(float p)
173  {
174  List<Transition> transitions = m_transitions.get(m_currentState);
175  float sum_prob = 0f;
176  int last_state = -1;
177  for (Transition t : transitions)
178  {
179  sum_prob += t.getProbability();
180  last_state = t.getDestination();
181  if (p <= sum_prob)
182  {
183  break;
184  }
185  }
186  return last_state;
187  }
188 
189  @Override
190  public MarkovChain<T> duplicate(boolean with_state)
191  {
193  mmm.m_transitions.putAll(m_transitions);
194  mmm.m_pickers.putAll(m_pickers);
195  mmm.m_exhaust = m_exhaust;
196  if (with_state)
197  {
199  }
200  return mmm;
201  }
202 
203  @Override
204  public void reset()
205  {
206  m_currentState = 0;
207  }
208 
209  /**
210  * Produces a rendition of the machine as Graphviz input file.
211  * @param out The print stream to which the graph is written
212  */
213  public void toDot(PrintStream out)
214  {
215  out.println("digraph G {");
216  out.println(" node [style=filled];");
217  for (Map.Entry<Integer,Picker<? extends T>> e : m_pickers.entrySet())
218  {
219  out.print(" ");
220  out.print(e.getKey());
221  out.print(" [label=\"");
222  out.print(e.getValue().toString());
223  out.println("\"];");
224  }
225  for (Map.Entry<Integer,List<Transition>> e : m_transitions.entrySet())
226  {
227  for (Transition t : e.getValue())
228  {
229  out.print(" ");
230  out.print(e.getKey());
231  out.print(" -> ");
232  out.print(t.getDestination());
233  out.print(" [label=\"");
234  out.print(t.getProbability());
235  out.println("\"];");
236  }
237  }
238  out.println("}");
239  }
240 
241  /**
242  * Representation of a probabilistic transition in the state machine
243  */
244  public static class Transition
245  {
246  protected int m_destination;
247 
248  protected float m_probability;
249 
250  /**
251  * Creates a new transition
252  * @param destination The destination state
253  * @param probability The probability of taking this transition (between 0 and 1)
254  */
255  public Transition(int destination, float probability)
256  {
257  super();
258  m_destination = destination;
259  m_probability = probability;
260  }
261 
262  public int getDestination()
263  {
264  return m_destination;
265  }
266 
267  public float getProbability()
268  {
269  return m_probability;
270  }
271 
272  @Override
273  public String toString()
274  {
275  return "-> " + m_destination + " (" + m_probability + ")";
276  }
277  }
278 }
ca.uqac.lif.synthia.sequence.MarkovChain.toDot
void toDot(PrintStream out)
Produces a rendition of the machine as Graphviz input file.
Definition: MarkovChain.java:213
ca.uqac.lif.synthia.sequence.MarkovChain.m_currentState
int m_currentState
The index of the current state of the machine.
Definition: MarkovChain.java:52
ca.uqac.lif.synthia.Picker
Picks an object.
Definition: Picker.java:36
ca.uqac.lif.synthia.sequence.MarkovChain.add
MarkovChain< T > add(int state, Picker< T > p)
Associates a picker to a state.
Definition: MarkovChain.java:116
ca.uqac.lif.synthia.sequence.MarkovChain.pick
T pick()
Picks an object.
Definition: MarkovChain.java:136
ca.uqac.lif.synthia.sequence.MarkovChain.m_pickers
Map< Integer, Picker<? extends T > > m_pickers
The pickers associated to each state.
Definition: MarkovChain.java:57
ca.uqac.lif.synthia.sequence.MarkovChain.add
MarkovChain< T > add(int source, int destination, Number probability)
Adds a new transition to the machine.
Definition: MarkovChain.java:97
ca.uqac
ca.uqac.lif.synthia.sequence.MarkovChain.reset
void reset()
Puts the picker back into its initial state.
Definition: MarkovChain.java:204
ca.uqac.lif.synthia
Definition: Bounded.java:19
ca.uqac.lif.synthia.sequence.MarkovChain.m_floatSource
Picker< Float > m_floatSource
A source of numbers between 0 and 1.
Definition: MarkovChain.java:69
ca.uqac.lif.synthia.PickerException
Generic runtime exception thrown by the Picker interface.
Definition: PickerException.java:26
ca.uqac.lif.synthia.sequence.MarkovChain.duplicate
MarkovChain< T > duplicate(boolean with_state)
Creates a copy of the picker.
Definition: MarkovChain.java:190
ca.uqac.lif
ca.uqac.lif.synthia.sequence.MarkovChain.MarkovChain
MarkovChain(Picker< Float > float_source)
Creates a new empty Markov chain.
Definition: MarkovChain.java:81
ca
ca.uqac.lif.synthia.Picker.pick
T pick()
Picks an object.
ca.uqac.lif.synthia.sequence.MarkovChain
Generates a sequence of objects by a random walk in a Markov chain.
Definition: MarkovChain.java:47
ca.uqac.lif.synthia.sequence.MarkovChain.m_transitions
Map< Integer, List< Transition > > m_transitions
A map associating state numbers with the list of their outgoing transitions.
Definition: MarkovChain.java:63
ca.uqac.lif.synthia.sequence.MarkovChain.selectTransition
int selectTransition(float p)
From the current state, randomly selects a destination state based on the probabilities associated to...
Definition: MarkovChain.java:172
ca.uqac.lif.synthia.sequence.MarkovChain.exhaust
MarkovChain< T > exhaust(boolean b)
Sets whether to exhaust the picker associated to a state before transitioning to another state.
Definition: MarkovChain.java:129
ca.uqac.lif.synthia.sequence.MarkovChain.m_exhaust
boolean m_exhaust
Whether to exhaust each state before moving to another.
Definition: MarkovChain.java:74
ca.uqac.lif.synthia.Picker.reset
void reset()
Puts the picker back into its initial state.