annotate src/AgentFeederPoly.h @ 187:1697457458b7 noteagent

Don't retain returned hypotheses -- to avoid having to test whether we've seen them before
author Chris Cannam
date Wed, 28 May 2014 15:16:00 +0100
parents 9b9cdfccbd14
children 462b165c8c0f
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@184 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@184 78 hs.insert(*i);
Chris@184 79 }
Chris@187 80 m_state.completed.clear();
Chris@184 81 return hs;
Chris@181 82 }
Chris@181 83
Chris@181 84 private:
Chris@184 85 State update(State s, AgentHypothesis::Observation o) {
Chris@181 86
Chris@181 87 /*
Chris@181 88 An observation can "belong" to any number of provisional
Chris@181 89 hypotheses, but only to one satisfied hypothesis.
Chris@181 90
Chris@181 91 A new observation is first offered to the hypotheses that
Chris@181 92 have already been satisfied. If one of these accepts it, it
Chris@181 93 gets to keep it and no other hypothesis can have it.
Chris@181 94
Chris@181 95 Any observation not accepted by a hypothesis in satisfied
Chris@181 96 state is then offered to the provisional hypotheses; any
Chris@181 97 number of these may accept it. Also, every observation that
Chris@181 98 belongs to no satisfied hypothesis is used as the first
Chris@181 99 observation in its own new hypothesis (regardless of how
Chris@181 100 many other provisional hypotheses have accepted it).
Chris@181 101
Chris@181 102 When a hypothesis subsequently becomes satisfied, all other
Chris@181 103 provisional hypotheses containing any of its observations
Chris@181 104 must be discarded.
Chris@181 105 */
Chris@181 106
Chris@184 107 State newState;
Chris@184 108
Chris@181 109 // We only ever add to the completed hypotheses, never remove
Chris@181 110 // anything from them. But we may remove from provisional (if
Chris@181 111 // rejected or transferred to satisfied) and satisfied (when
Chris@181 112 // completed).
Chris@184 113
Chris@184 114 newState.completed = s.completed;
Chris@181 115
Chris@181 116 bool swallowed = false;
Chris@181 117
Chris@181 118 for (typename Hypotheses::iterator i = s.satisfied.begin();
Chris@181 119 i != s.satisfied.end(); ++i) {
Chris@181 120
Chris@181 121 Hypothesis h = *i;
Chris@181 122
Chris@181 123 assert(h.getState() == Hypothesis::Satisfied);
Chris@181 124
Chris@181 125 if (swallowed) {
Chris@181 126
Chris@181 127 // An observation that has already been accepted by a
Chris@181 128 // hypothesis cannot be offered to any other, because
Chris@181 129 // it can only belong to one satisfied hypothesis. Any
Chris@181 130 // subsequent satisfied hypotheses are retained
Chris@181 131 // unchanged in our updated state. We can't test them
Chris@181 132 // for expiry, because the state is only updated when
Chris@181 133 // accept() is called.
Chris@181 134 //!!! That looks like a limitation in the Hypothesis API
Chris@184 135 newState.satisfied.push_back(h);
Chris@181 136
Chris@181 137 } else { // !swallowed
Chris@181 138
Chris@181 139 if (h.accept(o)) {
Chris@181 140 #ifdef DEBUG_FEEDER
Chris@181 141 std::cerr << "accepted by satisfied hypothesis " << &(*i) << ", state is " << h.getState() << std::endl;
Chris@181 142 #endif
Chris@181 143 swallowed = true;
Chris@184 144 newState.satisfied.push_back(h);
Chris@181 145 } else if (h.getState() == Hypothesis::Expired) {
Chris@184 146 newState.completed.push_back(h);
Chris@184 147 } else {
Chris@184 148 newState.satisfied.push_back(h);
Chris@181 149 }
Chris@181 150 }
Chris@181 151 }
Chris@181 152
Chris@181 153 if (swallowed) {
Chris@181 154
Chris@181 155 #ifdef DEBUG_FEEDER
Chris@181 156 std::cerr << "was swallowed by satisfied hypothesis" << std::endl;
Chris@181 157 #endif
Chris@181 158 // no provisional hypotheses have become satisfied, no new
Chris@181 159 // ones have been introduced
Chris@184 160 newState.provisional = s.provisional;
Chris@181 161
Chris@181 162 } else {
Chris@181 163
Chris@181 164 #ifdef DEBUG_FEEDER
Chris@181 165 std::cerr << "remained unswallowed by " << newState.satisfied.size() << " satisfied hypotheses" << std::endl;
Chris@181 166 #endif
Chris@181 167
Chris@181 168 // not swallowed by any satisfied hypothesis
Chris@181 169
Chris@181 170 Hypothesis promoted;
Chris@181 171
Chris@181 172 for (typename Hypotheses::iterator i = s.provisional.begin();
Chris@181 173 i != s.provisional.end(); ++i) {
Chris@181 174
Chris@181 175 Hypothesis h = *i;
Chris@181 176
Chris@181 177 assert(h.getState() == Hypothesis::Provisional);
Chris@181 178
Chris@181 179 // can only have one satisfied hypothesis for each
Chris@181 180 // observation, so try this only if promoted has not been
Chris@181 181 // set to something else yet
Chris@181 182 if (promoted == Hypothesis() &&
Chris@181 183 h.accept(o) &&
Chris@181 184 h.getState() == Hypothesis::Satisfied) {
Chris@184 185 newState.satisfied.push_back(h);
Chris@181 186 #ifdef DEBUG_FEEDER
Chris@181 187 std::cerr << "promoting a hypothesis to satisfied, have " << newState.satisfied.size() << " satisfied now" << std::endl;
Chris@181 188 #endif
Chris@181 189 promoted = h;
Chris@181 190 } else if (h.getState() != Hypothesis::Rejected) {
Chris@184 191 newState.provisional.push_back(h);
Chris@181 192 }
Chris@181 193 }
Chris@181 194
Chris@181 195 if (promoted == Hypothesis()) {
Chris@181 196
Chris@181 197 // No provisional hypothesis has become satisfied
Chris@181 198
Chris@181 199 Hypothesis h;
Chris@181 200 h.accept(o);
Chris@181 201
Chris@181 202 if (h.getState() == Hypothesis::Provisional) {
Chris@184 203 newState.provisional.push_back(h);
Chris@181 204 } else if (h.getState() == Hypothesis::Satisfied) {
Chris@184 205 newState.satisfied.push_back(h);
Chris@181 206 }
Chris@181 207
Chris@181 208 #ifdef DEBUG_FEEDER
Chris@181 209 std::cerr << "update: new hypothesis of state " << h.getState() << ", provisional count -> " << newState.provisional.size() << std::endl;
Chris@181 210 #endif
Chris@184 211 } else {
Chris@181 212
Chris@184 213 #ifdef DEBUG_FEEDER
Chris@184 214 std::cerr << "a hypothesis became satisfied, reaping its observations" << std::endl;
Chris@184 215 #endif
Chris@184 216 newState = reap(newState);
Chris@181 217 }
Chris@181 218 }
Chris@181 219
Chris@184 220 return newState;
Chris@181 221 }
Chris@181 222
Chris@184 223 State reap(State s) {
Chris@181 224
Chris@181 225 // "When a hypothesis subsequently becomes satisfied, all
Chris@181 226 // other provisional hypotheses containing any of its
Chris@181 227 // observations must be discarded."
Chris@181 228
Chris@184 229 if (s.provisional.empty()) return s;
Chris@181 230
Chris@181 231 int reaped = 0;
Chris@181 232
Chris@184 233 Hypotheses prior = s.provisional;
Chris@184 234 s.provisional = Hypotheses();
Chris@181 235
Chris@184 236 for (typename Hypotheses::const_iterator hi = prior.begin();
Chris@184 237 hi != prior.end(); ++hi) {
Chris@181 238
Chris@181 239 const AgentHypothesis::Observations obs =
Chris@181 240 hi->getAcceptedObservations();
Chris@181 241
Chris@181 242 bool keep = true;
Chris@181 243
Chris@181 244 for (AgentHypothesis::Observations::const_iterator oi = obs.begin();
Chris@181 245 oi != obs.end(); ++oi) {
Chris@181 246
Chris@181 247 for (typename Hypotheses::const_iterator si = s.satisfied.end();
Chris@181 248 si != s.satisfied.begin(); ) {
Chris@181 249
Chris@181 250 --si;
Chris@181 251
Chris@181 252 const AgentHypothesis::Observations sobs =
Chris@181 253 si->getAcceptedObservations();
Chris@181 254
Chris@181 255 if (sobs.find(*oi) != sobs.end()) {
Chris@181 256 keep = false;
Chris@181 257 break;
Chris@181 258 }
Chris@181 259 }
Chris@181 260
Chris@181 261 if (!keep) {
Chris@181 262 break;
Chris@181 263 }
Chris@181 264 }
Chris@181 265
Chris@184 266 if (keep) {
Chris@184 267 s.provisional.push_back(*hi);
Chris@184 268 } else {
Chris@181 269 ++reaped;
Chris@181 270 }
Chris@181 271 }
Chris@181 272
Chris@181 273 #ifdef DEBUG_FEEDER
Chris@181 274 std::cerr << "reap: have "
Chris@181 275 << s.satisfied.size() << " satisfied, "
Chris@181 276 << s.provisional.size() << " provisional, "
Chris@181 277 << s.completed.size() << " completed, reaped "
Chris@181 278 << reaped << std::endl;
Chris@181 279 #endif
Chris@184 280
Chris@184 281 return s;
Chris@181 282 }
Chris@181 283 };
Chris@181 284
Chris@181 285 #endif