annotate src/AgentFeederPoly.h @ 203:aac739775e29 noteagent

Rename file, add labels
author Chris Cannam
date Wed, 04 Jun 2014 12:12:56 +0100
parents c02db1f4680a
children
rev   line source
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