summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--least_squares.cpp161
-rw-r--r--tensor.hpp108
2 files changed, 80 insertions, 189 deletions
diff --git a/least_squares.cpp b/least_squares.cpp
index f667e28..3aa9948 100644
--- a/least_squares.cpp
+++ b/least_squares.cpp
@@ -1,95 +1,91 @@
#include <eigen3/Eigen/Dense>
#include <getopt.h>
+#include <eigen3/unsupported/Eigen/CXX11/Tensor>
+
#include "pcg-cpp/include/pcg_random.hpp"
#include "randutils/randutils.hpp"
-#include "tensor.hpp"
+using Rng = randutils::random_generator<pcg32>;
-Vector normalize(const Vector& z) {
- return z * sqrt((Real)z.size() / (Real)(z.transpose() * z));
-}
+using Real = double;
+using Vector = Eigen::Matrix<Real, Eigen::Dynamic, 1>;
+using Matrix = Eigen::Matrix<Real, Eigen::Dynamic, Eigen::Dynamic>;
-template <int... ps>
-class Model {
-private:
- std::tuple<Tensor<ps>...> Js;
+/* Eigen tensor manipulations are quite annoying, especially with the need to convert other types
+ * into tensors beforehand. Here I overload multiplication operators to allow contraction between
+ * vectors and the first or last index of a tensor.
+ */
+class Tensor : public Eigen::Tensor<Real, 3> {
+ using Eigen::Tensor<Real, 3>::Tensor;
public:
- unsigned N;
- unsigned M;
- template <class Generator, typename... T>
- Model(unsigned N, unsigned M, Generator& r, T... μs) : N(N), M(M) {
- Js = std::make_tuple(μs * generateRealPSpinCouplings<ps>(N, M, r)...);
- }
-
- unsigned numPs() const {
- return std::tuple_size(Js);
+ Matrix operator*(const Vector& x) const {
+ const std::array<Eigen::IndexPair<int>, 1> ip20 = {Eigen::IndexPair<int>(2, 0)};
+ const Eigen::Tensor<Real, 1> xT = Eigen::TensorMap<const Eigen::Tensor<Real, 1>>(x.data(), x.size());
+ const Eigen::Tensor<Real, 2> JxT = contract(xT, ip20);
+ return Eigen::Map<const Matrix>(JxT.data(), dimension(0), dimension(1));
}
+};
-private:
- std::tuple<Vector, Matrix, Tensor<3>> hamGradTensorHelper(const Vector& z, const Tensor<1>& J) const {
- Tensor<3> J3(N, N, M);;
- J3.setZero();
- Matrix Jz = Matrix::Zero(N, M);
- Vector Jzz = Eigen::Map<const Vector>(J.data(), M);
-
- return {Jzz, Jz, J3};
- }
-
- std::tuple<Vector, Matrix, Tensor<3>> hamGradTensorHelper(const Vector& z, const Tensor<2>& J) const {
- Tensor<3> J3(N, N, M);;
- J3.setZero();
- Matrix Jz = Eigen::Map<const Matrix>(J.data(), N, M);
- Vector Jzz = z.transpose() * Jz;
-
- return {Jzz, Jz, J3};
- }
+Matrix operator*(const Eigen::Matrix<Real, 1, Eigen::Dynamic>& x, const Tensor& J) {
+ const std::array<Eigen::IndexPair<int>, 1> ip00 = {Eigen::IndexPair<int>(0, 0)};
+ const Eigen::Tensor<Real, 1> xT = Eigen::TensorMap<const Eigen::Tensor<Real, 1>>(x.data(), x.size());
+ const Eigen::Tensor<Real, 2> JxT = J.contract(xT, ip00);
+ return Eigen::Map<const Matrix>(JxT.data(), J.dimension(1), J.dimension(2));
+}
- template <int p>
- std::tuple<Vector, Matrix, Tensor<3>> hamGradTensorHelper(const Vector z, const Tensor<p>& J) const {
- Tensor<3> J3 = contractDown(J, z);
- Tensor<1> zT = Eigen::TensorMap<constTensor<1>>(z.data(), N);
- Tensor<2> J3zT = J3.contract(zT, ip00);
- Matrix Jz = Eigen::Map<const Matrix>(J3zT.data(), N, M);
- Vector Jzz = z.transpose() * Jz;
+Vector normalize(const Vector& x) {
+ return x * sqrt((Real)x.size() / x.squaredNorm());
+}
- return {Jzz, Jz, J3};
- }
+class QuadraticModel {
+private:
+ Tensor J;
+ Matrix A;
+ Vector b;
- template <int p, int... qs>
- std::tuple<Vector, Matrix, Tensor<3>> hamGradHessHelper(const Vector& z, const Tensor<p>& J, const Tensor<qs>& ...Js) const {
- auto [Jzz, Jz, J3] = hamGradTensorHelper(z, J);
+public:
+ unsigned N;
+ unsigned M;
- Real pBang = factorial(p-1);
+ template <class Generator>
+ QuadraticModel(unsigned N, unsigned M, Generator& r, double μ1, double μ2, double μ3) : N(N), M(M), J(M, N, N), A(M, N), b(M) {
+ std::normal_distribution<Real> distribution(0, 1);
- Tensor<3> ddH = ((p - 1) * p / pBang) * J3;
- Matrix dH = (p / pBang) * Jz;
- Vector H = Jzz / pBang;
+ for (unsigned i = 0; i < M; i++) {
+ for (unsigned j = 0; j < N; j++) {
+ for (unsigned k = 0; k < N; k++) {
+ J(i, j, k) = (2 * μ3 / N) * distribution(r);
+ }
+ }
+ }
- if constexpr (sizeof...(Js) > 0) {
- auto [Hs, dHs, ddHs] = hamGradHessHelper(z, Js...);
- H += Hs;
- dH += dHs;
- ddH += ddHs;
+ for (unsigned i = 0; i < M; i++) {
+ for (unsigned j = 0; j < N; j++) {
+ A(i, j) = (μ2 / sqrt(N)) * distribution(r);
+ }
}
- return {H, dH, ddH};
+ for (unsigned i = 0; i < M; i++) {
+ b(i) = μ1 * distribution(r);
+ }
}
-public:
- std::tuple<Vector, Matrix, Tensor<3>> VdVddV(const Vector& z) const {
- return std::apply([&z, this](const Tensor<ps>& ...Ks) -> std::tuple<Vector, Matrix, Tensor<3>> { return hamGradHessHelper(z, Ks...); }, Js);
+ std::tuple<Vector, Matrix, const Tensor&> VdVddV(const Vector& x) const {
+ Matrix Jx = J * x;
+ Vector V1 = (A + 0.5 * Jx) * x;
+ Matrix dV = A + Jx;
+
+ return {b + V1, dV, J};
}
- std::tuple<Real, Vector, Matrix> HdHddH(const Vector& z) const {
- auto [V, dV, ddV] = VdVddV(z);
+ std::tuple<Real, Vector, Matrix> HdHddH(const Vector& x) const {
+ auto [V, dV, ddV] = VdVddV(x);
Real H = 0.5 * V.squaredNorm();
- Vector dH = dV * V;
- Tensor<1> VT = Eigen::TensorMap<constTensor<1>>(V.data(), M);
- Tensor<2> ddVzT = ddV.contract(VT, ip20);
- Matrix ddH = Eigen::Map<const Matrix>(ddVzT.data(), N, N) + dV * dV.transpose();
+ Vector dH = V.transpose() * dV;
+ Matrix ddH = V.transpose() * ddV + dV.transpose() * dV;
return {H, dH, ddH};
}
@@ -103,7 +99,7 @@ public:
return {H, gradH, hessH};
}
- Vector HessSpectrum(const Vector& x) const {
+ Vector spectrum(const Vector& x) const {
Matrix hessH;
std::tie(std::ignore, std::ignore, hessH) = hamGradHess(x);
Eigen::EigenSolver<Matrix> eigenS(hessH);
@@ -111,24 +107,22 @@ public:
}
};
-template <int ...ps>
-Vector findMinimum(const Model<ps...>& M, const Vector& x0, Real ε) {
+Vector findMinimum(const QuadraticModel& M, const Vector& x0, Real ε) {
Vector x = x0;
Real λ = 100;
auto [H, g, m] = M.hamGradHess(x0);
while (g.norm() / x.size() > ε && λ < 1e8) {
- Vector dz = (m + λ * (Matrix)abs(m.diagonal().array()).matrix().asDiagonal()).partialPivLu().solve(g);
- dz -= x.dot(dz) * x / M.N;
- Vector zNew = normalize(x - dz);
+ Vector dx = (m + λ * (Matrix)abs(m.diagonal().array()).matrix().asDiagonal()).partialPivLu().solve(g);
+ dx -= x.dot(dx) * x / M.N;
+ Vector xNew = normalize(x - dx);
- auto [HNew, gNew, mNew] = M.hamGradHess(zNew);
+ auto [HNew, gNew, mNew] = M.hamGradHess(xNew);
if (HNew * 1.0001 <= H) {
- x = zNew;
+ x = xNew;
H = HNew;
-
g = gNew;
m = mNew;
@@ -141,17 +135,16 @@ Vector findMinimum(const Model<ps...>& M, const Vector& x0, Real ε) {
return x;
}
-using Rng = randutils::random_generator<pcg32>;
-using Real = double;
-
int main(int argc, char* argv[]) {
unsigned N = 10;
Real α = 1;
Real σ = 1;
+ Real A = 1;
+ Real J = 1;
int opt;
- while ((opt = getopt(argc, argv, "N:a:s:")) != -1) {
+ while ((opt = getopt(argc, argv, "N:a:s:A:J:")) != -1) {
switch (opt) {
case 'N':
N = (unsigned)atof(optarg);
@@ -162,6 +155,12 @@ int main(int argc, char* argv[]) {
case 's':
σ = atof(optarg);
break;
+ case 'A':
+ A = atof(optarg);
+ break;
+ case 'J':
+ J = atof(optarg);
+ break;
default:
exit(1);
}
@@ -171,7 +170,7 @@ int main(int argc, char* argv[]) {
Rng r;
- Model<1, 2> leastSquares(N, M, r.engine(), σ, 1);
+ QuadraticModel leastSquares(N, M, r.engine(), σ, A, J);
Vector x = Vector::Zero(N);
x(0) = sqrt(N);
@@ -185,7 +184,7 @@ int main(int argc, char* argv[]) {
std::tie(energy, std::ignore, std::ignore) = leastSquares.hamGradHess(xMin);
std::cout << energy / N << std::endl;
- std::cout << leastSquares.HessSpectrum(xMin)(1) / N << std::endl;
+ std::cout << leastSquares.spectrum(xMin)(1) / N << std::endl;
return 0;
}
diff --git a/tensor.hpp b/tensor.hpp
deleted file mode 100644
index 7f98d02..0000000
--- a/tensor.hpp
+++ /dev/null
@@ -1,108 +0,0 @@
-#pragma once
-
-#include <array>
-#include <functional>
-
-#include <eigen3/unsupported/Eigen/CXX11/Tensor>
-
-#include "types.hpp"
-#include "factorial.hpp"
-
-using Vector = Eigen::Matrix<Real, Eigen::Dynamic, 1>;
-
-using Matrix = Eigen::Matrix<Real, Eigen::Dynamic, Eigen::Dynamic>;
-
-template <int p>
-using Tensor = Eigen::Tensor<Real, p>;
-
-template <int p>
-using constTensor = Eigen::Tensor<const Real, p>;
-
-template <int p, std::size_t... Indices>
-Tensor<p> initializeJHelper(unsigned N, unsigned M, std::index_sequence<Indices...>) {
- std::array<unsigned, p> Ns;
- std::fill_n(Ns.begin(), p, N);
- Ns[p-1] = M;
- return Tensor<p>(std::get<Indices>(Ns)...);
-}
-
-template <int p>
-Tensor<p> initializeJ(unsigned N, unsigned M) {
- return initializeJHelper<p>(N, M, std::make_index_sequence<p>());
-}
-
-template <int p, std::size_t... Indices>
-void setJHelper(Tensor<p>& J, const std::array<unsigned, p>& ind, Real val, std::index_sequence<Indices...>) {
- J(std::get<Indices>(ind)...) = val;
-}
-
-template <int p>
-void setJ(Tensor<p>& J, std::array<unsigned, p> ind, Real val) {
- setJHelper<p>(J, ind, val, std::make_index_sequence<p>());
-}
-
-template <int p, std::size_t... Indices>
-Real getJHelper(const Tensor<p>& J, const std::array<unsigned, p>& ind, std::index_sequence<Indices...>) {
- return J(std::get<Indices>(ind)...);
-}
-
-template <int p>
-Real getJ(const Tensor<p>& J, const std::array<unsigned, p>& ind) {
- return getJHelper<p>(J, ind, std::make_index_sequence<p>());
-}
-
-template <int p>
-void iterateOverHelper(Tensor<p>& J,
- std::function<void(Tensor<p>&, std::array<unsigned, p>)>& f,
- unsigned l, std::array<unsigned, p> is) {
- if (l == 0) {
- f(J, is);
- } else {
- for (unsigned i = 0; i < J.dimension(p - l); i++) {
- std::array<unsigned, p> js = is;
- js[p - l] = i;
- iterateOverHelper<p>(J, f, l - 1, js);
- }
- }
-}
-
-template <int p>
-void iterateOver(Tensor<p>& J, std::function<void(Tensor<p>&, std::array<unsigned, p>)>& f) {
- std::array<unsigned, p> is;
- iterateOverHelper<p>(J, f, p, is);
-}
-
-template <int p, class Distribution, class Generator>
-Tensor<p> generateCouplings(unsigned N, unsigned M, Distribution d, Generator& r) {
- Tensor<p> J = initializeJ<p>(N, M);
-
- std::function<void(Tensor<p>&, std::array<unsigned, p>)> setRandom =
- [&d, &r] (Tensor<p>& JJ, std::array<unsigned, p> ind) {
- setJ<p>(JJ, ind, d(r));
- };
-
- iterateOver<p>(J, setRandom);
-
- return J;
-}
-
-template <int p, class Generator>
-Tensor<p> generateRealPSpinCouplings(unsigned N, unsigned M, Generator& r) {
- Real σp = sqrt(factorial(p-1) / ((Real)pow(N, p - 1)));
-
- return generateCouplings<p>(N, M, std::normal_distribution<Real>(0, σp), r);
-}
-
-Tensor<3> contractDown(const Tensor<3>& J, const Vector& z) {
- return J;
-}
-
-const std::array<Eigen::IndexPair<int>, 1> ip00 = {Eigen::IndexPair<int>(0, 0)};
-const std::array<Eigen::IndexPair<int>, 1> ip20 = {Eigen::IndexPair<int>(2, 0)};
-
-template <int r>
-Tensor<3> contractDown(const Tensor<r>& J, const Vector& z) {
- Tensor<1> zT = Eigen::TensorMap<constTensor<1>>(z.data(), {z.size()});
- Tensor<r - 1> Jz = J.contract(zT, ip00);
- return contractDown(Jz, z);
-}