170 lines
5.1 KiB
C++
170 lines
5.1 KiB
C++
#include <iostream>
|
|
#include <vector>
|
|
#include <cmath>
|
|
#include <cassert>
|
|
#include <chrono>
|
|
#include <random>
|
|
using namespace std;
|
|
|
|
double sigmoid(double x) {
|
|
return 1.0 / (1.0 + exp(-x));
|
|
}
|
|
|
|
class DenseMatrix {
|
|
private:
|
|
int n, m;
|
|
vector<double> data;
|
|
|
|
public:
|
|
// Constructor
|
|
DenseMatrix(int n_, int m_) : n(n_), m(m_), data(n_ * m_) { //initalizes n,m, allocates data with n*m elements = 0
|
|
int nm;
|
|
if (n > m)
|
|
nm = n;
|
|
else
|
|
nm = m;
|
|
|
|
for (int i = 0; i < n; ++i) { //for each row index compute xi
|
|
double xi = 10.0 * i / (nm - 1) - 5.0;
|
|
for (int j = 0; j < m; ++j) { //for each column index compute xj
|
|
double xj = 10.0 * j / (nm - 1) - 5.0;
|
|
data[i * m + j] = sigmoid(xi) * sigmoid(xj); //compute every element
|
|
}
|
|
}
|
|
}
|
|
|
|
vector<double> Mult(const vector<double> &u) const {
|
|
assert((int)u.size() == m); //checks if the dimensions match
|
|
vector<double> result(n, 0.0); //prepares the empty vector, size n
|
|
for (int i = 0; i < n; ++i) {
|
|
for (int j = 0; j < m; ++j) {
|
|
result[i] += data[i * m + j] * u[j]; //row wise acces
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
vector<double> MultT(const vector<double> &v) const { //same as above, but the other way
|
|
assert((int)v.size() == n);
|
|
vector<double> result(m, 0.0);
|
|
for (int j = 0; j < m; ++j) {
|
|
for (int i = 0; i < n; ++i) {
|
|
result[j] += data[i * m + j] * v[i];
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
};
|
|
|
|
class TensorMatrix { //rank one matrix M=u*v^T
|
|
private:
|
|
int n;
|
|
vector<double> u, v;
|
|
|
|
public:
|
|
TensorMatrix(int n_) : n(n_), u(n_), v(n_) {
|
|
for (int k = 0; k < n; ++k) {
|
|
double xk = 10.0 * k / (n - 1) - 5.0;
|
|
u[k] = sigmoid(xk);
|
|
v[k] = sigmoid(xk); //ensures simmetry
|
|
}
|
|
}
|
|
|
|
vector<double> Mult(const vector<double> &x) const {
|
|
assert((int)x.size() == n); //does u*(v^T*x) so the parenthesis first
|
|
vector<double> result(n, 0.0);
|
|
double dot_vx = 0.0;
|
|
for (int k = 0; k < n; ++k)
|
|
dot_vx += v[k] * x[k];
|
|
for (int i = 0; i < n; ++i)
|
|
result[i] = u[i] * dot_vx;
|
|
return result;
|
|
}
|
|
|
|
vector<double> MultT(const vector<double> &y) const { //same as above
|
|
assert((int)y.size() == n);
|
|
vector<double> result(n, 0.0);
|
|
double dot_uy = 0.0;
|
|
for (int k = 0; k < n; ++k)
|
|
dot_uy += u[k] * y[k];
|
|
for (int j = 0; j < n; ++j)
|
|
result[j] = v[j] * dot_uy;
|
|
return result;
|
|
}
|
|
};
|
|
|
|
|
|
int main() {
|
|
DenseMatrix M(5, 3); //example with a small dense matrix
|
|
vector<double> u = {1, 2, 3};
|
|
vector<double> f1 = M.Mult(u);
|
|
vector<double> v = {-1, 2, -3, 4, -5};
|
|
vector<double> f2 = M.MultT(v);
|
|
|
|
cout << "Result of M.Mult(u): ";
|
|
for (double x : f1) cout << x << " ";
|
|
cout << endl;
|
|
cout << "Result of M.MultT(v): ";
|
|
for (double x : f2) cout << x << " ";
|
|
cout << endl;
|
|
|
|
int n = 1000; //example with a big matrix
|
|
|
|
vector<double> w(n); //random non trivial test vector w
|
|
std::mt19937 gen(42);
|
|
std::uniform_real_distribution<double> dist(-1.0, 1.0); //values uniformly distributed in [-1,1]
|
|
for (int i = 0; i < n; ++i)
|
|
w[i] = dist(gen);
|
|
|
|
int NLOOPS = 50; //how many repetitions
|
|
|
|
DenseMatrix M2(n, n); //Matrix
|
|
vector<double> res1, res2;
|
|
|
|
auto t0 = chrono::system_clock::now();
|
|
for (int k = 0; k < NLOOPS; ++k)
|
|
res1 = M2.Mult(w);
|
|
auto t1 = chrono::system_clock::now();
|
|
chrono::duration<double> timeMult = (t1 - t0) / NLOOPS;
|
|
|
|
t0 = chrono::system_clock::now();
|
|
for (int k = 0; k < NLOOPS; ++k)
|
|
res2 = M2.MultT(w);
|
|
t1 = chrono::system_clock::now();
|
|
chrono::duration<double> timeMultT = (t1 - t0) / NLOOPS;
|
|
|
|
cout << "\nAverage time per Mult: " << timeMult.count() << " sec\n";
|
|
cout << "Average time per MultT: " << timeMultT.count() << " sec\n";
|
|
|
|
double diff = 0.0;
|
|
for (int i = 0; i < n; ++i)
|
|
diff += fabs(res1[i] - res2[i]);
|
|
diff /= n;
|
|
cout << "Average difference between Mult and MultT results: " << diff << endl;
|
|
|
|
TensorMatrix T(n); //Tensor
|
|
vector<double> res1T, res2T;
|
|
|
|
auto t0T = chrono::system_clock::now();
|
|
for (int k = 0; k < NLOOPS; ++k)
|
|
res1T = T.Mult(w);
|
|
auto t1T = chrono::system_clock::now();
|
|
chrono::duration<double> timeMult2 = (t1T - t0T) / NLOOPS;
|
|
|
|
t0T = chrono::system_clock::now();
|
|
for (int k = 0; k < NLOOPS; ++k)
|
|
res2T = T.MultT(w);
|
|
t1T = chrono::system_clock::now();
|
|
chrono::duration<double> timeMultT2 = (t1T - t0T) / NLOOPS;
|
|
|
|
cout << "\nTensorMatrix Mult time: " << timeMult2.count() << " sec\n";
|
|
cout << "TensorMatrix MultT time: " << timeMultT2.count() << " sec\n";
|
|
|
|
double diffT = 0.0;
|
|
for (int i = 0; i < n; ++i)
|
|
diffT += fabs(res1T[i] - res2T[i]);
|
|
diffT /= n;
|
|
cout << "Average difference between Tensor Mult and MultT results: " << diffT << endl;
|
|
|
|
return 0;
|
|
}
|