#pragma once #include #include #include "types.hpp" #include "tensor.hpp" #include "factorial.hpp" template Vector normalize(const Eigen::MatrixBase& z) { return z * sqrt((Real)z.size() / (typename Derived::Scalar)(z.transpose() * z)); } template std::tuple, Matrix> hamGradHess(const Tensor& J, const Vector& z) { Matrix Jz = contractDown(J, z); // Contracts J into p - 2 copies of z. Vector Jzz = Jz * z; Scalar Jzzz = Jzz.transpose() * z; Real pBang = factorial(p); Matrix hessian = ((p - 1) * p / pBang) * Jz; Vector gradient = (p / pBang) * Jzz; Scalar hamiltonian = Jzzz / pBang; return {hamiltonian, gradient, hessian}; } template Scalar getHamiltonian(const Tensor& J, const Vector& z) { Scalar H; std::tie(H, std::ignore, std::ignore) = hamGradHess(J, z); return H; } template Vector getGradient(const Tensor& J, const Vector& z) { Vector dH; std::tie(std::ignore, dH, std::ignore) = hamGradHess(J, z); return dH; } template Matrix getHessian(const Tensor& J, const Vector& z) { Matrix ddH; std::tie(std::ignore, std::ignore, ddH) = hamGradHess(J, z); return ddH; } template Real getThresholdEnergyDensity(unsigned p, Scalar κ, Scalar ε, Real a) { Real apm2 = pow(a, p - 2); Scalar δ = κ / apm2; Real θ = arg(κ) + 2 * arg(ε); return (p - 1) * apm2 / (2 * p) * pow(1 - norm(δ), 2) / (1 + norm(δ) - 2 * abs(δ) * cos(θ)); } template Real getProportionOfThreshold(Scalar κ, const Tensor& J, const Vector& z) { Real N = z.size(); Scalar ε = getHamiltonian(J, z) / N; Real a = z.squaredNorm() / N; return norm(ε) / getThresholdEnergyDensity(p, κ, ε, a); } template Vector zDot(const Vector& z, const Vector& dH) { return -dH.conjugate() + (dH.dot(z) / z.squaredNorm()) * z.conjugate(); } template std::tuple> WdW(const Tensor& J, const Vector& z) { Vector dH; Matrix ddH; std::tie(std::ignore, dH, ddH) = hamGradHess(J, z); Vector dzdt = zDot(z, dH); Real a = z.squaredNorm(); Scalar A = (Scalar)(z.transpose() * dzdt) / a; Scalar B = dH.dot(z) / a; Real W = dzdt.squaredNorm(); Vector dW = ddH * (dzdt - A * z.conjugate()) + 2 * (conj(A) * B * z).real() - conj(B) * dzdt - conj(A) * dH.conjugate(); return {W, dW}; } template Matrix dzDot(const Vector& z, const Vector& dH) { Real z² = z.squaredNorm(); return (dH.conjugate() - (dH.dot(z) / z²) * z.conjugate()) * z.adjoint() / z²; } template Matrix dzDotConjugate(const Vector& z, const Vector& dH, const Matrix& ddH) { Real z² = z.squaredNorm(); return -ddH + (ddH * z.conjugate()) * z.transpose() / z² + (z.dot(dH) / z²) * ( Matrix::Identity(ddH.rows(), ddH.cols()) - z.conjugate() * z.transpose() / z² ); } template Tensor ddzDot(const Vector& z, const Vector& dH) { Tensor zT = Eigen::TensorMap>(z.data(), {z.size()}); Tensor dHT = Eigen::TensorMap>(dH.data(), {dH.size()}); Eigen::array, 0> ei = {}; Scalar z² = z.squaredNorm(); return - zT.conjugate().contract(dHT.conjugate(), ei).contract(zT.conjugate(), ei) / pow(z², 2) - dHT.conjugate().contract(zT.conjugate(), ei).contract(zT.conjugate(), ei) / pow(z², 2) + zT.conjugate().contract(zT.conjugate(), ei).contract(zT.conjugate(), ei) * (2.0 * dH.dot(z) / pow(z², 3)); } template Tensor ddzDotConjugate(const Vector& z, const Vector& dH, const Matrix& ddH, const Tensor& dddH) { Tensor zT = Eigen::TensorMap>(z.data(), {z.size()}); Tensor dHT = Eigen::TensorMap>(dH.data(), {dH.size()}); Tensor ddHT = Eigen::TensorMap>(ddH.data(), {z.size(), z.size()}); Matrix I = Matrix::Identity(z.size(), z.size()); Tensor IT = Eigen::TensorMap>(I.data(), {z.size(), z.size()}); Eigen::array, 0> ei = {}; Eigen::array, 1> ip00 = {Eigen::IndexPair(0, 0)}; Scalar z² = z.squaredNorm(); return - dddH + dddH.contract(zT.conjugate(), ip00).contract(zT, ei) / z² + IT.contract(ddHT.contract(zT.conjugate(), ip00), ei).shuffle((std::array){0, 2, 1}) / z² + ddHT.contract(zT.conjugate(), ip00).contract(IT, ei) / z² - zT.conjugate().contract(ddHT.contract(zT.conjugate(), ip00), ei).contract(zT, ei) / pow(z², 2) - zT.conjugate().contract(IT, ei) * (z.dot(dH) / pow(z², 2)) - IT.contract(zT.conjugate(), ei).shuffle((std::array){0, 2, 1}) * (z.dot(dH) / pow(z², 2)) - ddHT.contract(zT.conjugate(), ip00).contract(zT.conjugate(), ei).contract(zT, ei) / pow(z², 2) + zT.conjugate().contract(zT.conjugate(), ei).contract(zT, ei) * (2.0 * z.dot(dH) / pow(z², 3)); }