initial commit

This commit is contained in:
matthias@quintern.xyz 2024-12-03 00:37:54 +01:00
commit cc805c0166
29 changed files with 6241 additions and 0 deletions

3
src/.clangd Normal file
View File

@ -0,0 +1,3 @@
CompileFlags: # Tweak the parse settings
Add: [ -std=c++23, -Wall, -Wpedantic, -IFastor, -I., -I.., -I../include, -Imodels, -I../models, -Ialgorithms, -DEMSCRIPTEN ]
# https://clangd.llvm.org/config

2924
src/.doxygen_config Normal file

File diff suppressed because it is too large Load Diff

70
src/.old/mps_fastor.cpp Normal file
View File

@ -0,0 +1,70 @@
#include "Fastor/Fastor.h"
#include "Fastor/tensor/AbstractTensorFunctions.h"
namespace f = Fastor;
using dtype = double;
template<unsigned maxBondDim, unsigned localDim>
class MPS {
using BTensor_t = f::Tensor<dtype, maxBondDim, localDim, maxBondDim>;
public:
MPS(std::vector<f::Tensor<dtype>>&& Bs, std::vector<f::Tensor<dtype>>&& Ss) {
assert(Bs.size() == Ss.size());
this->Bs = std::move(Bs);
this->Ss = std::move(Ss);
this->L = Bs.size();
}
private:
std::vector<f::Tensor<dtype>> Bs;
std::vector<f::Tensor<dtype>> Ss;
unsigned L;
};
// class TensorContainer {
// public:
// TensorContainer(Tensor t);
// };
int main() {
// std::vector<f::Tensor<dtype, 2, 2, 1>> Bs = {
// // f::Tensor<dtype, 1, 2, 1>({{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}}}),
// // f::Tensor<dtype, 1, 2, 1>({{{1, 2}}}),
// f::Tensor<dtype, 1, 2, 1>({{{1}, {2}}}),
// // f::Tensor<dtype, 1, 10, 1>({{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}}}),
// };
f::Tensor<dtype, 10, 2, 10> t1;
t1.zeros();
std::cout << t1 << std::endl;
t1(0,1,0) = .69;
std::cout << t1 << std::endl;
f::Tensor<dtype, 10, 2, 10> t2;
t2.ones();
std::vector<f::Tensor<dtype, 10, 2, 10>> Bs = { t1, t2 };
//({{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}}});
// auto t2 = f::Tensor<dtype, 1, 10, 1>({{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}}});
// f::Tensor<dtype, 1, 5, 1>
// for (auto& b : Bs) {
// std::cout << b << std::endl;
// }
// f::Tensor<dtype, 1, 2, 1> t1View = t1(f::seq(0, 1), f::all, f::seq(0, 1));
auto t1View = t1(f::seq(0, 10), f::all, f::seq(0, 10));
auto t2View = t2(f::seq(0, 10), f::all, f::seq(0, 10));
// auto t1View = t1(f::seq(0, 1), f::all, f::seq(0, 1));
// std::cout << t1View.dimension(0) << std::endl;
// std::cout << t1View.dimension(1) << std::endl;
// std::cout << t1View.dimension(2) << std::endl;
// std::cout << t1View << std::endl;
f::Tensor<dtype, 1> t3 = f::evaluate(f::einsum<f::Index<0, 1, 2>, f::Index<0, 1, 2>>(t1View, t2View));
// f::Tensor<dtype, 1> t3 = f::einsum<f::Index<0, 1, 2>, f::Index<0, 1, 2>>(t1, t2);
std::cout << t3.dimension(0) << std::endl;
std::cout << t3(0) << std::endl;
return 0;
}

22
src/.vimspector.json Normal file
View File

@ -0,0 +1,22 @@
{
"configurations": {
"gze": {
"adapter": "vscode-cpptools",
"configuration": {
"name": "mps (cpp)",
"type": "cppdbg",
"request": "launch",
"externalConsole": true,
"logging": {
"engineLogging": true
},
"stopOnEntry": true,
"stopAtEntry": true,
"debugOptions": [],
"MIMode": "gdb",
"cwd": "~/Projekte/science/cpp-mps/src",
"program": "../mps"
}
}
}
}

131
src/Makefile Executable file
View File

@ -0,0 +1,131 @@
# SETTINGS
OBJECT_DIR = ../build
WEBOBJECT_DIR = ../web-build
EXEC = ../mps
WEBHTML = $(WEBOBJECT_DIR)/mps.html
# WEBEXEC = $(WEBOBJECT_DIR)/mps.js
WEBEXEC = ~/Projekte/web/html-quintern/src/script/mps/mps-wasm.js
# LOG_LEVEL = LOG_LEVEL_0
CXX = /usr/bin/g++
CXXFLAGS = -std=c++23 -MMD -MP -Wpedantic -Wall #-O3: optimierungsstufe 3, -MMD .d files, -MP leeres Target, Wextra alle Warnungen
CXXDEBUGFLAGS = -g -fsanitize=address -fno-omit-frame-pointer -O1
WEBCXX = em++
WEBCXXFLAGS = --cache "$(WEBOBJECT_DIR)/emscripten-cache" -Wno-c++11-narrowing -Wno-unused-main -DEMSCRIPTEN
WEBCXXDEBUGFLAGS = -gsource-map -g2 -O0
WEBLDFLAGS = -s EXPORTED_FUNCTIONS="['_free']" -s ASYNCIFY -s EXPORTED_RUNTIME_METHODS="['cwrap', 'getValue']" -lembind -s INITIAL_MEMORY=2GB -s ALLOW_MEMORY_GROWTH=1
# treated as error by em++
LDFLAGS = #-lgzutil
IFLAGS = -I../include/
SRCDIRS = $(wildcard */)
IFLAGS += $(foreach dir,$(SRCDIRS), -I$(dir))
IFLAGS += $(foreach dir,$(SRCDIRS), -I../$(dir))
IFLAGS += -I../ -I./
CXXFLAGS += $(IFLAGS)
# CXXFLAGS += -D $(LOG_LEVEL)
# CXXFLAGS += -fsanitize=address
# CXXFLAGS += -fconcepts-diagnostics-depth=2
SRC = $(wildcard *.cpp) $(wildcard */*.cpp)
# OBJECTS = $(SRC:%.cpp=$(OBJECT_DIR)/%.o)
OBJECTS = $($(notdir SRC):%.cpp=$(OBJECT_DIR)/%.o)
WEBOBJECTS = $($(notdir SRC):%.cpp=$(WEBOBJECT_DIR)/%.o)
OBJECT_DIRS = $(foreach dir,$(SRCDIRS), $(OBJECT_DIR)/$(dir))
WEBOBJECT_DIRS = $(foreach dir,$(SRCDIRS), $(WEBOBJECT_DIR)/$(dir))
DEPENDS = ${OBJECTS:.o=.d}
FMT_MESSAGE="\e[1;34m%s\e[0m %s %s %s\n"
default:
@printf $(FMT_MESSAGE) "CXXFLAGS = " "$(CXXFLAGS)"
@printf $(FMT_MESSAGE) "LDFLAGS = " "$(LDFLAGS)"
default: CXXFLAGS += -include pch.hpp
default: $(EXEC)
web: CXXFLAGS += $(WEBCXXFLAGS)
web: $(WEBEXEC)
webDebug: WEBLDFLAGS += -sASSERTIONS
webDebug: CXXFLAGS += $(WEBCXXDEBUGFLAGS)
webDebug: web
webO0: CXXFLAGS += $(WEBCXXFLAGS) -O0
webO0: web
webO1: CXXFLAGS += $(WEBCXXFLAGS) -O1
webO1: web
webO2: CXXFLAGS += $(WEBCXXFLAGS) -O2
webO2: web
webO3: CXXFLAGS += $(WEBCXXFLAGS) -O3
webO3: web
webrun: CXXFLAGS += $(WEBCXXFLAGS)
webrun: $(WEBHTML)
cd $(WEBOBJECT_DIR) && emrun $(WEBHTML)
.PHONY: release
release: CXXFLAGS += -O3
release: default
O0: CXXFLAGS += -O0
O0: default
O1: CXXFLAGS += -O1
O1: default
O2: CXXFLAGS += -O2
O2: default
O3: CXXFLAGS += -O3
O3: default
# rule for the executable
$(EXEC): $(OBJECT_DIRS) $(OBJECTS)
$(CXX) $(OBJECTS) -o $@ $(CXXFLAGS) $(LDFLAGS)
$(WEBHTML) $(WEBEXEC): $(WEBOBJECT_DIRS) $(WEBOBJECTS)
$(WEBCXX) $(WEBOBJECTS) -o $@ $(CXXFLAGS) $(LDFLAGS) $(WEBLDFLAGS)
# include the makefiles generated by the -M flag
-include $(DEPENDS)
# rule for all ../build/*.o files
$(OBJECT_DIR)/%.o: $(shell echo $<) %.cpp
@printf $(FMT_MESSAGE) "Building:" $< "->" $@
@$(CXX) -c $< -o $@ $(CXXFLAGS) $(LDFLAGS)
$(WEBOBJECT_DIR)/%.o: $(shell echo $<) %.cpp
@printf $(FMT_MESSAGE) "Building:" $< "->" $@
@$(WEBCXX) -c $< -o $@ $(CXXFLAGS) $(LDFLAGS)
$(OBJECT_DIRS) $(WEBOBJECT_DIRS):
mkdir -p $@
#
# Extras Options
#
# with debug flags
.PHONY += debug run gdb pch clean docs
debug: CXXFLAGS += $(CXXDEBUGFLAGS)
debug: default
# make with debug flags and run afterwards
run: CXXFLAGS += $(CXXDEBUGFLAGS)
run: default
-pwd; cd $(shell dirname $(EXEC)); pwd; ./$(shell basename $(EXEC))
# with debug flags and run gnu debugger
gdb: CXXFLAGS += -g
gdb: default
gdb $(EXEC) -ex "layout src"
# build pch
pch:
$(CXX) pch.hpp -std=c++23 -O3 -g $(IFLAGS)
# remove all object and dependecy files
clean:
-rm -r $(OBJECT_DIR)
-rm -r $(WEBOBJECT_DIR)
docs:
doxygen .doxygen_config

131
src/algorithms/tebd.cpp Normal file
View File

@ -0,0 +1,131 @@
#include "tebd.hpp"
#include "type.hpp"
#include "util.hpp"
#include <cstdio>
#include <ostream>
#include <unordered_map>
#include <print>
template<typename InType, typename OutType>
std::vector<std::shared_ptr<OutType>> forEachUnique(std::function<OutType(const InType&)> f, const std::vector<std::shared_ptr<InType>>& inRange) {
std::unordered_map<std::shared_ptr<InType>, std::shared_ptr<OutType>> cache;
std::vector<std::shared_ptr<OutType>> outRange;
outRange.reserve(inRange.size());
for (const auto& inPtr : inRange) {
if (cache.contains(inPtr)) {
outRange.push_back(cache.at(inPtr));
} else {
std::shared_ptr<OutType> outPtr = std::make_shared<OutType>(f(*inPtr));
// cache.emplace(std::make_pair(inPtr, outPtr));
cache.emplace(inPtr, outPtr);
outRange.push_back(outPtr);
}
}
return outRange;
}
template<typename T>
e::Tensor<T, 4> getTimeEvolutionMatrix(const e::MatrixX<T>& bond, T dt, unsigned localDim) {
e::MatrixX<T> U = (-bond * dt).exp();
// std::cout << "U = \n" << U << "\n\n";
return bondMatrix2Tensor<T>(U, localDim);
}
template<typename T>
Bonds<T> getTimeEvolutionMatrices(const std::vector<std::shared_ptr<e::MatrixX<T>>>& bonds, T dt, unsigned int localDim) {
std::function<e::Tensor<T, 4>(const e::MatrixX<T>&)> f = std::bind(getTimeEvolutionMatrix<T>, std::placeholders::_1, dt, localDim);
std::vector<std::shared_ptr<e::Tensor<T, 4>>> U_Bonds = forEachUnique(f, bonds);
return U_Bonds;
}
template<typename T>
void updateBond(MPS<T>& psi, size_t i, const e::Tensor<T, 4>& U, unsigned chiMax, T eps) {
// std::println("BOND {}", i);
auto theta = psi.getTheta2(i); // vL i j vR
// apply U: i j i* j*
// contraction gives vL vR i j -> shuffle to vL i j vR
// std::cout << theta.dimension(2) << " - " << U.dimension(2) << " / " << theta.dimensions() << std::endl;
e::Tensor<T, 4> a = theta.contract(U, e::array<iP, 2>({iP{1, 2}, iP{2, 3}}));
e::Tensor<T, 4> b = a.shuffle(e::array<int, 4>{0, 2, 3, 1});
// e::Tensor<T, 4> b = a.shuffle(e::array<int, 4>{2, 0, 1, 3});
// printTensor(b, "Utheta");
// SVD and truncation
auto [Ai, Sj, Bj] = split_truncate_theta<T>(b, chiMax, eps);
// printTensor(Ai, "Ai");
// printTensor(Sj, "Sj");
// printTensor(Bj, "Bj");
// std::cout << "a: " << a.dimensions() << "\nA: " << Ai.dimensions() << "\nS: " << Sj.dimensions() << "\nB: " << Bj.dimensions() << std::endl;
// renormalize to Bi
e::Tensor<T, 1> SiInv = 1. / psi.S(i);
e::Tensor<T, 2> SiDiv = diagonal(SiInv);
e::Tensor<T, 3> Gi = SiDiv.contract(Ai, e::array<iP, 1>{iP{1, 0}}); // vL [vL], [vL] i vC
// printTensor(SiDiv, "SiDiv");
// printTensor(Gi, "Gi");
// e::Tensor<T, 0> SS = Sj.contract(Sj.conjugate(), e::array<iP, 1>{iP{0,0}});
// std::println("Bond {:03}, Sj contracted with its conjugate = {}", i, SS(0));
// psi.B(i) = Gi.contract(diagonal(Sj), e::array<iP, 1>{iP{2, 0}}); // vL i [vC], [vC] vC
// psi.B(i+1) = std::move(Bj);
// psi.S(i+1) = std::move(Sj);
MPS<dtype>::BTensor_t Bi = Gi.contract(diagonal(Sj), e::array<iP, 1>{iP{2, 0}}); // vL i [vC], [vC] vC
// e::Tensor<bool, 0> equal = (psi.B(i) == Bi).all();
// printTensor(psi.B(i), "old Bi");
// printTensor(Bi, "new Bi");
psi.B(i) = Bi;
psi.B(i+1) = Bj;
psi.S(i+1) = Sj;
}
template<typename T>
void runTEBD_brickwall(MPS<T>& psi, const Bonds<T>& bonds, unsigned chiMax, T eps, unsigned nSteps, postUpdateFunction_t<T> postUpdateFunc) {
for (unsigned n = 0; n < nSteps; n++) {
// std::print("i = {:03}\r", n);
// std::flush(std::cout);
for (unsigned row = 0; row < 2; row++) {
for (unsigned i = row; i < bonds.size(); i+=2) {
// std::cout << "i = " << i << std::endl;
updateBond(psi, i, *(bonds.at(i)), chiMax, eps);
}
}
postUpdateFunc(psi, n);
}
}
// TODO
/**
* @brief Run TEBD using a second-order decomposition
* @todo I think this is currently only correct for even system sizes => check decomposition
*/
template<typename T>
void runTEBD_secondOrder(MPS<T>& psi, const Bonds<T>& bonds, const Bonds<T>& bondsHalfDT, unsigned chiMax, T eps, unsigned nSteps, postUpdateFunction_t<T> postUpdateFunc) {
for (unsigned n = 0; n < nSteps; n++) {
// std::print("i = {:03}\r", n);
// std::flush(std::cout);
for (unsigned row = 0; row < 2; row++) {
const Bonds<T>* useBonds = &bonds;
if (row == 0 && (n != 0 || n != nSteps-1)) {
useBonds = &bondsHalfDT;
}
for (unsigned i = row; i < bonds.size(); i+=2) {
// std::cout << "i = " << i << std::endl;
updateBond(psi, i, *(useBonds->at(i)), chiMax, eps);
}
}
postUpdateFunc(psi, n);
}
}
// Instantiations
template std::vector<std::shared_ptr<e::Tensor<dtype, 4>>> forEachUnique(std::function<e::Tensor<dtype, 4> (const e::MatrixX<dtype> &)> f, const std::vector<std::shared_ptr<e::MatrixX<dtype>>>&);
template e::Tensor<dtype, 4> getTimeEvolutionMatrix<dtype>(const e::MatrixX<dtype>& bond, dtype dt, unsigned localDim);
template Bonds<dtype> getTimeEvolutionMatrices<dtype>(const std::vector<std::shared_ptr<e::MatrixX<dtype>>>& bonds, dtype dt, unsigned int localDim);
template void updateBond<dtype>(MPS<dtype>& psi, size_t i, const e::Tensor<dtype, 4>& U, unsigned chiMax, dtype eps);
template void postUpdateNoop<dtype>(const MPS<dtype>&, unsigned);
template void runTEBD_brickwall<dtype>(MPS<dtype>& psi, const Bonds<dtype>& bonds, unsigned chiMax, dtype eps, unsigned nSteps, postUpdateFunction_t<dtype> postUpdateFunc);
template void runTEBD_secondOrder<dtype>(MPS<dtype>& psi, const Bonds<dtype>& bonds, const Bonds<dtype>&, unsigned chiMax, dtype eps, unsigned nSteps, postUpdateFunction_t<dtype> postUpdateFunc);

54
src/algorithms/tebd.hpp Normal file
View File

@ -0,0 +1,54 @@
#pragma once
#include "model.hpp"
#include "mps.hpp"
#include <eigen3/Eigen/Eigen>
#include <eigen3/unsupported/Eigen/CXX11/Tensor>
#include <eigen3/unsupported/Eigen/MatrixFunctions>
/**
* @brief Apply the function f on every element if inRange, but reuse a cached result when two pointers point to the same object
*/
template<typename InType, typename OutType>
std::vector<std::shared_ptr<OutType>> forEachUnique(std::function<OutType(const InType&)> f, const std::vector<std::shared_ptr<InType>>& inRange);
template<typename T>
using Bonds = std::vector<std::shared_ptr<e::Tensor<T, 4>>>;
/**
* @brief Apply time evolution on a bond
* @details
* \f[U = \exp{ B \, \del t} \f]
*/
template<typename T>
e::Tensor<T, 4> getTimeEvolutionMatrix(const e::MatrixX<T>& bond, T dt, unsigned localDim);
template<typename T>
Bonds<T> getTimeEvolutionMatrices(const std::vector<std::shared_ptr<e::MatrixX<T>>>& bonds, T dt, unsigned int localDim);
/**
* @brief Apply U on bond i, perform SVD and truncate, update MPS
*/
template<typename T>
void updateBond(MPS<T>& psi, size_t i, const e::Tensor<T, 4>& U, unsigned chiMax, T eps);
template<typename T>
using postUpdateFunction_t = std::function<void (const MPS<T>&, unsigned)>;
template<typename T>
void postUpdateNoop(const MPS<T>&, unsigned) {};
/**
* @brief Brickwall TEBS scheme
* @details
* Apply U on every second bond starting from the first,
* then apply U on every second bond starting from the second
*
* First order scheme
*
* @param postUpdateFunc function that can be used to track expectation values after each update
*/
template<typename T>
void runTEBD_brickwall(MPS<T>& psi, const Bonds<T>& bonds, unsigned chiMax, T eps, unsigned nSteps, postUpdateFunction_t<T> postUpdateFunc);
template<typename T>
void runTEBD_secondOrder(MPS<T>& psi, const Bonds<T>& bonds, const Bonds<T>& bondsHalfDT, unsigned chiMax, T eps, unsigned nSteps, postUpdateFunction_t<T> postUpdateFunc);

194
src/benchmark.cpp Normal file
View File

@ -0,0 +1,194 @@
#include "benchmark.hpp"
#include <print>
#include <vector>
#include <fstream>
#include "mps.hpp"
#include "tebd.hpp"
#include "transverse_field_ising.hpp"
#include "util.hpp"
#include "type.hpp"
#include "stopwatch.hpp"
struct Results {
std::vector<double> E;
std::vector<double> M;
std::vector<double> S;
};
Results _runTEBD(unsigned L, double J, double g, unsigned nSteps, dtype dt, dtype eps, unsigned chiMax) {
auto psi = initMPS_spinUp<dtype>(L);
auto model = TransverseFieldIsingModel1D<dtype>(L, J, g);
// Sanity checks
throwIfNaN(eps, "eps");
throwIfNaN(dt, "dt");
throwIfLessThan(nSteps, 1u, "nSteps");
throwIfLessThan(chiMax, 1u, "chiMax");
if (psi.getL() != model.L) {
throw std::runtime_error("psi and model have different L");
}
Results result;
result.E.reserve(nSteps+1);
result.M.reserve(nSteps+1);
result.S.reserve(nSteps+1);
e::Tensor<dtype, 2> sigma_z(2, 2);
sigma_z.setValues({{1, 0}, {0, -1}});
size_t middleBond = model.L / 2;
// collect M over time in an array
auto F = postUpdateFunction_t<dtype>([&result, &sigma_z, &model, middleBond](const MPS<dtype>& psi, unsigned i){
result.M.push_back(psi.getSiteExpectationValueSum(sigma_z).real());
result.E.push_back(model.energy(psi).real());
result.S.push_back(psi.getEntanglementEntropy(middleBond).real());
});
// initial values now
F(psi, 0);
const Bonds<dtype> bonds = getTimeEvolutionMatrices(model.getH_BondsMatrices(), dt, model.localDim);
runTEBD_brickwall(psi, bonds, chiMax, eps, nSteps, F);
return result;
};
template<typename T, typename... Args>
double _runBenchmark(std::function<T(Args...)> f, Args... args) {
Stopwatch watch;
watch.start();
T result = f(std::forward<Args>(args)...);;
double time = watch.stop();
return time;
}
int runBenchmark_oneAtATime() {
// testing parameters
std::vector<unsigned> chiMaxs{5, 10, 15, 25, 30};
std::vector<dtype> epss{1e-5, 1e-15, 1e-20};
std::vector<unsigned> Ls{4, 8, 16, 20, 24, 28, 32};
std::vector<unsigned> nStepss{400, 600, 800, 1000};
std::map<std::string, double> times;
// default parameters
unsigned L = 12;
double J = 1;
double g = 0.7;
unsigned nSteps = 200;
dtype dt = 0.05;
dtype eps = 1e-10;
unsigned chiMax = 20;
// get a unique string including all test parameters
std::function<Results (unsigned, double, double, unsigned, dtype, dtype, unsigned)> f(&_runTEBD);
auto getName = [](unsigned L, unsigned nSteps, dtype eps, unsigned chiMax) {
return std::format("L={:02}_nSteps={:04}_eps={:e}_chiMax={:02}", L, nSteps, eps.real(), chiMax);
};
// test default
{
std::string name = getName(L, nSteps, eps, chiMax);
std::println("Running {}", name);
double t = _runBenchmark(f, L, J, g, nSteps, dt, eps, chiMax);
times[name] = t;
}
for (auto _chiMax : chiMaxs) {
std::string name = getName(L, nSteps, eps, _chiMax);
std::println("Running {}", name);
double t = _runBenchmark(f, L, J, g, nSteps, dt, eps, _chiMax);
times[name] = t;
}
for (auto _eps : epss) {
std::string name = getName(L, nSteps, _eps, chiMax);
std::println("Running {}", name);
double t = _runBenchmark(f, L, J, g, nSteps, dt, _eps, chiMax);
times[name] = t;
}
for (auto _L : Ls) {
std::string name = getName(_L, nSteps, eps, chiMax);
std::println("Running {}", name);
double t = _runBenchmark(f, _L, J, g, nSteps, dt, eps, chiMax);
times[name] = t;
}
for (auto _nSteps : nStepss) {
std::string name = getName(L, _nSteps, eps, chiMax);
std::println("Running {}", name);
double t = _runBenchmark(f, L, J, g, _nSteps, dt, eps, chiMax);
times[name] = t;
}
std::ofstream file("benchmark.csv", std::ios::app);
if (!file.is_open()) {
std::cerr << "Error opening file.\n";
return 1;
}
for (auto [name, t] : times) {
std::println("{}: {:02f}", name, t);
file << std::format("\"{}\",{:04f}\n", name, t);
}
file.close();
return 0;
}
int runBenchmark_all() {
// testing parameters
// 6 * 4 * 7 * 5 = 840
// ~10s each => 9000s => 2h30m
std::vector<unsigned> chiMaxs{5, 15, 25, 35};
std::vector<dtype> epss{1e-5, 1e-10, 1e-15, 1e-20};
// std::vector<unsigned> Ls{4, 8, 12, 16, 20, 24, 28, 32};
std::vector<unsigned> Ls{32, 28, 24, 20, 16, 12, 8, 4};
// std::vector<unsigned> nStepss{200, 400, 600, 800, 1000};
std::vector<unsigned> nStepss{200, 400, 600};
std::map<std::string, double> times;
// default parameters
double J = 1;
double g = 0.7;
dtype dt = 0.05;
// get a unique string including all test parameters
std::function<Results (unsigned, double, double, unsigned, dtype, dtype, unsigned)> f(&_runTEBD);
auto getName = [](unsigned L, unsigned nSteps, dtype eps, unsigned chiMax) {
return std::format("L={:02}_nSteps={:04}_eps={:e}_chiMax={:02}", L, nSteps, eps.real(), chiMax);
};
for (auto L : Ls) {
for (auto nSteps : nStepss) {
for (auto eps : epss) {
for (auto chiMax : chiMaxs) {
std::string name = getName(L, nSteps, eps, chiMax);
std::println("Running {}", name);
double t = _runBenchmark(f, L, J, g, nSteps, dt, eps, chiMax);
times[name] = t;
}
}
}
}
std::ofstream file("benchmark.csv", std::ios::app);
if (!file.is_open()) {
std::cerr << "Error opening file.\n";
return 1;
}
for (auto [name, t] : times) {
std::println("{}: {:02f}", name, t);
file << std::format("\"{}\",{:04f}\n", name, t);
}
file.close();
return 0;
}
#ifdef EMSCRIPTEN
#include <emscripten/bind.h>
namespace ems = emscripten;
EMSCRIPTEN_BINDINGS(benchmark) {
ems::function("runBenchmark_oneAtATime", &runBenchmark_oneAtATime);
ems::function("runBenchmark_all", &runBenchmark_all);
};
#endif

3
src/benchmark.hpp Normal file
View File

@ -0,0 +1,3 @@
#pragma once
int runBenchmark_all();
int runBenchmark_oneAtATime();

193
src/main.cpp Normal file
View File

@ -0,0 +1,193 @@
#include <print>
#include <iostream>
// #include <array>
#include "bose_hubbard.hpp"
#include "mps.hpp"
#include "transverse_field_ising.hpp"
#include "tebd.hpp"
#include "util.hpp"
#include "type.hpp"
#include "benchmark.hpp"
using namespace std::complex_literals;
void testSVD() {
e::Tensor<dtype, 4> t4(2, 3, 4, 5);
auto data = t4.data();
for (unsigned i = 0; i < t4.size(); i++) {
data[i] = i;
}
printTensor(t4, "t4");
auto [A, S, B] = split_truncate_theta<dtype>(t4, 20, 1e-10);
printTensor(A, "A");
printTensor(S, "S");
printTensor(B, "B");
e::Tensor<dtype, 4> t4reconstructed = A.contract(diagonal(S).contract(B, e::array<iP, 1>{iP{1, 0}}), e::array<iP, 1>{iP{2, 0}});
// this can only yield the original tensor if the normalization of the schmidt values in the split_truncate_theta function is removed
printTensor(t4reconstructed, "t4reconstructed");
}
void testBoseHubbardTEBD() {
unsigned L = 8;
unsigned nMax = 5;
auto model = BoseHubbardModel1D<dtype>(L, dtype(1.), dtype(2.50), dtype(0.5), nMax);
dtype eps = 1e-10;
unsigned chiMax = 20;
dtype dt = 0.05;
std::println("dt={}", dt);
// std::cout << "leftT:\n" << *(model.getH_Bonds().front()) << std::endl;
// std::cout << "leftT2:\n" << bondMatrix2Tensor(*(model.getH_BondsMatrices().front()), model.localDim) << std::endl;
std::cout << "middleT:\n" << *(model.getH_Bonds().at(1)) << std::endl;
//
auto bonds = getTimeEvolutionMatrices(model.getH_BondsMatrices(), dt, model.localDim);
std::cout << "N:\n" << model.getLadderOperators().getN() << std::endl;
std::cout << "a:\n" << model.getLadderOperators().getA() << std::endl;
std::cout << "aDagger:\n" << model.getLadderOperators().getADagger() << std::endl;
// std::cout << "U_Bond:\n" << *(bonds.front()) << std::endl;
// std::cout << "H_Bond:\n" << *(bonds.front()) << std::endl;
// MPS<dtype> psi = initMPS_spinUp<dtype>(L);
MPS<dtype> psi = initMPS_nParticles<dtype>(L, nMax, 2);
unsigned nSteps = 300;
// postUpdateFunction_t<dtype> fUpdate = postUpdateNoop<dtype>;
e::TensorMap<const e::Tensor<dtype, 2>> particleNumberOperator(model.getLadderOperators().getN().data(), model.localDim, model.localDim);
postUpdateFunction_t<dtype> fUpdate = [&model, &particleNumberOperator](const MPS<dtype>& psi, unsigned i) {
std::println("{:03}: N={:.2f} {}", i, psi.getSiteExpectationValueSum(particleNumberOperator).real() / model.L, psi.getChi());
};
// auto fUpdate = postUpdateFunction_t<dtype>([](const MPS<dtype>& psi, unsigned i){
// std::println("i={:03}: <psi|psi>={}", i, psi.collapse(0));
// });
std::println("E = {}", model.energy(psi).real());
// std::println("m = {}", psi.getSiteExpectationValues(sigma_z));
std::println("chiS: {:b}", psi.getChi());
psi.print();
std::println("<psi|psi>={}", psi.collapse(0));
runTEBD_brickwall(psi, bonds, chiMax, eps, nSteps, fUpdate);
std::println("E = {}", model.energy(psi).real());
std::println("<N> = {}", psi.getSiteExpectationValueSum(particleNumberOperator).real() / model.L);
// std::println("m = {}", psi.getSiteExpectationValues(sigma_z));
std::println("chiS: {:b}", psi.getChi());
// psi.print();
std::println("<psi|psi>={}", psi.collapse(0));
}
void testTFI_TEBD() {
unsigned L = 8;
std::println("Make model");
auto model = TransverseFieldIsingModel1D<dtype>(L, dtype(1.), dtype(1.5));
// auto& mpo = *(model.getH_MPO().front());
// e::Tensor<std::complex<float>, 4> mpo(3, 3, 2, 2);
// mpo.setValues({
// {
// {{0,1},{2,3}},
// {{4,5},{6,7}},
// {{8,9},{10,11}}
// }, {
// {{12,13},{14,15}},
// {{16,17},{18,19}},
// {{20,21},{22,23}},
// }, {
// {{24,25},{26,27}},
// {{28,29},{30,31}},
// {{32,33},{34,35}}
// }});
// std::cout << "first MPO: \n" << mpo << std::endl;
// std::cout << "mapped : \n" << e::Map<e::Matrix<dtype, 6, 6, e::RowMajor>>(mpo.data()) << std::endl;
// for (unsigned i = 0; i < mpo.size(); i++) {
// std::cout << mpo.data()[i] << " ";
// if ((i+1) % 2 == 0) { std::cout << "\n"; }
// if ((i+1) % 4 == 0) { std::cout << "\n"; }
// }
e::Tensor<dtype, 2> sigma_z(2, 2);
sigma_z.setValues({{1, 0}, {0, -1}});
dtype eps = 1e-10;
unsigned chiMax = 20;
dtype dt = 0.05i;
std::println("dt={}", dt);
std::cout << "leftT:\n" << *(model.getH_Bonds().front()) << std::endl;
std::cout << "leftT2:\n" << bondMatrix2Tensor(*(model.getH_BondsMatrices().front()), model.localDim) << std::endl;
std::cout << "middleT:\n" << *(model.getH_Bonds().at(1)) << std::endl;
auto bonds = getTimeEvolutionMatrices(model.getH_BondsMatrices(), dt, model.localDim);
std::cout << "U_Bond:\n" << *(bonds.front()) << std::endl;
// std::cout << "H_Bond:\n" << *(bonds.front()) << std::endl;
// MPS<dtype> psi = initMPS_spinUp<dtype>(L);
MPS<dtype> psi = initMPS_spinRight<dtype>(L);
unsigned nSteps = 100;
postUpdateFunction_t<dtype> fUpdate = postUpdateNoop<dtype>;
// auto fUpdate = postUpdateFunction_t<dtype>([](const MPS<dtype>& psi, unsigned i){
// std::println("i={:03}: <psi|psi>={}", i, psi.collapse(0));
// });
std::println("E = {}", model.energy(psi).real());
std::println("m = {}", psi.getSiteExpectationValues(sigma_z));
std::println("chiS: {:b}", psi.getChi());
psi.print();
std::println("<psi|psi>={}", psi.collapse(0));
runTEBD_brickwall(psi, bonds, chiMax, eps, nSteps, fUpdate);
// updateBond(psi, 0, *(bonds.at(0)), chiMax, eps);
// updateBond(psi, 2, *(bonds.at(2)), chiMax, eps);
// updateBond(psi, 4, *(bonds.at(4)), chiMax, eps);
// updateBond(psi, 6, *(bonds.at(6)), chiMax, eps);
// updateBond(psi, 1, *(bonds.at(1)), chiMax, eps);
// updateBond(psi, 3, *(bonds.at(3)), chiMax, eps);
// updateBond(psi, 5, *(bonds.at(5)), chiMax, eps);
// updateBond(psi, 7, *(bonds.at(7)), chiMax, eps);
std::println("E = {}", model.energy(psi).real());
std::println("m = {}", psi.getSiteExpectationValues(sigma_z));
std::println("chiS: {:b}", psi.getChi());
psi.print();
std::println("<psi|psi>={}", psi.collapse(0));
// for (unsigned i = 0; i < L; i++) {
// std::println("i={:02}: <psi|psi>={}",i, psi.collapse(i));
// }
}
int main() {
// testSVD(); return 0;
testBoseHubbardTEBD();
// testTFI_TEBD();
// return runBenchmark_oneAtATime();
// return runBenchmark_all();
// std::println("Start calculations");
// e::Tensor<dtype, 3> t1(10, 2, 10);
// t1.setZero();
// std::cout << t1 << std::endl;
// t1(0,1,0) = .69;
// std::cout << t1 << std::endl;
// e::Tensor<dtype, 3> t2(10, 2, 10);
// t2.setRandom();
// std::cout << t2 << std::endl;
// e::array<iP, 3> productDims = { iP(0, 0), iP(1, 1), iP(2, 2) };
// auto t3 = t1.contract(t2, productDims);
// std::cout << t3 << std::endl;
// double eps = 1e-8;
// size_t chi_max = 5;
// verified energy function works as expected
// unsigned L = 14;
// for (double g : {0.5, 1., 1.5}) {
// auto model2 = TFIModel<dtype>(L, dtype(1.), dtype(g));
// auto mps = initMPSspinUp<dtype>(L);
// std::println("g={} => E = {}", g, model2.energy(mps));
//
return 0;
}

90
src/model.cpp Normal file
View File

@ -0,0 +1,90 @@
#include "model.hpp"
#include "type.hpp"
#include "util.hpp"
template<typename T>
T Model1D<T>::energy(const MPS<T>& psi) const {
assert(psi.getL() == this->L);
T E = 0;
for (unsigned i = 0; i < this->L-1; i++) {
E += psi.getBondExpectationValue(*(this->H_Bonds.at(i)), i);
}
return E;
}
template<typename T>
e::Tensor<T, 4> constructMPO(const std::initializer_list<std::initializer_list<e::MatrixX<T>>>& matrices) {
size_t rows = matrices.size();
assert(rows > 0);
size_t cols = matrices.begin()->size();
assert(rows > 1);
size_t localDim = matrices.begin()->begin()->rows();
// TODO: check every matrix?
assert(localDim == matrices.begin()->begin()->cols());
e::Tensor<T, 4> mpo(rows, cols, localDim, localDim);
// for (const auto& [rowIdx, row] : std::ranges::views::enumerate(matrices)) {
size_t rowIdx = 0;
for (const auto& row : matrices) {
assert(row.size() == cols);
size_t colIdx = 0;
// for (const auto& [colIdx, mat] : std::ranges::views::enumerate(row)) {
for (const auto& mat : row) {
for (size_t i = 0; i < localDim; i++) {
for (size_t j = 0; j < localDim; j++) {
mpo(rowIdx, colIdx, i, j) = mat(i, j);
}
}
colIdx++;
}
rowIdx++;
}
return mpo;
}
template<typename T>
e::MatrixX<T> MPO_Tensor2Matrix(const e::Tensor<T, 4>& mpo) {
size_t dim = mpo.dimension(0);
assert(mpo.dimension(1) == dim);
size_t localDim = mpo.dimension(2);
assert(mpo.dimension(3) == localDim);
e::MatrixX<T> matrix(dim * localDim, dim * localDim);
for (size_t rowIdx = 0; rowIdx < dim; rowIdx++) {
for (size_t colIdx = 0; colIdx < dim; colIdx++) {
for (size_t i = 0; i < localDim; i++) {
for (size_t j = 0; j < localDim; j++) {
matrix(rowIdx * localDim + i, colIdx * localDim + j) = mpo(rowIdx, colIdx, i, j);
}
}
}
}
return matrix;
}
template<typename T>
e::Tensor<T, 4> bondMatrix2Tensor(const e::MatrixX<T>& bondMatrix, unsigned localDim) {
e::Index matDim = bondMatrix.rows();
assert(bondMatrix.cols() == matDim);
assert(localDim > 0);
assert(matDim % localDim == 0);
e::Index tensorDim = matDim / localDim;
e::Tensor<T, 4> bondTensor(tensorDim, tensorDim, localDim, localDim);
for (unsigned rowIdx = 0; rowIdx < tensorDim; rowIdx++) {
for (unsigned colIdx = 0; colIdx < tensorDim; colIdx++) {
for (unsigned i = 0; i < localDim; i++) {
for (unsigned j = 0; j < localDim; j++) {
bondTensor(rowIdx, i, colIdx, j) = bondMatrix(rowIdx * localDim + i, colIdx * localDim + j);
}
}
}
}
return bondTensor;
}
template e::Tensor<dtype, 4> constructMPO<dtype>(const std::initializer_list<std::initializer_list<e::MatrixX<dtype>>>& matrices);
template e::MatrixX<dtype> MPO_Tensor2Matrix<dtype>(const e::Tensor<dtype, 4>& mpo);
template e::Tensor<dtype, 4> bondMatrix2Tensor<dtype>(const e::MatrixX<dtype>&, unsigned);
template class Model1D<dtype>;

80
src/model.hpp Normal file
View File

@ -0,0 +1,80 @@
#pragma once
#include "mps.hpp"
#include <eigen3/Eigen/Eigen>
#include <eigen3/unsupported/Eigen/CXX11/Tensor>
#include <initializer_list>
#include <stdexcept>
namespace e = Eigen;
using namespace std::complex_literals;
/**
* @brief Construct a matrix product operator in tensor form
* @matrices N x N initializer lists containing localDim x localDim matrices
* @details
* Constructs a tensor with indices r c i j, where r and c specify the matrix and i j are indices into the matrix
*/
template<typename T>
e::Tensor<T, 4> constructMPO(const std::initializer_list<std::initializer_list<e::MatrixX<T>>>& matrices);
template<typename T>
e::MatrixX<T> MPO_Tensor2Matrix(const e::Tensor<T, 4>& mpo);
/**
* @brief Reshape a N*localDim x N*localDim matrix into a N x localDim x N x localDim tensor
*/
template<typename T>
e::Tensor<T, 4> bondMatrix2Tensor(const e::MatrixX<T>& bondMatrix, unsigned localDim);
template<typename T>
class Model1D {
public:
Model1D(size_t L, unsigned localDim) : L(L), localDim(localDim) {
if (L <= 2) { // needs two bonds to function correctly
throw std::invalid_argument("L must be larger than 2");
}
}
virtual ~Model1D() {};
inline const std::vector<std::shared_ptr<e::MatrixX<T>>>& getH_BondsMatrices() const { return H_BondsMatrices; }
inline const std::vector<std::shared_ptr<e::Tensor<T, 4>>>& getH_Bonds() const { return H_Bonds; }
inline const std::vector<std::shared_ptr<e::Tensor<T, 4>>>& getH_MPO() const { return H_MPO; }
const size_t L;
const unsigned localDim;
T energy(const MPS<T>& psi) const;
virtual std::string format() const = 0;
protected:
virtual void initH_Bonds() = 0;
virtual void initH_MPO() = 0;
/// Bonds in tensor form, used for `energy()` function
std::vector<std::shared_ptr<e::Tensor<T, 4>>> H_Bonds;
/// Bonds in matrix form, used for calculating time evolution
std::vector<std::shared_ptr<e::MatrixX<T>>> H_BondsMatrices;
std::vector<std::shared_ptr<e::Tensor<T, 4>>> H_MPO;
};
/**
* @brief Class of pauli matrices
* @details
* Intendend for static initialization
*/
template<typename T>
class PauliMatrices {
public:
PauliMatrices() :
sigmaX({{0, 1}, {1, 0}}),
sigmaY({{0if, -1if}, {1if, 0if}}),
sigmaZ({{1, 0}, {0, -1}}),
identity({{1, 0}, {0, 1}}) {}
inline const e::Matrix2<T>& getSigmaX() const { return sigmaX; }
inline const e::Matrix2<T>& getSigmaY() const { return sigmaY; }
inline const e::Matrix2<T>& getSigmaZ() const { return sigmaZ; }
inline const e::Matrix2<T>& getIdentity() const { return identity; }
private:
e::Matrix2<T> sigmaX;
e::Matrix2<T> sigmaY;
e::Matrix2<T> sigmaZ;
e::Matrix2<T> identity;
};

103
src/models/bose_hubbard.cpp Normal file
View File

@ -0,0 +1,103 @@
#include "bose_hubbard.hpp"
#include <eigen3/unsupported/Eigen/KroneckerProduct>
template<typename T>
LadderOperators<T>::LadderOperators(unsigned dim) : identity(dim, dim), aDagger(dim, dim), a(dim, dim), n(dim, dim) {
identity.setZero();
aDagger.setZero();
a.setZero();
n.setZero();
identity(0, 0) = 1;
for (unsigned i = 1; i < dim; i++) {
identity(i, i) = 1;
n(i, i) = i;
// TODO what if T = std::complex<float>?
double sqrtN = std::sqrt(static_cast<double>(i));
a(i-1, i) = sqrtN;
aDagger(i, i-1) = sqrtN;
}
}
template<typename T>
void BoseHubbardModel1D<T>::initH_Bonds() {
T tHalf = this->t * static_cast<T>(0.5);
T tFull = this->t;
/// @todo TODO figure out which is right. Doesnt t need to be halfed at the edges and not the middle?
e::MatrixX<T> N_NminusI = this->lo.getN() * (this->lo.getN() - this->lo.getIdentity());
e::MatrixX<T> singleParticlePart(
(
e::kroneckerProduct(this->lo.getIdentity(), N_NminusI)
- e::kroneckerProduct(N_NminusI, this->lo.getIdentity())
) * (this->U/static_cast<T>(2))
- e::kroneckerProduct(this->lo.getN(), this->lo.getIdentity()) * (this->mu/static_cast<T>(2))
- e::kroneckerProduct(this->lo.getIdentity(), this->lo.getN()) * (this->mu/static_cast<T>(2))
);
// TODO this is wrong, needs A and Adagger; check which t or t/half or identities are required
auto left = std::make_shared<e::MatrixX<T>>(
singleParticlePart
- tFull * e::kroneckerProduct(this->lo.getADagger(), this->lo.getA())
- tFull * e::kroneckerProduct(this->lo.getA(), this->lo.getADagger())
);
auto right = std::make_shared<e::MatrixX<T>>(
singleParticlePart
- tFull * e::kroneckerProduct(this->lo.getADagger(), this->lo.getA())
- tFull * e::kroneckerProduct(this->lo.getA(), this->lo.getADagger())
);
auto middle = std::make_shared<e::MatrixX<T>>(
singleParticlePart
- tFull * e::kroneckerProduct(this->lo.getADagger(), this->lo.getA())
- tFull * e::kroneckerProduct(this->lo.getA(), this->lo.getADagger())
);
// The matrices are N*N x N*N -> reshape to N x N x N x N tensors
unsigned N = this->localDim;
auto leftT = std::make_shared<e::Tensor<T, 4>>(N, N, N, N);
auto middleT = std::make_shared<e::Tensor<T, 4>>(N, N, N, N);
auto rightT = std::make_shared<e::Tensor<T, 4>>(N, N, N, N);
for (unsigned i1 = 0; i1 < N; i1++) {
for (unsigned i2 = 0; i2 < N; i2++) {
for (unsigned i3 = 0; i3 < N; i3++) {
for (unsigned i4 = 0; i4 < N; i4++) {
(*leftT)(i1, i2, i3, i4) = (*left)(i1 * N + i2, i3 * N + i4);
(*middleT)(i1, i2, i3, i4) = (*middle)(i1 * N + i2, i3 * N+ i4);
(*rightT)(i1, i2, i3, i4) = (*right)(i1 * N + i2, i3 * N + i4);
}
}
}
}
this->H_Bonds.reserve(this->L-1);
this->H_Bonds.push_back(leftT);
for (unsigned i = 0; i < this->L - 3; i++) { // there are L-3 middle bonds
this->H_Bonds.push_back(middleT);
}
this->H_Bonds.push_back(rightT);
// TODO: merge or remove tensor version
this->H_BondsMatrices.reserve(this->L-1);
this->H_BondsMatrices.push_back(left);
for (unsigned i = 0; i < this->L - 3; i++) { // there are L-3 middle bonds
this->H_BondsMatrices.push_back(middle);
}
this->H_BondsMatrices.push_back(right);
}
template<typename T>
void BoseHubbardModel1D<T>::initH_MPO() {
e::MatrixX<T> zero(this->localDim, this->localDim);
zero.setZero();
auto W = std::make_shared<e::Tensor<T, 4>>(std::move(constructMPO<T>({
{this->lo.getIdentity(), this->lo.getADagger(), this->lo.getA(), (this->U / 2. * (this->lo.getN() * this->lo.getN() - this->lo.getN()) - this->mu * this->lo.getN())},
{zero, zero, zero, (-this->t * this->lo.getA())},
{zero, zero, zero, (-this->t * this->lo.getADagger())},
{zero, zero, zero, this->lo.getIdentity()}
})));
// same for all sites
this->H_MPO = std::vector<std::shared_ptr<e::Tensor<T, 4>>>(this->L, W);
}
template class LadderOperators<dtype>;
template class BoseHubbardModel1D<dtype>;

View File

@ -0,0 +1,57 @@
#pragma once
#include "model.hpp"
/**
* @brief Helper class constructing identity, particle number and both ladder operators in one loop.
*/
template<typename T>
class LadderOperators {
public:
LadderOperators(unsigned dim);
inline const e::MatrixX<T>& getIdentity() const { return identity; }
inline const e::MatrixX<T>& getADagger() const { return aDagger; }
inline const e::MatrixX<T>& getA() const { return a; }
inline const e::MatrixX<T>& getN() const { return n; }
private:
e::MatrixX<T> identity;
e::MatrixX<T> aDagger;
e::MatrixX<T> a;
e::MatrixX<T> n;
};
/**
* @brief Bose-Hubbard Model in 1 dimension
* @details
* The middle bonds are:
* TODO \f[ B_i = J \, \sigma^x_i \otimes \sigma^x_{i+1} + \frac{g}{2} \,\sigma^z_i \otimes I + \frac{g}{2} \, I \otimes \sigma^z_{i+1} \f]
*
* The MPO is:
* \f[ W = \begin{pmatrix} I & \hat{a}^\dagger & \hat{a} & \frac{U}{2}\hat{n}(\hat{n} - I) - \mu\hat{n} \\ 0 & 0 & 0 & -t \hat{a} \\ 0 & 0 & 0 & -t \hat{a}^\dagger\\ 0 & 0 & 0 & I \end{pmatrix} \f]
*/
template<typename T>
class BoseHubbardModel1D : public Model1D<T> {
public:
BoseHubbardModel1D(size_t L, T U, T t, T mu, size_t nMax) : Model1D<T>(L, nMax+1), U(U), t(t), mu(mu), lo(nMax+1) {
this->initH_Bonds();
this->initH_MPO();
}
inline const T& getU() const { return U; }
inline const T& gett() const { return t; }
inline const T& getmu() const { return mu; }
inline const LadderOperators<T>& getLadderOperators() const { return lo; }
virtual std::string format() const override {
return std::format("BoseHubbardModel1D(L={},U={},t={},mu={})", this->L, this->U, this->t, this->mu);
}
protected:
void initH_Bonds() override;
void initH_MPO() override;
private:
T U;
T t;
T mu;
LadderOperators<T> lo;
};

View File

@ -0,0 +1,74 @@
#include "transverse_field_ising.hpp"
#include <eigen3/unsupported/Eigen/KroneckerProduct>
#include "model.hpp"
template<typename T>
PauliMatrices<T> TransverseFieldIsingModel1D<T>::pm;
template<typename T>
void TransverseFieldIsingModel1D<T>::initH_Bonds() {
T gHalf = this->g * static_cast<T>(0.5);
// TODO: improve code or directly initialize the tensors
auto left = std::make_shared<e::MatrixX<T>>(
e::kroneckerProduct(pm.getSigmaX(), pm.getSigmaX()) * (-J) -
this->g * e::kroneckerProduct(pm.getSigmaZ(), pm.getIdentity()) -
gHalf * e::kroneckerProduct(pm.getIdentity(), pm.getSigmaZ())
);
auto right = std::make_shared<e::MatrixX<T>>(
e::kroneckerProduct(pm.getSigmaX(), pm.getSigmaX()) * (-J) -
gHalf * e::kroneckerProduct(pm.getSigmaZ(), pm.getIdentity()) -
this->g * e::kroneckerProduct(pm.getIdentity(), pm.getSigmaZ())
);
auto middle = std::make_shared<e::MatrixX<T>>(
e::kroneckerProduct(pm.getSigmaX(), pm.getSigmaX()) * (-J) -
gHalf * e::kroneckerProduct(pm.getSigmaZ(), pm.getIdentity()) -
gHalf * e::kroneckerProduct(pm.getIdentity(), pm.getSigmaZ())
);
auto leftT = std::make_shared<e::Tensor<T, 4>>(2, 2, 2, 2);
auto middleT = std::make_shared<e::Tensor<T, 4>>(2, 2, 2, 2);
auto rightT = std::make_shared<e::Tensor<T, 4>>(2, 2, 2, 2);
for (unsigned i1 = 0; i1 < 2; i1++) {
for (unsigned i2 = 0; i2 < 2; i2++) {
for (unsigned i3 = 0; i3 < 2; i3++) {
for (unsigned i4 = 0; i4 < 2; i4++) {
(*leftT)(i1, i2, i3, i4) = (*left)(i1 * 2 + i2, i3 * 2 + i4);
(*middleT)(i1, i2, i3, i4) = (*middle)(i1 * 2 + i2, i3 * 2+ i4);
(*rightT)(i1, i2, i3, i4) = (*right)(i1 * 2 + i2, i3 * 2 + i4);
}
}
}
}
// *leftT = Bond_Matrix2Tensor(*left, 2);
// *middleT = Bond_Matrix2Tensor(*middle, 2);
// *rightT = Bond_Matrix2Tensor(*right, 2);
this->H_Bonds.reserve(this->L-1);
this->H_Bonds.push_back(leftT);
for (unsigned i = 0; i < this->L - 3; i++) { // there are L-3 middle bonds
this->H_Bonds.push_back(middleT);
}
this->H_Bonds.push_back(rightT);
// TODO: merge or remove tensor version
this->H_BondsMatrices.reserve(this->L-1);
this->H_BondsMatrices.push_back(left);
for (unsigned i = 0; i < this->L - 3; i++) { // there are L-3 middle bonds
this->H_BondsMatrices.push_back(middle);
}
this->H_BondsMatrices.push_back(right);
}
template<typename T>
void TransverseFieldIsingModel1D<T>::initH_MPO() {
e::Matrix2<T> zero(2, 2);
zero.setZero();
auto W = std::make_shared<e::Tensor<T, 4>>(std::move(constructMPO<T>({
{this->pm.getIdentity(), this->pm.getSigmaZ(), (this->g * this->pm.getSigmaX())},
{zero, zero, (-this->J * this->pm.getSigmaZ())},
{zero, zero, this->pm.getIdentity()}
})));
// same for all sites
this->H_MPO = std::vector<std::shared_ptr<e::Tensor<T, 4>>>(this->L, W);
}
template class TransverseFieldIsingModel1D<dtype>;

View File

@ -0,0 +1,36 @@
#pragma once
#include "model.hpp"
/**
* @brief Transverse Field Ising Model in 1 dimension
* @details
* The middle bonds are:
* \f[ B_i = J \, \sigma^x_i \otimes \sigma^x_{i+1} + \frac{g}{2} \,\sigma^z_i \otimes I + \frac{g}{2} \, I \otimes \sigma^z_{i+1} \f]
*
* The MPO is:
* \f[ W = \begin{pmatrix} I & \sigma_z & g \, \sigma_x \\ 0 & 0 & J\,\sigma_z \\ 0 & 0 & I \end{pmatrix} \f]
*/
template<typename T>
class TransverseFieldIsingModel1D : public Model1D<T> {
public:
TransverseFieldIsingModel1D(size_t L, T J, T g) : Model1D<T>(L, 2), J(J), g(g) {
// std::println("TFIModel: L={}, J={}, g={}", L, J, g);
this->initH_Bonds();
this->initH_MPO();
}
inline const T& getJ() const { return J; }
inline const T& getg() const { return g; }
virtual std::string format() const override {
return std::format("TransverseFieldIsingModel1D(L={},J={},g={})", this->L, this->J, this->g);
}
protected:
void initH_Bonds() override;
void initH_MPO() override;
private:
T J;
T g;
static PauliMatrices<T> pm;
};

154
src/mpl_colors.py Normal file
View File

@ -0,0 +1,154 @@
import matplotlib as mpl
import matplotlib.pyplot as plt
from cycler import cycler
# Palette
P = {
"bg0": "#282828",
"bg0-hard": "#1d2021",
"bg0-soft": "#32302f",
"bg1": "#3c3836",
"bg2": "#504945",
"bg3": "#665c54",
"bg4": "#7c6f64",
"fg0": "#fbf1c7",
"fg0-hard": "#f9f5d7",
"fg0-soft": "#f2e5bc",
"fg1": "#ebdbb2",
"fg2": "#d5c4a1",
"fg3": "#bdae93",
"fg4": "#a89984",
"dark-red": "#cc241d",
"dark-green": "#98971a",
"dark-yellow": "#d79921",
"dark-blue": "#458588",
"dark-purple": "#b16286",
"dark-aqua": "#689d6a",
"dark-orange": "#d65d0e",
"dark-gray": "#928374",
"light-red": "#fb4934",
"light-green": "#b8bb26",
"light-yellow": "#fabd2f",
"light-blue": "#83a598",
"light-purple": "#d3869b",
"light-aqua": "#8ec07c",
"light-orange": "#f38019",
"light-gray": "#a89984",
"alt-red": "#9d0006",
"alt-green": "#79740e",
"alt-yellow": "#b57614",
"alt-blue": "#076678",
"alt-purple": "#8f3f71",
"alt-aqua": "#427b58",
"alt-orange": "#af3a03",
"alt-gray": "#7c6f64",
}
COLORS_LIGHT = [
P["light-blue"],
P["light-orange"],
P["light-green"],
P["light-red"],
P["light-purple"],
P["light-yellow"],
P["light-aqua"],
P["light-gray"]]
COLORS_DARK = [
P["dark-blue"],
P["dark-orange"],
P["dark-green"],
P["dark-red"],
P["dark-purple"],
P["dark-yellow"],
P["dark-aqua"],
P["dark-gray"]]
COLORS_ALT = [
P["alt-blue"],
P["alt-orange"],
P["alt-green"],
P["alt-red"],
P["alt-purple"],
P["alt-yellow"],
P["alt-aqua"],
P["alt-gray"]]
COLORS = COLORS_LIGHT
def set_colorscheme(variant="dark"):
global COLORS
if variant == "dark":
FIG_BG_COLOR = P["bg0"]
PLT_FG_COLOR = P["fg0"]
PLT_BG_COLOR = P["bg0"]
PLT_GRID_COLOR = P["bg2"]
LEGEND_FG_COLOR = PLT_FG_COLOR
LEGEND_BG_COLOR = P["bg1"]
LEGEND_BORDER_COLOR = P["bg2"]
COLORS = COLORS_LIGHT
else:
FIG_BG_COLOR = P["fg0"]
PLT_FG_COLOR = P["bg0"]
PLT_BG_COLOR = P["fg0"]
PLT_GRID_COLOR = P["fg2"]
LEGEND_FG_COLOR = PLT_FG_COLOR
LEGEND_BG_COLOR = P["fg1"]
LEGEND_BORDER_COLOR = P["fg2"]
COLORS = COLORS_DARK
if variant == "alt":
COLORS = COLORS_ALT
color_rcParams = {
'axes.edgecolor': PLT_FG_COLOR,
'axes.facecolor': PLT_BG_COLOR,
'axes.labelcolor': PLT_FG_COLOR,
'axes.titlecolor': 'auto',
# 'axes.prop_cycle': cycler('color', ['#1f77b4', '#ff7f0e', '#2ca02c', '#d62728', '#9467bd', '#8c564b', '#e377c2', '#7f7f7f', '#bcbd22', '#17becf']),
'axes.prop_cycle': cycler('color', COLORS),
# 'axes3d.xaxis.panecolor': (0.95, 0.95, 0.95, 0.5),
# 'axes3d.yaxis.panecolor': (0.9, 0.9, 0.9, 0.5),
# 'axes3d.zaxis.panecolor': (0.925, 0.925, 0.925, 0.5),
# 'boxplot.boxprops.color': 'black',
# 'boxplot.capprops.color': 'black',
# 'boxplot.flierprops.color': 'black',
# 'boxplot.flierprops.markeredgecolor': 'black',
# 'boxplot.flierprops.markeredgewidth': 1.0,
# 'boxplot.flierprops.markerfacecolor': 'none',
# 'boxplot.meanprops.color': 'C2',
# 'boxplot.meanprops.markeredgecolor': 'C2',
# 'boxplot.meanprops.markerfacecolor': 'C2',
# 'boxplot.meanprops.markersize': 6.0,
# 'boxplot.medianprops.color': 'C1',
# 'boxplot.whiskerprops.color': 'black',
'figure.edgecolor': PLT_BG_COLOR,
'figure.facecolor': PLT_BG_COLOR,
# 'figure.figsize': [6.4, 4.8],
# 'figure.frameon': True,
# 'figure.labelsize': 'large',
'grid.color': PLT_GRID_COLOR,
# 'hatch.color': 'black',
'legend.edgecolor': LEGEND_BORDER_COLOR,
'legend.facecolor': LEGEND_BG_COLOR,
'xtick.color': PLT_FG_COLOR,
'ytick.color': PLT_FG_COLOR,
'xtick.labelcolor': PLT_FG_COLOR,
'ytick.labelcolor': PLT_FG_COLOR,
# 'lines.color': 'C0',
'text.color': PLT_FG_COLOR,
}
for k, v in color_rcParams.items():
plt.rcParams[k] = v
# override single char codes
mpl.colors.get_named_colors_mapping()["b"] = COLORS[0]
mpl.colors.get_named_colors_mapping()["o"] = COLORS[1]
mpl.colors.get_named_colors_mapping()["g"] = COLORS[2]
mpl.colors.get_named_colors_mapping()["r"] = COLORS[3]
mpl.colors.get_named_colors_mapping()["m"] = COLORS[4]
mpl.colors.get_named_colors_mapping()["y"] = COLORS[5]
mpl.colors.get_named_colors_mapping()["c"] = COLORS[6]
mpl.colors.get_named_colors_mapping()["k"] = P["fg0"]
mpl.colors.get_named_colors_mapping()["w"] = P["bg0"]

289
src/mps.cpp Normal file
View File

@ -0,0 +1,289 @@
#include "mps.hpp"
#include "type.hpp"
#include "util.hpp"
#include <print>
template<typename T>
MPS<T>::MPS(std::vector<BTensor_t>&& Bs, std::vector<STensor_t>&& Ss) {
this->L = Bs.size();
assert(this->L > 0);
assert(Ss.size() == this->L);
this->Bs = std::move(Bs);
this->Ss = std::move(Ss);
}
template<typename T>
MPS<T>::BTensor_t MPS<T>::getTheta1(size_t siteIdx) const {
assert(siteIdx < L);
auto diag = diagonal(this->S(siteIdx));
return diag.contract(this->B(siteIdx), e::array<iP, 1>{iP(1, 0)});
}
template<typename T>
e::Tensor<T, 4> MPS<T>::getTheta2(size_t bondIdx) const {
size_t j = bondIdx + 1;
assert(j < L);
auto theta1 = this->getTheta1(bondIdx);
return theta1.contract(this->B(j), e::array<iP, 1>{iP(2, 0)});
}
template<typename T>
std::vector<unsigned> MPS<T>::getChi() const {
std::vector<unsigned> chis(this->L-1);
for (size_t i = 0; i < (L - 1); i++) {
chis.at(i) = this->B(i).dimension(2);
}
return chis;
}
template<typename T>
T MPS<T>::getSiteExpectationValue(const e::Tensor<T, 2>& op, size_t siteIdx) const {
auto theta1 = this->getTheta1(siteIdx);
// optheta: vL, vR, i
e::Tensor<T, 3> opTheta = theta1.contract(op, e::array<iP, 1>{iP(1, 1)});
e::Tensor<T, 0> expVal = opTheta.contract(theta1.conjugate(), e::array<iP, 3>{iP(0, 0), iP(2, 1), iP(1, 2)});
T retVal = expVal(0);
return retVal;
}
template<typename T>
std::vector<T> MPS<T>::getSiteExpectationValues(const e::Tensor<T, 2>& op) const {
std::vector<T> results;
results.reserve(this->L);
for (size_t i = 0; i < this->L; i++) {
results.push_back(this->getSiteExpectationValue(op, i));
}
return results;
}
template<typename T>
T MPS<T>::getSiteExpectationValueSum(const e::Tensor<T, 2>& op) const {
dtype expVal = 0;
for (unsigned i = 0; i < this->getL(); i++) {
expVal += this->getSiteExpectationValue(op, i);
}
return expVal;
}
template<typename T>
T MPS<T>::getBondExpectationValue(const e::Tensor<T, 4>& op, size_t bondIdx) const {
auto theta2 = this->getTheta2(bondIdx); // vL i j vR
// vL [i] [i+1] vR x i i+1 [i*] [i+1*]
e::Tensor<T, 4> opTheta = theta2.contract(op, e::array<iP, 2>{iP{1, 2}, iP{2, 3}});
// [vL] [vR] [i] [i+1] x [vL*] [i*] [i+1*] [vR*]
e::Tensor<T, 0> expVal = opTheta.contract(theta2.conjugate(), e::array<iP, 4>{iP(0, 0), iP(1, 3), iP(2, 1), iP{3, 2}});
T retVal = expVal(0);
return retVal;
}
template<typename T>
T MPS<T>::getEntanglementEntropy(size_t bondIndex) const {
assert(bondIndex < this->L-1);
const auto& S = this->S(bondIndex+1);
STensor_t Ssquared = S * S;
e::Tensor<dtype, 0> retVal = -(Ssquared * Ssquared.log()).sum();
return retVal(0);
}
template<typename T>
T MPS<T>::collapse(size_t siteIndex) const {
// const STensor_t& S = this->S(siteIndex);
// e::Tensor<T, 0> SS = S.contract(S.conjugate(), e::array<iP, 1>{iP{0,0}});
// std::println("S[{}] contracted with its conjugate = {}", siteIndex, SS(0));
auto theta = this->getTheta1(siteIndex);
// vR vR*
e::Tensor<dtype, 2> thetaTheta = theta.contract(theta.conjugate(), e::array<iP, 2>{iP{0, 0}, iP{1, 1}});
for (size_t i = siteIndex+1; i < this->L; i++) {
// vL vR vL* vR*
e::Tensor<dtype, 4> BB = this->B(i).contract(this->B(i).conjugate(), e::array<iP, 1>{iP{1, 1}});
e::Tensor<dtype, 2> thetaTheta2 = thetaTheta.contract(BB, e::array<iP, 2>{iP{0, 0}, iP{1, 2}});
std::swap(thetaTheta, thetaTheta2);
}
e::Tensor<dtype, 1> id(thetaTheta.dimension(0));
id.setConstant(1);
auto idDiag = diagonal(id);
e::Tensor<dtype, 0> retval = thetaTheta.contract(idDiag, e::array<iP, 2>{iP{0, 0}, iP{1, 1}});
return retval(0);
}
template<typename T>
e::MatrixX<T> tensor2Matrix(const e::Tensor<T, 4>& t) {
size_t d0 = t.dimension(0);
size_t d1 = t.dimension(1);
size_t d2 = t.dimension(2);
size_t d3 = t.dimension(3);
e::MatrixX<T> matrix(d0 * d1, d2 * d3);
for (size_t i0 = 0; i0 < d0; i0++) {
for (size_t i1 = 0; i1 < d1; i1++) {
for (size_t i2 = 0; i2 < d2; i2++) {
for (size_t i3 = 0; i3 < d3; i3++) {
// Variant 1 (consistent with what a np.reshape would give)
// matrix(i0 * d1 + i1, i2 * d3 + i3) = t(i0, i1, i2, i3);
// Variant 2 (consistent with what using Eigen::Map on Tensor<ColumnMajor> produces)
matrix(i0 * d1 + i1, i3 * d2 + i2) = t(i0, i1, i2, i3);
}
}
}
}
return matrix;
}
template<typename dtype>
std::tuple<e::Tensor<dtype, 3>, e::Tensor<dtype, 1>, e::Tensor<dtype, 3>>
split_truncate_theta(const e::Tensor<dtype, 4>& theta, size_t chiMax, dtype eps) {
// Unpack dimensions of the input tensor
e::Index chivL = theta.dimension(0);
e::Index dL = theta.dimension(1);
e::Index dR = theta.dimension(2);
e::Index chivR = theta.dimension(3);
// Reshape theta to 2D matrix for the SVD
using MatX = e::Matrix<dtype, -1, -1>;
/// todo (DONE): Possible optimization: either use a Map and later adjust the matrix to tensor loops, or globally use RowMajor memory layout?
// The map approach produces the same Matrix is tensor2Matrix with Variant 2
MatX theta_mat = e::Map<const MatX>(theta.data(), chivL * dL, dR * chivR);
// MatX theta_mat = tensor2Matrix(theta);
// std::cout << "theta:\n" << theta << "theta_mat\n": << theta_mat << std::endl;
// std::cout << "theta_mat = " << std::endl;
// std::cout << theta_mat << std::endl;
// perform SVD: theta = U * diag(S) * V^T
constexpr int SVDOptions = e::ComputeThinU | e::ComputeThinV;
e::JacobiSVD<MatX, SVDOptions> svd(theta_mat);
// e::BDCSVD<MatX, SVDOptions> svd(theta_mat);
const MatX& U = svd.matrixU();
const e::VectorX<dtype>& S = svd.singularValues();
const MatX& V = svd.matrixV().transpose();
// determine the truncation dimension based on chi_max and eps
int chivC = static_cast<int>(std::min(chiMax, static_cast<size_t>((S.real().array() > std::real(eps)).count())));
if (chivC < 1) {
std::println("WARNING: chivC = 0. Setting to 1");
chivC = 1;
}
assert(chivC >= 1);
assert(chivC <= S.size());
// NOT NEEDED BECAUSE ITS ALREADY RETURNED IN ORDER
// sort indices by singular values (descending order) and truncate
// std::vector<size_t> indices(S.size());
// std::iota(indices.begin(), indices.end(), 0); // fill indices with [0, 1, 2, ..., Y.size()-1]
// std::partial_sort(indices.begin(), indices.begin() + chivC, indices.end(),
// [&](size_t a, size_t b) { return S(a) > S(b); });
// std::println("chivL={}, dL={}, dR={}, chivR={}", chivL, dL, dR, chivR);
// std::println("theta_mat.shape = {}, {}", theta_mat.rows(), theta_mat.cols());
// std::println("U.shape = {}, {}", U.rows(), U.cols());
// std::println("S.shape = {}, {}", S.asDiagonal().rows(), S.asDiagonal().cols());
// std::println("V.shape = {}, {}", V.rows(), V.cols());
// std::println("S.shape = {}", S.rows());
// std::println("chivC={}", chivC);
// TEST
// MatX theta2 = U * S.asDiagonal() * V;
// std::cout << "theta2" << std::endl << theta2 << std::endl;
// for (int i = 0; i < theta2.size(); i++) {
// std::print("{} ", theta2.data()[i]);
// }
// std::println();
// std::cout << "U" << std::endl << U << std::endl;
// std::cout << "U[0, 1]=" << U(0, 1) << std::endl;
// std::cout << "S" << std::endl << S << std::endl;
// std::cout << "V" << std::endl << V << std::endl;
// MatX Xtrunc(dL * chivL, chivC)
// Truncated versions for debugging
// MatX Utrunc = U(e::placeholders::all, e::seq(0, chivC));
// e::VectorXd Strunc = S(e::seq(0, chivC));
// MatX Vtrunc = V(e::seq(0, chivC), e::placeholders::all);
// std::cout << "Utrunc" << std::endl << Utrunc << std::endl;
// std::cout << "Strunc" << std::endl << Strunc << std::endl;
// std::cout << "Vtrunc" << std::endl << Vtrunc << std::endl;
// std::println("U.shape = {}, {}", Utrunc.rows(), Utrunc.cols());
// std::println("S.shape = {}, {}", Strunc.asDiagonal().rows(), Strunc.asDiagonal().cols());
// std::println("V.shape = {}, {}", Vtrunc.rows(), Vtrunc.cols());
// MatX theta3 = Utrunc * Strunc.asDiagonal() * Vtrunc;
// std::cout << "theta3" << std::endl << theta2 << std::endl;
// for (int i = 0; i < theta3.size(); i++) {
// std::print("{} ", theta3.data()[i]);
// }
// std::println();
// reshape X and Z into 3D tensors A and B
e::Tensor<dtype, 3> A(chivL, dL, chivC);
e::Tensor<dtype, 3> B(chivC, dR, chivR);
for (int ivC = 0; ivC < chivC; ivC++) {
for (int ivL = 0; ivL < chivL; ivL++) {
for (int idL = 0; idL < dL; idL++) {
// Variant 1: When the Matrix is constructed with matrix(i0 * d1 + i1) = tensor(i0, i1, i2, i3);
// dtype value = U(idL + ivL * dL, ivC);
// std::println("A(ivL={}, idL={}, ivC={}) = U(idL={}+ivL={}*dL={}={}, ivC={}) = {}",
// ivL, idL, ivC, idL, ivL, dL, idL+ivL*dL, ivC, value);
// Variant 2: When the Matrix is constructed with matrix(i1 * d0 + i0) = tensor(i0, i1, i2, i3);
dtype value = U(ivL + idL * chivL, ivC);
// std::println("A(ivL={}, idL={}, ivC={}) = U(ivL={}+idL={}*chivL={}={}, ivC={}) = {}",
// ivL, idL, ivC, ivL, idL, chivL, ivL+idL*chivL, ivC, value
// );
A(ivL, idL, ivC) = value;
}
}
for (int ivR = 0; ivR < chivR; ivR++) {
for (int idR = 0; idR < dR; idR++) {
// dtype value = V(ivC, ivR + idR * chivR);
// Variant 1: When the Matrix is constructed with matrix(..., i2 * d3 + i3) = tensor(i0, i1, i2, i3);
// dtype value = V(ivC, ivR + idR * chivR);
// std::println("B(ivC={}, idR={}, ivR={}) = V(ivC={}, ivR={}+idR={}*chivR={}={}) = {}",
// ivC, idR, ivR, ivC, idR, ivR, dR, ivR+idR*chivR, value);
// Variant 2: When the Matrix is constructed with matrix(..., i3 * d2 + i2) = tensor(i0, i1, i2, i3);
dtype value = V(ivC, idR + ivR * dR);
// std::println("B(ivC={}, idR={}, ivR={}) = V(ivC={}, idR={}+ivR={}*dR={}={}) = {}",
// ivC, idR, ivR, ivC, idR, ivR, dR, idR+ivR*dR, value
// );
B(ivC, idR, ivR) = value;
}
}
}
// S: truncate, normalize, and put into a Tensor
auto norm = S.head(chivC).norm();
e::Tensor<dtype, 1> Sret(chivC);
for (int ivC = 0; ivC < chivC; ivC++) {
Sret(ivC) = S(ivC) / norm;
// skip normalization for testing if the SVD returns the original matrix after truncation:
// Sret(ivC) = S(ivC);
}
// Verify that A * S * B gives the original matrix back
// std::cout << "A" << std::endl << A << std::endl;
// std::println("B.shape = {}, {}, {}", B.dimension(0), B.dimension(1), B.dimension(2));
// std::cout << "B" << std::endl << B << std::endl;
// std::println("A.shape = {}, {}, {}", A.dimension(0), A.dimension(1), A.dimension(2));
// e::Tensor<dtype, 2> Sdiag = diagonal(Sret);
// std::println("Sdiag.shape = {}, {}", Sdiag.dimension(0), Sdiag.dimension(1));
// e::Tensor<dtype, 3> theta4 = A.contract(Sdiag, e::array<iP, 1>{iP(2, 0)});
// e::Tensor<dtype, 4> theta5 = theta4.contract(B, e::array<iP, 1>{iP(2, 0)});
// std::cout << "theta5" << std::endl << theta5 << std::endl;
// std::println("theta5.shape = {}, {}, {} {}", theta5.dimension(0), theta5.dimension(1), theta5.dimension(2), theta5.dimension(3));
return {A, Sret, B};
}
// instantiations
template class MPS<dtype>;
template std::tuple<e::Tensor<dtype, 3>, e::Tensor<dtype, 1>, e::Tensor<dtype, 3>>
split_truncate_theta<dtype>(const e::Tensor<dtype, 4>&, size_t, dtype);

164
src/mps.hpp Normal file
View File

@ -0,0 +1,164 @@
#pragma once
#include "util.hpp"
#include <eigen3/Eigen/Eigen>
#include <eigen3/unsupported/Eigen/CXX11/Tensor>
#include <vector>
#include <cassert>
#include <iostream>
namespace e = Eigen;
using iP = e::IndexPair<int>;
template<typename T>
e::Tensor<T, 2> diagonal(const e::Tensor<T, 1>& t) {
e::Tensor<T, 2> out(t.dimension(0), t.dimension(0));
out.setZero();
for (e::Index i = 0; i < t.dimension(0); i++) {
out(i, i) = t(i);
}
return out;
}
using namespace std::literals;
/**
* @brief Matrix Product State class
* @todo Possible Optimization: Store S as full tensor and not just the diagonal values: we always need to construct a diagonal tensor with `diagonal` function which leads to memory allocations. Shouldnt be too bad from storage perspective
*/
template<typename T>
class MPS {
public:
using BTensor_t = e::Tensor<T, 3>;
using STensor_t = e::Tensor<T, 1>;
MPS() {};
MPS(std::vector<BTensor_t>&& Bs, std::vector<STensor_t>&& Ss);
/**
* @brief Return single site contraction of the $S_i * B_i$ tensors
* @returns Contraction with legs `vL i vR`
*/
BTensor_t getTheta1(size_t siteIdx) const;
/**
* @brief Return two-site contraction of the $S_i * S_B * B_{i+1}$ tensors
* @returns Contraction with legs `vL i i+1 vR`
*/
e::Tensor<T, 4> getTheta2(size_t bondIdx) const;
/**
* @brief Return the dimensions of all bonds
*/
std::vector<unsigned> getChi() const;
/**
* @brief Compute the expectation value of a single-site operator on a single site
* @param op Single-site operator with legs i i*
*/
T getSiteExpectationValue(const e::Tensor<T, 2>& op, size_t siteIdx) const;
std::vector<T> getSiteExpectationValues(const e::Tensor<T, 2>& op) const;
T getSiteExpectationValueSum(const e::Tensor<T, 2>& op) const;
/**
* @brief Compute the expectation value of a two-site operator on a single bond
* @param op Two-site operator with legs i j i* j*
*/
T getBondExpectationValue(const e::Tensor<T, 4>& op, size_t bondIdx) const;
T getEntanglementEntropy(size_t bondIndex) const;
/**
* @brief Collapse the MPS, calculating \f[<\psi|\psi>\f]
*/
T collapse(size_t sizeIndex) const;
BTensor_t& B(size_t i) { return this->Bs.at(i); }
STensor_t& S(size_t i) { return this->Ss.at(i); }
const BTensor_t& B(size_t i) const { return this->Bs.at(i); }
const STensor_t& S(size_t i) const { return this->Ss.at(i); }
size_t getL() const { return this->L; }
void print() const {
for (size_t i = 0; i < this->L; i++) {
printTensor(Ss.at(i), "S["s + std::to_string(i) + "]");
printTensor(Bs.at(i), "B["s + std::to_string(i) + "]");
}
}
private:
std::vector<BTensor_t> Bs;
std::vector<STensor_t> Ss;
size_t L;
};
/**
* @brief Function to perform split and truncate on a two-site wave function
* @param theta Two-site wave function in mixed canonical form, with legs `vL, i, j, vR`
* @param chi_max Maximum number of singular values to keep
* @param eps Discard any singular values smaller than that.
*
* @returns (A, S, B) where A is the left-canonical matrix on site i (legs `vL, i, vC`),
* S are the singular values and
* B is the right-canonical matrix on site i+1, with legs `vC, j, vR`
*/
template<typename dtype>
std::tuple<e::Tensor<dtype, 3>, e::Tensor<dtype, 1>, e::Tensor<dtype, 3>>
split_truncate_theta(const e::Tensor<dtype, 4>& theta, size_t chiMax, dtype eps);
/**
* @brief Initialize a MPS in spin-up state
* @details
* Return a MPS spin-up MPS with length L and bond dimension 1
*/
template<typename T>
MPS<T> initMPS_spinUp(size_t L) {
assert(L > 0);
e::Tensor<T, 3> B(1, 2, 1);
B.setZero();
B(0, 0, 0) = 1;
std::vector<e::Tensor<T, 3>> Bs(L, B);
e::Tensor<T, 1> S(1);
S(0) = 1;
std::vector<e::Tensor<T, 1>> Ss(L, S);
return MPS<T>(std::move(Bs), std::move(Ss));
}
/**
* @brief Initialize a MPS in spin-right state
* @details
* Return a MPS spin-right MPS with length L and bond dimension 1
*/
template<typename T>
MPS<T> initMPS_spinRight(size_t L) {
assert(L > 0);
e::Tensor<T, 3> B(1, 2, 1);
B.setZero();
T value = std::sqrt(0.5);
B(0, 0, 0) = value;
B(0, 1, 0) = value;
std::vector<e::Tensor<T, 3>> Bs(L, B);
e::Tensor<T, 1> S(1);
S(0) = 1;
std::vector<e::Tensor<T, 1>> Ss(L, S);
return MPS<T>(std::move(Bs), std::move(Ss));
}
/**
* @brief Initialize a MPS with particle number states
* @details
* Return a MPS of dimension `nMax+1`, where each site is occupied by `nParticles`.
*/
template<typename T>
MPS<T> initMPS_nParticles(size_t L, size_t nMax, size_t nParticles) {
assert(L > 0);
assert(nParticles <= nMax);
e::Tensor<T, 3> B(1, nMax+1, 1);
B.setZero();
B(0, nParticles, 0) = 1;
std::vector<e::Tensor<T, 3>> Bs(L, B);
e::Tensor<T, 1> S(1);
S(0) = 1;
std::vector<e::Tensor<T, 1>> Ss(L, S);
return MPS<T>(std::move(Bs), std::move(Ss));
}

562
src/output Normal file
View File

@ -0,0 +1,562 @@
Building: main.cpp -> ../build/main.o
In file included from main.cpp:3:
mps.hpp: In instantiation of 'std::tuple<Eigen::Tensor<dtype, 3, Storage, long int>, Eigen::Tensor<dtype, 1, Storage, long int>, Eigen::Tensor<dtype, 3, Storage, long int> > split_truncate_theta(const Eigen::Tensor<dtype, 4, Storage>&, size_t, dtype) [with dtype = std::complex<float>; unsigned int Storage = 1; size_t = long unsigned int]':
main.cpp:37:62: required from here
37 | auto [A, S, B] = split_truncate_theta<dtype, e::RowMajor>(t4, chi_max, eps);
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~
mps.hpp:140:83: error: no match for 'operator>' (operand types are 'const Eigen::ArrayWrapper<const Eigen::Matrix<double, -1, 1> >' and 'std::complex<float>')
140 | int chivC = static_cast<int>(std::min(chi_max, static_cast<size_t>((S.array() > eps).count())));
| ~~~~~~~~~~~^~~~~~
In file included from /usr/include/c++/14.2.1/bits/stl_algobase.h:64,
from /usr/include/c++/14.2.1/vector:62,
from ./pch.hpp:1,
from <command-line>:
/usr/include/c++/14.2.1/bits/stl_pair.h:1023:5: note: candidate: 'template<class _T1, class _T2, class _U1, class _U2> constexpr std::common_comparison_category_t<decltype (std::__detail::__synth3way(declval<_T1&>(), declval<_U1&>())), decltype (std::__detail::__synth3way(declval<_T2&>(), declval<_U2&>()))> std::operator<=>(const pair<_T1, _T2>&, const pair<_U1, _U2>&)' (reversed)
1023 | operator<=>(const pair<_T1, _T2>& __x, const pair<_U1, _U2>& __y)
| ^~~~~~~~
/usr/include/c++/14.2.1/bits/stl_pair.h:1023:5: note: template argument deduction/substitution failed:
mps.hpp:140:83: note: 'std::complex<float>' is not derived from 'const std::pair<_T1, _T2>'
140 | int chivC = static_cast<int>(std::min(chi_max, static_cast<size_t>((S.array() > eps).count())));
| ~~~~~~~~~~~^~~~~~
In file included from /usr/include/c++/14.2.1/bits/stl_algobase.h:67:
/usr/include/c++/14.2.1/bits/stl_iterator.h:576:5: note: candidate: 'template<class _IteratorL, class _IteratorR> requires three_way_comparable_with<_IteratorR, _IteratorL, std::partial_ordering> constexpr std::compare_three_way_result_t<_IteratorL, _IteratorR> std::operator<=>(const reverse_iterator<_IteratorL>&, const reverse_iterator<_IteratorR>&)' (reversed)
576 | operator<=>(const reverse_iterator<_IteratorL>& __x,
| ^~~~~~~~
/usr/include/c++/14.2.1/bits/stl_iterator.h:576:5: note: template argument deduction/substitution failed:
mps.hpp:140:83: note: 'std::complex<float>' is not derived from 'const std::reverse_iterator<_IteratorL>'
140 | int chivC = static_cast<int>(std::min(chi_max, static_cast<size_t>((S.array() > eps).count())));
| ~~~~~~~~~~~^~~~~~
/usr/include/c++/14.2.1/bits/stl_iterator.h:1679:5: note: candidate: 'template<class _IteratorL, class _IteratorR> requires three_way_comparable_with<_IteratorR, _IteratorL, std::partial_ordering> constexpr std::compare_three_way_result_t<_IteratorL, _IteratorR> std::operator<=>(const move_iterator<_IteratorL>&, const move_iterator<_IteratorR>&)' (reversed)
1679 | operator<=>(const move_iterator<_IteratorL>& __x,
| ^~~~~~~~
/usr/include/c++/14.2.1/bits/stl_iterator.h:1679:5: note: template argument deduction/substitution failed:
mps.hpp:140:83: note: 'std::complex<float>' is not derived from 'const std::move_iterator<_IteratorL>'
140 | int chivC = static_cast<int>(std::min(chi_max, static_cast<size_t>((S.array() > eps).count())));
| ~~~~~~~~~~~^~~~~~
In file included from /usr/include/c++/14.2.1/bits/uses_allocator_args.h:39,
from /usr/include/c++/14.2.1/bits/memory_resource.h:41,
from /usr/include/c++/14.2.1/vector:87:
/usr/include/c++/14.2.1/tuple:2589:5: note: candidate: 'template<class ... _Tps, class ... _Ups> constexpr std::common_comparison_category_t<decltype (std::__detail::__synth3way(declval<_Tps&>(), declval<_Ups&>()))...> std::operator<=>(const tuple<_Ts ...>&, const tuple<_Elements ...>&)' (reversed)
2589 | operator<=>(const tuple<_Tps...>& __t, const tuple<_Ups...>& __u)
| ^~~~~~~~
/usr/include/c++/14.2.1/tuple:2589:5: note: template argument deduction/substitution failed:
mps.hpp:140:83: note: 'std::complex<float>' is not derived from 'const std::tuple<_Ts ...>'
140 | int chivC = static_cast<int>(std::min(chi_max, static_cast<size_t>((S.array() > eps).count())));
| ~~~~~~~~~~~^~~~~~
In file included from /usr/include/c++/14.2.1/bits/basic_string.h:47,
from /usr/include/c++/14.2.1/string:54,
from /usr/include/c++/14.2.1/bits/locale_classes.h:40,
from /usr/include/c++/14.2.1/locale:41,
from /usr/include/c++/14.2.1/format:47,
from /usr/include/c++/14.2.1/print:41,
from ./pch.hpp:2:
/usr/include/c++/14.2.1/string_view:617:5: note: candidate: 'template<class _CharT, class _Traits> constexpr decltype (__char_traits_cmp_cat<_Traits>(0)) std::operator<=>(basic_string_view<_CharT, _Traits>, __type_identity_t<basic_string_view<_CharT, _Traits> >)' (reversed)
617 | operator<=>(basic_string_view<_CharT, _Traits> __x,
| ^~~~~~~~
/usr/include/c++/14.2.1/string_view:617:5: note: template argument deduction/substitution failed:
mps.hpp:140:83: note: 'std::complex<float>' is not derived from 'std::basic_string_view<_CharT, _Traits>'
140 | int chivC = static_cast<int>(std::min(chi_max, static_cast<size_t>((S.array() > eps).count())));
| ~~~~~~~~~~~^~~~~~
/usr/include/c++/14.2.1/bits/basic_string.h:3805:5: note: candidate: 'template<class _CharT, class _Traits, class _Alloc> constexpr decltype (__char_traits_cmp_cat<_Traits>(0)) std::operator<=>(const __cxx11::basic_string<_CharT, _Traits, _Allocator>&, const _CharT*)' (reversed)
3805 | operator<=>(const basic_string<_CharT, _Traits, _Alloc>& __lhs,
| ^~~~~~~~
/usr/include/c++/14.2.1/bits/basic_string.h:3805:5: note: template argument deduction/substitution failed:
mps.hpp:140:83: note: 'std::complex<float>' is not derived from 'const std::__cxx11::basic_string<_CharT, _Traits, _Allocator>'
140 | int chivC = static_cast<int>(std::min(chi_max, static_cast<size_t>((S.array() > eps).count())));
| ~~~~~~~~~~~^~~~~~
In file included from /usr/include/c++/14.2.1/format:48:
/usr/include/c++/14.2.1/optional:1283:5: note: candidate: 'template<class _Tp, class _Up> requires three_way_comparable_with<_Up, _Tp, std::partial_ordering> constexpr std::compare_three_way_result_t<_IteratorL, _IteratorR> std::operator<=>(const optional<_Tp>&, const optional<_Up>&)' (reversed)
1283 | operator<=>(const optional<_Tp>& __x, const optional<_Up>& __y)
| ^~~~~~~~
/usr/include/c++/14.2.1/optional:1283:5: note: template argument deduction/substitution failed:
mps.hpp:140:83: note: 'std::complex<float>' is not derived from 'const std::optional<_Tp>'
140 | int chivC = static_cast<int>(std::min(chi_max, static_cast<size_t>((S.array() > eps).count())));
| ~~~~~~~~~~~^~~~~~
/usr/include/c++/14.2.1/optional:1298:5: note: candidate: 'template<class _Tp> constexpr std::strong_ordering std::operator<=>(const optional<_Tp>&, nullopt_t)' (reversed)
1298 | operator<=>(const optional<_Tp>& __x, nullopt_t) noexcept
| ^~~~~~~~
/usr/include/c++/14.2.1/optional:1298:5: note: template argument deduction/substitution failed:
mps.hpp:140:83: note: 'std::complex<float>' is not derived from 'const std::optional<_Tp>'
140 | int chivC = static_cast<int>(std::min(chi_max, static_cast<size_t>((S.array() > eps).count())));
| ~~~~~~~~~~~^~~~~~
/usr/include/c++/14.2.1/optional:1435:5: note: candidate: 'template<class _Tp, class _Up> requires !(__is_optional_v<_Up>) && (three_way_comparable_with<_Up, _Tp, std::partial_ordering>) constexpr std::compare_three_way_result_t<_IteratorL, _IteratorR> std::operator<=>(const optional<_Tp>&, const _Up&)' (reversed)
1435 | operator<=>(const optional<_Tp>& __x, const _Up& __v)
| ^~~~~~~~
/usr/include/c++/14.2.1/optional:1435:5: note: template argument deduction/substitution failed:
mps.hpp:140:83: note: 'std::complex<float>' is not derived from 'const std::optional<_Tp>'
140 | int chivC = static_cast<int>(std::min(chi_max, static_cast<size_t>((S.array() > eps).count())));
| ~~~~~~~~~~~^~~~~~
In file included from /usr/include/c++/14.2.1/bits/shared_ptr_base.h:59,
from /usr/include/c++/14.2.1/bits/shared_ptr.h:53,
from /usr/include/c++/14.2.1/chrono:49,
from /usr/include/eigen3/unsupported/Eigen/CXX11/Tensor:38,
from ./pch.hpp:4:
/usr/include/c++/14.2.1/bits/unique_ptr.h:996:5: note: candidate: 'template<class _Tp, class _Dp, class _Up, class _Ep> requires three_way_comparable_with<typename std::unique_ptr<_Tp, _Dp>::pointer, typename std::unique_ptr<_Up, _Ep>::pointer, std::partial_ordering> constexpr std::compare_three_way_result_t<typename std::unique_ptr<_Tp, _Dp>::pointer, typename std::unique_ptr<_Up, _Ep>::pointer> std::operator<=>(const unique_ptr<_Tp, _Dp>&, const unique_ptr<_Up, _Ep>&)' (reversed)
996 | operator<=>(const unique_ptr<_Tp, _Dp>& __x,
| ^~~~~~~~
/usr/include/c++/14.2.1/bits/unique_ptr.h:996:5: note: template argument deduction/substitution failed:
mps.hpp:140:83: note: 'std::complex<float>' is not derived from 'const std::unique_ptr<_Tp, _Dp>'
140 | int chivC = static_cast<int>(std::min(chi_max, static_cast<size_t>((S.array() > eps).count())));
| ~~~~~~~~~~~^~~~~~
/usr/include/c++/14.2.1/bits/unique_ptr.h:1005:5: note: candidate: 'template<class _Tp, class _Dp> requires three_way_comparable<typename std::unique_ptr<_Tp, _Dp>::pointer, std::partial_ordering> constexpr std::compare_three_way_result_t<typename std::unique_ptr<_Tp, _Dp>::pointer> std::operator<=>(const unique_ptr<_Tp, _Dp>&, nullptr_t)' (reversed)
1005 | operator<=>(const unique_ptr<_Tp, _Dp>& __x, nullptr_t)
| ^~~~~~~~
/usr/include/c++/14.2.1/bits/unique_ptr.h:1005:5: note: template argument deduction/substitution failed:
mps.hpp:140:83: note: 'std::complex<float>' is not derived from 'const std::unique_ptr<_Tp, _Dp>'
140 | int chivC = static_cast<int>(std::min(chi_max, static_cast<size_t>((S.array() > eps).count())));
| ~~~~~~~~~~~^~~~~~
/usr/include/c++/14.2.1/bits/shared_ptr_base.h:1809:5: note: candidate: 'template<class _Tp, class _Up, __gnu_cxx::_Lock_policy _Lp> std::strong_ordering std::operator<=>(const __shared_ptr<_Tp1, _Lp>&, const __shared_ptr<_Tp2, _Lp>&)' (reversed)
1809 | operator<=>(const __shared_ptr<_Tp, _Lp>& __a,
| ^~~~~~~~
/usr/include/c++/14.2.1/bits/shared_ptr_base.h:1809:5: note: template argument deduction/substitution failed:
mps.hpp:140:83: note: 'std::complex<float>' is not derived from 'const std::__shared_ptr<_Tp1, _Lp>'
140 | int chivC = static_cast<int>(std::min(chi_max, static_cast<size_t>((S.array() > eps).count())));
| ~~~~~~~~~~~^~~~~~
/usr/include/c++/14.2.1/bits/shared_ptr_base.h:1815:5: note: candidate: 'template<class _Tp, __gnu_cxx::_Lock_policy _Lp> std::strong_ordering std::operator<=>(const __shared_ptr<_Tp, _Lp>&, nullptr_t)' (reversed)
1815 | operator<=>(const __shared_ptr<_Tp, _Lp>& __a, nullptr_t) noexcept
| ^~~~~~~~
/usr/include/c++/14.2.1/bits/shared_ptr_base.h:1815:5: note: template argument deduction/substitution failed:
mps.hpp:140:83: note: 'std::complex<float>' is not derived from 'const std::__shared_ptr<_Tp, _Lp>'
140 | int chivC = static_cast<int>(std::min(chi_max, static_cast<size_t>((S.array() > eps).count())));
| ~~~~~~~~~~~^~~~~~
/usr/include/c++/14.2.1/bits/shared_ptr.h:566:5: note: candidate: 'template<class _Tp, class _Up> std::strong_ordering std::operator<=>(const shared_ptr<_Tp>&, const shared_ptr<_Tp>&)' (reversed)
566 | operator<=>(const shared_ptr<_Tp>& __a,
| ^~~~~~~~
/usr/include/c++/14.2.1/bits/shared_ptr.h:566:5: note: template argument deduction/substitution failed:
mps.hpp:140:83: note: 'std::complex<float>' is not derived from 'const std::shared_ptr<_Tp>'
140 | int chivC = static_cast<int>(std::min(chi_max, static_cast<size_t>((S.array() > eps).count())));
| ~~~~~~~~~~~^~~~~~
/usr/include/c++/14.2.1/bits/shared_ptr.h:572:5: note: candidate: 'template<class _Tp> std::strong_ordering std::operator<=>(const shared_ptr<_Tp>&, nullptr_t)' (reversed)
572 | operator<=>(const shared_ptr<_Tp>& __a, nullptr_t) noexcept
| ^~~~~~~~
/usr/include/c++/14.2.1/bits/shared_ptr.h:572:5: note: template argument deduction/substitution failed:
mps.hpp:140:83: note: 'std::complex<float>' is not derived from 'const std::shared_ptr<_Tp>'
140 | int chivC = static_cast<int>(std::min(chi_max, static_cast<size_t>((S.array() > eps).count())));
| ~~~~~~~~~~~^~~~~~
/usr/include/c++/14.2.1/bits/stl_iterator.h:594:5: note: candidate: 'template<class _Iterator> requires three_way_comparable<_Iterator, std::partial_ordering> constexpr std::compare_three_way_result_t<_Iterator, _Iterator> std::operator<=>(const reverse_iterator<_IteratorL>&, const reverse_iterator<_IteratorL>&)' (rewritten)
594 | operator<=>(const reverse_iterator<_Iterator>& __x,
| ^~~~~~~~
/usr/include/c++/14.2.1/bits/stl_iterator.h:594:5: note: template argument deduction/substitution failed:
mps.hpp:140:83: note: 'const Eigen::ArrayWrapper<const Eigen::Matrix<double, -1, 1> >' is not derived from 'const std::reverse_iterator<_IteratorL>'
140 | int chivC = static_cast<int>(std::min(chi_max, static_cast<size_t>((S.array() > eps).count())));
| ~~~~~~~~~~~^~~~~~
/usr/include/c++/14.2.1/bits/stl_iterator.h:1745:5: note: candidate: 'template<class _Iterator> requires three_way_comparable<_Iterator, std::partial_ordering> constexpr std::compare_three_way_result_t<_Iterator, _Iterator> std::operator<=>(const move_iterator<_IteratorL>&, const move_iterator<_IteratorL>&)' (rewritten)
1745 | operator<=>(const move_iterator<_Iterator>& __x,
| ^~~~~~~~
/usr/include/c++/14.2.1/bits/stl_iterator.h:1745:5: note: template argument deduction/substitution failed:
mps.hpp:140:83: note: 'const Eigen::ArrayWrapper<const Eigen::Matrix<double, -1, 1> >' is not derived from 'const std::move_iterator<_IteratorL>'
140 | int chivC = static_cast<int>(std::min(chi_max, static_cast<size_t>((S.array() > eps).count())));
| ~~~~~~~~~~~^~~~~~
In file included from /usr/include/c++/14.2.1/vector:66:
/usr/include/c++/14.2.1/bits/stl_vector.h:2069:5: note: candidate: 'template<class _Tp, class _Alloc> constexpr std::__detail::__synth3way_t<_Iterator> std::operator<=>(const vector<_Tp, _Alloc>&, const vector<_Tp, _Alloc>&)' (rewritten)
2069 | operator<=>(const vector<_Tp, _Alloc>& __x, const vector<_Tp, _Alloc>& __y)
| ^~~~~~~~
/usr/include/c++/14.2.1/bits/stl_vector.h:2069:5: note: template argument deduction/substitution failed:
mps.hpp:140:83: note: 'const Eigen::ArrayWrapper<const Eigen::Matrix<double, -1, 1> >' is not derived from 'const std::vector<_Tp, _Alloc>'
140 | int chivC = static_cast<int>(std::min(chi_max, static_cast<size_t>((S.array() > eps).count())));
| ~~~~~~~~~~~^~~~~~
In file included from /usr/include/c++/14.2.1/format:43:
/usr/include/c++/14.2.1/array:310:5: note: candidate: 'template<class _Tp, long unsigned int _Nm> constexpr std::__detail::__synth3way_t<_Iterator> std::operator<=>(const array<_Tp, _Nm>&, const array<_Tp, _Nm>&)' (rewritten)
310 | operator<=>(const array<_Tp, _Nm>& __a, const array<_Tp, _Nm>& __b)
| ^~~~~~~~
/usr/include/c++/14.2.1/array:310:5: note: template argument deduction/substitution failed:
mps.hpp:140:83: note: 'const Eigen::ArrayWrapper<const Eigen::Matrix<double, -1, 1> >' is not derived from 'const std::array<_Tp, _Nm>'
140 | int chivC = static_cast<int>(std::min(chi_max, static_cast<size_t>((S.array() > eps).count())));
| ~~~~~~~~~~~^~~~~~
/usr/include/c++/14.2.1/bits/basic_string.h:3790:5: note: candidate: 'template<class _CharT, class _Traits, class _Alloc> constexpr decltype (__char_traits_cmp_cat<_Traits>(0)) std::operator<=>(const __cxx11::basic_string<_CharT, _Traits, _Allocator>&, const __cxx11::basic_string<_CharT, _Traits, _Allocator>&)' (rewritten)
3790 | operator<=>(const basic_string<_CharT, _Traits, _Alloc>& __lhs,
| ^~~~~~~~
/usr/include/c++/14.2.1/bits/basic_string.h:3790:5: note: template argument deduction/substitution failed:
mps.hpp:140:83: note: 'const Eigen::ArrayWrapper<const Eigen::Matrix<double, -1, 1> >' is not derived from 'const std::__cxx11::basic_string<_CharT, _Traits, _Allocator>'
140 | int chivC = static_cast<int>(std::min(chi_max, static_cast<size_t>((S.array() > eps).count())));
| ~~~~~~~~~~~^~~~~~
In file included from /usr/include/c++/14.2.1/format:52:
/usr/include/c++/14.2.1/variant:1280:5: note: candidate: 'template<class ... _Types> requires (three_way_comparable<_Types, std::partial_ordering> && ...) constexpr std::common_comparison_category_t<typename std::__detail::__cmp3way_res_impl<_Types, _Types>::type ...> std::operator<=>(const variant<_Types ...>&, const variant<_Types ...>&)' (rewritten)
1280 | operator<=>(const variant<_Types...>& __v, const variant<_Types...>& __w)
| ^~~~~~~~
/usr/include/c++/14.2.1/variant:1280:5: note: template argument deduction/substitution failed:
mps.hpp:140:83: note: 'const Eigen::ArrayWrapper<const Eigen::Matrix<double, -1, 1> >' is not derived from 'const std::variant<_Types ...>'
140 | int chivC = static_cast<int>(std::min(chi_max, static_cast<size_t>((S.array() > eps).count())));
| ~~~~~~~~~~~^~~~~~
In file included from /usr/include/c++/14.2.1/map:63,
from /usr/include/eigen3/Eigen/SparseCore:16,
from /usr/include/eigen3/Eigen/Sparse:26,
from /usr/include/eigen3/Eigen/Eigen:2,
from ./pch.hpp:5:
/usr/include/c++/14.2.1/bits/stl_map.h:1533:5: note: candidate: 'template<class _Key, class _Tp, class _Compare, class _Alloc> std::__detail::__synth3way_t<std::pair<const _Key, _Val> > std::operator<=>(const map<_Key, _Tp, _Compare, _Allocator>&, const map<_Key, _Tp, _Compare, _Allocator>&)' (rewritten)
1533 | operator<=>(const map<_Key, _Tp, _Compare, _Alloc>& __x,
| ^~~~~~~~
/usr/include/c++/14.2.1/bits/stl_map.h:1533:5: note: template argument deduction/substitution failed:
mps.hpp:140:83: note: 'const Eigen::ArrayWrapper<const Eigen::Matrix<double, -1, 1> >' is not derived from 'const std::map<_Key, _Tp, _Compare, _Allocator>'
140 | int chivC = static_cast<int>(std::min(chi_max, static_cast<size_t>((S.array() > eps).count())));
| ~~~~~~~~~~~^~~~~~
In file included from /usr/include/c++/14.2.1/map:64:
/usr/include/c++/14.2.1/bits/stl_multimap.h:1155:5: note: candidate: 'template<class _Key, class _Tp, class _Compare, class _Alloc> std::__detail::__synth3way_t<std::pair<const _Key, _Val> > std::operator<=>(const multimap<_Key, _Tp, _Compare, _Allocator>&, const multimap<_Key, _Tp, _Compare, _Allocator>&)' (rewritten)
1155 | operator<=>(const multimap<_Key, _Tp, _Compare, _Alloc>& __x,
| ^~~~~~~~
/usr/include/c++/14.2.1/bits/stl_multimap.h:1155:5: note: template argument deduction/substitution failed:
mps.hpp:140:83: note: 'const Eigen::ArrayWrapper<const Eigen::Matrix<double, -1, 1> >' is not derived from 'const std::multimap<_Key, _Tp, _Compare, _Allocator>'
140 | int chivC = static_cast<int>(std::min(chi_max, static_cast<size_t>((S.array() > eps).count())));
| ~~~~~~~~~~~^~~~~~
In file included from /usr/include/c++/14.2.1/list:65,
from /usr/include/eigen3/Eigen/src/IterativeLinearSolvers/IncompleteCholesky.h:15,
from /usr/include/eigen3/Eigen/IterativeLinearSolvers:47,
from /usr/include/eigen3/Eigen/Sparse:31:
/usr/include/c++/14.2.1/bits/stl_list.h:2158:5: note: candidate: 'template<class _Tp, class _Alloc> std::__detail::__synth3way_t<_Iterator> std::operator<=>(const __cxx11::list<_Tp, _Alloc>&, const __cxx11::list<_Tp, _Alloc>&)' (rewritten)
2158 | operator<=>(const list<_Tp, _Alloc>& __x, const list<_Tp, _Alloc>& __y)
| ^~~~~~~~
/usr/include/c++/14.2.1/bits/stl_list.h:2158:5: note: template argument deduction/substitution failed:
mps.hpp:140:83: note: 'const Eigen::ArrayWrapper<const Eigen::Matrix<double, -1, 1> >' is not derived from 'const std::__cxx11::list<_Tp, _Alloc>'
140 | int chivC = static_cast<int>(std::min(chi_max, static_cast<size_t>((S.array() > eps).count())));
| ~~~~~~~~~~~^~~~~~
In file included from /usr/include/c++/14.2.1/bits/ios_base.h:46,
from /usr/include/c++/14.2.1/bits/locale_facets.h:43,
from /usr/include/c++/14.2.1/locale:42:
/usr/include/c++/14.2.1/system_error:316:3: note: candidate: 'std::strong_ordering std::operator<=>(const error_code&, const error_code&)' (rewritten)
316 | operator<=>(const error_code& __lhs, const error_code& __rhs) noexcept
| ^~~~~~~~
/usr/include/c++/14.2.1/system_error:316:33: note: no known conversion for argument 1 from 'const Eigen::ArrayWrapper<const Eigen::Matrix<double, -1, 1> >' to 'const std::error_code&'
316 | operator<=>(const error_code& __lhs, const error_code& __rhs) noexcept
| ~~~~~~~~~~~~~~~~~~^~~~~
/usr/include/c++/14.2.1/system_error:498:3: note: candidate: 'std::strong_ordering std::operator<=>(const error_condition&, const error_condition&)' (rewritten)
498 | operator<=>(const error_condition& __lhs,
| ^~~~~~~~
/usr/include/c++/14.2.1/system_error:498:38: note: no known conversion for argument 1 from 'const Eigen::ArrayWrapper<const Eigen::Matrix<double, -1, 1> >' to 'const std::error_condition&'
498 | operator<=>(const error_condition& __lhs,
| ~~~~~~~~~~~~~~~~~~~~~~~^~~~~
/usr/include/c++/14.2.1/variant:1303:3: note: candidate: 'constexpr std::strong_ordering std::operator<=>(monostate, monostate)' (rewritten)
1303 | operator<=>(monostate, monostate) noexcept { return strong_ordering::equal; }
| ^~~~~~~~
/usr/include/c++/14.2.1/variant:1303:15: note: no known conversion for argument 1 from 'const Eigen::ArrayWrapper<const Eigen::Matrix<double, -1, 1> >' to 'std::monostate'
1303 | operator<=>(monostate, monostate) noexcept { return strong_ordering::equal; }
| ^~~~~~~~~
In file included from /usr/include/eigen3/unsupported/Eigen/CXX11/Tensor:45:
/usr/include/c++/14.2.1/thread:75:3: note: candidate: 'std::strong_ordering std::operator<=>(thread::id, thread::id)' (rewritten)
75 | operator<=>(thread::id __x, thread::id __y) noexcept
| ^~~~~~~~
/usr/include/c++/14.2.1/thread:75:26: note: no known conversion for argument 1 from 'const Eigen::ArrayWrapper<const Eigen::Matrix<double, -1, 1> >' to 'std::thread::id'
75 | operator<=>(thread::id __x, thread::id __y) noexcept
| ~~~~~~~~~~~^~~
In file included from /usr/include/eigen3/Eigen/src/Core/ArrayBase.h:99,
from /usr/include/eigen3/Eigen/Core:309,
from /usr/include/eigen3/unsupported/Eigen/CXX11/Tensor:14:
/usr/include/eigen3/Eigen/src/plugins/ArrayCwiseBinaryOps.inc:204:1: note: candidate: 'template<class OtherDerived> const Eigen::CwiseBinaryOp<Eigen::internal::scalar_cmp_op<typename OtherDerived::Scalar, typename Eigen::internal::traits<T>::Scalar, Eigen::internal::cmp_LT>, const OtherDerived, const Derived> Eigen::ArrayBase<Derived>::operator>(const Eigen::ArrayBase<OtherDerived>&) const [with Derived = Eigen::ArrayWrapper<const Eigen::Matrix<double, -1, 1> >]'
204 | EIGEN_MAKE_CWISE_COMP_R_OP(operator>, operator<, LT)
| ^~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/include/eigen3/Eigen/src/plugins/ArrayCwiseBinaryOps.inc:204:1: note: template argument deduction/substitution failed:
mps.hpp:140:83: note: 'std::complex<float>' is not derived from 'const Eigen::ArrayBase<Derived>'
140 | int chivC = static_cast<int>(std::min(chi_max, static_cast<size_t>((S.array() > eps).count())));
| ~~~~~~~~~~~^~~~~~
/usr/include/eigen3/Eigen/src/plugins/ArrayCwiseBinaryOps.inc:204:1: note: candidate: 'const Eigen::ArrayBase<Derived>::RCmpLTReturnType Eigen::ArrayBase<Derived>::operator>(const Scalar&) const [with Derived = Eigen::ArrayWrapper<const Eigen::Matrix<double, -1, 1> >; RCmpLTReturnType = Eigen::CwiseBinaryOp<Eigen::internal::scalar_cmp_op<double, double, Eigen::internal::cmp_LT, false>, const Eigen::CwiseNullaryOp<Eigen::internal::scalar_constant_op<double>, Eigen::Array<double, -1, 1> >, const Eigen::ArrayWrapper<const Eigen::Matrix<double, -1, 1> > >; Scalar = double]'
204 | EIGEN_MAKE_CWISE_COMP_R_OP(operator>, operator<, LT)
| ^~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/include/eigen3/Eigen/src/plugins/ArrayCwiseBinaryOps.inc:204:1: note: no known conversion for argument 1 from 'std::complex<float>' to 'const Eigen::ArrayBase<Eigen::ArrayWrapper<const Eigen::Matrix<double, -1, 1> > >::Scalar&' {aka 'const double&'}
204 | EIGEN_MAKE_CWISE_COMP_R_OP(operator>, operator<, LT)
| ^~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/include/c++/14.2.1/bits/stl_iterator.h:551:5: note: candidate: 'template<class _IteratorL, class _IteratorR> constexpr bool std::operator>(const reverse_iterator<_IteratorL>&, const reverse_iterator<_IteratorR>&) requires requires{{std::operator>::__x->base() < std::operator>::__y->base()} -> decltype(auto) [requires std::convertible_to<<placeholder>, bool>];}'
551 | operator>(const reverse_iterator<_IteratorL>& __x,
| ^~~~~~~~
/usr/include/c++/14.2.1/bits/stl_iterator.h:551:5: note: template argument deduction/substitution failed:
mps.hpp:140:83: note: 'const Eigen::ArrayWrapper<const Eigen::Matrix<double, -1, 1> >' is not derived from 'const std::reverse_iterator<_IteratorL>'
140 | int chivC = static_cast<int>(std::min(chi_max, static_cast<size_t>((S.array() > eps).count())));
| ~~~~~~~~~~~^~~~~~
/usr/include/c++/14.2.1/bits/stl_iterator.h:1714:5: note: candidate: 'template<class _IteratorL, class _IteratorR> constexpr bool std::operator>(const move_iterator<_IteratorL>&, const move_iterator<_IteratorR>&) requires requires{{std::operator>::__y->base() < std::operator>::__x->base()} -> decltype(auto) [requires std::convertible_to<<placeholder>, bool>];}'
1714 | operator>(const move_iterator<_IteratorL>& __x,
| ^~~~~~~~
/usr/include/c++/14.2.1/bits/stl_iterator.h:1714:5: note: template argument deduction/substitution failed:
mps.hpp:140:83: note: 'const Eigen::ArrayWrapper<const Eigen::Matrix<double, -1, 1> >' is not derived from 'const std::move_iterator<_IteratorL>'
140 | int chivC = static_cast<int>(std::min(chi_max, static_cast<size_t>((S.array() > eps).count())));
| ~~~~~~~~~~~^~~~~~
/usr/include/c++/14.2.1/optional:1258:5: note: candidate: 'template<class _Tp, class _Up> constexpr std::__optional_gt_t<_Tp, _Up> std::operator>(const optional<_Tp>&, const optional<_Up>&)'
1258 | operator>(const optional<_Tp>& __lhs, const optional<_Up>& __rhs)
| ^~~~~~~~
/usr/include/c++/14.2.1/optional:1258:5: note: template argument deduction/substitution failed:
mps.hpp:140:83: note: 'const Eigen::ArrayWrapper<const Eigen::Matrix<double, -1, 1> >' is not derived from 'const std::optional<_Tp>'
140 | int chivC = static_cast<int>(std::min(chi_max, static_cast<size_t>((S.array() > eps).count())));
| ~~~~~~~~~~~^~~~~~
/usr/include/c++/14.2.1/optional:1396:5: note: candidate: 'template<class _Tp, class _Up> constexpr std::__optional_gt_t<_Tp, _Up> std::operator>(const optional<_Tp>&, const _Up&)'
1396 | operator>(const optional<_Tp>& __lhs, const _Up& __rhs)
| ^~~~~~~~
/usr/include/c++/14.2.1/optional:1396:5: note: template argument deduction/substitution failed:
mps.hpp:140:83: note: 'const Eigen::ArrayWrapper<const Eigen::Matrix<double, -1, 1> >' is not derived from 'const std::optional<_Tp>'
140 | int chivC = static_cast<int>(std::min(chi_max, static_cast<size_t>((S.array() > eps).count())));
| ~~~~~~~~~~~^~~~~~
/usr/include/c++/14.2.1/optional:1402:5: note: candidate: 'template<class _Tp, class _Up> constexpr std::__optional_gt_t<_Up, _Tp> std::operator>(const _Up&, const optional<_Tp>&)'
1402 | operator>(const _Up& __lhs, const optional<_Tp>& __rhs)
| ^~~~~~~~
/usr/include/c++/14.2.1/optional:1402:5: note: template argument deduction/substitution failed:
mps.hpp:140:83: note: 'std::complex<float>' is not derived from 'const std::optional<_Tp>'
140 | int chivC = static_cast<int>(std::min(chi_max, static_cast<size_t>((S.array() > eps).count())));
| ~~~~~~~~~~~^~~~~~
/usr/include/c++/14.2.1/variant:1269:3: note: candidate: 'template<class ... _Types> constexpr bool std::operator>(const variant<_Types ...>&, const variant<_Types ...>&)'
1269 | _VARIANT_RELATION_FUNCTION_TEMPLATE(>, greater)
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/include/c++/14.2.1/variant:1269:3: note: template argument deduction/substitution failed:
mps.hpp:140:83: note: 'const Eigen::ArrayWrapper<const Eigen::Matrix<double, -1, 1> >' is not derived from 'const std::variant<_Types ...>'
140 | int chivC = static_cast<int>(std::min(chi_max, static_cast<size_t>((S.array() > eps).count())));
| ~~~~~~~~~~~^~~~~~
/usr/include/c++/14.2.1/bits/unique_ptr.h:942:5: note: candidate: 'template<class _Tp, class _Dp, class _Up, class _Ep> constexpr bool std::operator>(const unique_ptr<_Tp, _Dp>&, const unique_ptr<_Up, _Ep>&)'
942 | operator>(const unique_ptr<_Tp, _Dp>& __x,
| ^~~~~~~~
/usr/include/c++/14.2.1/bits/unique_ptr.h:942:5: note: template argument deduction/substitution failed:
mps.hpp:140:83: note: 'const Eigen::ArrayWrapper<const Eigen::Matrix<double, -1, 1> >' is not derived from 'const std::unique_ptr<_Tp, _Dp>'
140 | int chivC = static_cast<int>(std::min(chi_max, static_cast<size_t>((S.array() > eps).count())));
| ~~~~~~~~~~~^~~~~~
/usr/include/c++/14.2.1/bits/unique_ptr.h:950:5: note: candidate: 'template<class _Tp, class _Dp> constexpr bool std::operator>(const unique_ptr<_Tp, _Dp>&, nullptr_t)'
950 | operator>(const unique_ptr<_Tp, _Dp>& __x, nullptr_t)
| ^~~~~~~~
/usr/include/c++/14.2.1/bits/unique_ptr.h:950:5: note: template argument deduction/substitution failed:
mps.hpp:140:83: note: 'const Eigen::ArrayWrapper<const Eigen::Matrix<double, -1, 1> >' is not derived from 'const std::unique_ptr<_Tp, _Dp>'
140 | int chivC = static_cast<int>(std::min(chi_max, static_cast<size_t>((S.array() > eps).count())));
| ~~~~~~~~~~~^~~~~~
/usr/include/c++/14.2.1/bits/unique_ptr.h:960:5: note: candidate: 'template<class _Tp, class _Dp> constexpr bool std::operator>(nullptr_t, const unique_ptr<_Tp, _Dp>&)'
960 | operator>(nullptr_t, const unique_ptr<_Tp, _Dp>& __x)
| ^~~~~~~~
/usr/include/c++/14.2.1/bits/unique_ptr.h:960:5: note: template argument deduction/substitution failed:
mps.hpp:140:83: note: 'std::complex<float>' is not derived from 'const std::unique_ptr<_Tp, _Dp>'
140 | int chivC = static_cast<int>(std::min(chi_max, static_cast<size_t>((S.array() > eps).count())));
| ~~~~~~~~~~~^~~~~~
/usr/include/eigen3/Eigen/src/plugins/ArrayCwiseBinaryOps.inc:204:1: note: candidate: 'const Eigen::ArrayBase<Eigen::ArrayWrapper<const Eigen::Matrix<double, -1, 1> > >::CmpLTReturnType Eigen::operator>(const ArrayBase<ArrayWrapper<const Matrix<double, -1, 1> > >::Scalar&, const ArrayWrapper<const Matrix<double, -1, 1> >&)'
204 | EIGEN_MAKE_CWISE_COMP_R_OP(operator>, operator<, LT)
| ^~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/include/eigen3/Eigen/src/plugins/ArrayCwiseBinaryOps.inc:204:1: note: no known conversion for argument 1 from 'const Eigen::ArrayWrapper<const Eigen::Matrix<double, -1, 1> >' to 'const Eigen::ArrayBase<Eigen::ArrayWrapper<const Eigen::Matrix<double, -1, 1> > >::Scalar&' {aka 'const double&'}
204 | EIGEN_MAKE_CWISE_COMP_R_OP(operator>, operator<, LT)
| ^~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from /usr/include/eigen3/Eigen/Core:300:
/usr/include/eigen3/Eigen/src/Core/Product.h: In instantiation of 'struct Eigen::internal::traits<Eigen::Product<Eigen::Matrix<std::complex<float>, -1, -1, 1, -1, -1>, Eigen::DiagonalWrapper<const Eigen::Matrix<double, -1, 1> >, 1> >':
/usr/include/eigen3/Eigen/src/Core/Product.h:248:7: required from 'class Eigen::internal::dense_product_base<Eigen::Matrix<std::complex<float>, -1, -1, 1, -1, -1>, Eigen::DiagonalWrapper<const Eigen::Matrix<double, -1, 1> >, 1, 8>'
248 | class dense_product_base : public internal::dense_xpr_base<Product<Lhs, Rhs, Option>>::type {};
| ^~~~~~~~~~~~~~~~~~
/usr/include/eigen3/Eigen/src/Core/Product.h:276:7: required from 'class Eigen::ProductImpl<Eigen::Matrix<std::complex<float>, -1, -1, 1, -1, -1>, Eigen::DiagonalWrapper<const Eigen::Matrix<double, -1, 1> >, 1, Eigen::Dense>'
276 | class ProductImpl<Lhs, Rhs, Option, Dense> : public internal::dense_product_base<Lhs, Rhs, Option> {
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/include/eigen3/Eigen/src/Core/Product.h:198:7: required from 'class Eigen::Product<Eigen::Matrix<std::complex<float>, -1, -1, 1, -1, -1>, Eigen::DiagonalWrapper<const Eigen::Matrix<double, -1, 1> >, 1>'
198 | class Product
| ^~~~~~~
mps.hpp:159:21: required from 'std::tuple<Eigen::Tensor<dtype, 3, Storage, long int>, Eigen::Tensor<dtype, 1, Storage, long int>, Eigen::Tensor<dtype, 3, Storage, long int> > split_truncate_theta(const Eigen::Tensor<dtype, 4, Storage>&, size_t, dtype) [with dtype = std::complex<float>; unsigned int Storage = 1; size_t = long unsigned int]'
159 | MatX theta2 = U * S.asDiagonal() * V;
| ~~^~~~~~~~~~~~~~~~
main.cpp:37:62: required from here
37 | auto [A, S, B] = split_truncate_theta<dtype, e::RowMajor>(t4, chi_max, eps);
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~
/usr/include/eigen3/Eigen/src/Core/Product.h:33:90: error: no type named 'ReturnType' in 'struct Eigen::ScalarBinaryOpTraits<std::complex<float>, double, Eigen::internal::scalar_product_op<std::complex<float>, double> >'
33 | typename traits<RhsCleaned>::Scalar>::ReturnType Scalar;
| ^~~~~~
In file included from /usr/include/eigen3/Eigen/Core:296:
/usr/include/eigen3/Eigen/src/Core/DenseBase.h: In instantiation of 'class Eigen::DenseBase<Eigen::Product<Eigen::Matrix<std::complex<float>, -1, -1, 1, -1, -1>, Eigen::DiagonalWrapper<const Eigen::Matrix<double, -1, 1> >, 1> >':
/usr/include/eigen3/Eigen/src/Core/MatrixBase.h:52:7: required from 'class Eigen::MatrixBase<Eigen::Product<Eigen::Matrix<std::complex<float>, -1, -1, 1, -1, -1>, Eigen::DiagonalWrapper<const Eigen::Matrix<double, -1, 1> >, 1> >'
52 | class MatrixBase : public DenseBase<Derived> {
| ^~~~~~~~~~
/usr/include/eigen3/Eigen/src/Core/Product.h:248:7: required from 'class Eigen::internal::dense_product_base<Eigen::Matrix<std::complex<float>, -1, -1, 1, -1, -1>, Eigen::DiagonalWrapper<const Eigen::Matrix<double, -1, 1> >, 1, 8>'
248 | class dense_product_base : public internal::dense_xpr_base<Product<Lhs, Rhs, Option>>::type {};
| ^~~~~~~~~~~~~~~~~~
/usr/include/eigen3/Eigen/src/Core/Product.h:276:7: required from 'class Eigen::ProductImpl<Eigen::Matrix<std::complex<float>, -1, -1, 1, -1, -1>, Eigen::DiagonalWrapper<const Eigen::Matrix<double, -1, 1> >, 1, Eigen::Dense>'
276 | class ProductImpl<Lhs, Rhs, Option, Dense> : public internal::dense_product_base<Lhs, Rhs, Option> {
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/include/eigen3/Eigen/src/Core/Product.h:198:7: required from 'class Eigen::Product<Eigen::Matrix<std::complex<float>, -1, -1, 1, -1, -1>, Eigen::DiagonalWrapper<const Eigen::Matrix<double, -1, 1> >, 1>'
198 | class Product
| ^~~~~~~
mps.hpp:159:21: required from 'std::tuple<Eigen::Tensor<dtype, 3, Storage, long int>, Eigen::Tensor<dtype, 1, Storage, long int>, Eigen::Tensor<dtype, 3, Storage, long int> > split_truncate_theta(const Eigen::Tensor<dtype, 4, Storage>&, size_t, dtype) [with dtype = std::complex<float>; unsigned int Storage = 1; size_t = long unsigned int]'
159 | MatX theta2 = U * S.asDiagonal() * V;
| ~~^~~~~~~~~~~~~~~~
main.cpp:37:62: required from here
37 | auto [A, S, B] = split_truncate_theta<dtype, e::RowMajor>(t4, chi_max, eps);
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~
/usr/include/eigen3/Eigen/src/Core/DenseBase.h:72:15: error: 'coeff' has not been declared in 'Eigen::DenseBase<Eigen::Product<Eigen::Matrix<std::complex<float>, -1, -1, 1, -1, -1>, Eigen::DiagonalWrapper<const Eigen::Matrix<double, -1, 1> >, 1> >::Base'
72 | using Base::coeff;
| ^~~~~
/usr/include/eigen3/Eigen/src/Core/DenseBase.h:73:15: error: 'coeffByOuterInner' has not been declared in 'Eigen::DenseBase<Eigen::Product<Eigen::Matrix<std::complex<float>, -1, -1, 1, -1, -1>, Eigen::DiagonalWrapper<const Eigen::Matrix<double, -1, 1> >, 1> >::Base'
73 | using Base::coeffByOuterInner;
| ^~~~~~~~~~~~~~~~~
/usr/include/eigen3/Eigen/src/Core/DenseBase.h:81:24: error: 'operator()' has not been declared in 'Eigen::DenseBase<Eigen::Product<Eigen::Matrix<std::complex<float>, -1, -1, 1, -1, -1>, Eigen::DiagonalWrapper<const Eigen::Matrix<double, -1, 1> >, 1> >::Base'
81 | using Base::operator();
| ^
/usr/include/eigen3/Eigen/src/Core/DenseBase.h:82:24: error: 'operator[]' has not been declared in 'Eigen::DenseBase<Eigen::Product<Eigen::Matrix<std::complex<float>, -1, -1, 1, -1, -1>, Eigen::DiagonalWrapper<const Eigen::Matrix<double, -1, 1> >, 1> >::Base'
82 | using Base::operator[];
| ^
/usr/include/eigen3/Eigen/src/Core/DenseBase.h:88:15: error: 'w' has not been declared in 'Eigen::DenseBase<Eigen::Product<Eigen::Matrix<std::complex<float>, -1, -1, 1, -1, -1>, Eigen::DiagonalWrapper<const Eigen::Matrix<double, -1, 1> >, 1> >::Base'
88 | using Base::w;
| ^
/usr/include/eigen3/Eigen/src/Core/DenseBase.h:89:15: error: 'x' has not been declared in 'Eigen::DenseBase<Eigen::Product<Eigen::Matrix<std::complex<float>, -1, -1, 1, -1, -1>, Eigen::DiagonalWrapper<const Eigen::Matrix<double, -1, 1> >, 1> >::Base'
89 | using Base::x;
| ^
/usr/include/eigen3/Eigen/src/Core/DenseBase.h:90:15: error: 'y' has not been declared in 'Eigen::DenseBase<Eigen::Product<Eigen::Matrix<std::complex<float>, -1, -1, 1, -1, -1>, Eigen::DiagonalWrapper<const Eigen::Matrix<double, -1, 1> >, 1> >::Base'
90 | using Base::y;
| ^
/usr/include/eigen3/Eigen/src/Core/DenseBase.h:91:15: error: 'z' has not been declared in 'Eigen::DenseBase<Eigen::Product<Eigen::Matrix<std::complex<float>, -1, -1, 1, -1, -1>, Eigen::DiagonalWrapper<const Eigen::Matrix<double, -1, 1> >, 1> >::Base'
91 | using Base::z;
| ^
In file included from /usr/include/eigen3/Eigen/Core:297:
/usr/include/eigen3/Eigen/src/Core/MatrixBase.h: In instantiation of 'class Eigen::MatrixBase<Eigen::Product<Eigen::Matrix<std::complex<float>, -1, -1, 1, -1, -1>, Eigen::DiagonalWrapper<const Eigen::Matrix<double, -1, 1> >, 1> >':
/usr/include/eigen3/Eigen/src/Core/Product.h:248:7: required from 'class Eigen::internal::dense_product_base<Eigen::Matrix<std::complex<float>, -1, -1, 1, -1, -1>, Eigen::DiagonalWrapper<const Eigen::Matrix<double, -1, 1> >, 1, 8>'
248 | class dense_product_base : public internal::dense_xpr_base<Product<Lhs, Rhs, Option>>::type {};
| ^~~~~~~~~~~~~~~~~~
/usr/include/eigen3/Eigen/src/Core/Product.h:276:7: required from 'class Eigen::ProductImpl<Eigen::Matrix<std::complex<float>, -1, -1, 1, -1, -1>, Eigen::DiagonalWrapper<const Eigen::Matrix<double, -1, 1> >, 1, Eigen::Dense>'
276 | class ProductImpl<Lhs, Rhs, Option, Dense> : public internal::dense_product_base<Lhs, Rhs, Option> {
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/include/eigen3/Eigen/src/Core/Product.h:198:7: required from 'class Eigen::Product<Eigen::Matrix<std::complex<float>, -1, -1, 1, -1, -1>, Eigen::DiagonalWrapper<const Eigen::Matrix<double, -1, 1> >, 1>'
198 | class Product
| ^~~~~~~
mps.hpp:159:21: required from 'std::tuple<Eigen::Tensor<dtype, 3, Storage, long int>, Eigen::Tensor<dtype, 1, Storage, long int>, Eigen::Tensor<dtype, 3, Storage, long int> > split_truncate_theta(const Eigen::Tensor<dtype, 4, Storage>&, size_t, dtype) [with dtype = std::complex<float>; unsigned int Storage = 1; size_t = long unsigned int]'
159 | MatX theta2 = U * S.asDiagonal() * V;
| ~~^~~~~~~~~~~~~~~~
main.cpp:37:62: required from here
37 | auto [A, S, B] = split_truncate_theta<dtype, e::RowMajor>(t4, chi_max, eps);
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~
/usr/include/eigen3/Eigen/src/Core/MatrixBase.h:72:15: error: 'coeff' has not been declared in 'Eigen::MatrixBase<Eigen::Product<Eigen::Matrix<std::complex<float>, -1, -1, 1, -1, -1>, Eigen::DiagonalWrapper<const Eigen::Matrix<double, -1, 1> >, 1> >::Base'
72 | using Base::coeff;
| ^~~~~
/usr/include/eigen3/Eigen/src/Core/MatrixBase.h:77:15: error: 'eval' has not been declared in 'Eigen::MatrixBase<Eigen::Product<Eigen::Matrix<std::complex<float>, -1, -1, 1, -1, -1>, Eigen::DiagonalWrapper<const Eigen::Matrix<double, -1, 1> >, 1> >::Base'
77 | using Base::eval;
| ^~~~
/usr/include/eigen3/Eigen/src/Core/MatrixBase.h:81:23: error: 'operator-' has not been declared in 'Eigen::MatrixBase<Eigen::Product<Eigen::Matrix<std::complex<float>, -1, -1, 1, -1, -1>, Eigen::DiagonalWrapper<const Eigen::Matrix<double, -1, 1> >, 1> >::Base'
81 | using Base::operator-;
| ^
/usr/include/eigen3/Eigen/src/Core/MatrixBase.h:84:23: error: 'operator*=' has not been declared in 'Eigen::MatrixBase<Eigen::Product<Eigen::Matrix<std::complex<float>, -1, -1, 1, -1, -1>, Eigen::DiagonalWrapper<const Eigen::Matrix<double, -1, 1> >, 1> >::Base'
84 | using Base::operator*=;
| ^~
/usr/include/eigen3/Eigen/src/Core/MatrixBase.h:85:23: error: 'operator/=' has not been declared in 'Eigen::MatrixBase<Eigen::Product<Eigen::Matrix<std::complex<float>, -1, -1, 1, -1, -1>, Eigen::DiagonalWrapper<const Eigen::Matrix<double, -1, 1> >, 1> >::Base'
85 | using Base::operator/=;
| ^~
/usr/include/eigen3/Eigen/src/Core/DenseBase.h: In instantiation of 'class Eigen::DenseBase<Eigen::Product<Eigen::Product<Eigen::Matrix<std::complex<float>, -1, -1, 1, -1, -1>, Eigen::DiagonalWrapper<const Eigen::Matrix<double, -1, 1> >, 1>, Eigen::Matrix<std::complex<float>, -1, -1, 1, -1, -1>, 0> >':
/usr/include/eigen3/Eigen/src/Core/MatrixBase.h:52:7: required from 'class Eigen::MatrixBase<Eigen::Product<Eigen::Product<Eigen::Matrix<std::complex<float>, -1, -1, 1, -1, -1>, Eigen::DiagonalWrapper<const Eigen::Matrix<double, -1, 1> >, 1>, Eigen::Matrix<std::complex<float>, -1, -1, 1, -1, -1>, 0> >'
52 | class MatrixBase : public DenseBase<Derived> {
| ^~~~~~~~~~
/usr/include/eigen3/Eigen/src/Core/Product.h:248:7: required from 'class Eigen::internal::dense_product_base<Eigen::Product<Eigen::Matrix<std::complex<float>, -1, -1, 1, -1, -1>, Eigen::DiagonalWrapper<const Eigen::Matrix<double, -1, 1> >, 1>, Eigen::Matrix<std::complex<float>, -1, -1, 1, -1, -1>, 0, 8>'
248 | class dense_product_base : public internal::dense_xpr_base<Product<Lhs, Rhs, Option>>::type {};
| ^~~~~~~~~~~~~~~~~~
/usr/include/eigen3/Eigen/src/Core/Product.h:276:7: required from 'class Eigen::ProductImpl<Eigen::Product<Eigen::Matrix<std::complex<float>, -1, -1, 1, -1, -1>, Eigen::DiagonalWrapper<const Eigen::Matrix<double, -1, 1> >, 1>, Eigen::Matrix<std::complex<float>, -1, -1, 1, -1, -1>, 0, Eigen::Dense>'
276 | class ProductImpl<Lhs, Rhs, Option, Dense> : public internal::dense_product_base<Lhs, Rhs, Option> {
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/include/eigen3/Eigen/src/Core/Product.h:198:7: required from 'class Eigen::Product<Eigen::Product<Eigen::Matrix<std::complex<float>, -1, -1, 1, -1, -1>, Eigen::DiagonalWrapper<const Eigen::Matrix<double, -1, 1> >, 1>, Eigen::Matrix<std::complex<float>, -1, -1, 1, -1, -1>, 0>'
198 | class Product
| ^~~~~~~
mps.hpp:159:38: required from 'std::tuple<Eigen::Tensor<dtype, 3, Storage, long int>, Eigen::Tensor<dtype, 1, Storage, long int>, Eigen::Tensor<dtype, 3, Storage, long int> > split_truncate_theta(const Eigen::Tensor<dtype, 4, Storage>&, size_t, dtype) [with dtype = std::complex<float>; unsigned int Storage = 1; size_t = long unsigned int]'
159 | MatX theta2 = U * S.asDiagonal() * V;
| ~~~~~~~~~~~~~~~~~~~^~~
main.cpp:37:62: required from here
37 | auto [A, S, B] = split_truncate_theta<dtype, e::RowMajor>(t4, chi_max, eps);
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~
/usr/include/eigen3/Eigen/src/Core/DenseBase.h:72:15: error: 'coeff' has not been declared in 'Eigen::DenseBase<Eigen::Product<Eigen::Product<Eigen::Matrix<std::complex<float>, -1, -1, 1, -1, -1>, Eigen::DiagonalWrapper<const Eigen::Matrix<double, -1, 1> >, 1>, Eigen::Matrix<std::complex<float>, -1, -1, 1, -1, -1>, 0> >::Base'
72 | using Base::coeff;
| ^~~~~
/usr/include/eigen3/Eigen/src/Core/DenseBase.h:73:15: error: 'coeffByOuterInner' has not been declared in 'Eigen::DenseBase<Eigen::Product<Eigen::Product<Eigen::Matrix<std::complex<float>, -1, -1, 1, -1, -1>, Eigen::DiagonalWrapper<const Eigen::Matrix<double, -1, 1> >, 1>, Eigen::Matrix<std::complex<float>, -1, -1, 1, -1, -1>, 0> >::Base'
73 | using Base::coeffByOuterInner;
| ^~~~~~~~~~~~~~~~~
/usr/include/eigen3/Eigen/src/Core/DenseBase.h:81:24: error: 'operator()' has not been declared in 'Eigen::DenseBase<Eigen::Product<Eigen::Product<Eigen::Matrix<std::complex<float>, -1, -1, 1, -1, -1>, Eigen::DiagonalWrapper<const Eigen::Matrix<double, -1, 1> >, 1>, Eigen::Matrix<std::complex<float>, -1, -1, 1, -1, -1>, 0> >::Base'
81 | using Base::operator();
| ^
/usr/include/eigen3/Eigen/src/Core/DenseBase.h:82:24: error: 'operator[]' has not been declared in 'Eigen::DenseBase<Eigen::Product<Eigen::Product<Eigen::Matrix<std::complex<float>, -1, -1, 1, -1, -1>, Eigen::DiagonalWrapper<const Eigen::Matrix<double, -1, 1> >, 1>, Eigen::Matrix<std::complex<float>, -1, -1, 1, -1, -1>, 0> >::Base'
82 | using Base::operator[];
| ^
/usr/include/eigen3/Eigen/src/Core/DenseBase.h:88:15: error: 'w' has not been declared in 'Eigen::DenseBase<Eigen::Product<Eigen::Product<Eigen::Matrix<std::complex<float>, -1, -1, 1, -1, -1>, Eigen::DiagonalWrapper<const Eigen::Matrix<double, -1, 1> >, 1>, Eigen::Matrix<std::complex<float>, -1, -1, 1, -1, -1>, 0> >::Base'
88 | using Base::w;
| ^
/usr/include/eigen3/Eigen/src/Core/DenseBase.h:89:15: error: 'x' has not been declared in 'Eigen::DenseBase<Eigen::Product<Eigen::Product<Eigen::Matrix<std::complex<float>, -1, -1, 1, -1, -1>, Eigen::DiagonalWrapper<const Eigen::Matrix<double, -1, 1> >, 1>, Eigen::Matrix<std::complex<float>, -1, -1, 1, -1, -1>, 0> >::Base'
89 | using Base::x;
| ^
/usr/include/eigen3/Eigen/src/Core/DenseBase.h:90:15: error: 'y' has not been declared in 'Eigen::DenseBase<Eigen::Product<Eigen::Product<Eigen::Matrix<std::complex<float>, -1, -1, 1, -1, -1>, Eigen::DiagonalWrapper<const Eigen::Matrix<double, -1, 1> >, 1>, Eigen::Matrix<std::complex<float>, -1, -1, 1, -1, -1>, 0> >::Base'
90 | using Base::y;
| ^
/usr/include/eigen3/Eigen/src/Core/DenseBase.h:91:15: error: 'z' has not been declared in 'Eigen::DenseBase<Eigen::Product<Eigen::Product<Eigen::Matrix<std::complex<float>, -1, -1, 1, -1, -1>, Eigen::DiagonalWrapper<const Eigen::Matrix<double, -1, 1> >, 1>, Eigen::Matrix<std::complex<float>, -1, -1, 1, -1, -1>, 0> >::Base'
91 | using Base::z;
| ^
/usr/include/eigen3/Eigen/src/Core/MatrixBase.h: In instantiation of 'class Eigen::MatrixBase<Eigen::Product<Eigen::Product<Eigen::Matrix<std::complex<float>, -1, -1, 1, -1, -1>, Eigen::DiagonalWrapper<const Eigen::Matrix<double, -1, 1> >, 1>, Eigen::Matrix<std::complex<float>, -1, -1, 1, -1, -1>, 0> >':
/usr/include/eigen3/Eigen/src/Core/Product.h:248:7: required from 'class Eigen::internal::dense_product_base<Eigen::Product<Eigen::Matrix<std::complex<float>, -1, -1, 1, -1, -1>, Eigen::DiagonalWrapper<const Eigen::Matrix<double, -1, 1> >, 1>, Eigen::Matrix<std::complex<float>, -1, -1, 1, -1, -1>, 0, 8>'
248 | class dense_product_base : public internal::dense_xpr_base<Product<Lhs, Rhs, Option>>::type {};
| ^~~~~~~~~~~~~~~~~~
/usr/include/eigen3/Eigen/src/Core/Product.h:276:7: required from 'class Eigen::ProductImpl<Eigen::Product<Eigen::Matrix<std::complex<float>, -1, -1, 1, -1, -1>, Eigen::DiagonalWrapper<const Eigen::Matrix<double, -1, 1> >, 1>, Eigen::Matrix<std::complex<float>, -1, -1, 1, -1, -1>, 0, Eigen::Dense>'
276 | class ProductImpl<Lhs, Rhs, Option, Dense> : public internal::dense_product_base<Lhs, Rhs, Option> {
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/include/eigen3/Eigen/src/Core/Product.h:198:7: required from 'class Eigen::Product<Eigen::Product<Eigen::Matrix<std::complex<float>, -1, -1, 1, -1, -1>, Eigen::DiagonalWrapper<const Eigen::Matrix<double, -1, 1> >, 1>, Eigen::Matrix<std::complex<float>, -1, -1, 1, -1, -1>, 0>'
198 | class Product
| ^~~~~~~
mps.hpp:159:38: required from 'std::tuple<Eigen::Tensor<dtype, 3, Storage, long int>, Eigen::Tensor<dtype, 1, Storage, long int>, Eigen::Tensor<dtype, 3, Storage, long int> > split_truncate_theta(const Eigen::Tensor<dtype, 4, Storage>&, size_t, dtype) [with dtype = std::complex<float>; unsigned int Storage = 1; size_t = long unsigned int]'
159 | MatX theta2 = U * S.asDiagonal() * V;
| ~~~~~~~~~~~~~~~~~~~^~~
main.cpp:37:62: required from here
37 | auto [A, S, B] = split_truncate_theta<dtype, e::RowMajor>(t4, chi_max, eps);
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~
/usr/include/eigen3/Eigen/src/Core/MatrixBase.h:72:15: error: 'coeff' has not been declared in 'Eigen::MatrixBase<Eigen::Product<Eigen::Product<Eigen::Matrix<std::complex<float>, -1, -1, 1, -1, -1>, Eigen::DiagonalWrapper<const Eigen::Matrix<double, -1, 1> >, 1>, Eigen::Matrix<std::complex<float>, -1, -1, 1, -1, -1>, 0> >::Base'
72 | using Base::coeff;
| ^~~~~
/usr/include/eigen3/Eigen/src/Core/MatrixBase.h:77:15: error: 'eval' has not been declared in 'Eigen::MatrixBase<Eigen::Product<Eigen::Product<Eigen::Matrix<std::complex<float>, -1, -1, 1, -1, -1>, Eigen::DiagonalWrapper<const Eigen::Matrix<double, -1, 1> >, 1>, Eigen::Matrix<std::complex<float>, -1, -1, 1, -1, -1>, 0> >::Base'
77 | using Base::eval;
| ^~~~
/usr/include/eigen3/Eigen/src/Core/MatrixBase.h:81:23: error: 'operator-' has not been declared in 'Eigen::MatrixBase<Eigen::Product<Eigen::Product<Eigen::Matrix<std::complex<float>, -1, -1, 1, -1, -1>, Eigen::DiagonalWrapper<const Eigen::Matrix<double, -1, 1> >, 1>, Eigen::Matrix<std::complex<float>, -1, -1, 1, -1, -1>, 0> >::Base'
81 | using Base::operator-;
| ^
/usr/include/eigen3/Eigen/src/Core/MatrixBase.h:84:23: error: 'operator*=' has not been declared in 'Eigen::MatrixBase<Eigen::Product<Eigen::Product<Eigen::Matrix<std::complex<float>, -1, -1, 1, -1, -1>, Eigen::DiagonalWrapper<const Eigen::Matrix<double, -1, 1> >, 1>, Eigen::Matrix<std::complex<float>, -1, -1, 1, -1, -1>, 0> >::Base'
84 | using Base::operator*=;
| ^~
/usr/include/eigen3/Eigen/src/Core/MatrixBase.h:85:23: error: 'operator/=' has not been declared in 'Eigen::MatrixBase<Eigen::Product<Eigen::Product<Eigen::Matrix<std::complex<float>, -1, -1, 1, -1, -1>, Eigen::DiagonalWrapper<const Eigen::Matrix<double, -1, 1> >, 1>, Eigen::Matrix<std::complex<float>, -1, -1, 1, -1, -1>, 0> >::Base'
85 | using Base::operator/=;
| ^~
/usr/include/c++/14.2.1/format: In instantiation of 'class std::__format::_Checking_scanner<char, std::complex<float> >':
/usr/include/c++/14.2.1/format:4210:4: required from 'consteval std::basic_format_string<_CharT, _Args>::basic_format_string(const _Tp&) [with _Tp = char [4]; _CharT = char; _Args = {std::complex<float>&}]'
4210 | __scanner(_M_str);
| ^~~~~~~~~
mps.hpp:162:19: required from 'std::tuple<Eigen::Tensor<dtype, 3, Storage, long int>, Eigen::Tensor<dtype, 1, Storage, long int>, Eigen::Tensor<dtype, 3, Storage, long int> > split_truncate_theta(const Eigen::Tensor<dtype, 4, Storage>&, size_t, dtype) [with dtype = std::complex<float>; unsigned int Storage = 1; size_t = long unsigned int]'
162 | std::print("{} ", theta2.data()[i]);
| ~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~
main.cpp:37:62: required from here
37 | auto [A, S, B] = split_truncate_theta<dtype, e::RowMajor>(t4, chi_max, eps);
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~
/usr/include/c++/14.2.1/format:4065:10: error: static assertion failed: std::formatter must be specialized for each type being formatted
4065 | (is_default_constructible_v<formatter<_Args, _CharT>> && ...),
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/include/c++/14.2.1/format:4065:10: note: 'std::is_default_constructible_v<std::formatter<std::complex<float>, char> >' evaluates to false
/usr/include/c++/14.2.1/format: In instantiation of 'constexpr void std::__format::_Checking_scanner<_CharT, _Args>::_M_parse_format_spec(std::size_t) [with _Tp = std::complex<float>; _OtherArgs = {}; _CharT = char; _Args = {std::complex<float>}; std::size_t = long unsigned int]':
/usr/include/c++/14.2.1/format:4082:33: required from 'std::tuple<Eigen::Tensor<dtype, 3, Storage, long int>, Eigen::Tensor<dtype, 1, Storage, long int>, Eigen::Tensor<dtype, 3, Storage, long int> > split_truncate_theta(const Eigen::Tensor<dtype, 4, Storage>&, size_t, dtype) [with dtype = std::complex<float>; unsigned int Storage = 1; size_t = long unsigned int]'
4082 | _M_parse_format_spec<_Args...>(__id);
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~
main.cpp:37:62: required from here
37 | auto [A, S, B] = split_truncate_theta<dtype, e::RowMajor>(t4, chi_max, eps);
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~
mps.hpp:162:19: in 'constexpr' expansion of 'std::basic_format_string<char, std::complex<float>&>("{} ")'
/usr/include/c++/14.2.1/format:4211:19: in 'constexpr' expansion of '__scanner.std::__format::_Checking_scanner<char, std::complex<float> >::std::__format::_Scanner<char>.std::__format::_Scanner<char>::_M_scan()'
/usr/include/c++/14.2.1/format:3952:30: in 'constexpr' expansion of '((std::__format::_Scanner<char>*)this)->std::__format::_Scanner<char>::_M_on_replacement_field()'
/usr/include/c++/14.2.1/format:4004:15: in 'constexpr' expansion of '((std::__format::_Scanner<char>*)this)->std::__format::_Scanner<char>::_M_format_arg(__id)'
/usr/include/c++/14.2.1/format:4095:38: error: use of deleted function 'std::formatter<_Tp, _CharT>::formatter() [with _Tp = std::complex<float>; _CharT = char]'
4095 | formatter<_Tp, _CharT> __f;
| ^~~
/usr/include/c++/14.2.1/format:178:7: note: declared here
178 | formatter() = delete; // No std::formatter specialization for this type.
| ^~~~~~~~~
/usr/include/c++/14.2.1/format:4095:38: note: use '-fdiagnostics-all-candidates' to display considered candidates
4095 | formatter<_Tp, _CharT> __f;
| ^~~
/usr/include/c++/14.2.1/format:4096:42: error: 'class std::formatter<std::complex<float>, char>' has no member named 'parse'
4096 | this->_M_pc.advance_to(__f.parse(this->_M_pc));
| ~~~~^~~~~
mps.hpp: In instantiation of 'std::tuple<Eigen::Tensor<dtype, 3, Storage, long int>, Eigen::Tensor<dtype, 1, Storage, long int>, Eigen::Tensor<dtype, 3, Storage, long int> > split_truncate_theta(const Eigen::Tensor<dtype, 4, Storage>&, size_t, dtype) [with dtype = std::complex<float>; unsigned int Storage = 1; size_t = long unsigned int]':
main.cpp:37:62: required from here
37 | auto [A, S, B] = split_truncate_theta<dtype, e::RowMajor>(t4, chi_max, eps);
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~
mps.hpp:162:19: error: call to consteval function 'std::basic_format_string<char, std::complex<float>&>("{} ")' is not a constant expression
162 | std::print("{} ", theta2.data()[i]);
| ~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~
In file included from /usr/include/eigen3/Eigen/Core:175:
/usr/include/eigen3/Eigen/src/Core/AssignEvaluator.h: In instantiation of 'constexpr void Eigen::internal::call_assignment_no_alias(Dst&, const Src&, const Func&) [with Dst = Eigen::Matrix<double, -1, 1>; Src = Eigen::Matrix<float, -1, 1>; Func = assign_op<double, float>]':
/usr/include/eigen3/Eigen/src/Core/PlainObjectBase.h:770:39: required from 'constexpr Derived& Eigen::PlainObjectBase<Derived>::_set_noalias(const Eigen::DenseBase<OtherDerived>&) [with OtherDerived = Eigen::Matrix<float, -1, 1>; Derived = Eigen::Matrix<double, -1, 1>]'
770 | internal::call_assignment_no_alias(this->derived(), other.derived(),
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
771 | internal::assign_op<Scalar, typename OtherDerived::Scalar>());
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/include/eigen3/Eigen/src/Core/PlainObjectBase.h:565:17: required from 'Eigen::PlainObjectBase<Derived>::PlainObjectBase(const Eigen::DenseBase<OtherDerived>&) [with OtherDerived = Eigen::Matrix<float, -1, 1>; Derived = Eigen::Matrix<double, -1, 1>]'
565 | _set_noalias(other);
| ~~~~~~~~~~~~^~~~~~~
/usr/include/eigen3/Eigen/src/Core/Matrix.h:386:108: required from 'Eigen::Matrix<Scalar_, Rows_, Cols_, Options_, MaxRows_, MaxCols_>::Matrix(const Eigen::EigenBase<OtherDerived>&) [with OtherDerived = Eigen::Matrix<float, -1, 1>; Scalar_ = double; int Rows_ = -1; int Cols_ = 1; int Options_ = 0; int MaxRows_ = -1; int MaxCols_ = 1]'
386 | EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Matrix(const EigenBase<OtherDerived>& other) : Base(other.derived()) {}
| ^
mps.hpp:136:24: required from 'std::tuple<Eigen::Tensor<dtype, 3, Storage, long int>, Eigen::Tensor<dtype, 1, Storage, long int>, Eigen::Tensor<dtype, 3, Storage, long int> > split_truncate_theta(const Eigen::Tensor<dtype, 4, Storage>&, size_t, dtype) [with dtype = std::complex<float>; unsigned int Storage = 1; size_t = long unsigned int]'
136 | const e::VectorXd& S = svd.singularValues();
| ^
main.cpp:37:62: required from here
37 | auto [A, S, B] = split_truncate_theta<dtype, e::RowMajor>(t4, chi_max, eps);
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~
/usr/include/eigen3/Eigen/src/Core/AssignEvaluator.h:839:3: error: static assertion failed: YOU_MIXED_DIFFERENT_NUMERIC_TYPES__YOU_NEED_TO_USE_THE_CAST_METHOD_OF_MATRIXBASE_TO_CAST_NUMERIC_TYPES_EXPLICITLY
839 | EIGEN_CHECK_BINARY_COMPATIBILIY(Func, typename ActualDstTypeCleaned::Scalar, typename Src::Scalar);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/include/eigen3/Eigen/src/Core/AssignEvaluator.h:839:3: note: 'Eigen::internal::has_ReturnType<Eigen::ScalarBinaryOpTraits<double, float, Eigen::internal::assign_op<double, float> > >::value' evaluates to false
make: *** [Makefile:47: ../build/main.o] Error 1

18
src/pch.hpp Normal file
View File

@ -0,0 +1,18 @@
#pragma once
#include <vector>
#include <print>
#include <algorithm>
#include <eigen3/unsupported/Eigen/CXX11/Tensor>
#include <eigen3/Eigen/Eigen>
#include <iostream>
#include <complex>
#include <ranges>
// using dtype = std::complex<float>;
// namespace e = Eigen;
// template class e::Matrix<dtype, 2, 2>;
// template class e::Matrix<dtype, 4, 4>;
// template class e::Tensor<dtype, 1>;
// template class e::Tensor<dtype, 2>;
// template class e::Tensor<dtype, 3>;
// template class e::Tensor<dtype, 4>;

BIN
src/pch.hpp.gch Normal file

Binary file not shown.

270
src/process_benchmarks.py Normal file
View File

@ -0,0 +1,270 @@
import numpy as np
import matplotlib.pyplot as plt
from sys import argv
import os
import re
from scipy.optimize import curve_fit
import matplotlib as mpl
import mpl_colors
def get_name(L, n_steps, eps, chi_max):
return f"L={L:02}_nSteps={n_steps:04}_eps={eps:e}_chiMax={chi_max:02}"
def power2_(x, a, b):
return a * 2**x + b
def power2(x, a, b, c):
return a * 2**(x*b) + c
def powerX(x, a, b, c, d):
return a * b**(x*c) + d
def power2squared(x, a, b, c):
return a * 2**(x**2 *b) + c
def exp(x, a, b):
return a * np.exp(b * x)
def total4D(x, a, b, c, d, e):
L, n, eps, chi = np.split(x, 4, axis=1)
return (a * n * e**(L*d) * (1 + b * eps) * 2**chi + c).flatten()
# fit_and_plot(Ldata, 0, powerX, axL)
# fit_and_plot(Ndata, 1, lambda x, a, b: a * x + b, axN)
# fit_and_plot(Epsdata, 2, lambda x, a: x * 0 + a, axEps)
# fit_and_plot(Chidata, 3, power2, axChi)
def fit(f, x, y):
popt, pcov = curve_fit(f, x, y)
yfit = f(x, *popt)
return yfit, popt, pcov
platform_colors = {
"firefox": "o",
"chromium": "b",
"native": "g",
}
def process_benchmarks_1AT(benchmarks):
L_default = 12
n_steps_default = 200
eps_default = 1e-10
chi_max_default = 20
get_benchmarks_lambdas = [
lambda a: a[(a[:,1] == n_steps_default) & (a[:,2] == eps_default) & (a[:,3] == chi_max_default)],
lambda a: a[(a[:,0] == L_default) & (a[:,2] == eps_default) & (a[:,3] == chi_max_default)],
lambda a: a[(a[:,0] == L_default) & (a[:,1] == n_steps_default) & (a[:,3] == chi_max_default)],
lambda a: a[(a[:,0] == L_default) & (a[:,1] == n_steps_default) & (a[:,2] == eps_default)],
]
fit_fs = [
# powerX,
power2,
lambda x, a, b: a * x + b,
lambda x, a: x * 0 + a,
power2,
]
params = ["L", "N", "Eps", "Chi"]
N_params = len(params)
xlabels = ["$L$", r"$n_\text{steps}$", r"$\epsilon$", r"$\chi_\text{max}$"]
colors = ["b", "g", "o", "r"]
fig_ax_platform = [plt.subplots(figsize=(4,3)) for _ in range(N_params)]
linestyles = {"desktop": "solid", "laptop": "dashed"}
markers = {"desktop": "o", "laptop": "x"}
for device, platforms in benchmarks.items():
linestyle = linestyles[device]
marker = markers[device]
for platform, opt_levels in platforms.items():
fig_ax = [plt.subplots() for _ in range(N_params)]
for opt, data in sorted(opt_levels.items()):
score = int(np.sum(data[:,4], axis=0))
print(f"{platform} O{opt}: {score}")
for i in range(N_params):
c = colors[opt]
d = get_benchmarks_lambdas[i](data)
x = d[:,i]
y = d[:,4]
y_fit, popt, pcov = fit(fit_fs[i], x, y)
print(f"{platform:10} O{opt} {params[i]:3}: {popt}")
ax = fig_ax[i][1]
ax.scatter(x, y, color=c, marker=marker)
ax.plot(x, y_fit, color=c, label=f"O{opt}", linestyle=linestyle)
ax.set_xlabel(xlabels[i])
if params[i] in ["Eps"]: ax.set_xscale("log")
if opt == 2: # platform comparison
c = platform_colors[platform]
ax = fig_ax_platform[i][1]
ax.scatter(x, y, color=c, gid=f"{platform}", marker=marker)
ax.plot(x, y_fit, color=c, label=f"{platform} {device}", gid=f"{platform}", linestyle=linestyle)
ax.set_xlabel(xlabels[i])
if params[i] in ["Eps"]: ax.set_xscale("log")
# if params[i] in ["L"]: ax.set_yscale("log")
for i, (fig, ax) in enumerate(fig_ax):
ax.legend()
ax.set_ylabel("$t$ [s]")
ax.set_title(f"{xlabels[i]} Benchmark")
fig.tight_layout()
# fig.savefig(f"plt_{platform}_{params[i]}.svg")
from matplotlib.lines import Line2D
legend_elements = []
for platform in platform_colors.keys():
legend_elements.append(Line2D([0], [0], linestyle="", marker='o', color=platform_colors[platform], label=platform, markersize=12))
for device in benchmarks.keys():
legend_elements.append(Line2D([0], [0], linestyle=linestyles[device], color=mpl_colors.P["fg0"], label=device))
for i, (fig, ax) in enumerate(fig_ax_platform):
# ax.legend()
ax.legend(handles=legend_elements)
ax.set_ylabel("$t$ [s]")
ax.set_title(f"{xlabels[i]} Benchmark")
fig.tight_layout()
fig.savefig(f"plt_platforms_{params[i]}.svg")
plt.close('all')
def process_benchmarks_all(benchmarks):
for platform, opt_levels in benchmarks.items():
figL, axL = plt.subplots()
figN, axN = plt.subplots()
figEps, axEps = plt.subplots()
figChi, axChi = plt.subplots()
axs = [axL, axN, axEps, axChi]
colors = ["blue", "green", "orange", "red"]
for opt, data in sorted(opt_levels.items()):
c = colors[opt]
score = int(np.sum(data[:,4], axis=0))
print(f"{platform} O{opt}: {score}")
# fit
x = data[:,0:4]
y = data[:,4].flatten()
y_fit, popt, pcov = fit(total4D, x, y)
print(popt, pcov)
# for plots with only one param varied
L, n, eps, chi = np.split(x, 4, axis=1)
fixed_vals = [np.max(L), np.max(n), np.min(eps), np.max(chi)]
for i in range(4):
ax = axs[i]
# selected data
sel_data = data
sel_y_fit = y_fit
for j in range(4):
if i == j: continue
selection = np.where(sel_data[:,j] == fixed_vals[j])
sel_data = sel_data[selection]
sel_y_fit = sel_y_fit[selection]
sel_x = sel_data[:,i]
sel_y = sel_data[:,4].flatten()
ax.scatter(sel_x, sel_y, color=c)
ax.plot(sel_x, sel_y_fit, color=c, label=f"O{opt}")
for ax in axs:
ax.legend()
ax.set_ylabel("$t$ [s]")
ax.set_title(f"{platform}")
# ax.set_yscale("log")
axL.set_xlabel("$L$")
axN.set_xlabel(r"$n_\text{steps}$")
axEps.set_xlabel(r"$\epsilon$")
axEps.set_xscale("log")
axChi.set_xlabel(r"$\chi_\text{max}$")
plt.show()
def get_benchmarks(filename_begin):
files = [f for f in os.listdir('.') if f.startswith(filename_begin) and f.endswith('.csv')]
if len(files) == 0:
print("No benchmarks found")
exit(1)
benchmarks = {}
# read the files
for f in files:
match = re.fullmatch(filename_begin + r"-?(\w+)-(\w+)(?:-\d+)?-O(\d).csv", f)
if not match:
print("Failed to parse benchmark: " + f)
continue
device = match.group(1)
platform = match.group(2)
opt_level = int(match.group(3))
if not device in benchmarks: benchmarks[device] = {}
if not platform in benchmarks[device]: benchmarks[device][platform] = {}
benchmarks[device][platform][opt_level] = []
with open(f, 'r') as file:
for line in file:
n, time = line.strip("\n").split(",")
time = float(time)
match = re.fullmatch(r"L=(\d+)_nSteps=(\d+)_eps=(.+)_chiMax=(\d+)", n.replace("\"", ""))
if not match:
print("Failed to parse benchmark: " + n)
exit(1)
L = int(match.group(1))
n_steps = int(match.group(2))
eps = float(match.group(3))
chi_max = int(match.group(4))
benchmarks[device][platform][opt_level].append([L, n_steps, eps, chi_max, time])
benchmarks[device][platform][opt_level] = np.array(benchmarks[device][platform][opt_level])
return benchmarks
def repl_colors():
"""
In all svgs in img_dir, replace all occurances of hex colors defined in mpl_colors.P with
var(--<color-name>)
so that when the svg is inlined into a html it can be fully (and dynamically!) styled with css
"""
img_dir = "."
hex_to_name = {v.lower(): k for k, v in mpl_colors.P.items()}
hex_color_pattern = re.compile(r"#([0-9a-fA-F]{6})")
for filename in os.listdir(img_dir):
if not filename.endswith(".svg"): continue
print(f"Replacing colors in {filename}")
file_path = os.path.join(img_dir, filename)
with open(file_path, "r") as file:
content = file.read()
hex_colors = hex_color_pattern.findall(content)
modified_content = content
for hex_color in hex_colors:
hex_code = f"#{hex_color.lower()}"
# Check if the hex code is in our map
if hex_code in hex_to_name:
color_name = hex_to_name[hex_code]
css_name = f"var(--{color_name})"
modified_content = modified_content.replace(hex_code, css_name)
else:
# Print a warning if the color is not defined
print(f"Warning: Color {hex_code} in {filename} is not defined in the color map.")
# Write the modified content back to the SVG file
with open(file_path, "w", encoding="utf-8") as file:
file.write(modified_content)
if __name__ == '__main__':
# correct fonts
mpl.rcParams["mathtext.fontset"] = "stix"
mpl.rcParams["font.family"] = "Ubuntu"
# set replaceable colorscheme
mpl_colors.set_colorscheme("dark")
benchmarks1 =get_benchmarks("benchmarks-1AT")
process_benchmarks_1AT(benchmarks1)
repl_colors()
# benchmarks2 =get_benchmarks("benchmarks-all")
# process_benchmarks_all(benchmarks2)

390
src/run.cpp Normal file
View File

@ -0,0 +1,390 @@
#include <memory>
#include <type_traits>
#ifdef EMSCRIPTEN
#include <cmath>
#include <print>
// #include <array>
#include "mps.hpp"
#include "transverse_field_ising.hpp"
#include "bose_hubbard.hpp"
#include "tebd.hpp"
#include "util.hpp"
#include "type.hpp"
#include <functional>
#include <stdexcept>
#include <emscripten.h>
#include <emscripten/bind.h>
namespace ems = emscripten;
using TfiModel = TransverseFieldIsingModel1D<dtype>;
using BhModel = BoseHubbardModel1D<dtype>;
/**
* @brief A class for generating a range of values.
* @details
* Returns `nSteps` values with `start` being the first and `stop` being the last value.
* The same value is returned `countsPerStep` times before the next value is returned.
* After the last `stop` value has been returned, the cycle is restarted from the first `start` value.
*/
template<typename T>
struct Range {
using value_type = T;
Range(T start, T stop, unsigned nSteps, unsigned countsPerStep=1) : start(start), stop(stop), nSteps(nSteps), stepCounter(0), countsPerStep(countsPerStep), counter(0) {
if (nSteps <= 1) {
throw std::invalid_argument("nSteps must be greater than 1");
}
step = (stop - start) / (nSteps - 1);
};
T getNext() {
T result = start + stepCounter * step;
if (++counter >= countsPerStep) {
counter = 0;
if (++stepCounter >= nSteps) {
stepCounter = 0;
}
}
return result;
};
private:
T start;
T stop;
T step;
unsigned nSteps;
unsigned stepCounter;
unsigned countsPerStep;
unsigned counter;
};
/**
* This ugly mechanism lets the javascript pass
* any combination of Range and basic types to a function.
* Every time the function is called, the Range classes will return a new value while the rest stays the same.
*/
template<class T, template<class> class U>
inline constexpr bool is_instance_of_v = std::false_type{};
template<template<class> class U, class V>
inline constexpr bool is_instance_of_v<U<V>,U> = std::true_type{};
template<typename T>
concept isRange = is_instance_of_v<std::remove_reference_t<T>, Range>;
// All types except Range
template <typename T, typename Enable = void>
struct transform_type {
using type = T;
};
// Range
template <typename T>
struct transform_type<T, std::enable_if_t<isRange<T>>> {
using type = typename std::remove_reference_t<T>::value_type; // If T is a Range, return its value_type
};
static_assert(isRange<Range<int>>);
static_assert(isRange<Range<double>&>);
static_assert(!isRange<double>);
// Dispatcher
template<isRange T>
std::remove_reference_t<T>::value_type processType(T&& obj) {
return obj.getNext();
}
template<typename T>
requires(!isRange<T>)
T processType(T obj) {
return obj;
}
/// Make a shared model pointer from a arguments, which may contain Range objects
template<typename Model, typename... Args>
std::shared_ptr<Model> makeModel(Args&&... args) {
std::tuple<typename transform_type<Args>::type...> results = std::make_tuple(processType(std::forward<Args>(args))...);
return std::apply([](auto&&... args) { return std::make_shared<Model>(args...); }, results);
}
// template<typename Arg>
// void printArgs(Arg&& val) {
// std::cout << val << std::endl;
// }
// template<typename Arg, typename... Args>
// void printArgs(Arg&& val, Args&&... values) {
// std::cout << val << ", ";
// printArgs(std::forward<Args>(values)...);
// }
// template<typename... Args>
// void processDebug(Args&&... args) {
// std::tuple<typename transform_type<Args>::type...> results = std::make_tuple(processType(std::forward<Args>(args))...);
// std::apply([](auto&&... args){ printArgs(args...); }, results);
// }
/**
*/
template<typename Model>
class Results {
public:
virtual ~Results() {};
virtual postUpdateFunction_t<dtype> getPostUpdateFunction(std::shared_ptr<Model> model) = 0;
virtual void postUpdateFunction(std::shared_ptr<Model> model, const MPS<dtype>& psi) = 0;
};
class TfiResults : public Results<TfiModel> {
public:
using Model_t = TfiModel;
TfiResults(ems::val callback) : callback(callback) {
sigmaZ.setValues({{1, 0}, {0, -1}});
};
/// @todo write a second postUpdateFunction without the JS callback and return that one if callback is null
postUpdateFunction_t<dtype> getPostUpdateFunction(std::shared_ptr<TfiModel> model) override {
return [model, this](const MPS<dtype>& psi, unsigned i){ this->postUpdateFunction(model, psi); };
}
void postUpdateFunction(std::shared_ptr<TfiModel> model, const MPS<dtype>& psi) override {
auto Mval = psi.getSiteExpectationValueSum(this->sigmaZ).real();
auto Eval = model->energy(psi).real();
auto Sval = psi.getEntanglementEntropy(psi.getL()/2).real();
callback(Mval, Eval, Sval);
// hand control back to browser, otherwise site hangs and callback will not be executed
emscripten_sleep(0);
}
private:
e::TensorFixedSize<dtype, e::Sizes<2, 2>> sigmaZ;
ems::val callback;
};
/**
* @brief Measures <N>, E and S
*/
class BoseHubbardResults : public Results<BhModel> {
public:
BoseHubbardResults(ems::val callback) : callback(callback) {};
postUpdateFunction_t<dtype> getPostUpdateFunction(std::shared_ptr<BhModel> model) override {
return [this, model](const MPS<dtype>& psi, unsigned) { this->postUpdateFunction(model, psi); };
}
void postUpdateFunction(std::shared_ptr<BhModel> model, const MPS<dtype>& psi) override {
e::TensorMap<const e::Tensor<dtype, 2>> particleNumberOperator(model->getLadderOperators().getN().data(), model->localDim, model->localDim);
auto Nval = psi.getSiteExpectationValueSum(particleNumberOperator).real() / model->L;
auto Eval = model->energy(psi).real();
auto Sval = psi.getEntanglementEntropy(model->L/2).real();
callback(Nval, Eval, Sval);
// hand control back to browser, otherwise site hangs and callback will not be executed
emscripten_sleep(0);
}
private:
ems::val callback;
};
// EMSCRIPTEN_BINDINGS(my_class_example) {
// class_<MyClass>("MyClass")
// .constructor<int, std::string>()
// .function("incrementX", &MyClass::incrementX)
// .property("x", &MyClass::getX, &MyClass::setX)
// .property("x_readonly", &MyClass::getX)
// .class_function("getStringFromInstance", &MyClass::getStringFromInstance)
// ;
// };
enum Tfi_INIT {
INIT_SPIN_UP = 0,
};
// struct Tfi {
// Tfi(unsigned L, double _g, double _J, Tfi_INIT init) {
// throwIfLessThan(L, 2u, "L");
// dtype g(_g);
// dtype J(_J);
// throwIfNaN(g, "g");
// throwIfNaN(J, "J");
// this->model = TfiModel<dtype>(L, J, g);
// if (init == INIT_SPIN_UP) {
// mps = initMPSspinUp<dtype>(L);
// }
// }
// TfiModel<dtype> model;
// MPS<dtype> mps;
// };
// TODO unify
// Model1D<dtype> initModel_Tfi(unsigned L, double _g, double _J) {
// dtype g(_g);
// dtype J(_J);
// throwIfNaN(g, "g");
// throwIfNaN(J, "J");
// throwIfLessThan(L, 3u, "L");
// return TfiModel<dtype>(L, J, g);
// }
enum class TEBD_TYPE {
BRICKWALL = 0,
SECOND_ORDER = 1,
};
// EMSCRIPTEN_BINDINGS(tebd_module) {
// enum_<TEBD_TYPE>(TEBD_BRICKWALL)
// .value("TEBD_BRICKWALL", TEBD_BRICKWALL)
// ;
// class<MPS<dtype>>("MPS")
// .constructor<unsigned>()
// .function("getL", &MPS<dtype>::getL)
// .function("getN", &MPS<dtype>::getN)
// .function("getN_real",
// };
/**
* @brief Run TEBD on a model and measure energy, magnetization and half-chain entropy after each TEBD iteration
* @param callback: A javascript function taking 3 parameters, M, E and S after each iteration
*/
template<typename Model>
void runTEBD(MPS<dtype>& psi, const std::shared_ptr<Model> model, unsigned nSteps, double dtReal, double dtImag, double _eps, unsigned chiMax, TEBD_TYPE type, Results<Model>& result) {
dtype eps(_eps);
dtype dt(dtReal, dtImag);
// Sanity checks
throwIfNaN(eps, "eps");
throwIfNaN(dt, "dt");
throwIfLessThan(nSteps, 1u, "nSteps");
throwIfLessThan(chiMax, 1u, "chiMax");
if (psi.getL() != model->L) {
throw std::runtime_error("psi and model have different L");
}
std::println("Running TEBD type={}, nSteps={}, dt={}, eps={}, chiMax={}", static_cast<int>(type), nSteps, dt, eps, chiMax);
// collect M over time in an array
auto F = result.getPostUpdateFunction(model);
// initial values now
F(psi, 0);
// std::println("Bonds:");
const auto& bondsMat = model->getH_BondsMatrices();
// std::cout << "First: \n" << *(bondsMat.front()) << std::endl;
// std::cout << "Secnd: \n" << *(bondsMat.at(1)) << std::endl;
// std::cout << "Last : \n" << *(bondsMat.back()) << std::endl;
// const Bonds<dtype> bonds = model.getH_Bonds();
const Bonds<dtype> bonds = getTimeEvolutionMatrices(model->getH_BondsMatrices(), dt, model->localDim);
// std::println("U:");
// int i = 0;
// for (const auto& bond : bonds) {
// printTensor(*bond, std::format("Bond[{:02}]", i));
// i++;
// }
if (type == TEBD_TYPE::BRICKWALL) {
runTEBD_brickwall(psi, bonds, chiMax, eps, nSteps, F);
} else if (type == TEBD_TYPE::SECOND_ORDER) {
const Bonds<dtype> bondsHalf = getTimeEvolutionMatrices(model->getH_BondsMatrices(), dt/2., model->localDim);
runTEBD_secondOrder(psi, bonds, bondsHalf, chiMax, eps, nSteps, F);
} else {
throw std::invalid_argument(std::format("Got invalid TEBD_TYPE: {}", static_cast<int>(type)));
}
std::println("Chis: {}", psi.getChi());
std::println("Collapse: {}", psi.collapse(0));
};
// @TODO
template<typename Model, typename... ModelArgs>
void runTEBD_phaseDiagram(MPS<dtype>& psiI, unsigned nSteps, double dtReal, double dtImag, double _eps, unsigned chiMax, TEBD_TYPE type, unsigned nValues, Results<Model>& result, ModelArgs&&... args) {
dtype eps(_eps);
dtype dt(dtReal, dtImag);
size_t L = psiI.getL();
// Sanity checks
throwIfNaN(eps, "eps");
throwIfNaN(dt, "dt");
throwIfLessThan(nSteps, 2u, "nSteps");
throwIfLessThan(chiMax, 1u, "chiMax");
std::println("Running TEBD type={}, nSteps={}, dt={}, eps={}, chiMax={}", static_cast<int>(type), nSteps, dt, eps, chiMax);
postUpdateFunction_t<dtype> F = [](const MPS<dtype>&, unsigned) {
emscripten_sleep(0);
};
for (unsigned i = 0; i < nValues; i++) {
// TfiModel model(L, U, g);
std::shared_ptr<Model> model = makeModel<Model>(std::forward<ModelArgs>(args)...);
std::println("{:02} - Model: {}", i, *model);
auto psi = psiI;
const Bonds<dtype> bonds = getTimeEvolutionMatrices(model->getH_BondsMatrices(), dt, model->localDim);
runTEBD_brickwall(psi, bonds, chiMax, eps, nSteps, F);
result.postUpdateFunction(model, psi);
}
}
void test(unsigned x) {
Range<double> range(0, 1.0, 3, 2);
std::println("test({})", x);
std::println("range({})", range.getNext());
std::println("range({})", range.getNext());
std::println("range({})", range.getNext());
std::println("range({})", range.getNext());
std::println("range({})", range.getNext());
std::println("range({})", range.getNext());
std::println("range({})", range.getNext());
std::println("range({})", range.getNext());
std::println("range({})", range.getNext());
std::println("range({})", range.getNext());
std::println("{}", processType(0.5));
std::println("{}", processType(range));
// processDebug(range, 4.3, -3, range);
// process2(range);
}
EMSCRIPTEN_BINDINGS(run) {
// see https://emscripten.org/docs/porting/connecting_cpp_and_javascript/embind.html#advanced-class-concepts
// Results
ems::class_<Results<TfiModel>>("ResultsBaseTfi");
ems::class_<Results<BhModel>>("ResultsBaseBh");
ems::class_<TfiResults, ems::base<Results<TfiModel>>>("ResultsTfi")
.constructor<ems::val>()
;
ems::class_<BoseHubbardResults, ems::base<Results<BhModel>>>("ResultsBh")
.constructor<ems::val>()
;
// Models
ems::class_<Model1D<dtype>>("Model1D")
.smart_ptr<std::shared_ptr<Model1D<dtype>>>("Model1D")
;
ems::class_<TfiModel, ems::base<Model1D<dtype>>>("ModelTfi")
// .constructor<unsigned, double ,double>()
.smart_ptr_constructor("ModelTfi", &std::make_shared<TfiModel, unsigned, double ,double>)
;
ems::class_<BhModel, ems::base<Model1D<dtype>>>("ModelBh")
// .constructor<unsigned, double, double, double, unsigned>()
.smart_ptr_constructor("ModelBh", &std::make_shared<BhModel, unsigned, double, double, double, unsigned>)
;
ems::class_<Range<double>>("DoubleRange")
.constructor<double, double, unsigned, unsigned>()
;
// Other
ems::class_<MPS<dtype>>("MPS");
ems::function("initMpsSpinUp", &initMPS_spinUp<dtype>);
ems::function("initMpsSpinRight", &initMPS_spinRight<dtype>);
ems::function("initMpsNParticles", &initMPS_nParticles<dtype>);
// ems::function("initMPS_spinUp", &initMPS_spinUp);
ems::register_vector<double>("stdVectorDouble");
ems::register_vector<std::vector<double>>("stdVectorDouble2D");
// ems::class_<Results>("TEBDResults")
// .property("M", &Results::M)
// .property("E", &Results::E)
// .property("S", &Results::S)
// ;
ems::enum_<TEBD_TYPE>("TEBD_TYPE")
.value("BRICKWALL", TEBD_TYPE::BRICKWALL)
.value("SECOND_ORDER", TEBD_TYPE::SECOND_ORDER)
;
ems::function("runTebdTfi", &runTEBD<TfiModel>);
ems::function("runTebdPhaseDiagramTfi", &runTEBD_phaseDiagram<TfiModel, unsigned, Range<double>, Range<double>>);
ems::function("runTebdBh", &runTEBD<BhModel>);
ems::function("runTebdPhaseDiagramBh", &runTEBD_phaseDiagram<BhModel, unsigned, double, Range<double>, Range<double>, unsigned>);
ems::function("test", &test);
};
#endif

22
src/stopwatch.cpp Normal file
View File

@ -0,0 +1,22 @@
#include "stopwatch.hpp"
#include <cmath>
#include <chrono>
Stopwatch::Stopwatch() : running(false) {}
void Stopwatch::start() {
t_start = std::chrono::steady_clock::now();
running = true;
}
double Stopwatch::stop() {
if (!running) { return 0; }
else {
running = false;
// (time_point - time_point) = std::chrono::duration, not double
return std::chrono::duration<double>(std::chrono::steady_clock::now() - t_start).count();
}
}

36
src/stopwatch.hpp Normal file
View File

@ -0,0 +1,36 @@
#pragma once
#include <chrono>
/**
* @brief Stopwatches measure the time between two points
* @details
* The stopwatch makes use of std::chronos::steady_clock to measure the time between
* when it was startet and when it was stoped. It returnes the time in seconds.
*/
class Stopwatch {
public:
/**
* @brief Creates a new Stopwatch instance
*/
Stopwatch();
/**
* @brief Start the stopwatch
* @details
* If the watch is started while already running, it restarts
*/
void start();
/**
* @brief Stop the stopwatch
* @returns The elapsed time in seconds as double. If the watch was not started, it returns 0
*/
double stop();
///@returns true if stopwatch is running, false if not
bool isRunning() { return running; }
private:
std::chrono::steady_clock::time_point t_start;
bool running;
};

4
src/type.hpp Normal file
View File

@ -0,0 +1,4 @@
#include <complex>
/// the datatype for all template instantiations
using dtype = std::complex<double>;

132
src/util.hpp Normal file
View File

@ -0,0 +1,132 @@
#pragma once
#include <format>
#include <complex>
#include <iostream>
#include <ranges>
#include <vector>
#include "type.hpp"
template<typename U>
struct std::formatter<std::vector<U>> {
using T = std::vector<U>;
// using std::formatter<int>::parse;
constexpr auto parse(std::format_parse_context& ctx){
auto pos = ctx.begin();
while (pos != ctx.end() && *pos != '}') {
if (*pos == 'a') { useChar = 1; }
else if (*pos == 'b') { useChar = 2; }
else if (*pos == 'p') { useChar = 3; }
++pos;
}
return pos; // expect `}` at this position, otherwise its an error
}
auto format(const T& v, auto& ctx) const {
// TODO figure out why tf it doesnt compile with openChars.at(..)
auto out = ctx.out();
// out = std::format_to(out, "{}", openChars.at(useChar));
char c = '{';
if (useChar > 0) { c = '<'; }
out = std::format_to(out, "{}", c);
auto it = v.begin();
if (it != v.end()) {
out = std::format_to(out, "{}", *it);
}
it++;
for (; it != v.end(); it++) {
out = std::format_to(out, ", {}", *it);
}
// return std::format_to(out, "{}", closeChars.at(useChar));
c = '}';
if (useChar > 0) { c = '>'; }
return std::format_to(out, "{}", c);
// return out;
}
public:
size_t useChar = 0;
static constexpr std::string openChars{"(<[{"};
static constexpr std::string closeChars{")>]}"};
};
template<typename U>
struct std::formatter<std::complex<U>> : std::formatter<U> {
using T = std::complex<U>;
using std::formatter<U>::parse;
auto format(const T& v, auto& ctx) const {
auto out = ctx.out();
out = std::format_to(out, "({}, {})", v.real(), v.imag());
return out;
}
};
// // Formatter specialization for std::complex
// template <typename T>
// struct std::formatter<std::complex<T>> {
// std::formatter<char> inner_formatter; // Inner formatter for real/imaginary parts
// // Parse the format specifier
// constexpr auto parse(std::format_parse_context& ctx) {
// // Use the same format specifier for real/imaginary parts as for other types
// return inner_formatter.parse(ctx);
// }
// // Format the complex number
// template <typename FormatContext>
// auto format(const std::complex<T>& c, FormatContext& ctx) {
// // Format both real and imaginary parts using the inner formatter
// return std::format_to(
// ctx.out(),
// "({} + {}i)",
// std::format(inner_formatter, c.real()), // Format real part
// std::format(inner_formatter, c.imag()) // Format imaginary part
// );
// }
// };
template<typename T, typename string_type>
void printTensor(const T& t, string_type name="") {
std::cout << name << "(" << t.dimensions() << ") = \n" << t << "\n" << std::endl;
}
template<typename T, typename string_type>
void printTensorDims(const T& t, string_type name="") {
std::cout << name << "(" << t.dimensions() << ")" << std::endl;
}
template<typename T>
void throwIfNaN(const std::complex<T>& v, std::string_view name) {
if (std::isnan(v.real())) {
throw std::invalid_argument(std::format("Real part of variable '{}' is NaN", name));
}
else if (std::isnan(v.imag())) {
throw std::invalid_argument(std::format("Imaginary part of variable '{}' is NaN", name));
}
}
template<typename T>
void throwIfLessThan(const T& v, const T& compare, std::string_view name) {
if (v < compare) {
throw std::invalid_argument(std::format("Variable '{}' must not be less than '{}', but is '{}'", name, v, compare));
}
}
// Make every class with public std::string format() const member formatable
template <typename T>
concept HasFormat = requires(const T obj) {
{ obj.format() } -> std::convertible_to<std::string>;
};
template<HasFormat T>
struct std::formatter<T> {
constexpr auto parse(std::format_parse_context& ctx) {
return ctx.begin();
}
auto format(const T& obj, auto& ctx) const {
return std::format_to(ctx.out(), "{}", obj.format());
}
};

35
src/wasm-test.cpp Normal file
View File

@ -0,0 +1,35 @@
#ifdef EMSCRIPTEN
#include <numeric>
#include <vector>
#include <print>
#include "model.hpp"
#include "util.hpp"
#include <emscripten/bind.h>
namespace ems = emscripten;
struct Test {
std::vector<int> v;
size_t var1;
size_t var2;
};
Test makeTest(unsigned n) {
Test test;
test.v.resize(n);
std::iota(test.v.begin(), test.v.end(), 0);
return test;
}
void useTest(const Test& test, unsigned x) {
std::println("useTest: size={}, x={}", test.v.size(), x);
}
// EMSCRIPTEN_BINDINGS(test) {
// // ems::class_<TFIModel<dtype>>("TFIModel");
// // ems::class_<MPS<dtype>>("MPS");
// ems::function("makeTest", &makeTest);
// ems::function("useTest", &useTest);
// }
#endif