double weakBoundDistance(const VList & oldV, const VList & newV) { // Here we implement a weak bound (can also be seen in Cassandra's code) // This is mostly because a strong bound is more costly (it requires performing // multiple LPs) and also the code at the moment does not support it cleanly, so // I prefer waiting until I have a good implementation of an LP class that hides // complexity from here. // // The logic of the weak bound is the following: the variation between the old // VList and the new one is equal to the maximum distance between a ValueFunction // in the old VList with its closest match in the new VList. So the farthest from // closest. // // We define distance between two ValueFunctions as the maximum between their // element-wise difference. if ( !oldV.size() ) return 0.0; double distance = 0.0; for ( const auto & newVE : newV ) { // Initialize closest distance for newVE as infinity double closestDistance = std::numeric_limits<double>::infinity(); for ( const auto & oldVE : oldV ) { // Compute the distance, we pick the max double distance = (newVE.values - oldVE.values).cwiseAbs().maxCoeff(); // Keep the closest, we pick the min closestDistance = std::min(closestDistance, distance); } // Keep the maximum distance between a new VList and its closest old VList distance = std::max(distance, closestDistance); } return distance; }
VList IncrementalPruning::crossSum(const VList & l1, const VList & l2, size_t a, bool order) { VList c; if ( ! ( l1.size() && l2.size() ) ) return c; // We can get the sizes of the observation vectors // outside since all VEntries for our input VLists // are guaranteed to be sized equally. const auto O1size = std::get<OBS>(l1[0]).size(); const auto O2size = std::get<OBS>(l2[0]).size(); for ( const auto & v1 : l1 ) { auto O1begin = std::begin(std::get<OBS>(v1)); auto O1end = std::end (std::get<OBS>(v1)); for ( const auto & v2 : l2 ) { auto O2begin = std::begin(std::get<OBS>(v2)); auto O2end = std::end (std::get<OBS>(v2)); // Cross sum auto v = std::get<VALUES>(v1) + std::get<VALUES>(v2); // This step now depends on which order the two lists // are. This function is only used in this class, so we // know that the two lists are "adjacent"; however one // is after the other. `order` tells us which one comes // first, and we join the observation vectors accordingly. VObs obs; obs.reserve(O1size + O2size); if ( order ) { obs.insert(std::end(obs),O1begin, O1end); obs.insert(std::end(obs),O2begin, O2end); } else { obs.insert(std::end(obs),O2begin, O2end); obs.insert(std::end(obs),O1begin, O1end); } c.emplace_back(std::move(v), a, std::move(obs)); } } return c; }