aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSamuel Fadel <samuelfadel@gmail.com>2015-05-22 18:40:07 -0300
committerSamuel Fadel <samuelfadel@gmail.com>2015-05-22 18:40:07 -0300
commit6a7b60784a44013156382843a5e72af272810674 (patch)
treef5cfea6392033e1d4e228eb0f388b343434994ce
parente8bb632cae4e7c9a320a3412cbd487f859ae104c (diff)
Improvements to UI and performance.
-rw-r--r--interactionhandler.cpp11
-rw-r--r--interactionhandler.h2
-rw-r--r--lamp.cpp34
-rw-r--r--main_view.qml6
-rw-r--r--mp.h1
-rw-r--r--pm.pro4
-rw-r--r--scatterplot.cpp65
-rw-r--r--scatterplot.h14
8 files changed, 69 insertions, 68 deletions
diff --git a/interactionhandler.cpp b/interactionhandler.cpp
index 217f4a5..50d0653 100644
--- a/interactionhandler.cpp
+++ b/interactionhandler.cpp
@@ -6,26 +6,23 @@ InteractionHandler::InteractionHandler(const arma::mat &X,
const arma::vec &labels,
const arma::uvec &sampleIndices)
: m_X(X)
+ , m_Y(X.n_rows, 3)
, m_labels(labels)
, m_sampleIndices(sampleIndices)
, m_technique(TECHNIQUE_LAMP)
{
+ m_Y.col(2) = m_labels;
}
void InteractionHandler::setSubsample(const arma::mat &Ys)
{
- arma::mat embedding(m_X.n_rows, Ys.n_cols);
switch (m_technique) {
case TECHNIQUE_PLMP:
case TECHNIQUE_LSP:
case TECHNIQUE_LAMP:
- embedding = mp::lamp(m_X, m_sampleIndices, Ys);
+ mp::lamp(m_X, m_sampleIndices, Ys, m_Y);
break;
}
- arma::mat Y(embedding.n_rows, embedding.n_cols + 1);
- Y.cols(0, embedding.n_cols - 1) = embedding;
- Y.col(Y.n_cols - 1) = m_labels;
-
- emit subsampleChanged(Y);
+ emit subsampleChanged(m_Y);
}
diff --git a/interactionhandler.h b/interactionhandler.h
index 3dbeb8a..5468dab 100644
--- a/interactionhandler.h
+++ b/interactionhandler.h
@@ -24,7 +24,7 @@ public slots:
void setSubsample(const arma::mat &Ys);
private:
- arma::mat m_X;
+ arma::mat m_X, m_Y;
arma::vec m_labels;
arma::uvec m_sampleIndices;
InteractiveTechnique m_technique;
diff --git a/lamp.cpp b/lamp.cpp
index a9d7a8b..90be140 100644
--- a/lamp.cpp
+++ b/lamp.cpp
@@ -4,12 +4,19 @@
arma::mat mp::lamp(const arma::mat &X, const arma::uvec &sampleIndices, const arma::mat &Ys)
{
- arma::mat Xs = X.rows(sampleIndices);
- arma::uword sampleSize = sampleIndices.n_elem;
arma::mat projection(X.n_rows, 2);
+ lamp(X, sampleIndices, Ys, projection);
+ return projection;
+}
+void mp::lamp(const arma::mat &X, const arma::uvec &sampleIndices, const arma::mat &Ys, arma::mat &Y)
+{
+ const arma::mat &Xs = X.rows(sampleIndices);
+ arma::uword sampleSize = sampleIndices.n_elem;
+
+ #pragma omp parallel for shared(X, Xs, sampleIndices, Ys, Y)
for (arma::uword i = 0; i < X.n_rows; i++) {
- arma::rowvec point = X.row(i);
+ const arma::rowvec &point = X.row(i);
// calculate alphas
arma::rowvec alphas(sampleSize);
@@ -19,7 +26,6 @@ arma::mat mp::lamp(const arma::mat &X, const arma::uvec &sampleIndices, const ar
}
double alphas_sum = arma::accu(alphas);
- arma::rowvec alphas_sqrt = arma::sqrt(alphas);
// calculate \tilde{X} and \tilde{Y}
arma::rowvec Xtil = arma::sum(alphas * Xs, 0) / alphas_sum;
@@ -32,19 +38,19 @@ arma::mat mp::lamp(const arma::mat &X, const arma::uvec &sampleIndices, const ar
Yhat.each_row() -= Ytil;
// calculate A and B
- arma::mat At = Xhat.t();
- At.each_row() %= alphas_sqrt;
- arma::mat B = Yhat;
- B.each_col() %= alphas_sqrt.t();
-
- arma::mat U, V;
- arma::vec s;
+ alphas = arma::sqrt(alphas);
+ arma::mat &At = Xhat;
+ inplace_trans(At);
+ At.each_row() %= alphas;
+ arma::mat &B = Yhat;
+ B.each_col() %= alphas.t();
+
+ arma::mat U(Ys.n_rows, Ys.n_cols), V(Ys.n_cols, Ys.n_cols);
+ arma::vec s(Ys.n_cols);
arma::svd(U, s, V, At * B);
arma::mat M = U.cols(0, 1) * V.t();
// the projection of point i
- projection.row(i) = (point - Xtil) * M + Ytil;
+ Y(i, arma::span(0, 1)) = (point - Xtil) * M + Ytil;
}
-
- return projection;
}
diff --git a/main_view.qml b/main_view.qml
index 5d86c36..5afe270 100644
--- a/main_view.qml
+++ b/main_view.qml
@@ -11,15 +11,11 @@ ApplicationWindow {
menuBar: MenuBar {
Menu {
- title: "Application"
- MenuItem { action: quitAction }
- }
-
- Menu {
title: "File"
MenuItem { action: openAction }
MenuItem { action: savePlotAction }
MenuItem { action: saveDataAction }
+ MenuItem { action: quitAction }
}
}
diff --git a/mp.h b/mp.h
index 6508d9d..996da87 100644
--- a/mp.h
+++ b/mp.h
@@ -6,6 +6,7 @@ static const double EPSILON = 1e-3;
double euclidean(const arma::rowvec &x1, const arma::rowvec &x2);
arma::mat dist(const arma::mat &X, double (*distCalc)(const arma::rowvec &, const arma::rowvec &) = euclidean);
arma::mat lamp(const arma::mat &X, const arma::uvec &sampleIndices, const arma::mat &Ys);
+void lamp(const arma::mat &X, const arma::uvec &sampleIndices, const arma::mat &Ys, arma::mat &Y);
arma::mat forceScheme(const arma::mat &D, arma::mat &Y, size_t maxIter = 20, double tol = 1e-3, double fraction = 8);
} // namespace mp
diff --git a/pm.pro b/pm.pro
index 9608b89..af9a10d 100644
--- a/pm.pro
+++ b/pm.pro
@@ -1,7 +1,7 @@
QT += qml quick widgets
-QMAKE_CXXFLAGS += -std=c++11
-QMAKE_LIBS += -larmadillo
+QMAKE_CXXFLAGS += -std=c++11 -fopenmp
+QMAKE_LIBS += -larmadillo -fopenmp
HEADERS += colorscale.h \
scatterplot.h \
interactionhandler.h \
diff --git a/scatterplot.cpp b/scatterplot.cpp
index b0c8034..8fb0b56 100644
--- a/scatterplot.cpp
+++ b/scatterplot.cpp
@@ -1,12 +1,6 @@
#include "scatterplot.h"
#include <cmath>
-#include <QSGGeometry>
-#include <QSGGeometryNode>
-#include <QSGOpacityNode>
-#include <QSGMaterial>
-#include <QSGFlatColorMaterial>
-#include <QSGSimpleRectNode>
static const qreal GLYPH_OPACITY = 0.3;
static const qreal GLYPH_OPACITY_SELECTED = 1.0;
@@ -25,8 +19,8 @@ Scatterplot::Scatterplot(QQuickItem *parent)
QColor("#9467bd"),
QColor("#8c564b"),
QColor("#e377c2"),
- QColor("#7f7f7f"),
QColor("#17becf"),
+ QColor("#7f7f7f"),
}
{
setClip(true);
@@ -49,10 +43,7 @@ void Scatterplot::setData(const arma::mat &data)
m_ymax = data.col(1).max();
m_colorScale.setExtents(m_data.col(2).min(), m_data.col(2).max());
-
m_selectedGlyphs.clear();
- for (arma::uword i = 0; i < m_data.n_rows; i++)
- m_selectedGlyphs.append(false);
update();
}
@@ -151,21 +142,23 @@ QSGNode *Scatterplot::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
yt = m_dragCurrentPos.y() - m_dragOriginPos.y();
}
- QSGNode *glyphOpacityNode = root->firstChild()->firstChild();
+ QSGNode *node = root->firstChild()->firstChild();
for (arma::uword i = 0; i < m_data.n_rows; i++) {
arma::rowvec row = m_data.row(i);
- moveTranslationF = m_selectedGlyphs[i] ? 1.0 : 0.0;
+ bool isSelected = m_selectedGlyphs.contains(i);
+
+ QSGOpacityNode *glyphOpacityNode = static_cast<QSGOpacityNode *>(node);
+ glyphOpacityNode->setOpacity(isSelected ? GLYPH_OPACITY_SELECTED : GLYPH_OPACITY);
+
+ QSGGeometryNode *glyphNode = static_cast<QSGGeometryNode *>(node->firstChild());
+ QSGGeometry *geometry = glyphNode->geometry();
+ moveTranslationF = isSelected ? 1.0 : 0.0;
x = fromDataXToScreenX(row[0]) + xt * moveTranslationF;
y = fromDataYToScreenY(row[1]) + yt * moveTranslationF;
-
- QSGNode *glyphNode = glyphOpacityNode->firstChild();
- QSGGeometry *geometry = static_cast<QSGGeometryNode *>(glyphNode)->geometry();
updateCircleGeometry(geometry, GLYPH_SIZE, x, y);
glyphNode->markDirty(QSGNode::DirtyGeometry);
- static_cast<QSGOpacityNode *>(glyphOpacityNode)->setOpacity(m_selectedGlyphs[i] ? GLYPH_OPACITY_SELECTED : GLYPH_OPACITY);
-
- glyphOpacityNode = glyphOpacityNode->nextSibling();
+ node = node->nextSibling();
}
// Draw selection
@@ -203,21 +196,28 @@ void Scatterplot::mousePressEvent(QMouseEvent *event)
break;
case INTERACTION_SELECTING:
case INTERACTION_MOVING:
- return; // should not be reached
+ event->ignore();
+ return;
}
}
void Scatterplot::mouseMoveEvent(QMouseEvent *event)
{
switch (m_currentState) {
- case INTERACTION_NONE:
- case INTERACTION_SELECTED:
- return;
case INTERACTION_SELECTING:
+ m_dragCurrentPos = event->localPos();
+ update();
+ break;
case INTERACTION_MOVING:
m_dragCurrentPos = event->localPos();
+ updateData();
update();
+ m_dragOriginPos = m_dragCurrentPos;
break;
+ case INTERACTION_NONE:
+ case INTERACTION_SELECTED:
+ event->ignore();
+ return;
}
}
@@ -235,7 +235,6 @@ void Scatterplot::mouseReleaseEvent(QMouseEvent *event)
case INTERACTION_MOVING:
m_currentState = INTERACTION_SELECTED;
- updateData();
update();
break;
case INTERACTION_NONE:
@@ -246,6 +245,9 @@ void Scatterplot::mouseReleaseEvent(QMouseEvent *event)
bool Scatterplot::selectGlyphs(bool mergeSelection)
{
+ if (!mergeSelection)
+ m_selectedGlyphs.clear();
+
qreal x, y;
QRectF selectionRect(m_dragOriginPos, m_dragCurrentPos);
@@ -255,9 +257,11 @@ bool Scatterplot::selectGlyphs(bool mergeSelection)
x = fromDataXToScreenX(row[0]);
y = fromDataYToScreenY(row[1]);
- bool contains = selectionRect.contains(x, y);
- anySelected = anySelected || contains;
- m_selectedGlyphs[i] = (mergeSelection && m_selectedGlyphs[i]) || contains;
+ if (selectionRect.contains(x, y)) {
+ m_selectedGlyphs.insert(i);
+ if (!anySelected)
+ anySelected = true;
+ }
}
return anySelected;
@@ -270,14 +274,11 @@ void Scatterplot::updateData()
xt /= (width() - PADDING);
yt /= (height() - PADDING);
- for (arma::uword i = 0; i < m_data.n_rows; i++) {
- if (!m_selectedGlyphs[i])
- continue;
-
- arma::rowvec row = m_data.row(i);
+ for (auto it = m_selectedGlyphs.cbegin(); it != m_selectedGlyphs.cend(); it++) {
+ arma::rowvec row = m_data.row(*it);
row[0] = ((row[0] - m_xmin) / (m_xmax - m_xmin) + xt) * (m_xmax - m_xmin) + m_xmin;
row[1] = ((row[1] - m_ymin) / (m_ymax - m_ymin) + yt) * (m_ymax - m_ymin) + m_ymin;
- m_data.row(i) = row;
+ m_data.row(*it) = row;
}
// does not send last column (labels)
diff --git a/scatterplot.h b/scatterplot.h
index 2b5aed8..a155daf 100644
--- a/scatterplot.h
+++ b/scatterplot.h
@@ -2,8 +2,7 @@
#define SCATTERPLOT_H
#include <armadillo>
-#include <QQuickItem>
-#include <QSGNode>
+#include <QtQuick>
#include "colorscale.h"
@@ -34,6 +33,11 @@ private:
float fromDataXToScreenX(float x);
float fromDataYToScreenY(float y);
+ arma::mat m_data;
+ float m_xmin, m_xmax, m_ymin, m_ymax;
+
+ ColorScale m_colorScale;
+
enum InteractionState {
INTERACTION_NONE,
INTERACTION_SELECTING,
@@ -41,12 +45,8 @@ private:
INTERACTION_MOVING
} m_currentState;
QPointF m_dragOriginPos, m_dragCurrentPos;
- QList<bool> m_selectedGlyphs;
- arma::mat m_data;
- float m_xmin, m_xmax, m_ymin, m_ymax;
-
- ColorScale m_colorScale;
+ QSet<int> m_selectedGlyphs;
};
#endif // SCATTERPLOT_H