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@184
|
23 #include <stdexcept>
|
Chris@181
|
24
|
Chris@188
|
25 //#define DEBUG_FEEDER 1
|
Chris@181
|
26
|
Chris@181
|
27 /**
|
Chris@181
|
28 * Take a series of observations or estimates (one at a time) and feed
|
Chris@181
|
29 * them to a set of agent hypotheses, creating a new candidate agent
|
Chris@181
|
30 * for each observation and also testing the observation against the
|
Chris@181
|
31 * existing set of hypotheses.
|
Chris@181
|
32 *
|
Chris@181
|
33 *!!! -- todo: document poly-ness of it
|
Chris@181
|
34 *
|
Chris@181
|
35 * Call feed() to provide a new observation. Call finish() when all
|
Chris@181
|
36 * observations have been provided. The set of hypotheses returned by
|
Chris@181
|
37 * getAcceptedHypotheses() will not be complete unless finish() has
|
Chris@181
|
38 * been called.
|
Chris@181
|
39 */
|
Chris@181
|
40 template <typename Hypothesis>
|
Chris@181
|
41 class AgentFeederPoly : public AgentFeeder
|
Chris@181
|
42 {
|
Chris@181
|
43 private:
|
Chris@184
|
44 typedef std::vector<Hypothesis> Hypotheses;
|
Chris@184
|
45
|
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@184
|
58 std::cerr << "\nfeed: have observation [value = " << o.value << ", time = " << o.time << "]" << std::endl;
|
Chris@181
|
59 #endif
|
Chris@181
|
60
|
Chris@184
|
61 m_state = 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@184
|
70 m_state.completed.push_back(*i);
|
Chris@181
|
71 }
|
Chris@181
|
72 }
|
Chris@181
|
73
|
Chris@187
|
74 std::set<Hypothesis> retrieveAcceptedHypotheses() {
|
Chris@184
|
75 std::set<Hypothesis> hs;
|
Chris@184
|
76 for (typename Hypotheses::const_iterator i = m_state.completed.begin();
|
Chris@184
|
77 i != m_state.completed.end(); ++i) {
|
Chris@190
|
78 Hypothesis h(*i);
|
Chris@190
|
79 #ifdef DEBUG_FEEDER
|
Chris@190
|
80 std::cerr << "returning accepted hypothesis: strengths: ";
|
Chris@190
|
81 for (typename Hypothesis::Observations::const_iterator j =
|
Chris@190
|
82 h.getAcceptedObservations().begin();
|
Chris@190
|
83 j != h.getAcceptedObservations().end(); ++j) {
|
Chris@190
|
84 std::cerr << j->confidence << " ";
|
Chris@190
|
85 }
|
Chris@190
|
86 std::cerr << std::endl;
|
Chris@190
|
87 #endif
|
Chris@190
|
88 hs.insert(h);
|
Chris@184
|
89 }
|
Chris@187
|
90 m_state.completed.clear();
|
Chris@184
|
91 return hs;
|
Chris@181
|
92 }
|
Chris@181
|
93
|
Chris@181
|
94 private:
|
Chris@184
|
95 State update(State s, AgentHypothesis::Observation o) {
|
Chris@181
|
96
|
Chris@181
|
97 /*
|
Chris@181
|
98 An observation can "belong" to any number of provisional
|
Chris@181
|
99 hypotheses, but only to one satisfied hypothesis.
|
Chris@181
|
100
|
Chris@181
|
101 A new observation is first offered to the hypotheses that
|
Chris@181
|
102 have already been satisfied. If one of these accepts it, it
|
Chris@181
|
103 gets to keep it and no other hypothesis can have it.
|
Chris@181
|
104
|
Chris@181
|
105 Any observation not accepted by a hypothesis in satisfied
|
Chris@181
|
106 state is then offered to the provisional hypotheses; any
|
Chris@181
|
107 number of these may accept it. Also, every observation that
|
Chris@181
|
108 belongs to no satisfied hypothesis is used as the first
|
Chris@181
|
109 observation in its own new hypothesis (regardless of how
|
Chris@181
|
110 many other provisional hypotheses have accepted it).
|
Chris@181
|
111
|
Chris@181
|
112 When a hypothesis subsequently becomes satisfied, all other
|
Chris@181
|
113 provisional hypotheses containing any of its observations
|
Chris@181
|
114 must be discarded.
|
Chris@181
|
115 */
|
Chris@181
|
116
|
Chris@184
|
117 State newState;
|
Chris@184
|
118
|
Chris@181
|
119 // We only ever add to the completed hypotheses, never remove
|
Chris@181
|
120 // anything from them. But we may remove from provisional (if
|
Chris@181
|
121 // rejected or transferred to satisfied) and satisfied (when
|
Chris@181
|
122 // completed).
|
Chris@184
|
123
|
Chris@184
|
124 newState.completed = s.completed;
|
Chris@181
|
125
|
Chris@181
|
126 bool swallowed = false;
|
Chris@181
|
127
|
Chris@181
|
128 for (typename Hypotheses::iterator i = s.satisfied.begin();
|
Chris@181
|
129 i != s.satisfied.end(); ++i) {
|
Chris@181
|
130
|
Chris@181
|
131 Hypothesis h = *i;
|
Chris@181
|
132
|
Chris@181
|
133 assert(h.getState() == Hypothesis::Satisfied);
|
Chris@181
|
134
|
Chris@181
|
135 if (swallowed) {
|
Chris@181
|
136
|
Chris@181
|
137 // An observation that has already been accepted by a
|
Chris@181
|
138 // hypothesis cannot be offered to any other, because
|
Chris@181
|
139 // it can only belong to one satisfied hypothesis. Any
|
Chris@181
|
140 // subsequent satisfied hypotheses are retained
|
Chris@181
|
141 // unchanged in our updated state. We can't test them
|
Chris@181
|
142 // for expiry, because the state is only updated when
|
Chris@181
|
143 // accept() is called.
|
Chris@181
|
144 //!!! That looks like a limitation in the Hypothesis API
|
Chris@184
|
145 newState.satisfied.push_back(h);
|
Chris@181
|
146
|
Chris@181
|
147 } else { // !swallowed
|
Chris@181
|
148
|
Chris@181
|
149 if (h.accept(o)) {
|
Chris@181
|
150 #ifdef DEBUG_FEEDER
|
Chris@181
|
151 std::cerr << "accepted by satisfied hypothesis " << &(*i) << ", state is " << h.getState() << std::endl;
|
Chris@181
|
152 #endif
|
Chris@181
|
153 swallowed = true;
|
Chris@184
|
154 newState.satisfied.push_back(h);
|
Chris@181
|
155 } else if (h.getState() == Hypothesis::Expired) {
|
Chris@184
|
156 newState.completed.push_back(h);
|
Chris@184
|
157 } else {
|
Chris@184
|
158 newState.satisfied.push_back(h);
|
Chris@181
|
159 }
|
Chris@181
|
160 }
|
Chris@181
|
161 }
|
Chris@181
|
162
|
Chris@181
|
163 if (swallowed) {
|
Chris@181
|
164
|
Chris@181
|
165 #ifdef DEBUG_FEEDER
|
Chris@181
|
166 std::cerr << "was swallowed by satisfied hypothesis" << std::endl;
|
Chris@181
|
167 #endif
|
Chris@181
|
168 // no provisional hypotheses have become satisfied, no new
|
Chris@181
|
169 // ones have been introduced
|
Chris@184
|
170 newState.provisional = s.provisional;
|
Chris@181
|
171
|
Chris@181
|
172 } else {
|
Chris@181
|
173
|
Chris@181
|
174 #ifdef DEBUG_FEEDER
|
Chris@181
|
175 std::cerr << "remained unswallowed by " << newState.satisfied.size() << " satisfied hypotheses" << std::endl;
|
Chris@181
|
176 #endif
|
Chris@181
|
177
|
Chris@181
|
178 // not swallowed by any satisfied hypothesis
|
Chris@181
|
179
|
Chris@181
|
180 Hypothesis promoted;
|
Chris@181
|
181
|
Chris@181
|
182 for (typename Hypotheses::iterator i = s.provisional.begin();
|
Chris@181
|
183 i != s.provisional.end(); ++i) {
|
Chris@181
|
184
|
Chris@181
|
185 Hypothesis h = *i;
|
Chris@181
|
186
|
Chris@181
|
187 assert(h.getState() == Hypothesis::Provisional);
|
Chris@181
|
188
|
Chris@181
|
189 // can only have one satisfied hypothesis for each
|
Chris@181
|
190 // observation, so try this only if promoted has not been
|
Chris@181
|
191 // set to something else yet
|
Chris@181
|
192 if (promoted == Hypothesis() &&
|
Chris@181
|
193 h.accept(o) &&
|
Chris@181
|
194 h.getState() == Hypothesis::Satisfied) {
|
Chris@184
|
195 newState.satisfied.push_back(h);
|
Chris@181
|
196 #ifdef DEBUG_FEEDER
|
Chris@181
|
197 std::cerr << "promoting a hypothesis to satisfied, have " << newState.satisfied.size() << " satisfied now" << std::endl;
|
Chris@181
|
198 #endif
|
Chris@181
|
199 promoted = h;
|
Chris@181
|
200 } else if (h.getState() != Hypothesis::Rejected) {
|
Chris@184
|
201 newState.provisional.push_back(h);
|
Chris@181
|
202 }
|
Chris@181
|
203 }
|
Chris@181
|
204
|
Chris@181
|
205 if (promoted == Hypothesis()) {
|
Chris@181
|
206
|
Chris@181
|
207 // No provisional hypothesis has become satisfied
|
Chris@181
|
208
|
Chris@181
|
209 Hypothesis h;
|
Chris@181
|
210 h.accept(o);
|
Chris@181
|
211
|
Chris@181
|
212 if (h.getState() == Hypothesis::Provisional) {
|
Chris@184
|
213 newState.provisional.push_back(h);
|
Chris@181
|
214 } else if (h.getState() == Hypothesis::Satisfied) {
|
Chris@184
|
215 newState.satisfied.push_back(h);
|
Chris@181
|
216 }
|
Chris@181
|
217
|
Chris@181
|
218 #ifdef DEBUG_FEEDER
|
Chris@181
|
219 std::cerr << "update: new hypothesis of state " << h.getState() << ", provisional count -> " << newState.provisional.size() << std::endl;
|
Chris@181
|
220 #endif
|
Chris@184
|
221 } else {
|
Chris@181
|
222
|
Chris@184
|
223 #ifdef DEBUG_FEEDER
|
Chris@184
|
224 std::cerr << "a hypothesis became satisfied, reaping its observations" << std::endl;
|
Chris@184
|
225 #endif
|
Chris@184
|
226 newState = reap(newState);
|
Chris@181
|
227 }
|
Chris@181
|
228 }
|
Chris@181
|
229
|
Chris@184
|
230 return newState;
|
Chris@181
|
231 }
|
Chris@181
|
232
|
Chris@184
|
233 State reap(State s) {
|
Chris@181
|
234
|
Chris@181
|
235 // "When a hypothesis subsequently becomes satisfied, all
|
Chris@181
|
236 // other provisional hypotheses containing any of its
|
Chris@181
|
237 // observations must be discarded."
|
Chris@181
|
238
|
Chris@184
|
239 if (s.provisional.empty()) return s;
|
Chris@181
|
240
|
Chris@181
|
241 int reaped = 0;
|
Chris@181
|
242
|
Chris@184
|
243 Hypotheses prior = s.provisional;
|
Chris@184
|
244 s.provisional = Hypotheses();
|
Chris@181
|
245
|
Chris@184
|
246 for (typename Hypotheses::const_iterator hi = prior.begin();
|
Chris@184
|
247 hi != prior.end(); ++hi) {
|
Chris@181
|
248
|
Chris@181
|
249 const AgentHypothesis::Observations obs =
|
Chris@181
|
250 hi->getAcceptedObservations();
|
Chris@181
|
251
|
Chris@181
|
252 bool keep = true;
|
Chris@181
|
253
|
Chris@181
|
254 for (AgentHypothesis::Observations::const_iterator oi = obs.begin();
|
Chris@181
|
255 oi != obs.end(); ++oi) {
|
Chris@181
|
256
|
Chris@181
|
257 for (typename Hypotheses::const_iterator si = s.satisfied.end();
|
Chris@181
|
258 si != s.satisfied.begin(); ) {
|
Chris@181
|
259
|
Chris@181
|
260 --si;
|
Chris@181
|
261
|
Chris@181
|
262 const AgentHypothesis::Observations sobs =
|
Chris@181
|
263 si->getAcceptedObservations();
|
Chris@181
|
264
|
Chris@181
|
265 if (sobs.find(*oi) != sobs.end()) {
|
Chris@181
|
266 keep = false;
|
Chris@181
|
267 break;
|
Chris@181
|
268 }
|
Chris@181
|
269 }
|
Chris@181
|
270
|
Chris@181
|
271 if (!keep) {
|
Chris@181
|
272 break;
|
Chris@181
|
273 }
|
Chris@181
|
274 }
|
Chris@181
|
275
|
Chris@184
|
276 if (keep) {
|
Chris@184
|
277 s.provisional.push_back(*hi);
|
Chris@184
|
278 } else {
|
Chris@181
|
279 ++reaped;
|
Chris@181
|
280 }
|
Chris@181
|
281 }
|
Chris@181
|
282
|
Chris@181
|
283 #ifdef DEBUG_FEEDER
|
Chris@181
|
284 std::cerr << "reap: have "
|
Chris@181
|
285 << s.satisfied.size() << " satisfied, "
|
Chris@181
|
286 << s.provisional.size() << " provisional, "
|
Chris@181
|
287 << s.completed.size() << " completed, reaped "
|
Chris@181
|
288 << reaped << std::endl;
|
Chris@181
|
289 #endif
|
Chris@184
|
290
|
Chris@184
|
291 return s;
|
Chris@181
|
292 }
|
Chris@181
|
293 };
|
Chris@181
|
294
|
Chris@181
|
295 #endif
|