<commit_before>//
// Created by david on 2019-07-06.
//

#include <general/nmspc_tensor_extra.h>
#include <general/nmspc_tensor_omp.h>
// textra must appear first
#include "class_mps_site.h"
#include <config/debug.h>
#include <math/hash.h>
#include <tools/common/fmt.h>
#include <utility>

using Scalar = class_mps_site::Scalar;

class_mps_site::class_mps_site() = default;
class_mps_site::class_mps_site(const Eigen::Tensor<Scalar, 3> &M_, const Eigen::Tensor<Scalar, 1> &L_, size_t pos, double error, std::string label_)
    : M(M_), L(L_), position(pos), truncation_error(error), label(std::move(label_)) {}

class_mps_site::class_mps_site(const Eigen::Tensor<Scalar, 3> &M_, std::optional<Eigen::Tensor<Scalar, 1>> L_, size_t pos, double error, std::string label_)
    : M(M_), L(std::move(L_)), position(pos), truncation_error(error), label(std::move(label_)) {}

// We need to define the destructor and other special functions
// because we enclose data in unique_ptr for this pimpl idiom.
// Otherwise unique_ptr will forcibly inline its own default deleter.
// Here we follow "rule of five", so we must also define
// our own copy/move ctor and copy/move assignments
// This has the side effect that we must define our own
// operator= and copy assignment constructor.
// Read more: https://stackoverflow.com/questions/33212686/how-to-use-unique-ptr-with-forward-declared-type
// And here:  https://stackoverflow.com/questions/6012157/is-stdunique-ptrt-required-to-know-the-full-definition-of-t
class_mps_site::~class_mps_site()                      = default;            // default dtor
class_mps_site::class_mps_site(class_mps_site &&other) = default;            // default move ctor
class_mps_site &class_mps_site::operator=(class_mps_site &&other) = default; // default move assign
class_mps_site::class_mps_site(const class_mps_site &other)       = default;
class_mps_site &class_mps_site::operator=(const class_mps_site &other) = default;

bool class_mps_site::isCenter() const { return LC.has_value(); }

bool class_mps_site::has_L() const { return L.has_value(); }
bool class_mps_site::has_M() const { return M.has_value(); }
bool class_mps_site::has_LC() const { return LC.has_value(); }

Eigen::DSizes<long, 3> class_mps_site::dimensions() const { return Eigen::DSizes<long, 3>{spin_dim(), get_chiL(), get_chiR()}; }

bool class_mps_site::is_real() const { return Textra::isReal(get_M_bare(), "M_bare") and Textra::isReal(get_L(), "L"); }
bool class_mps_site::has_nan() const { return Textra::hasNaN(get_M_bare(), "M_bare") and Textra::hasNaN(get_L(), "L"); }
void class_mps_site::assert_validity() const {
    if(has_nan()) throw std::runtime_error(fmt::format("class_mps_site::assert_validity(): MPS (M or L) at position {} has NaN's", get_position()));
}

void class_mps_site::assert_dimensions() const {
    if(get_label() == "B" and get_chiR() != get_L().dimension(0))
        throw std::runtime_error(fmt::format("class_mps_site: Assert failed: Dimensions for B and L are incompatible at site {}: B {} | L {}", get_position(),
                                             get_M_bare().dimensions(), get_L().dimensions()));
    if(get_label() == "A" and get_chiL() != get_L().dimension(0))
        throw std::runtime_error(fmt::format("class_mps_site: Assert failed: Dimensions for A and L are incompatible at site {}: A {} | L {}", get_position(),
                                             get_M_bare().dimensions(), get_L().dimensions()));
    if(get_label() == "AC") {
        if(get_chiL() != get_L().dimension(0))
            throw std::runtime_error(fmt::format("class_mps_site: Assert failed: Dimensions for AC and L are incompatible at site {}: AC {} | L {}",
                                                 get_position(), get_M_bare().dimensions(), get_L().dimensions()));
        if(get_chiR() != get_LC().dimension(0))
            throw std::runtime_error(fmt::format("class_mps_site: Assert failed: Dimensions for AC and L are incompatible at site {}: AC {} | LC{}",
                                                 get_position(), get_M_bare().dimensions(), get_L().dimensions()));
    }
}

void class_mps_site::assert_identity() const {
    if(get_label() == "B") {
        Eigen::Tensor<Scalar, 2> id = get_M_bare().contract(get_M_bare().conjugate(), Textra::idx({0, 2}, {0, 2}));
        if(not Textra::TensorMatrixMap(id).isIdentity(1e-4)) {
            throw std::runtime_error(fmt::format("class_mps_site: B^dagger B is not identity at pos {}", get_position()));
        }
    } else {
        Eigen::Tensor<Scalar, 2> id = get_M_bare().contract(get_M_bare().conjugate(), Textra::idx({0, 1}, {0, 1}));
        if(not Textra::TensorMatrixMap(id).isIdentity(1e-4)) {
            throw std::runtime_error(fmt::format("class_mps_site: A^dagger A is not identity at pos {}", get_position()));
        }
    }
}

const Eigen::Tensor<Scalar, 3> &class_mps_site::get_M_bare() const {
    if(M) {
        if(M.value().size() == 0) throw std::runtime_error(fmt::format("class_mps_site::get_M_bare(): M has size 0 at position {}", get_position()));
        return M.value();
    } else<commit_msg>Improved checks for left/right normalization of mps<commit_after>//
// Created by david on 2019-07-06.
//

#include <general/nmspc_tensor_extra.h>
#include <general/nmspc_tensor_omp.h>
// textra must appear first
#include "class_mps_site.h"
#include <config/debug.h>
#include <math/hash.h>
#include <tools/common/fmt.h>
#include <utility>

using Scalar = class_mps_site::Scalar;

class_mps_site::class_mps_site() = default;
class_mps_site::class_mps_site(const Eigen::Tensor<Scalar, 3> &M_, const Eigen::Tensor<Scalar, 1> &L_, size_t pos, double error, std::string label_)
    : M(M_), L(L_), position(pos), truncation_error(error), label(std::move(label_)) {}

class_mps_site::class_mps_site(const Eigen::Tensor<Scalar, 3> &M_, std::optional<Eigen::Tensor<Scalar, 1>> L_, size_t pos, double error, std::string label_)
    : M(M_), L(std::move(L_)), position(pos), truncation_error(error), label(std::move(label_)) {}

// We need to define the destructor and other special functions
// because we enclose data in unique_ptr for this pimpl idiom.
// Otherwise unique_ptr will forcibly inline its own default deleter.
// Here we follow "rule of five", so we must also define
// our own copy/move ctor and copy/move assignments
// This has the side effect that we must define our own
// operator= and copy assignment constructor.
// Read more: https://stackoverflow.com/questions/33212686/how-to-use-unique-ptr-with-forward-declared-type
// And here:  https://stackoverflow.com/questions/6012157/is-stdunique-ptrt-required-to-know-the-full-definition-of-t
class_mps_site::~class_mps_site()                      = default;            // default dtor
class_mps_site::class_mps_site(class_mps_site &&other) = default;            // default move ctor
class_mps_site &class_mps_site::operator=(class_mps_site &&other) = default; // default move assign
class_mps_site::class_mps_site(const class_mps_site &other)       = default;
class_mps_site &class_mps_site::operator=(const class_mps_site &other) = default;

bool class_mps_site::isCenter() const { return LC.has_value(); }

bool class_mps_site::has_L() const { return L.has_value(); }
bool class_mps_site::has_M() const { return M.has_value(); }
bool class_mps_site::has_LC() const { return LC.has_value(); }

Eigen::DSizes<long, 3> class_mps_site::dimensions() const { return Eigen::DSizes<long, 3>{spin_dim(), get_chiL(), get_chiR()}; }

bool class_mps_site::is_real() const { return Textra::isReal(get_M_bare(), "M_bare") and Textra::isReal(get_L(), "L"); }
bool class_mps_site::has_nan() const { return Textra::hasNaN(get_M_bare(), "M_bare") and Textra::hasNaN(get_L(), "L"); }
void class_mps_site::assert_validity() const {
    if(has_nan()) throw std::runtime_error(fmt::format("class_mps_site::assert_validity(): MPS (M or L) at position {} has NaN's", get_position()));
}

void class_mps_site::assert_dimensions() const {
    if(get_label() == "B" and get_chiR() != get_L().dimension(0))
        throw std::runtime_error(fmt::format("class_mps_site: Assert failed: Dimensions for B and L are incompatible at site {}: B {} | L {}", get_position(),
                                             get_M_bare().dimensions(), get_L().dimensions()));
    if(get_label() == "A" and get_chiL() != get_L().dimension(0))
        throw std::runtime_error(fmt::format("class_mps_site: Assert failed: Dimensions for A and L are incompatible at site {}: A {} | L {}", get_position(),
                                             get_M_bare().dimensions(), get_L().dimensions()));
    if(get_label() == "AC") {
        if(get_chiL() != get_L().dimension(0))
            throw std::runtime_error(fmt::format("class_mps_site: Assert failed: Dimensions for AC and L are incompatible at site {}: AC {} | L {}",
                                                 get_position(), get_M_bare().dimensions(), get_L().dimensions()));
        if(get_chiR() != get_LC().dimension(0))
            throw std::runtime_error(fmt::format("class_mps_site: Assert failed: Dimensions for AC and L are incompatible at site {}: AC {} | LC{}",
                                                 get_position(), get_M_bare().dimensions(), get_L().dimensions()));
    }
}

void class_mps_site::assert_identity() const {
    if(get_label() == "B") {
        Eigen::Tensor<Scalar, 2> id = get_M_bare().contract(get_M_bare().conjugate(), Textra::idx({0, 2}, {0, 2}));
        if(not Textra::TensorMatrixMap(id).isIdentity(1e-4)) {
            throw std::runtime_error(fmt::format("class_mps_site: {0}^dagger {0} is not identity at pos {1}: \n{2}",get_label(), get_position(), get_M_bare()));
        }
    } else {
        Eigen::Tensor<Scalar, 2> id = get_M_bare().contract(get_M_bare().conjugate(), Textra::idx({0, 1}, {0, 1}));
        if(not Textra::TensorMatrixMap(id).isIdentity(1e-4)) {
            throw std::runtime_error(fmt::format("class_mps_site: {0}^dagger {0} is not identity at pos {1}: \n{2}",get_label(), get_position(), get_M_bare()));
        }
    }
    if(isCenter() or get_label() == "AC"){
        Eigen::Tensor<Scalar, 0> MM = get_M().contract(get_M().conjugate(), Textra::idx({0, 1, 2}, {0, 1, 2}));
        auto norm = std::real(MM(0));
        if(std::abs(norm - 1) > 1e-4) throw std::runtime_error(fmt::format("class_mps_site: {0}^dagger {0} is not unity at pos {1}: {2:.16f}",get_label(), get_position(),norm));
    }
}

const Eigen::Tensor<Scalar, 3> &class_mps_site::get_M_bare() const {
    if(M) {
        if(M.value().size() == 0) throw std::runtime_error(fmt::format("class_mps_site::get_M_bare(): M has size 0 at position {}", get_position()));
        return M.value();
    } else
{'fraction_non_alphanumeric': 0.16194106163428465, 'fraction_numerical': 0.011620340243562332, 'mean_word_length': 4.057827926657263, 'pattern_counts': {'":': 0, '<': 36, '<?xml version=': 0, '>': 37, 'https://': 4, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 16, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2220011', 'n_tokens_mistral': 3704, 'n_tokens_neox': 3548, 'n_words': 803}