Chris@181
|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
Chris@181
|
2
|
Chris@181
|
3 /*
|
Chris@181
|
4 Silvet
|
Chris@181
|
5
|
Chris@181
|
6 A Vamp plugin for note transcription.
|
Chris@181
|
7 Centre for Digital Music, Queen Mary University of London.
|
Chris@181
|
8 This file Copyright 2012 Chris Cannam.
|
Chris@181
|
9
|
Chris@181
|
10 This program is free software; you can redistribute it and/or
|
Chris@181
|
11 modify it under the terms of the GNU General Public License as
|
Chris@181
|
12 published by the Free Software Foundation; either version 2 of the
|
Chris@181
|
13 License, or (at your option) any later version. See the file
|
Chris@181
|
14 COPYING included with this distribution for more information.
|
Chris@181
|
15 */
|
Chris@181
|
16
|
Chris@181
|
17 #ifndef AGENT_FEEDER_POLY_H
|
Chris@181
|
18 #define AGENT_FEEDER_POLY_H
|
Chris@181
|
19
|
Chris@181
|
20 #include "AgentFeeder.h"
|
Chris@181
|
21
|
Chris@181
|
22 #include <cassert>
|
Chris@181
|
23
|
Chris@181
|
24 //#define DEBUG_FEEDER 1
|
Chris@181
|
25
|
Chris@181
|
26 /**
|
Chris@181
|
27 * Take a series of observations or estimates (one at a time) and feed
|
Chris@181
|
28 * them to a set of agent hypotheses, creating a new candidate agent
|
Chris@181
|
29 * for each observation and also testing the observation against the
|
Chris@181
|
30 * existing set of hypotheses.
|
Chris@181
|
31 *
|
Chris@181
|
32 *!!! -- todo: document poly-ness of it
|
Chris@181
|
33 *
|
Chris@181
|
34 * Call feed() to provide a new observation. Call finish() when all
|
Chris@181
|
35 * observations have been provided. The set of hypotheses returned by
|
Chris@181
|
36 * getAcceptedHypotheses() will not be complete unless finish() has
|
Chris@181
|
37 * been called.
|
Chris@181
|
38 */
|
Chris@181
|
39 template <typename Hypothesis>
|
Chris@181
|
40 class AgentFeederPoly : public AgentFeeder
|
Chris@181
|
41 {
|
Chris@181
|
42 public:
|
Chris@181
|
43 typedef std::set<Hypothesis> Hypotheses;
|
Chris@181
|
44
|
Chris@181
|
45 private:
|
Chris@181
|
46 struct State {
|
Chris@181
|
47 Hypotheses provisional;
|
Chris@181
|
48 Hypotheses satisfied;
|
Chris@181
|
49 Hypotheses completed;
|
Chris@181
|
50 };
|
Chris@181
|
51 State m_state;
|
Chris@181
|
52
|
Chris@181
|
53 public:
|
Chris@181
|
54 AgentFeederPoly() { }
|
Chris@181
|
55
|
Chris@181
|
56 virtual void feed(AgentHypothesis::Observation o) {
|
Chris@181
|
57 #ifdef DEBUG_FEEDER
|
Chris@181
|
58 std::cerr << "feed: have observation [value = " << o.value << ", time = " << o.time << "]" << std::endl;
|
Chris@181
|
59 #endif
|
Chris@181
|
60
|
Chris@181
|
61 update(m_state, o);
|
Chris@181
|
62 }
|
Chris@181
|
63
|
Chris@181
|
64 virtual void finish() {
|
Chris@181
|
65 #ifdef DEBUG_FEEDER
|
Chris@181
|
66 std::cerr << "finish: satisfied count == " << m_state.satisfied.size() << std::endl;
|
Chris@181
|
67 #endif
|
Chris@181
|
68 for (typename Hypotheses::const_iterator i = m_state.satisfied.begin();
|
Chris@181
|
69 i != m_state.satisfied.end(); ++i) {
|
Chris@181
|
70 m_state.completed.insert(*i);
|
Chris@181
|
71 }
|
Chris@181
|
72 }
|
Chris@181
|
73
|
Chris@181
|
74 Hypotheses getAcceptedHypotheses() const {
|
Chris@181
|
75 return m_state.completed;
|
Chris@181
|
76 }
|
Chris@181
|
77
|
Chris@181
|
78 private:
|
Chris@181
|
79 void update(State &s, AgentHypothesis::Observation o) {
|
Chris@181
|
80
|
Chris@181
|
81 /*
|
Chris@181
|
82 An observation can "belong" to any number of provisional
|
Chris@181
|
83 hypotheses, but only to one satisfied hypothesis.
|
Chris@181
|
84
|
Chris@181
|
85 A new observation is first offered to the hypotheses that
|
Chris@181
|
86 have already been satisfied. If one of these accepts it, it
|
Chris@181
|
87 gets to keep it and no other hypothesis can have it.
|
Chris@181
|
88
|
Chris@181
|
89 Any observation not accepted by a hypothesis in satisfied
|
Chris@181
|
90 state is then offered to the provisional hypotheses; any
|
Chris@181
|
91 number of these may accept it. Also, every observation that
|
Chris@181
|
92 belongs to no satisfied hypothesis is used as the first
|
Chris@181
|
93 observation in its own new hypothesis (regardless of how
|
Chris@181
|
94 many other provisional hypotheses have accepted it).
|
Chris@181
|
95
|
Chris@181
|
96 When a hypothesis subsequently becomes satisfied, all other
|
Chris@181
|
97 provisional hypotheses containing any of its observations
|
Chris@181
|
98 must be discarded.
|
Chris@181
|
99 */
|
Chris@181
|
100
|
Chris@181
|
101 // We only ever add to the completed hypotheses, never remove
|
Chris@181
|
102 // anything from them. But we may remove from provisional (if
|
Chris@181
|
103 // rejected or transferred to satisfied) and satisfied (when
|
Chris@181
|
104 // completed).
|
Chris@181
|
105
|
Chris@181
|
106 Hypotheses toCompleted;
|
Chris@181
|
107 Hypotheses toSatisfied;
|
Chris@181
|
108 Hypotheses toProvisional;
|
Chris@181
|
109
|
Chris@181
|
110 bool swallowed = false;
|
Chris@181
|
111
|
Chris@181
|
112 for (typename Hypotheses::iterator i = s.satisfied.begin();
|
Chris@181
|
113 i != s.satisfied.end(); ++i) {
|
Chris@181
|
114
|
Chris@181
|
115 Hypothesis h = *i;
|
Chris@181
|
116
|
Chris@181
|
117 assert(h.getState() == Hypothesis::Satisfied);
|
Chris@181
|
118
|
Chris@181
|
119 if (swallowed) {
|
Chris@181
|
120
|
Chris@181
|
121 // An observation that has already been accepted by a
|
Chris@181
|
122 // hypothesis cannot be offered to any other, because
|
Chris@181
|
123 // it can only belong to one satisfied hypothesis. Any
|
Chris@181
|
124 // subsequent satisfied hypotheses are retained
|
Chris@181
|
125 // unchanged in our updated state. We can't test them
|
Chris@181
|
126 // for expiry, because the state is only updated when
|
Chris@181
|
127 // accept() is called.
|
Chris@181
|
128 //!!! That looks like a limitation in the Hypothesis API
|
Chris@181
|
129
|
Chris@181
|
130 } else { // !swallowed
|
Chris@181
|
131
|
Chris@181
|
132 if (h.accept(o)) {
|
Chris@181
|
133 #ifdef DEBUG_FEEDER
|
Chris@181
|
134 std::cerr << "accepted by satisfied hypothesis " << &(*i) << ", state is " << h.getState() << std::endl;
|
Chris@181
|
135 #endif
|
Chris@181
|
136 swallowed = true;
|
Chris@181
|
137 } else if (h.getState() == Hypothesis::Expired) {
|
Chris@181
|
138 toCompleted.insert(h);
|
Chris@181
|
139 }
|
Chris@181
|
140 }
|
Chris@181
|
141 }
|
Chris@181
|
142
|
Chris@181
|
143 if (swallowed) {
|
Chris@181
|
144
|
Chris@181
|
145 #ifdef DEBUG_FEEDER
|
Chris@181
|
146 std::cerr << "was swallowed by satisfied hypothesis" << std::endl;
|
Chris@181
|
147 #endif
|
Chris@181
|
148 // no provisional hypotheses have become satisfied, no new
|
Chris@181
|
149 // ones have been introduced
|
Chris@181
|
150
|
Chris@181
|
151 } else {
|
Chris@181
|
152
|
Chris@181
|
153 #ifdef DEBUG_FEEDER
|
Chris@181
|
154 std::cerr << "remained unswallowed by " << newState.satisfied.size() << " satisfied hypotheses" << std::endl;
|
Chris@181
|
155 #endif
|
Chris@181
|
156
|
Chris@181
|
157 // not swallowed by any satisfied hypothesis
|
Chris@181
|
158
|
Chris@181
|
159 Hypothesis promoted;
|
Chris@181
|
160
|
Chris@181
|
161 for (typename Hypotheses::iterator i = s.provisional.begin();
|
Chris@181
|
162 i != s.provisional.end(); ++i) {
|
Chris@181
|
163
|
Chris@181
|
164 Hypothesis h = *i;
|
Chris@181
|
165
|
Chris@181
|
166 assert(h.getState() == Hypothesis::Provisional);
|
Chris@181
|
167
|
Chris@181
|
168 // can only have one satisfied hypothesis for each
|
Chris@181
|
169 // observation, so try this only if promoted has not been
|
Chris@181
|
170 // set to something else yet
|
Chris@181
|
171 if (promoted == Hypothesis() &&
|
Chris@181
|
172 h.accept(o) &&
|
Chris@181
|
173 h.getState() == Hypothesis::Satisfied) {
|
Chris@181
|
174 toSatisfied.insert(h);
|
Chris@181
|
175 #ifdef DEBUG_FEEDER
|
Chris@181
|
176 std::cerr << "promoting a hypothesis to satisfied, have " << newState.satisfied.size() << " satisfied now" << std::endl;
|
Chris@181
|
177 #endif
|
Chris@181
|
178 promoted = h;
|
Chris@181
|
179 } else if (h.getState() != Hypothesis::Rejected) {
|
Chris@181
|
180 // leave as provisional
|
Chris@181
|
181 }
|
Chris@181
|
182 }
|
Chris@181
|
183
|
Chris@181
|
184 if (promoted == Hypothesis()) {
|
Chris@181
|
185
|
Chris@181
|
186 // No provisional hypothesis has become satisfied
|
Chris@181
|
187
|
Chris@181
|
188 Hypothesis h;
|
Chris@181
|
189 h.accept(o);
|
Chris@181
|
190
|
Chris@181
|
191 if (h.getState() == Hypothesis::Provisional) {
|
Chris@181
|
192 toProvisional.insert(h);
|
Chris@181
|
193 } else if (h.getState() == Hypothesis::Satisfied) {
|
Chris@181
|
194 toSatisfied.insert(h);
|
Chris@181
|
195 }
|
Chris@181
|
196
|
Chris@181
|
197 #ifdef DEBUG_FEEDER
|
Chris@181
|
198 std::cerr << "update: new hypothesis of state " << h.getState() << ", provisional count -> " << newState.provisional.size() << std::endl;
|
Chris@181
|
199 #endif
|
Chris@181
|
200
|
Chris@181
|
201 }
|
Chris@181
|
202 }
|
Chris@181
|
203
|
Chris@181
|
204 for (typename Hypotheses::const_iterator i = toCompleted.begin();
|
Chris@181
|
205 i != toCompleted.end(); ++i) {
|
Chris@181
|
206 s.satisfied.erase(*i);
|
Chris@181
|
207 s.completed.insert(*i);
|
Chris@181
|
208 }
|
Chris@181
|
209 for (typename Hypotheses::const_iterator i = toSatisfied.begin();
|
Chris@181
|
210 i != toSatisfied.end(); ++i) {
|
Chris@181
|
211 s.provisional.erase(*i);
|
Chris@181
|
212 s.satisfied.insert(*i);
|
Chris@181
|
213 }
|
Chris@181
|
214 for (typename Hypotheses::const_iterator i = toProvisional.begin();
|
Chris@181
|
215 i != toProvisional.end(); ++i) {
|
Chris@181
|
216 s.provisional.insert(*i);
|
Chris@181
|
217 }
|
Chris@181
|
218
|
Chris@181
|
219 reap(s);
|
Chris@181
|
220 }
|
Chris@181
|
221
|
Chris@181
|
222 void reap(State &s) {
|
Chris@181
|
223
|
Chris@181
|
224 // "When a hypothesis subsequently becomes satisfied, all
|
Chris@181
|
225 // other provisional hypotheses containing any of its
|
Chris@181
|
226 // observations must be discarded."
|
Chris@181
|
227
|
Chris@181
|
228 if (s.provisional.empty()) return;
|
Chris@181
|
229
|
Chris@181
|
230 int reaped = 0;
|
Chris@181
|
231
|
Chris@181
|
232 Hypotheses toRemove = Hypotheses();;
|
Chris@181
|
233
|
Chris@181
|
234 for (typename Hypotheses::const_iterator hi = s.provisional.begin();
|
Chris@181
|
235 hi != s.provisional.end(); ++hi) {
|
Chris@181
|
236
|
Chris@181
|
237 const AgentHypothesis::Observations obs =
|
Chris@181
|
238 hi->getAcceptedObservations();
|
Chris@181
|
239
|
Chris@181
|
240 bool keep = true;
|
Chris@181
|
241
|
Chris@181
|
242 for (AgentHypothesis::Observations::const_iterator oi = obs.begin();
|
Chris@181
|
243 oi != obs.end(); ++oi) {
|
Chris@181
|
244
|
Chris@181
|
245 for (typename Hypotheses::const_iterator si = s.satisfied.end();
|
Chris@181
|
246 si != s.satisfied.begin(); ) {
|
Chris@181
|
247
|
Chris@181
|
248 --si;
|
Chris@181
|
249
|
Chris@181
|
250 const AgentHypothesis::Observations sobs =
|
Chris@181
|
251 si->getAcceptedObservations();
|
Chris@181
|
252
|
Chris@181
|
253 if (sobs.find(*oi) != sobs.end()) {
|
Chris@181
|
254 keep = false;
|
Chris@181
|
255 break;
|
Chris@181
|
256 }
|
Chris@181
|
257 }
|
Chris@181
|
258
|
Chris@181
|
259 if (!keep) {
|
Chris@181
|
260 break;
|
Chris@181
|
261 }
|
Chris@181
|
262 }
|
Chris@181
|
263
|
Chris@181
|
264 if (!keep) {
|
Chris@181
|
265 toRemove.insert(*hi);
|
Chris@181
|
266 ++reaped;
|
Chris@181
|
267 }
|
Chris@181
|
268 }
|
Chris@181
|
269
|
Chris@181
|
270 for (typename Hypotheses::const_iterator i = toRemove.begin();
|
Chris@181
|
271 i != toRemove.end(); ++i) {
|
Chris@181
|
272 s.provisional.erase(i);
|
Chris@181
|
273 }
|
Chris@181
|
274
|
Chris@181
|
275 #ifdef DEBUG_FEEDER
|
Chris@181
|
276 std::cerr << "reap: have "
|
Chris@181
|
277 << s.satisfied.size() << " satisfied, "
|
Chris@181
|
278 << s.provisional.size() << " provisional, "
|
Chris@181
|
279 << s.completed.size() << " completed, reaped "
|
Chris@181
|
280 << reaped << std::endl;
|
Chris@181
|
281 #endif
|
Chris@181
|
282 }
|
Chris@181
|
283 };
|
Chris@181
|
284
|
Chris@181
|
285 #endif
|