#include "projectionhistory.h" #include <algorithm> #include <cmath> #include "mp.h" #include "numericrange.h" ProjectionHistory::ProjectionHistory(const arma::mat &X, const arma::uvec &cpIndices) : m_type(ObserverCurrent) , m_X(X) , m_cpIndices(cpIndices) , m_rpIndices(X.n_rows - cpIndices.n_elem) , m_cpSelectionEmpty(true) , m_rpSelectionEmpty(true) , m_values(X.n_rows) , m_firstValues(X.n_rows) , m_prevValues(X.n_rows) , m_hasFirst(false) , m_hasPrev(false) { m_distX = mp::dist(m_X); NumericRange<arma::uword> allIndices(0, m_X.n_rows); std::set_symmetric_difference(allIndices.cbegin(), allIndices.cend(), m_cpIndices.cbegin(), m_cpIndices.cend(), m_rpIndices.begin()); computeAlphas(); } void ProjectionHistory::computeAlphas() { m_influences.set_size(m_X.n_rows); m_alphas.set_size(m_rpIndices.n_elem, m_cpIndices.n_elem); for (arma::uword i = 0; i < m_rpIndices.n_elem; i++) { double sum = 0; const arma::rowvec &x = m_X.row(m_rpIndices[i]); for (arma::uword j = 0; j < m_cpIndices.n_elem; j++) { double norm = arma::norm(x - m_X.row(m_cpIndices[j])); m_alphas(i, j) = 1.0 / std::max(norm * norm, 1e-6); sum += m_alphas(i, j); } for (arma::uword j = 0; j < m_cpIndices.n_elem; j++) { m_alphas(i, j) /= sum; } } } void ProjectionHistory::undo() { if (m_hasPrev) { m_hasPrev = false; m_Y = m_prevY; m_distY = m_prevDistY; m_values = m_prevValues; updateUnreliability(); undoPerformed(); currentMapChanged(m_Y); if (m_cpSelectionEmpty && m_rpSelectionEmpty) { emitValuesChanged(); } } } void ProjectionHistory::reset() { if (m_hasFirst) { m_hasPrev = false; m_Y = m_firstY; m_distY = m_firstDistY; m_values = m_firstValues; updateUnreliability(); resetPerformed(); currentMapChanged(m_Y); if (m_cpSelectionEmpty && m_rpSelectionEmpty) { emitValuesChanged(); } } } void ProjectionHistory::addMap(const arma::mat &Y) { if (m_hasFirst) { m_hasPrev = true; m_prevY = m_Y; m_prevDistY = m_distY; m_prevValues = m_values; } m_Y = Y; m_distY = mp::dist(Y); updateUnreliability(); mp::aggregatedError(m_distX, m_distY, m_values); // qDebug("Aggr. error: min: %f, max: %f", m_values.min(), m_values.max()); if (!m_hasFirst) { m_hasFirst = true; m_firstY = m_Y; m_firstDistY = m_distY; m_firstValues = m_values; m_selection.assign(m_values.n_elem, false); } currentMapChanged(m_Y); if (m_cpSelectionEmpty && m_rpSelectionEmpty) { emitValuesChanged(); } } bool ProjectionHistory::setType(ObserverType type) { if (m_type == type) { return true; } if ((type == ObserverDiffPrevious && !m_hasPrev) || (type == ObserverDiffFirst && !m_hasFirst)) { return false; } m_type = type; if (!m_cpSelectionEmpty || !m_rpSelectionEmpty) { // We changed our type, but cannot emit values since we have non-empty // selections return true; } return emitValuesChanged(); } void ProjectionHistory::setCPSelection(const std::vector<bool> &cpSelection) { if (cpSelection.size() != m_cpIndices.n_elem) { return; } m_cpSelection.clear(); for (int i = 0; i < cpSelection.size(); i++) { m_selection[m_cpIndices[i]] = cpSelection[i]; if (cpSelection[i]) { m_cpSelection.push_back(i); } } m_cpSelectionEmpty = m_cpSelection.empty(); cpSelectionPostProcess(); selectionChanged(m_selection); } void ProjectionHistory::cpSelectionPostProcess() { if (!m_cpSelectionEmpty) { // compute the influence of CP selection on each RP for (arma::uword rp = 0; rp < m_rpIndices.n_elem; rp++) { m_influences[m_rpIndices[rp]] = 0; const arma::rowvec &row = m_alphas.row(rp); for (auto cp: m_cpSelection) { m_influences[m_rpIndices[rp]] += row[cp]; } } rpValuesChanged(m_influences(m_rpIndices), true); } else { emitValuesChanged(); } // TODO: emit cpSelectionChanged()? } void ProjectionHistory::setRPSelection(const std::vector<bool> &rpSelection) { if (rpSelection.size() != m_rpIndices.n_elem) { return; } m_rpSelection.clear(); for (int i = 0; i < rpSelection.size(); i++) { m_selection[m_rpIndices[i]] = rpSelection[i]; if (rpSelection[i]) { m_rpSelection.push_back(i); } } m_rpSelectionEmpty = m_rpSelection.empty(); rpSelectionPostProcess(); selectionChanged(m_selection); } void ProjectionHistory::rpSelectionPostProcess() { if (!m_rpSelectionEmpty) { // compute how influent is each CP on RP selection for (arma::uword cp = 0; cp < m_cpIndices.n_elem; cp++) { m_influences[m_cpIndices[cp]] = 0; for (auto rp: m_rpSelection) { m_influences[m_cpIndices[cp]] += m_alphas(rp, cp); } } cpValuesChanged(m_influences(m_cpIndices), true); } else { cpValuesChanged(arma::vec(), false); } // TODO: emit rpSelectionChanged()? } void ProjectionHistory::setSelection(const std::vector<bool> &selection) { m_selection = selection; m_rpSelection.clear(); for (auto i: m_rpIndices) { if (m_selection[i]) { m_rpSelection.push_back(i); } } m_rpSelectionEmpty = m_rpSelection.empty(); rpSelectionPostProcess(); m_cpSelection.clear(); for (auto i: m_cpIndices) { if (m_selection[i]) { m_cpSelection.push_back(i); } } m_cpSelectionEmpty = m_cpSelection.empty(); cpSelectionPostProcess(); selectionChanged(m_selection); } bool ProjectionHistory::emitValuesChanged() const { switch (m_type) { case ObserverCurrent: rpValuesChanged(m_values(m_rpIndices), false); valuesChanged(m_values, false); return true; case ObserverDiffPrevious: if (m_hasPrev) { arma::vec diff = m_values - m_prevValues; rpValuesChanged(diff(m_rpIndices), true); valuesChanged(diff, false); return true; } return false; case ObserverDiffFirst: if (m_hasFirst) { arma::vec diff = m_values - m_firstValues; rpValuesChanged(diff(m_rpIndices), true); valuesChanged(diff, true); return true; } return false; default: return false; } } void ProjectionHistory::setRewind(double t) { if (!m_hasPrev) { return; } arma::mat Y = m_Y * t + m_prevY * (1.0 - t); mapRewound(Y); if (!m_cpSelectionEmpty || !m_rpSelectionEmpty) { return; } arma::vec values = m_values * t + m_prevValues * (1.0 - t); // cpValuesRewound(values(m_cpIndices)); rpValuesRewound(values(m_rpIndices)); valuesRewound(values); } void ProjectionHistory::updateUnreliability() { m_unreliability.copy_size(m_alphas); m_unreliability = m_alphas % m_distY(m_rpIndices, m_cpIndices); }