From 4a4879a63f034483e55dac7448a92c796619b0be Mon Sep 17 00:00:00 2001 From: Manuel Schneider Date: Sun, 12 Apr 2026 02:34:24 +0800 Subject: [PATCH 01/40] _Save(std::fstream) and _Load(std::fstream) become new more generic methods: to_binary(std::ostream) and from_binary(std::istream) Clearer separation: Save and Load handle file IO, while to_binary and from_binary only write to ostream or istream implementation classes provide to_binary_dispatch(std::ostream) and from_binary_dispatch(std::istream) --- include/Bond.hpp | 4 ++-- include/Symmetry.hpp | 4 ++-- include/Tensor.hpp | 4 ++-- include/UniTensor.hpp | 20 ++++++++++---------- include/backend/Storage.hpp | 4 ++-- include/tn_algo/MPS.hpp | 16 ++++++++-------- src/BlockFermionicUniTensor.cpp | 8 ++++---- src/BlockUniTensor.cpp | 12 ++++-------- src/Bond.cpp | 14 ++++++-------- src/DenseUniTensor.cpp | 4 ++-- src/SparseUniTensor.cpp | 8 ++++---- src/Symmetry.cpp | 10 ++++------ src/Tensor.cpp | 20 ++++++-------------- src/UniTensor.cpp | 19 ++++++++----------- src/UniTensor_base.cpp | 4 ++-- src/backend/Storage.cpp | 17 ++++------------- src/tn_algo/MPS.cpp | 16 +++++++--------- src/tn_algo/MPS_base.cpp | 4 ++-- src/tn_algo/RegularMPS.cpp | 8 ++++---- src/tn_algo/iMPS.cpp | 8 ++++---- 20 files changed, 87 insertions(+), 117 deletions(-) diff --git a/include/Bond.hpp b/include/Bond.hpp index 58a7497a8..adeba70ee 100644 --- a/include/Bond.hpp +++ b/include/Bond.hpp @@ -875,8 +875,8 @@ namespace cytnx { static cytnx::Bond Load(const char *fname); /// @cond - void _Save(std::fstream &f) const; - void _Load(std::fstream &f); + void to_binary(std::ostream &f) const; + void from_binary(std::istream &f); /// @endcond /** diff --git a/include/Symmetry.hpp b/include/Symmetry.hpp index 46b02e5a1..f79fe0581 100644 --- a/include/Symmetry.hpp +++ b/include/Symmetry.hpp @@ -517,8 +517,8 @@ namespace cytnx { static Symmetry Load(const char *fname); /// @cond - void _Save(std::fstream &f) const; - void _Load(std::fstream &f); + void to_binary(std::ostream &f) const; + void from_binary(std::istream &f); /// @endcond /** diff --git a/include/Tensor.hpp b/include/Tensor.hpp index 5a4abf5f4..124283b89 100644 --- a/include/Tensor.hpp +++ b/include/Tensor.hpp @@ -316,8 +316,8 @@ namespace cytnx { //------------------------------------------- /// @cond - void _Save(std::fstream &f) const; - void _Load(std::fstream &f); + void to_binary(std::ostream &f) const; + void from_binary(std::istream &f); /// @endcond /** diff --git a/include/UniTensor.hpp b/include/UniTensor.hpp index d241fdb51..d2c86b352 100644 --- a/include/UniTensor.hpp +++ b/include/UniTensor.hpp @@ -434,8 +434,8 @@ namespace cytnx { virtual const vec2d &get_itoi() const; virtual vec2d &get_itoi(); - virtual void _save_dispatch(std::fstream &f) const; - virtual void _load_dispatch(std::fstream &f); + virtual void to_binary_dispatch(std::ostream &f) const; + virtual void from_binary_dispatch(std::istream &f); virtual ~UniTensor_base(){}; }; @@ -1027,8 +1027,8 @@ namespace cytnx { "\n"); } - void _save_dispatch(std::fstream &f) const; - void _load_dispatch(std::fstream &f); + void to_binary_dispatch(std::ostream &f) const; + void from_binary_dispatch(std::istream &f); const std::vector &get_qindices(const cytnx_uint64 &bidx) const { cytnx_error_msg(true, "[ERROR] get_qindices can only be unsed on UniTensor with Symmetry.%s", @@ -1687,8 +1687,8 @@ namespace cytnx { cytnx_uint16 &at_for_sparse(const std::vector &locator, const cytnx_uint16 &aux); cytnx_int16 &at_for_sparse(const std::vector &locator, const cytnx_int16 &aux); - void _save_dispatch(std::fstream &f) const; - void _load_dispatch(std::fstream &f); + void to_binary_dispatch(std::ostream &f) const; + void from_binary_dispatch(std::istream &f); // this will remove the [q_index]-th qnum at [bond_idx]-th Bond! void truncate_(const std::string &label, const cytnx_uint64 &q_index); @@ -2476,8 +2476,8 @@ namespace cytnx { cytnx_uint16 &at_for_sparse(const std::vector &locator, const cytnx_uint16 &aux); cytnx_int16 &at_for_sparse(const std::vector &locator, const cytnx_int16 &aux); - void _save_dispatch(std::fstream &f) const; - void _load_dispatch(std::fstream &f); + void to_binary_dispatch(std::ostream &f) const; + void from_binary_dispatch(std::istream &f); // this will remove the [q_index]-th qnum at [bond_idx]-th Bond! void truncate_(const std::string &label, const cytnx_uint64 &q_index); @@ -5307,8 +5307,8 @@ namespace cytnx { vec2d &get_itoi() { return this->_impl->get_itoi(); } /// @cond - void _Load(std::fstream &f); - void _Save(std::fstream &f) const; + void from_binary(std::istream &f); + void to_binary(std::ostream &f) const; /// @endcond UniTensor &convert_from(const UniTensor &rhs, const bool &force = false, diff --git a/include/backend/Storage.hpp b/include/backend/Storage.hpp index 4c693a280..0dfbe0952 100644 --- a/include/backend/Storage.hpp +++ b/include/backend/Storage.hpp @@ -524,8 +524,8 @@ namespace cytnx { ///@endcond /// @cond - void _Save(std::fstream &f) const; - void _Load(std::fstream &f); + void to_binary(std::ostream &f) const; + void from_binary(std::istream &f); void _Loadbinary(std::fstream &f, const unsigned int &dtype, const cytnx_uint64 &Nelem); void _Savebinary(std::fstream &f) const; diff --git a/include/tn_algo/MPS.hpp b/include/tn_algo/MPS.hpp index 838fd9f70..0e33db04b 100644 --- a/include/tn_algo/MPS.hpp +++ b/include/tn_algo/MPS.hpp @@ -74,8 +74,8 @@ namespace cytnx { virtual void S_mvleft(); virtual void S_mvright(); - virtual void _save_dispatch(std::fstream &f); - virtual void _load_dispatch(std::fstream &f); + virtual void to_binary_dispatch(std::ostream &f); + virtual void from_binary_dispatch(std::istream &f); }; // finite size: @@ -116,8 +116,8 @@ namespace cytnx { return out; } - void _save_dispatch(std::fstream &f); - void _load_dispatch(std::fstream &f); + void to_binary_dispatch(std::ostream &f); + void from_binary_dispatch(std::istream &f); }; // infinite size: @@ -160,8 +160,8 @@ namespace cytnx { return out; } Scalar norm() const; - void _save_dispatch(std::fstream &f); - void _load_dispatch(std::fstream &f); + void to_binary_dispatch(std::ostream &f); + void from_binary_dispatch(std::istream &f); }; ///@endcond @@ -287,8 +287,8 @@ namespace cytnx { cytnx_int64 &S_loc() { return this->_impl->S_loc; } ///@cond - void _Save(std::fstream &f) const; - void _Load(std::fstream &f); + void to_binary(std::ostream &f) const; + void from_binary(std::istream &f); ///@endcond void Save(const std::string &fname) const; diff --git a/src/BlockFermionicUniTensor.cpp b/src/BlockFermionicUniTensor.cpp index 095c1bea0..6007ab23e 100644 --- a/src/BlockFermionicUniTensor.cpp +++ b/src/BlockFermionicUniTensor.cpp @@ -2333,7 +2333,7 @@ namespace cytnx { return this->_blocks[bidx].at(loc_in_T); } - void BlockFermionicUniTensor::_save_dispatch(std::fstream &f) const { + void BlockFermionicUniTensor::to_binary_dispatch(std::ostream &f) const { //[21 Aug 2024] This is a copy from BlockUniTensor; saves signs as well cytnx_uint64 Nblocks = this->_blocks.size(); f.write((char *)&Nblocks, sizeof(cytnx_uint64)); @@ -2344,7 +2344,7 @@ namespace cytnx { } for (unsigned int b = 0; b < Nblocks; b++) { - this->_blocks[b]._Save(f); + this->_blocks[b].to_binary(f); } // Saving signs; each sign is saved as a char @@ -2355,7 +2355,7 @@ namespace cytnx { } } - void BlockFermionicUniTensor::_load_dispatch(std::fstream &f) { + void BlockFermionicUniTensor::from_binary_dispatch(std::istream &f) { //[21 Aug 2024] This is a copy from BlockUniTensor; reads signs as well cytnx_uint64 Nblocks; f.read((char *)&Nblocks, sizeof(cytnx_uint64)); @@ -2369,7 +2369,7 @@ namespace cytnx { this->_blocks.resize(Nblocks); for (unsigned int i = 0; i < this->_blocks.size(); i++) { - this->_blocks[i]._Load(f); + this->_blocks[i].from_binary(f); } // Loading signs; each sign is assumed to be saved as a char, with 0 being false diff --git a/src/BlockUniTensor.cpp b/src/BlockUniTensor.cpp index 9c1bdf75e..7213f3e14 100644 --- a/src/BlockUniTensor.cpp +++ b/src/BlockUniTensor.cpp @@ -1575,9 +1575,7 @@ namespace cytnx { return this->_blocks[bidx].at(loc_in_T); } - void BlockUniTensor::_save_dispatch(std::fstream &f) const { - // cytnx_error_msg(true,"[ERROR] Save for SparseUniTensor is under developing!!%s","\n"); - + void BlockUniTensor::to_binary_dispatch(std::ostream &f) const { cytnx_uint64 Nblocks = this->_blocks.size(); f.write((char *)&Nblocks, sizeof(cytnx_uint64)); @@ -1586,13 +1584,11 @@ namespace cytnx { f.write((char *)&this->_inner_to_outer_idx[b][0], sizeof(cytnx_uint64) * this->_bonds.size()); } for (unsigned int i = 0; i < this->_blocks.size(); i++) { - this->_blocks[i]._Save(f); + this->_blocks[i].to_binary(f); } } - void BlockUniTensor::_load_dispatch(std::fstream &f) { - // cytnx_error_msg(true,"[ERROR] Save for SparseUniTensor is under developing!!%s","\n"); - + void BlockUniTensor::from_binary_dispatch(std::istream &f) { cytnx_uint64 Nblocks; f.read((char *)&Nblocks, sizeof(cytnx_uint64)); @@ -1605,7 +1601,7 @@ namespace cytnx { this->_blocks.resize(Nblocks); for (unsigned int i = 0; i < this->_blocks.size(); i++) { - this->_blocks[i]._Load(f); + this->_blocks[i].from_binary(f); } } diff --git a/src/Bond.cpp b/src/Bond.cpp index 01dcb3b5d..bf8a53941 100644 --- a/src/Bond.cpp +++ b/src/Bond.cpp @@ -483,7 +483,7 @@ namespace cytnx { if (!f.is_open()) { cytnx_error_msg(true, "[ERROR] invalid file path for save.%s", "\n"); } - this->_Save(f); + this->to_binary(f); f.close(); } void Bond::Save(const char *fname) const { this->Save(string(fname)); } @@ -495,14 +495,13 @@ namespace cytnx { if (!f.is_open()) { cytnx_error_msg(true, "[ERROR] Cannot open file '%s'.\n", fname.c_str()); } - out._Load(f); + out.from_binary(f); f.close(); return out; } Bond Bond::Load(const char *fname) { return Bond::Load(string(fname)); } - void Bond::_Save(fstream &f) const { - cytnx_error_msg(!f.is_open(), "[ERROR][Bond] invalid fstream%s", "\n"); + void Bond::to_binary(std::ostream &f) const { unsigned int IDDs = 666; f.write((char *)&IDDs, sizeof(unsigned int)); @@ -546,13 +545,12 @@ namespace cytnx { if (Nsym != 0) { for (int j = 0; j < Nsym; j++) { - this->_impl->_syms[j]._Save(f); + this->_impl->_syms[j].to_binary(f); } } } - void Bond::_Load(fstream &f) { - cytnx_error_msg(!f.is_open(), "[ERROR][Bond] invalid fstream%s", "\n"); + void Bond::from_binary(std::istream &f) { unsigned int tmpIDDs; f.read((char *)&tmpIDDs, sizeof(unsigned int)); cytnx_error_msg(tmpIDDs != 666, "[ERROR] the object is not a cytnx Bond!%s", "\n"); @@ -605,7 +603,7 @@ namespace cytnx { if (Nsym_in != 0) { this->_impl->_syms.resize(Nsym_in); for (int j = 0; j < Nsym_in; j++) { - this->_impl->_syms[j]._Load(f); + this->_impl->_syms[j].from_binary(f); } } } diff --git a/src/DenseUniTensor.cpp b/src/DenseUniTensor.cpp index 3686ecd26..f3c7b2071 100644 --- a/src/DenseUniTensor.cpp +++ b/src/DenseUniTensor.cpp @@ -1206,8 +1206,8 @@ namespace cytnx { } void DenseUniTensor::normalize_() { this->_block /= linalg::Norm(this->_block); } - void DenseUniTensor::_save_dispatch(std::fstream &f) const { this->_block._Save(f); } - void DenseUniTensor::_load_dispatch(std::fstream &f) { this->_block._Load(f); } + void DenseUniTensor::to_binary_dispatch(std::ostream &f) const { this->_block.to_binary(f); } + void DenseUniTensor::from_binary_dispatch(std::istream &f) { this->_block.from_binary(f); } void DenseUniTensor::truncate_(const std::string &bond_label, const cytnx_uint64 &dim) { // if it is diagonal tensor, truncate will be done on both index! diff --git a/src/SparseUniTensor.cpp b/src/SparseUniTensor.cpp index c5025aaad..7d5b1130e 100644 --- a/src/SparseUniTensor.cpp +++ b/src/SparseUniTensor.cpp @@ -2332,18 +2332,18 @@ namespace cytnx { cytnx_error_msg(true, "[ERROR] truncate for SparseUniTensor is under developing!!%s", "\n"); } - void SparseUniTensor::_save_dispatch(std::fstream &f) const { + void SparseUniTensor::to_binary_dispatch(std::ostream &f) const { // cytnx_error_msg(true,"[ERROR] Save for SparseUniTensor is under developing!!%s","\n"); cytnx_uint64 Nblocks = this->_blocks.size(); f.write((char *)&Nblocks, sizeof(cytnx_uint64)); for (unsigned int i = 0; i < this->_blocks.size(); i++) { - this->_blocks[i]._Save(f); + this->_blocks[i].to_binary(f); } } - void SparseUniTensor::_load_dispatch(std::fstream &f) { + void SparseUniTensor::from_binary_dispatch(std::istream &f) { // cytnx_error_msg(true,"[ERROR] Save for SparseUniTensor is under developing!!%s","\n"); cytnx_uint64 Nblocks; @@ -2354,7 +2354,7 @@ namespace cytnx { "\n"); for (unsigned int i = 0; i < this->_blocks.size(); i++) { - this->_blocks[i]._Load(f); + this->_blocks[i].from_binary(f); } } diff --git a/src/Symmetry.cpp b/src/Symmetry.cpp index 3b65ff162..db4e9355c 100644 --- a/src/Symmetry.cpp +++ b/src/Symmetry.cpp @@ -255,7 +255,7 @@ namespace cytnx { if (!f.is_open()) { cytnx_error_msg(true, "[ERROR] invalid file path for save.%s", "\n"); } - this->_Save(f); + this->to_binary(f); f.close(); } void cytnx::Symmetry::Save(const char *fname) const { this->Save(string(fname)); } @@ -267,7 +267,7 @@ namespace cytnx { if (!f.is_open()) { cytnx_error_msg(true, "[ERROR] Cannot open file '%s'.\n", fname.c_str()); } - out._Load(f); + out.from_binary(f); f.close(); return out; } @@ -276,15 +276,13 @@ namespace cytnx { } //================== - void cytnx::Symmetry::_Save(fstream &f) const { - cytnx_error_msg(!f.is_open(), "[ERROR][Symmetry] invalid fstream%s", "\n"); + void cytnx::Symmetry::to_binary(std::ostream &f) const { unsigned int IDDs = 777; f.write((char *)&IDDs, sizeof(unsigned int)); f.write((char *)&this->_impl->stype_id, sizeof(int)); f.write((char *)&this->_impl->n, sizeof(int)); } - void cytnx::Symmetry::_Load(fstream &f) { - cytnx_error_msg(!f.is_open(), "[ERROR][Symmetry] invalid fstream%s", "\n"); + void cytnx::Symmetry::from_binary(std::istream &f) { unsigned int tmpIDDs; f.read((char *)&tmpIDDs, sizeof(unsigned int)); cytnx_error_msg(tmpIDDs != 777, "[ERROR] the object is not a cytnx symmetry!%s", "\n"); diff --git a/src/Tensor.cpp b/src/Tensor.cpp index 35dd12da8..e3beeadba 100644 --- a/src/Tensor.cpp +++ b/src/Tensor.cpp @@ -464,15 +464,11 @@ namespace cytnx { if (!f.is_open()) { cytnx_error_msg(true, "[ERROR] invalid file path for save.%s", "\n"); } - this->_Save(f); + this->to_binary(f); f.close(); } void Tensor::Save(const char *fname) const { this->Save(string(fname)); } - void Tensor::_Save(fstream &f) const { - // header - // check: - cytnx_error_msg(!f.is_open(), "[ERROR] invalid fstream!.%s", "\n"); - + void Tensor::to_binary(std::ostream &f) const { unsigned int IDDs = 888; f.write((char *)&IDDs, sizeof(unsigned int)); cytnx_uint64 shp = this->shape().size(); @@ -485,7 +481,7 @@ namespace cytnx { f.write((char *)&this->_impl->_invmapper[0], sizeof(cytnx_uint64) * shp); // pass to storage for save: - this->_impl->_storage._Save(f); + this->_impl->_storage.to_binary(f); } Tensor Tensor::Fromfile(const std::string &fname, const unsigned int &dtype, @@ -502,16 +498,12 @@ namespace cytnx { if (!f.is_open()) { cytnx_error_msg(true, "[ERROR] Cannot open file '%s'.\n", fname.c_str()); } - out._Load(f); + out.from_binary(f); f.close(); return out; } Tensor Tensor::Load(const char *fname) { return Tensor::Load(string(fname)); } - void Tensor::_Load(fstream &f) { - // header - // check: - cytnx_error_msg(!f.is_open(), "[ERROR] invalid fstream!.%s", "\n"); - + void Tensor::from_binary(std::istream &f) { unsigned int tmpIDDs; f.read((char *)&tmpIDDs, sizeof(unsigned int)); cytnx_error_msg(tmpIDDs != 888, "[ERROR] the object is not a cytnx tensor!%s", "\n"); @@ -530,7 +522,7 @@ namespace cytnx { f.read((char *)&this->_impl->_invmapper[0], sizeof(cytnx_uint64) * shp); // pass to storage for save: - this->_impl->_storage._Load(f); + this->_impl->_storage.from_binary(f); } Tensor Tensor::real() { diff --git a/src/UniTensor.cpp b/src/UniTensor.cpp index 0899c1c23..1d665063a 100644 --- a/src/UniTensor.cpp +++ b/src/UniTensor.cpp @@ -41,8 +41,7 @@ namespace cytnx { UniTensor UniTensor::Mul(const UniTensor &rhs) const { return cytnx::linalg::Mul(*this, rhs); } UniTensor UniTensor::Mul(const Scalar &rhs) const { return cytnx::linalg::Mul(*this, rhs); } - void UniTensor::_Save(std::fstream &f) const { - cytnx_error_msg(!f.is_open(), "[ERROR][UniTensor] invalid fstream!.%s", "\n"); + void UniTensor::to_binary(std::ostream &f) const { cytnx_error_msg(this->_impl->uten_type_id == UTenType.Void, "[ERROR][UniTensor] cannot save an uninitialized UniTensor.%s", "\n"); @@ -85,15 +84,13 @@ namespace cytnx { } // f.write((char *)&(this->_impl->_labels[0]), sizeof(cytnx_int64) * rank); for (cytnx_uint64 i = 0; i < rank; i++) { - this->_impl->_bonds[i]._Save(f); + this->_impl->_bonds[i].to_binary(f); } // second, let dispatch to do remaining saving. - this->_impl->_save_dispatch(f); + this->_impl->to_binary_dispatch(f); } - void UniTensor::_Load(std::fstream &f) { - cytnx_error_msg(!f.is_open(), "[ERROR][UniTensor] invalid fstream%s", "\n"); - + void UniTensor::from_binary(std::istream &f) { unsigned int tmpIDDs; f.read((char *)&tmpIDDs, sizeof(unsigned int)); cytnx_error_msg(tmpIDDs != 555, "[ERROR] the object is not a cytnx UniTensor!%s", "\n"); @@ -149,11 +146,11 @@ namespace cytnx { } // f.read((char *)&(this->_impl->_labels[0]), sizeof(cytnx_int64) * rank); for (cytnx_uint64 i = 0; i < rank; i++) { - this->_impl->_bonds[i]._Load(f); + this->_impl->_bonds[i].from_binary(f); } // second, let dispatch to do remaining loading. - this->_impl->_load_dispatch(f); + this->_impl->from_binary_dispatch(f); } void UniTensor::Save(const std::string &fname) const { @@ -172,7 +169,7 @@ namespace cytnx { if (!f.is_open()) { cytnx_error_msg(true, "[ERROR] invalid file path for save.%s", "\n"); } - this->_Save(f); + this->to_binary(f); f.close(); } void UniTensor::Save(const char *fname) const { Save(string(fname)); } @@ -184,7 +181,7 @@ namespace cytnx { if (!f.is_open()) { cytnx_error_msg(true, "[ERROR] invalid file path for load. >> %s\n", fname.c_str()); } - out._Load(f); + out.from_binary(f); f.close(); return out; } diff --git a/src/UniTensor_base.cpp b/src/UniTensor_base.cpp index f207db3b8..b990a43af 100644 --- a/src/UniTensor_base.cpp +++ b/src/UniTensor_base.cpp @@ -673,12 +673,12 @@ namespace cytnx { true, "[ERROR] fatal internal, cannot call on an un-initialized UniTensor_base%s", "\n"); } - void UniTensor_base::_save_dispatch(std::fstream &f) const { + void UniTensor_base::to_binary_dispatch(std::ostream &f) const { cytnx_error_msg( true, "[ERROR] fatal internal, cannot call on an un-initialized UniTensor_base%s", "\n"); } - void UniTensor_base::_load_dispatch(std::fstream &f) { + void UniTensor_base::from_binary_dispatch(std::istream &f) { cytnx_error_msg( true, "[ERROR] fatal internal, cannot call on an un-initialized UniTensor_base%s", "\n"); } diff --git a/src/backend/Storage.cpp b/src/backend/Storage.cpp index b11918b0a..b15c98779 100644 --- a/src/backend/Storage.cpp +++ b/src/backend/Storage.cpp @@ -98,7 +98,7 @@ namespace cytnx { if (!f.is_open()) { cytnx_error_msg(true, "[ERROR] invalid file path for save.%s", "\n"); } - this->_Save(f); + this->to_binary(f); f.close(); } void Storage::Save(const char *fname) const { this->Save(string(fname)); } @@ -128,11 +128,7 @@ namespace cytnx { this->_Savebinary(f); } - void Storage::_Save(fstream &f) const { - // header - // check: - cytnx_error_msg(!f.is_open(), "[ERROR] invalid fstream!.%s", "\n"); - + void Storage::to_binary(std::ostream &f) const { unsigned int IDDs = 999; f.write((char *)&IDDs, sizeof(unsigned int)); auto write_number = [&f](auto number) { @@ -236,20 +232,15 @@ namespace cytnx { if (!f.is_open()) { cytnx_error_msg(true, "[ERROR] Cannot open file '%s'.\n", fname.c_str()); } - out._Load(f); + out.from_binary(f); f.close(); return out; } Storage Storage::Load(const char *fname) { return Storage::Load(string(fname)); } - void Storage::_Load(fstream &f) { - // header + void Storage::from_binary(std::istream &f) { unsigned long long sz; unsigned int dt; int dv; - - // check: - cytnx_error_msg(!f.is_open(), "[ERROR] invalid fstream!.%s", "\n"); - // checking IDD unsigned int tmpIDDs; f.read((char *)&tmpIDDs, sizeof(unsigned int)); diff --git a/src/tn_algo/MPS.cpp b/src/tn_algo/MPS.cpp index c51635d43..774ec0ab6 100644 --- a/src/tn_algo/MPS.cpp +++ b/src/tn_algo/MPS.cpp @@ -17,8 +17,7 @@ namespace cytnx { return os; } - void MPS::_Save(std::fstream& f) const { - cytnx_error_msg(!f.is_open(), "[ERROR][MPS] invalid fstream!.%s", "\n"); + void MPS::to_binary(std::ostream& f) const { cytnx_error_msg(this->_impl->mps_type_id == MPSType.Void, "[ERROR][MPS] cannot save an uninitialize MPS.%s", "\n"); @@ -33,11 +32,10 @@ namespace cytnx { f.write((char*)&this->_impl->S_loc, sizeof(this->_impl->S_loc)); // second, dispatch to do remaining saving: - this->_impl->_save_dispatch(f); + this->_impl->to_binary_dispatch(f); } - void MPS::_Load(std::fstream& f) { - cytnx_error_msg(!f.is_open(), "[ERROR][MPS] invalid fstream%s", "\n"); + void MPS::from_binary(std::istream& f) { unsigned int tmpIDDs; f.read((char*)&tmpIDDs, sizeof(unsigned int)); cytnx_error_msg(tmpIDDs != 109, "[ERROR] the object is not a cytnx MPS!%s", "\n"); @@ -59,7 +57,7 @@ namespace cytnx { f.read((char*)&this->_impl->S_loc, sizeof(this->_impl->S_loc)); // second, let dispatch to do remaining loading. - this->_impl->_load_dispatch(f); + this->_impl->from_binary_dispatch(f); } void MPS::Save(const std::string& fname) const { @@ -79,7 +77,7 @@ namespace cytnx { if (!f.is_open()) { cytnx_error_msg(true, "[ERROR] invalid file path for save.%s", "\n"); } - this->_Save(f); + this->to_binary(f); f.close(); } void MPS::Save(const char* fname) const { @@ -89,7 +87,7 @@ namespace cytnx { if (!f.is_open()) { cytnx_error_msg(true, "[ERROR] invalid file path for save.%s", "\n"); } - this->_Save(f); + this->to_binary(f); f.close(); } @@ -100,7 +98,7 @@ namespace cytnx { if (!f.is_open()) { cytnx_error_msg(true, "[ERROR] Cannot open file '%s'.\n", fname.c_str()); } - out._Load(f); + out.from_binary(f); f.close(); return out; } diff --git a/src/tn_algo/MPS_base.cpp b/src/tn_algo/MPS_base.cpp index 820d89c65..8ff41f7dd 100644 --- a/src/tn_algo/MPS_base.cpp +++ b/src/tn_algo/MPS_base.cpp @@ -74,12 +74,12 @@ namespace cytnx { true, "[ERROR] MPS_Base should not be called. Please initialize the MPS first.%s", "\n"); } - void MPS_impl::_save_dispatch(fstream &f) { + void MPS_impl::to_binary_dispatch(std::ostream &f) { cytnx_error_msg( true, "[ERROR] MPS_Base should not be called. Please initialize the MPS first.%s", "\n"); } - void MPS_impl::_load_dispatch(fstream &f) { + void MPS_impl::from_binary_dispatch(std::istream &f) { cytnx_error_msg( true, "[ERROR] MPS_Base should not be called. Please initialize the MPS first.%s", "\n"); } diff --git a/src/tn_algo/RegularMPS.cpp b/src/tn_algo/RegularMPS.cpp index 65fbb8335..eaa2b6da4 100644 --- a/src/tn_algo/RegularMPS.cpp +++ b/src/tn_algo/RegularMPS.cpp @@ -266,16 +266,16 @@ namespace cytnx { } } - void RegularMPS::_save_dispatch(fstream &f) { + void RegularMPS::to_binary_dispatch(std::ostream &f) { cytnx_uint64 N = this->_TNs.size(); f.write((char *)&N, sizeof(cytnx_uint64)); // save UniTensor one by one: for (cytnx_uint64 i = 0; i < N; i++) { - this->_TNs[i]._Save(f); + this->_TNs[i].to_binary(f); } } - void RegularMPS::_load_dispatch(fstream &f) { + void RegularMPS::from_binary_dispatch(std::istream &f) { cytnx_uint64 N; f.read((char *)&N, sizeof(cytnx_uint64)); @@ -283,7 +283,7 @@ namespace cytnx { // Load UniTensor one by one: for (cytnx_uint64 i = 0; i < N; i++) { - this->_TNs[i]._Load(f); + this->_TNs[i].from_binary(f); } } diff --git a/src/tn_algo/iMPS.cpp b/src/tn_algo/iMPS.cpp index 43028bf3d..cca288c68 100644 --- a/src/tn_algo/iMPS.cpp +++ b/src/tn_algo/iMPS.cpp @@ -60,16 +60,16 @@ namespace cytnx { } } - void iMPS::_save_dispatch(fstream &f) { + void iMPS::to_binary_dispatch(std::ostream &f) { cytnx_uint64 N = this->_TNs.size(); f.write((char *)&N, sizeof(cytnx_uint64)); // save UniTensor one by one: for (cytnx_uint64 i = 0; i < N; i++) { - this->_TNs[i]._Save(f); + this->_TNs[i].to_binary(f); } } - void iMPS::_load_dispatch(fstream &f) { + void iMPS::from_binary_dispatch(std::istream &f) { cytnx_uint64 N; f.read((char *)&N, sizeof(cytnx_uint64)); @@ -77,7 +77,7 @@ namespace cytnx { // Load UniTensor one by one: for (cytnx_uint64 i = 0; i < N; i++) { - this->_TNs[i]._Load(f); + this->_TNs[i].from_binary(f); } } From 5d05e27246a2ba6c43eb769f4cde700d48e1987d Mon Sep 17 00:00:00 2001 From: Manuel Schneider Date: Sun, 12 Apr 2026 04:51:02 +0800 Subject: [PATCH 02/40] fixed bug: UniTensor::Load did not set the name of the UniTensor correctly because the char* is not terminated by \0. Potentially illegal memory access --- src/UniTensor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/UniTensor.cpp b/src/UniTensor.cpp index 1d665063a..d810f3fd4 100644 --- a/src/UniTensor.cpp +++ b/src/UniTensor.cpp @@ -127,7 +127,7 @@ namespace cytnx { if (len_name != 0) { char *cname = (char *)malloc(sizeof(char) * len_name); f.read(cname, sizeof(char) * len_name); - this->_impl->_name = std::string(cname); + this->_impl->_name = std::string(cname, len_name); free(cname); } From f45c7c84078febd579bd2bde19de3b0e5b1afcf6 Mon Sep 17 00:00:00 2001 From: Manuel Schneider Date: Sun, 12 Apr 2026 05:02:24 +0800 Subject: [PATCH 03/40] added pickle support, fixed MPS.Save() binding --- pybind/bond_py.cpp | 14 ++++++++++++++ pybind/storage_py.cpp | 15 +++++++++++++++ pybind/symmetry_py.cpp | 15 +++++++++++++++ pybind/tensor_py.cpp | 14 ++++++++++++++ pybind/tnalgo_py.cpp | 17 ++++++++++++++++- pybind/unitensor_py.cpp | 16 ++++++++++++++++ 6 files changed, 90 insertions(+), 1 deletion(-) diff --git a/pybind/bond_py.cpp b/pybind/bond_py.cpp index 1cffbd107..1af87dea9 100644 --- a/pybind/bond_py.cpp +++ b/pybind/bond_py.cpp @@ -183,5 +183,19 @@ void bond_binding(py::module &m) { .def_static( "Load", [](const std::string &fname) { return Bond::Load(fname); }, py::arg("fname")) + .def(py::pickle( + [](const Bond &self) { // __getstate__ + std::ostringstream oss(std::ios::binary); + self.to_binary(oss); + return py::bytes(oss.str()); + }, + [](py::bytes state) { // __setstate__ + std::string data = state; + std::istringstream iss(data, std::ios::binary); + Bond out; + out.from_binary(iss); + return out; + })) + ; // end of object line } diff --git a/pybind/storage_py.cpp b/pybind/storage_py.cpp index 2808b6672..ca0a83d78 100644 --- a/pybind/storage_py.cpp +++ b/pybind/storage_py.cpp @@ -274,6 +274,21 @@ void storage_binding(py::module &m) { return cytnx::Storage::Fromfile(fname, dtype, count); }, py::arg("fname"), py::arg("dtype"), py::arg("count") = (cytnx_int64)(-1)) + + .def(py::pickle( + [](const cytnx::Storage &self) { // __getstate__ + std::ostringstream oss(std::ios::binary); + self.to_binary(oss); + return py::bytes(oss.str()); + }, + [](py::bytes state) { // __setstate__ + std::string data = state; + std::istringstream iss(data, std::ios::binary); + cytnx::Storage out; + out.from_binary(iss); + return out; + })) + .def("real", &cytnx::Storage::real) .def("imag", &cytnx::Storage::imag) diff --git a/pybind/symmetry_py.cpp b/pybind/symmetry_py.cpp index 56eee0176..a089c0448 100644 --- a/pybind/symmetry_py.cpp +++ b/pybind/symmetry_py.cpp @@ -69,6 +69,21 @@ void symmetry_binding(py::module &m) { "Save", [](Symmetry &self, const std::string &fname) { self.Save(fname); }, py::arg("fname")) .def_static( "Load", [](const std::string &fname) { return Symmetry::Load(fname); }, py::arg("fname")) + + .def(py::pickle( + [](const Symmetry &self) { // __getstate__ + std::ostringstream oss(std::ios::binary); + self.to_binary(oss); + return py::bytes(oss.str()); + }, + [](py::bytes state) { // __setstate__ + std::string data = state; + std::istringstream iss(data, std::ios::binary); + Symmetry out; + out.from_binary(iss); + return out; + })) + .def( "__repr__", [](Symmetry &self) { diff --git a/pybind/tensor_py.cpp b/pybind/tensor_py.cpp index 6977a3c45..ba6d921bc 100644 --- a/pybind/tensor_py.cpp +++ b/pybind/tensor_py.cpp @@ -292,6 +292,20 @@ void tensor_binding(py::module &m) { .def_static( "Load", [](const std::string &fname) { return cytnx::Tensor::Load(fname); }, py::arg("fname")) + .def(py::pickle( + [](const cytnx::Tensor &self) { // __getstate__ + std::ostringstream oss(std::ios::binary); + self.to_binary(oss); + return py::bytes(oss.str()); + }, + [](py::bytes state) { // __setstate__ + std::string data = state; + std::istringstream iss(data, std::ios::binary); + cytnx::Tensor out; + out.from_binary(iss); + return out; + })) + .def( "Tofile", [](cytnx::Tensor &self, const std::string &fname) { self.Tofile(fname); }, py::arg("fname")) diff --git a/pybind/tnalgo_py.cpp b/pybind/tnalgo_py.cpp index 494597b6f..3df4b36ec 100644 --- a/pybind/tnalgo_py.cpp +++ b/pybind/tnalgo_py.cpp @@ -52,11 +52,26 @@ void tnalgo_binding(py::module &m) { .def("c_S_mvleft", &tn_algo::MPS::S_mvleft) .def("c_S_mvright", &tn_algo::MPS::S_mvright) .def( - "Save", [](cytnx::Storage &self, const std::string &fname) { self.Save(fname); }, + "Save", [](tn_algo::MPS &self, const std::string &fname) { self.Save(fname); }, py::arg("fname")) .def_static( "Load", [](const std::string &fname) { return cytnx::tn_algo::MPS::Load(fname); }, py::arg("fname")) + + .def(py::pickle( + [](const tn_algo::MPS &self) { // __getstate__ + std::ostringstream oss(std::ios::binary); + self.to_binary(oss); + return py::bytes(oss.str()); + }, + [](py::bytes state) { // __setstate__ + std::string data = state; + std::istringstream iss(data, std::ios::binary); + tn_algo::MPS out; + out.from_binary(iss); + return out; + })) + .def( "__repr__", [](cytnx::tn_algo::MPS &self) -> std::string { diff --git a/pybind/unitensor_py.cpp b/pybind/unitensor_py.cpp index 67e49e623..e772a41da 100644 --- a/pybind/unitensor_py.cpp +++ b/pybind/unitensor_py.cpp @@ -606,6 +606,22 @@ void unitensor_binding(py::module &m) { "Save", [](UniTensor &self, const std::string &fname) { self.Save(fname); }, py::arg("fname")) .def_static( "Load", [](const std::string &fname) { return UniTensor::Load(fname); }, py::arg("fname")) + + .def(py::pickle( + [](const UniTensor &self) { // __getstate__ + std::ostringstream oss(std::ios::binary); + self.to_binary(oss); + return py::bytes(oss.str()); + }, + [](py::bytes state) { // __setstate__ + std::string data = state; + std::istringstream iss(data, std::ios::binary); + UniTensor out; + out.from_binary(iss); + return out; + } + )) + //.def("permute",&UniTensor::permute,py::arg("mapper"),py::arg("rowrank")=(cytnx_int64)-1,py::arg("by_label")=false) //.def("permute_",&UniTensor::permute_,py::arg("mapper"),py::arg("rowrank")=(cytnx_int64)-1,py::arg("by_label")=false) .def( From c00d2384da84029a950c3e67309064256ab3060d Mon Sep 17 00:00:00 2001 From: Manuel Schneider Date: Tue, 28 Apr 2026 17:10:01 +0800 Subject: [PATCH 04/40] added Load_, argument restore_device, and cleaned up the code and docstrings --- include/Bond.hpp | 22 ++++-- include/Symmetry.hpp | 29 ++++++-- include/Tensor.hpp | 47 ++++++++---- include/UniTensor.hpp | 62 +++++++++------- include/backend/Storage.hpp | 108 ++++++++++++++++----------- include/backend/Tensor_impl.hpp | 2 +- include/tn_algo/MPS.hpp | 38 ++++++++-- pybind/bond_py.cpp | 3 + pybind/storage_py.cpp | 40 ++++++---- pybind/symmetry_py.cpp | 3 + pybind/tensor_py.cpp | 20 +++-- pybind/tnalgo_py.cpp | 4 + pybind/unitensor_py.cpp | 12 ++- src/BlockFermionicUniTensor.cpp | 4 +- src/BlockUniTensor.cpp | 4 +- src/Bond.cpp | 11 ++- src/DenseUniTensor.cpp | 4 +- src/Device.cpp | 2 - src/SparseUniTensor.cpp | 4 +- src/Symmetry.cpp | 15 ++-- src/Tensor.cpp | 32 +++++--- src/Type.cpp | 4 +- src/UniTensor.cpp | 21 ++++-- src/UniTensor_base.cpp | 2 +- src/backend/Storage.cpp | 126 ++++++++++++++------------------ src/tn_algo/MPS.cpp | 21 ++++-- src/tn_algo/MPS_base.cpp | 2 +- src/tn_algo/RegularMPS.cpp | 4 +- src/tn_algo/iMPS.cpp | 4 +- 29 files changed, 404 insertions(+), 246 deletions(-) diff --git a/include/Bond.hpp b/include/Bond.hpp index adeba70ee..824c23ace 100644 --- a/include/Bond.hpp +++ b/include/Bond.hpp @@ -862,18 +862,30 @@ namespace cytnx { void Save(const char *fname) const; /** - @brief Load the Bond object from the file. - @param[in] fname the file name of the Bond object. - @pre The file need to be the file of Bond object, which is saved by the - function Bond::Save(const std::string &fname) const. + @brief Load Bond from file and create new instance + @param fname[in] file name + @pre The file must be a Bond object which is saved by cytnx::Bond::Save. + @note This function creates a new Bond and keeps the original Bond unchanged. See \link + Load_(const std::string &fname) Load_() \endlink for loading the Bond to the current Bond. */ static cytnx::Bond Load(const std::string &fname); - /** @see Load(const std::string &fname) */ static cytnx::Bond Load(const char *fname); + /** + @brief Load Bond from file and overwrite current instance + @note This function overwrites the existing Bond. See \link Load(const std::string &fname) + Load() \endlink for creating a new Bond. + @see Load(const std::string &fname) + */ + void Load_(const std::string &fname); + /** + * @see Load_(const std::string &fname) + */ + void Load_(const char *fname); + /// @cond void to_binary(std::ostream &f) const; void from_binary(std::istream &f); diff --git a/include/Symmetry.hpp b/include/Symmetry.hpp index f79fe0581..eab3f0d2c 100644 --- a/include/Symmetry.hpp +++ b/include/Symmetry.hpp @@ -504,17 +504,30 @@ namespace cytnx { void Save(const char *fname) const; /** - * @brief Load a Symmetry object from a file. - * @param[in] fname the file name. - * @pre the file extension must be ".cysym". - * @return the loaded Symmetry object. - */ - static Symmetry Load(const std::string &fname); + @brief Load Symmetry from file and create new instance + @param fname[in] file name + @pre The file must be a Symmetry object which is saved by cytnx::Symmetry::Save. + @note This function creates a new Symmetry and keeps the original Symmetry unchanged. See \link + Load_(const std::string &fname) Load_() \endlink for loading the Symmetry to the current + Symmetry. + */ + static cytnx::Symmetry Load(const std::string &fname); + /** + @see Load(const std::string &fname) + */ + static cytnx::Symmetry Load(const char *fname); /** - * @brief Same as static Symmetry Load(const std::string &fname); + @brief Load Symmetry from file and overwrite current instance + @note This function overwrites the existing Symmetry. See \link Load(const std::string &fname) + Load() \endlink for creating a new Symmetry. + @see Load(const std::string &fname) + */ + void Load_(const std::string &fname); + /** + * @see Load_(const std::string &fname) */ - static Symmetry Load(const char *fname); + void Load_(const char *fname); /// @cond void to_binary(std::ostream &f) const; diff --git a/include/Tensor.hpp b/include/Tensor.hpp index 124283b89..b151c0fa0 100644 --- a/include/Tensor.hpp +++ b/include/Tensor.hpp @@ -317,7 +317,7 @@ namespace cytnx { /// @cond void to_binary(std::ostream &f) const; - void from_binary(std::istream &f); + void from_binary(std::istream &f, const bool restore_device = true); /// @endcond /** @@ -327,7 +327,7 @@ namespace cytnx { @details save the Tensor to file with file path specify with input param \p fname with postfix ".cytn" - @see Load(const std::string &fname) + @see Load(const std::string &fname, const bool restore_device) */ void Save(const std::string &fname) const; /** @@ -356,18 +356,34 @@ namespace cytnx { void Tofile(std::fstream &f) const; /** - @brief Load current Tensor from file + @brief Load Tensor from file and create new instance @param fname[in] file name - @details - load the Storage from file with file path specify with input param 'fname' - @pre the file must be a Tensor object which is saved by cytnx::Tensor::Save. + @param[in] restore_device whether to try restoring the device on which the data is stored; if + false, the data will be kept on the CPU. Use .to_() to move it to the target device after + loading. + @pre The file must be a Tensor object which is saved by cytnx::Tensor::Save. + @note This function creates a new Tensor and keeps the original Tensor unchanged. See \link + Load_(const std::string &fname, const bool restore_device) Load_() \endlink for loading the + Tensor to the current Tensor. */ - static Tensor Load(const std::string &fname); + static Tensor Load(const std::string &fname, const bool restore_device = true); /** * @see Load(const std::string &fname) */ - static Tensor Load(const char *fname); + static Tensor Load(const char *fname, const bool restore_device = true); + + /** + @brief Load Tensor from file and overwrite current instance + @note This function overwrites the existing Tensor. See \link Load(const std::string &fname, + const bool restore_device) Load() \endlink for creating a new Tensor. + @see Load(const std::string &fname, const bool restore_device) + */ + void Load_(const std::string &fname, const bool restore_device = true); + /** + * @see Load_(const std::string &fname, const bool restore_device) + */ + void Load_(const char *fname, const bool restore_device = true); /** * @brief Load current Tensor from the binary file @@ -380,6 +396,9 @@ namespace cytnx { * cytnx::Type. * @param count[in] the number of elements to be loaded from the binary file. If set to -1, * all elements in the binary file will be loaded. + * @param[in] restore_device whether to try restoring the device on which the data is stored; if + * false, the data will be kept on the CPU. Use .to_() to move it to the target device after + * loading. * @return Tensor * @pre * 1. The @p dtype cannot be Type.Void. @@ -390,9 +409,9 @@ namespace cytnx { * @see cytnx::Tensor::Tofile */ static Tensor Fromfile(const std::string &fname, const unsigned int &dtype, - const cytnx_int64 &count = -1); + const cytnx_int64 &count = -1, const bool restore_device = true); static Tensor Fromfile(const char *fname, const unsigned int &dtype, - const cytnx_int64 &count = -1); + const cytnx_int64 &count = -1, const bool restore_device = true); // static Tensor Frombinary(const std::string &fname); @@ -448,7 +467,7 @@ namespace cytnx { \verbinclude example/Tensor/Init.py.out */ void Init(const std::vector &shape, const unsigned int &dtype = Type.Double, - const int &device = -1, const bool &init_zero = true) { + const int &device = Device.cpu, const bool &init_zero = true) { boost::intrusive_ptr tmp(new Tensor_impl()); this->_impl = tmp; this->_impl->Init(shape, dtype, device, init_zero); @@ -459,7 +478,7 @@ namespace cytnx { // this->_impl->Init(storage); // } // void Init(const Storage& storage, const std::vector &shape, - // const unsigned int &dtype = Type.Double, const int &device = -1) { + // const unsigned int &dtype = Type.Double, const int &device = Device.cpu) { // boost::intrusive_ptr tmp(new Tensor_impl()); // this->_impl = tmp; // this->_impl->Init(storage, shape, dtype, device); @@ -478,7 +497,7 @@ namespace cytnx { * @see cytnx::Tensor::Init */ Tensor(const std::vector &shape, const unsigned int &dtype = Type.Double, - const int &device = -1, const bool &init_zero = 1) + const int &device = Device.cpu, const bool &init_zero = 1) : _impl(new Tensor_impl()) { this->Init(shape, dtype, device, init_zero); } @@ -487,7 +506,7 @@ namespace cytnx { // this->Init(storage); // } // Tensor(const Storage& storage, const std::vector &shape, - // const unsigned int &dtype = Type.Double, const int &device = -1) + // const unsigned int &dtype = Type.Double, const int &device = Device.cpu) // : _impl(new Tensor_impl()) { // this->Init(storage, shape, dtype, device); // } diff --git a/include/UniTensor.hpp b/include/UniTensor.hpp index d2c86b352..84d6b3397 100644 --- a/include/UniTensor.hpp +++ b/include/UniTensor.hpp @@ -435,7 +435,7 @@ namespace cytnx { virtual vec2d &get_itoi(); virtual void to_binary_dispatch(std::ostream &f) const; - virtual void from_binary_dispatch(std::istream &f); + virtual void from_binary_dispatch(std::istream &f, const bool restore_device = true); virtual ~UniTensor_base(){}; }; @@ -1028,7 +1028,7 @@ namespace cytnx { } void to_binary_dispatch(std::ostream &f) const; - void from_binary_dispatch(std::istream &f); + void from_binary_dispatch(std::istream &f, const bool restore_device = true); const std::vector &get_qindices(const cytnx_uint64 &bidx) const { cytnx_error_msg(true, "[ERROR] get_qindices can only be unsed on UniTensor with Symmetry.%s", @@ -1688,7 +1688,7 @@ namespace cytnx { cytnx_int16 &at_for_sparse(const std::vector &locator, const cytnx_int16 &aux); void to_binary_dispatch(std::ostream &f) const; - void from_binary_dispatch(std::istream &f); + void from_binary_dispatch(std::istream &f, const bool restore_device = true); // this will remove the [q_index]-th qnum at [bond_idx]-th Bond! void truncate_(const std::string &label, const cytnx_uint64 &q_index); @@ -2477,7 +2477,7 @@ namespace cytnx { cytnx_int16 &at_for_sparse(const std::vector &locator, const cytnx_int16 &aux); void to_binary_dispatch(std::ostream &f) const; - void from_binary_dispatch(std::istream &f); + void from_binary_dispatch(std::istream &f, const bool restore_device = true); // this will remove the [q_index]-th qnum at [bond_idx]-th Bond! void truncate_(const std::string &label, const cytnx_uint64 &q_index); @@ -5185,40 +5185,48 @@ namespace cytnx { } /** - @brief save a UniTensor to file - @details Save a UniTensor to file. The file extension will be extended as '.cytnx' - @param[in] fname the file name (exclude the file extension). - @see Load(const std::string &fname) + @brief Save UniTensor to file + @param[in] fname the file name + @see Load(const std::string &fname, const bool restore_device) */ void Save(const std::string &fname) const; /** - @brief save a UniTensor to file - @details Save a UniTensor to file. The file extension will be extended as '.cytnx' - @param[in] fname the file name (exclude the file extension). - @see Load(const char *fname) + @brief Save UniTensor to file + @param[in] fname the file name + @see Load(const char *fname, const bool restore_device) */ void Save(const char *fname) const; /** - @brief load a UniTensor from file - @param[in] fname the file name - @return the loaded UniTensor - @pre The file must be a UniTensor object. That is, the file must be created by - UniTensor::Save(). - @see Save(const std::string &fname) const + @brief Load UniTensor from file and create new instance + @param fname[in] file name + @param[in] restore_device whether to try restoring the device on which the data is stored; if + false, the data will be kept on the CPU. Use .to_() to move it to the target device after + loading. + @pre The file must be a UniTensor object which is saved by cytnx::UniTensor::Save. + @note This function creates a new UniTensor and keeps the original UniTensor unchanged. See + \link Load_(const std::string &fname, const bool restore_device) Load_() \endlink for loading + the UniTensor to the current UniTensor. */ - static UniTensor Load(const std::string &fname); + + static UniTensor Load(const std::string &fname, const bool restore_device = true); + /** + * @see Load(const std::string &fname) + */ + static UniTensor Load(const char *fname, const bool restore_device = true); /** - @brief load a UniTensor from file - @param[in] fname: the file name - @return the loaded UniTensor - @pre The file must be a UniTensor object. That is, the file must be created by - UniTensor::Save(). - @see Save(const char* fname) const + @brief Load UniTensor from file and overwrite current instance + @note This function overwrites the existing UniTensor. See \link Load(const std::string &fname, + const bool restore_device) Load() \endlink for creating a new UniTensor. + @see Load(const std::string &fname, const bool restore_device) */ - static UniTensor Load(const char *fname); + void Load_(const std::string &fname, const bool restore_device = true); + /** + * @see Load_(const std::string &fname, const bool restore_device) + */ + void Load_(const char *fname, const bool restore_device = true); /** * @brief truncate bond dimension of the UniTensor by the given bond label and dimension. @@ -5307,7 +5315,7 @@ namespace cytnx { vec2d &get_itoi() { return this->_impl->get_itoi(); } /// @cond - void from_binary(std::istream &f); + void from_binary(std::istream &f, const bool restore_device = true); void to_binary(std::ostream &f) const; /// @endcond diff --git a/include/backend/Storage.hpp b/include/backend/Storage.hpp index 0dfbe0952..59b1bb98b 100644 --- a/include/backend/Storage.hpp +++ b/include/backend/Storage.hpp @@ -162,10 +162,11 @@ namespace cytnx { // these is the one that do the work, and customize with Storage_base // virtual void Init(const std::vector &init_shape); - virtual void Init(const unsigned long long &len_in, const int &device = -1, + virtual void Init(const unsigned long long &len_in, const int &device = Device.cpu, const bool &init_zero = true); - virtual void _Init_byptr(void *rawptr, const unsigned long long &len_in, const int &device = -1, - const bool &iscap = false, const unsigned long long &cap_in = 0); + virtual void _Init_byptr(void *rawptr, const unsigned long long &len_in, + const int &device = Device.cpu, const bool &iscap = false, + const unsigned long long &cap_in = 0); // this function will return a new storage with the same type as the one // that initiate this function. @@ -242,9 +243,9 @@ namespace cytnx { public: StorageImplementation() : capacity_(0), size_(0), start_(nullptr), dtype_(Type.cy_typeid(DType())), device_(-1){}; - void Init(const unsigned long long &len_in, const int &device = -1, + void Init(const unsigned long long &len_in, const int &device = Device.cpu, const bool &init_zero = true); - void _Init_byptr(void *rawptr, const unsigned long long &len_in, const int &device = -1, + void _Init_byptr(void *rawptr, const unsigned long long &len_in, const int &device = Device.cpu, const bool &iscap = false, const unsigned long long &cap_in = 0); boost::intrusive_ptr _create_new_sametype(); boost::intrusive_ptr clone(); @@ -435,7 +436,7 @@ namespace cytnx { extern Storage_init_interface __SII; ///@endcond; - ///@brief an memeory storage with multi-type/multi-device support + ///@brief a memory storage with multi-type/multi-device support class Storage { private: // Interface: @@ -465,13 +466,13 @@ namespace cytnx { \verbinclude example/Storage/Init.py.out */ void Init(const unsigned long long &size, const unsigned int &dtype = Type.Double, - int device = -1, const bool &init_zero = true) { + int device = Device.cpu, const bool &init_zero = true) { cytnx_error_msg(dtype >= N_Type, "%s", "[ERROR] invalid argument: dtype"); this->_impl = __SII.USIInit[dtype](); this->_impl->Init(size, device, init_zero); } // void _Init_byptr(void *rawptr, const unsigned long long &len_in, const unsigned int &dtype = - // Type.Double, const int &device = -1, + // Type.Double, const int &device = Device.cpu, // const bool &iscap = false, const unsigned long long &cap_in = // 0){ // cytnx_error_msg(dtype >= N_Type, "%s", "[ERROR] invalid argument: dtype"); @@ -488,12 +489,12 @@ namespace cytnx { * &init_zero) */ Storage(const unsigned long long &size, const unsigned int &dtype = Type.Double, - int device = -1, const bool &init_zero = true) + int device = Device.cpu, const bool &init_zero = true) : _impl(new Storage_base()) { Init(size, dtype, device, init_zero); } // Storage(void *rawptr, const unsigned long long &len_in, const unsigned int &dtype = - // Type.Double, const int &device = -1, + // Type.Double, const int &device = Device.cpu, // const bool &iscap = false, const unsigned long long &cap_in = 0) // : _impl(new Storage_base()){ // _Init_byptr(rawptr,len_in,dtype,device,iscap,cap_in); @@ -525,9 +526,10 @@ namespace cytnx { /// @cond void to_binary(std::ostream &f) const; - void from_binary(std::istream &f); - void _Loadbinary(std::fstream &f, const unsigned int &dtype, const cytnx_uint64 &Nelem); - void _Savebinary(std::fstream &f) const; + void from_binary(std::istream &f, const bool restore_device = true); + void data_to_binary(std::ostream &f) const; + void data_from_binary(std::istream &f, const cytnx_uint64 &Nelem, const unsigned int &dtype, + const int &device = Device.cpu); /// @endcond @@ -556,45 +558,63 @@ namespace cytnx { void Tofile(std::fstream &f) const; /** - @brief Load current Storage from file - @param[in] fname file name - @details - load the Storage from file with file path specify with input param 'fname'. - @pre The file must be a Storage object, which is saved by the function - Save(const std::string &fname) const. + @brief Load Storage from file and create new instance + @param fname[in] file name + @param[in] restore_device whether to try restoring the device on which the data is stored; if + false, the data will be kept on the CPU. Use .to_() to move it to the target device after + loading. + @pre The file must be a Storage object which is saved by cytnx::Storage::Save. + @note This function creates a new Storage and keeps the original Storage unchanged. See \link + Load_(const std::string &fname, const bool restore_device) Load_() \endlink for loading the + Storage to the current Storage. */ - static Storage Load(const std::string &fname); + static Storage Load(const std::string &fname, const bool restore_device = true); + /** + * @see Load(const std::string &fname) + */ + static Storage Load(const char *fname, const bool restore_device = true); + + /** + @brief Load Storage from file and overwrite current instance + @note This function overwrites the existing Storage. See \link Load(const std::string &fname, + const bool restore_device) Load() \endlink for creating a new Storage. + @see Load(const std::string &fname, const bool restore_device) + */ + void Load_(const std::string &fname, const bool restore_device = true); /** - * @brief Load current Storage from file, same as \ref Load(const std::string &fname) + * @see Load_(const std::string &fname, const bool restore_device) */ - static Storage Load(const char *fname); + void Load_(const char *fname, const bool restore_device = true); + /** - * @brief Load the binary file, which only contains the raw data, to current Storage. + * @brief Load the binary file, which only contains the raw data. * @details This function will load the binary file, which only contains the raw data, * to current Storage with specified dtype and number of elements. * @param[in] fname file name - * @param[in] dtype the data type of the binary file. See cytnx::Type. - * @param[in] Nelem the number of elements you want to load from the binary file. If - * \p Nelem is -1, then it will load all the elements in the binary file. - * @pre + * @param[in] dtype the data type of the binary file. See cytnx.Type. + * @param[in] count the number of elements you want to load from the binary file. If + * \p count is -1, then it will load all the elements in the binary file. + * @param[in] restore_device whether to try restoring the device on which the data is stored; if + * false, the data will be kept on the CPU. Use .to_() to move it to the target device after + * loading. * 1. The @p dtype cannot be Type.Void. * 2. The @p dtype must be the same as the data type of the binary file. - * 3. The @p Nelem cannot be 0. - * 4. The @p Nelem cannot be larger than the number of elements in the binary file. + * 3. The @p count cannot be 0. + * 4. The @p count cannot be larger than the number of elements in the binary file. * 5. The file name @p fname must be valid. * * @see Tofile(const std::string &fname) const */ static Storage Fromfile(const std::string &fname, const unsigned int &dtype, - const cytnx_int64 &count = -1); + const cytnx_int64 &count = -1, const bool restore_device = true); /** * @see Fromfile(const std::string &fname, const unsigned int &dtype, const cytnx_int64 &count = * -1) */ static Storage Fromfile(const char *fname, const unsigned int &dtype, - const cytnx_int64 &count = -1); + const cytnx_int64 &count = -1, const bool restore_device = true); /** @brief cast the type of current Storage @@ -824,7 +844,7 @@ namespace cytnx { @note This function is C++ only */ template - static Storage from_vector(const std::vector &vin, const int device = -1) { + static Storage from_vector(const std::vector &vin, const int device = Device.cpu) { Storage out; out._from_vector(vin, device); return out; @@ -867,7 +887,7 @@ namespace cytnx { /// @cond template - void _from_vector(const std::vector &vin, const int device = -1) { + void _from_vector(const std::vector &vin, const int device = Device.cpu) { // auto dispatch: // check: cytnx_error_msg(1, "[FATAL] ERROR unsupport type%s", "\n"); @@ -875,57 +895,57 @@ namespace cytnx { // memcpy(this->_impl->data(),&vin[0],sizeof(T)*vin.size()); } - void _from_vector(const std::vector &vin, const int device = -1) { + void _from_vector(const std::vector &vin, const int device = Device.cpu) { this->_impl = __SII.USIInit[Type.ComplexDouble](); this->_impl->Init(vin.size(), device); memcpy(this->_impl->data(), &vin[0], sizeof(cytnx_complex128) * vin.size()); } - void _from_vector(const std::vector &vin, const int device = -1) { + void _from_vector(const std::vector &vin, const int device = Device.cpu) { this->_impl = __SII.USIInit[Type.ComplexFloat](); this->_impl->Init(vin.size(), device); memcpy(this->_impl->data(), &vin[0], sizeof(cytnx_complex64) * vin.size()); } - void _from_vector(const std::vector &vin, const int device = -1) { + void _from_vector(const std::vector &vin, const int device = Device.cpu) { this->_impl = __SII.USIInit[Type.Double](); this->_impl->Init(vin.size(), device); memcpy(this->_impl->data(), &vin[0], sizeof(cytnx_double) * vin.size()); } - void _from_vector(const std::vector &vin, const int device = -1) { + void _from_vector(const std::vector &vin, const int device = Device.cpu) { this->_impl = __SII.USIInit[Type.Float](); this->_impl->Init(vin.size(), device); memcpy(this->_impl->data(), &vin[0], sizeof(cytnx_float) * vin.size()); } - void _from_vector(const std::vector &vin, const int device = -1) { + void _from_vector(const std::vector &vin, const int device = Device.cpu) { this->_impl = __SII.USIInit[Type.Uint64](); this->_impl->Init(vin.size(), device); memcpy(this->_impl->data(), &vin[0], sizeof(cytnx_uint64) * vin.size()); } - void _from_vector(const std::vector &vin, const int device = -1) { + void _from_vector(const std::vector &vin, const int device = Device.cpu) { this->_impl = __SII.USIInit[Type.Int64](); this->_impl->Init(vin.size(), device); memcpy(this->_impl->data(), &vin[0], sizeof(cytnx_int64) * vin.size()); } - void _from_vector(const std::vector &vin, const int device = -1) { + void _from_vector(const std::vector &vin, const int device = Device.cpu) { this->_impl = __SII.USIInit[Type.Uint32](); this->_impl->Init(vin.size(), device); memcpy(this->_impl->data(), &vin[0], sizeof(cytnx_uint32) * vin.size()); } - void _from_vector(const std::vector &vin, const int device = -1) { + void _from_vector(const std::vector &vin, const int device = Device.cpu) { this->_impl = __SII.USIInit[Type.Int32](); this->_impl->Init(vin.size(), device); memcpy(this->_impl->data(), &vin[0], sizeof(cytnx_int32) * vin.size()); } - void _from_vector(const std::vector &vin, const int device = -1) { + void _from_vector(const std::vector &vin, const int device = Device.cpu) { this->_impl = __SII.USIInit[Type.Uint16](); this->_impl->Init(vin.size(), device); memcpy(this->_impl->data(), &vin[0], sizeof(cytnx_uint16) * vin.size()); } - void _from_vector(const std::vector &vin, const int device = -1) { + void _from_vector(const std::vector &vin, const int device = Device.cpu) { this->_impl = __SII.USIInit[Type.Int16](); this->_impl->Init(vin.size(), device); memcpy(this->_impl->data(), &vin[0], sizeof(cytnx_int16) * vin.size()); } - void _from_vector(const std::vector &vin, const int device = -1) { + void _from_vector(const std::vector &vin, const int device = Device.cpu) { this->_impl = __SII.USIInit[Type.Bool](); this->_impl->Init(vin.size(), device); this->_impl->_cpy_bool(this->_impl->data(), vin); diff --git a/include/backend/Tensor_impl.hpp b/include/backend/Tensor_impl.hpp index 66e151d21..deaf3b76a 100644 --- a/include/backend/Tensor_impl.hpp +++ b/include/backend/Tensor_impl.hpp @@ -51,7 +51,7 @@ namespace cytnx { Tensor_impl() : _contiguous(true){}; void Init(const std::vector &shape, const unsigned int &dtype = Type.Double, - int device = -1, const bool &init_zero = true); + int device = Device.cpu, const bool &init_zero = true); void Init(const Storage &in); // void Init(const Storage &in, const std::vector &shape, // const unsigned int &dtype, int device); diff --git a/include/tn_algo/MPS.hpp b/include/tn_algo/MPS.hpp index 0e33db04b..5113e8161 100644 --- a/include/tn_algo/MPS.hpp +++ b/include/tn_algo/MPS.hpp @@ -75,7 +75,7 @@ namespace cytnx { virtual void S_mvright(); virtual void to_binary_dispatch(std::ostream &f); - virtual void from_binary_dispatch(std::istream &f); + virtual void from_binary_dispatch(std::istream &f, const bool restore_device = true); }; // finite size: @@ -117,7 +117,7 @@ namespace cytnx { } void to_binary_dispatch(std::ostream &f); - void from_binary_dispatch(std::istream &f); + void from_binary_dispatch(std::istream &f, const bool restore_device = true); }; // infinite size: @@ -161,7 +161,7 @@ namespace cytnx { } Scalar norm() const; void to_binary_dispatch(std::ostream &f); - void from_binary_dispatch(std::istream &f); + void from_binary_dispatch(std::istream &f, const bool restore_device = true); }; ///@endcond @@ -288,14 +288,40 @@ namespace cytnx { ///@cond void to_binary(std::ostream &f) const; - void from_binary(std::istream &f); + void from_binary(std::istream &f, const bool restore_device = true); ///@endcond void Save(const std::string &fname) const; void Save(const char *fname) const; - static MPS Load(const std::string &fname); - static MPS Load(const char *fname); + /** + @brief Load MPS from file and create new instance + @param fname[in] file name + @param[in] restore_device whether to try restoring the device on which the data is stored; if + false, the data will be kept on the CPU. Use .to_() to move it to the target device after + loading. + @pre The file must be an MPS object which is saved by cytnx::MPS::Save. + @note This function creates a new MPS and keeps the original MPS unchanged. See \link + Load_(const std::string &fname, const bool restore_device) Load_() \endlink for loading the + MPS to the current MPS. + */ + static MPS Load(const std::string &fname, const bool restore_device = true); + /** + * @see Load(const std::string &fname) + */ + static MPS Load(const char *fname, const bool restore_device = true); + + /** + @brief Load MPS from file and overwrite current instance + @note This function overwrites the existing MPS. See \link Load(const std::string &fname, + const bool restore_device) Load() \endlink for creating a new MPS. + @see Load(const std::string &fname, const bool restore_device) + */ + void Load_(const std::string &fname, const bool restore_device = true); + /** + * @see Load_(const std::string &fname, const bool restore_device) + */ + void Load_(const char *fname, const bool restore_device = true); }; std::ostream &operator<<(std::ostream &os, const MPS &in); diff --git a/pybind/bond_py.cpp b/pybind/bond_py.cpp index 1af87dea9..e1af5ca63 100644 --- a/pybind/bond_py.cpp +++ b/pybind/bond_py.cpp @@ -182,6 +182,9 @@ void bond_binding(py::module &m) { "Save", [](Bond &self, const std::string &fname) { self.Save(fname); }, py::arg("fname")) .def_static( "Load", [](const std::string &fname) { return Bond::Load(fname); }, py::arg("fname")) + .def( + "Load_", [](cytnx::Bond &self, const std::string &fname) { return self.Load_(fname); }, + py::arg("fname")) .def(py::pickle( [](const Bond &self) { // __getstate__ diff --git a/pybind/storage_py.cpp b/pybind/storage_py.cpp index ca0a83d78..396e3b5cc 100644 --- a/pybind/storage_py.cpp +++ b/pybind/storage_py.cpp @@ -266,8 +266,17 @@ void storage_binding(py::module &m) { "Tofile", [](cytnx::Storage &self, const std::string &fname) { self.Tofile(fname); }, py::arg("fname")) .def_static( - "Load", [](const std::string &fname) { return cytnx::Storage::Load(fname); }, - py::arg("fname")) + "Load", + [](const std::string &fname, const bool restore_device) { + return cytnx::Storage::Load(fname, restore_device); + }, + py::arg("fname"), py::arg("restore_device") = true) + .def( + "Load_", + [](cytnx::Storage &self, const std::string &fname, const bool restore_device) { + return self.Load_(fname, restore_device); + }, + py::arg("fname"), py::arg("restore_device") = true) .def_static( "Fromfile", [](const std::string &fname, const unsigned int &dtype, const cytnx_int64 &count) { @@ -275,19 +284,20 @@ void storage_binding(py::module &m) { }, py::arg("fname"), py::arg("dtype"), py::arg("count") = (cytnx_int64)(-1)) - .def(py::pickle( - [](const cytnx::Storage &self) { // __getstate__ - std::ostringstream oss(std::ios::binary); - self.to_binary(oss); - return py::bytes(oss.str()); - }, - [](py::bytes state) { // __setstate__ - std::string data = state; - std::istringstream iss(data, std::ios::binary); - cytnx::Storage out; - out.from_binary(iss); - return out; - })) + .def( + py::pickle( + [](const cytnx::Storage &self) { // __getstate__ + std::ostringstream oss(std::ios::binary); + self.to_binary(oss); + return py::bytes(oss.str()); + }, + [](py::bytes state) { // __setstate__ + std::string data = state; + std::istringstream iss(data, std::ios::binary); + cytnx::Storage out; + out.from_binary(iss); + return out; + })) .def("real", &cytnx::Storage::real) .def("imag", &cytnx::Storage::imag) diff --git a/pybind/symmetry_py.cpp b/pybind/symmetry_py.cpp index a089c0448..5711c8ee9 100644 --- a/pybind/symmetry_py.cpp +++ b/pybind/symmetry_py.cpp @@ -69,6 +69,9 @@ void symmetry_binding(py::module &m) { "Save", [](Symmetry &self, const std::string &fname) { self.Save(fname); }, py::arg("fname")) .def_static( "Load", [](const std::string &fname) { return Symmetry::Load(fname); }, py::arg("fname")) + .def( + "Load_", [](cytnx::Symmetry &self, const std::string &fname) { return self.Load_(fname); }, + py::arg("fname")) .def(py::pickle( [](const Symmetry &self) { // __getstate__ diff --git a/pybind/tensor_py.cpp b/pybind/tensor_py.cpp index ba6d921bc..a45f93f37 100644 --- a/pybind/tensor_py.cpp +++ b/pybind/tensor_py.cpp @@ -290,7 +290,17 @@ void tensor_binding(py::module &m) { "Save", [](cytnx::Tensor &self, const std::string &fname) { self.Save(fname); }, py::arg("fname")) .def_static( - "Load", [](const std::string &fname) { return cytnx::Tensor::Load(fname); }, py::arg("fname")) + "Load", + [](const std::string &fname, const bool restore_device) { + return cytnx::Tensor::Load(fname, restore_device); + }, + py::arg("fname"), py::arg("restore_device") = true) + .def( + "Load_", + [](cytnx::Tensor &self, const std::string &fname, const bool restore_device) { + return self.Load_(fname, restore_device); + }, + py::arg("fname"), py::arg("restore_device") = true) .def(py::pickle( [](const cytnx::Tensor &self) { // __getstate__ @@ -311,10 +321,10 @@ void tensor_binding(py::module &m) { py::arg("fname")) .def_static( "Fromfile", - [](const std::string &fname, const unsigned int &dtype, const cytnx::cytnx_int64 &count) { - return cytnx::Tensor::Load(fname); - }, - py::arg("fname"), py::arg("dtype"), py::arg("count") = cytnx::cytnx_int64(-1)) + [](const std::string &fname, const unsigned int &dtype, const cytnx::cytnx_int64 &count, + const bool restore_device) { return cytnx::Tensor::Load(fname, restore_device); }, + py::arg("fname"), py::arg("dtype"), py::arg("count") = cytnx::cytnx_int64(-1), + py::arg("restore_device") = true) .def_static( "from_storage", diff --git a/pybind/tnalgo_py.cpp b/pybind/tnalgo_py.cpp index 3df4b36ec..5896f9686 100644 --- a/pybind/tnalgo_py.cpp +++ b/pybind/tnalgo_py.cpp @@ -57,6 +57,10 @@ void tnalgo_binding(py::module &m) { .def_static( "Load", [](const std::string &fname) { return cytnx::tn_algo::MPS::Load(fname); }, py::arg("fname")) + .def( + "Load_", + [](cytnx::tn_algo::MPS &self, const std::string &fname) { return self.Load_(fname); }, + py::arg("fname")) .def(py::pickle( [](const tn_algo::MPS &self) { // __getstate__ diff --git a/pybind/unitensor_py.cpp b/pybind/unitensor_py.cpp index e772a41da..34997e5a5 100644 --- a/pybind/unitensor_py.cpp +++ b/pybind/unitensor_py.cpp @@ -605,7 +605,17 @@ void unitensor_binding(py::module &m) { .def( "Save", [](UniTensor &self, const std::string &fname) { self.Save(fname); }, py::arg("fname")) .def_static( - "Load", [](const std::string &fname) { return UniTensor::Load(fname); }, py::arg("fname")) + "Load", + [](const std::string &fname, const bool restore_device) { + return cytnx::UniTensor::Load(fname, restore_device); + }, + py::arg("fname"), py::arg("restore_device") = true) + .def( + "Load_", + [](cytnx::UniTensor &self, const std::string &fname, const bool restore_device) { + return self.Load_(fname, restore_device); + }, + py::arg("fname"), py::arg("restore_device") = true) .def(py::pickle( [](const UniTensor &self) { // __getstate__ diff --git a/src/BlockFermionicUniTensor.cpp b/src/BlockFermionicUniTensor.cpp index 6007ab23e..905b9e35a 100644 --- a/src/BlockFermionicUniTensor.cpp +++ b/src/BlockFermionicUniTensor.cpp @@ -2355,7 +2355,7 @@ namespace cytnx { } } - void BlockFermionicUniTensor::from_binary_dispatch(std::istream &f) { + void BlockFermionicUniTensor::from_binary_dispatch(std::istream &f, const bool restore_device) { //[21 Aug 2024] This is a copy from BlockUniTensor; reads signs as well cytnx_uint64 Nblocks; f.read((char *)&Nblocks, sizeof(cytnx_uint64)); @@ -2369,7 +2369,7 @@ namespace cytnx { this->_blocks.resize(Nblocks); for (unsigned int i = 0; i < this->_blocks.size(); i++) { - this->_blocks[i].from_binary(f); + this->_blocks[i].from_binary(f, restore_device); } // Loading signs; each sign is assumed to be saved as a char, with 0 being false diff --git a/src/BlockUniTensor.cpp b/src/BlockUniTensor.cpp index 7213f3e14..12cd2a5d0 100644 --- a/src/BlockUniTensor.cpp +++ b/src/BlockUniTensor.cpp @@ -1588,7 +1588,7 @@ namespace cytnx { } } - void BlockUniTensor::from_binary_dispatch(std::istream &f) { + void BlockUniTensor::from_binary_dispatch(std::istream &f, const bool restore_device) { cytnx_uint64 Nblocks; f.read((char *)&Nblocks, sizeof(cytnx_uint64)); @@ -1601,7 +1601,7 @@ namespace cytnx { this->_blocks.resize(Nblocks); for (unsigned int i = 0; i < this->_blocks.size(); i++) { - this->_blocks[i].from_binary(f); + this->_blocks[i].from_binary(f, restore_device); } } diff --git a/src/Bond.cpp b/src/Bond.cpp index bf8a53941..59bab63d0 100644 --- a/src/Bond.cpp +++ b/src/Bond.cpp @@ -490,16 +490,21 @@ namespace cytnx { Bond Bond::Load(const std::string &fname) { Bond out; + out.Load_(fname); + return out; + } + Bond Bond::Load(const char *fname) { return Bond::Load(string(fname)); } + + void Bond::Load_(const std::string &fname) { fstream f; f.open(fname, ios::in | ios::binary); if (!f.is_open()) { cytnx_error_msg(true, "[ERROR] Cannot open file '%s'.\n", fname.c_str()); } - out.from_binary(f); + this->from_binary(f); f.close(); - return out; } - Bond Bond::Load(const char *fname) { return Bond::Load(string(fname)); } + void Bond::Load_(const char *fname) { this->Load_(string(fname)); } void Bond::to_binary(std::ostream &f) const { unsigned int IDDs = 666; diff --git a/src/DenseUniTensor.cpp b/src/DenseUniTensor.cpp index f3c7b2071..04cbcf265 100644 --- a/src/DenseUniTensor.cpp +++ b/src/DenseUniTensor.cpp @@ -1207,7 +1207,9 @@ namespace cytnx { void DenseUniTensor::normalize_() { this->_block /= linalg::Norm(this->_block); } void DenseUniTensor::to_binary_dispatch(std::ostream &f) const { this->_block.to_binary(f); } - void DenseUniTensor::from_binary_dispatch(std::istream &f) { this->_block.from_binary(f); } + void DenseUniTensor::from_binary_dispatch(std::istream &f, const bool restore_device) { + this->_block.from_binary(f, restore_device); + } void DenseUniTensor::truncate_(const std::string &bond_label, const cytnx_uint64 &dim) { // if it is diagonal tensor, truncate will be done on both index! diff --git a/src/Device.cpp b/src/Device.cpp index 0bded499c..8215e131e 100644 --- a/src/Device.cpp +++ b/src/Device.cpp @@ -8,7 +8,6 @@ using namespace std; namespace cytnx { Device_class::Device_class() : Ngpus(0), Ncpus(std::thread::hardware_concurrency()) { - // cout << "init_device class!" << endl; #ifdef UNI_GPU // get all available gpus @@ -49,7 +48,6 @@ namespace cytnx { if (device_id == this->cpu) { return string("cytnx device: CPU"); } else if (device_id >= 0) { - // cout << device_id << Ngpus << endl; if (device_id >= Ngpus) { cytnx_error_msg(true, "%s", "[ERROR] invalid device_id, gpuid exceed limit"); return string(""); diff --git a/src/SparseUniTensor.cpp b/src/SparseUniTensor.cpp index 7d5b1130e..b9bd160e2 100644 --- a/src/SparseUniTensor.cpp +++ b/src/SparseUniTensor.cpp @@ -2343,7 +2343,7 @@ namespace cytnx { } } - void SparseUniTensor::from_binary_dispatch(std::istream &f) { + void SparseUniTensor::from_binary_dispatch(std::istream &f, const bool restore_device) { // cytnx_error_msg(true,"[ERROR] Save for SparseUniTensor is under developing!!%s","\n"); cytnx_uint64 Nblocks; @@ -2354,7 +2354,7 @@ namespace cytnx { "\n"); for (unsigned int i = 0; i < this->_blocks.size(); i++) { - this->_blocks[i].from_binary(f); + this->_blocks[i].from_binary(f, restore_device); } } diff --git a/src/Symmetry.cpp b/src/Symmetry.cpp index db4e9355c..41ba6defd 100644 --- a/src/Symmetry.cpp +++ b/src/Symmetry.cpp @@ -262,18 +262,23 @@ namespace cytnx { cytnx::Symmetry cytnx::Symmetry::Load(const std::string &fname) { Symmetry out; + out.Load_(fname); + return out; + } + cytnx::Symmetry cytnx::Symmetry::Load(const char *fname) { + return cytnx::Symmetry::Load(string(fname)); + } + + void cytnx::Symmetry::Load_(const std::string &fname) { fstream f; f.open(fname, ios::in | ios::binary); if (!f.is_open()) { cytnx_error_msg(true, "[ERROR] Cannot open file '%s'.\n", fname.c_str()); } - out.from_binary(f); + this->from_binary(f); f.close(); - return out; - } - cytnx::Symmetry cytnx::Symmetry::Load(const char *fname) { - return cytnx::Symmetry::Load(string(fname)); } + void cytnx::Symmetry::Load_(const char *fname) { this->Load_(string(fname)); } //================== void cytnx::Symmetry::to_binary(std::ostream &f) const { diff --git a/src/Tensor.cpp b/src/Tensor.cpp index e3beeadba..eecd508f1 100644 --- a/src/Tensor.cpp +++ b/src/Tensor.cpp @@ -485,25 +485,37 @@ namespace cytnx { } Tensor Tensor::Fromfile(const std::string &fname, const unsigned int &dtype, - const cytnx_int64 &count) { - return Tensor::from_storage(Storage::Fromfile(fname, dtype, count)); + const cytnx_int64 &count, const bool restore_device) { + return Tensor::from_storage(Storage::Fromfile(fname, dtype, count, restore_device)); } - Tensor Tensor::Fromfile(const char *fname, const unsigned int &dtype, const cytnx_int64 &count) { - return Tensor::from_storage(Storage::Fromfile(fname, dtype, count)); + Tensor Tensor::Fromfile(const char *fname, const unsigned int &dtype, const cytnx_int64 &count, + const bool restore_device) { + return Tensor::from_storage(Storage::Fromfile(fname, dtype, count, restore_device)); } - Tensor Tensor::Load(const std::string &fname) { + + Tensor Tensor::Load(const std::string &fname, const bool restore_device) { Tensor out; + out.Load_(fname, restore_device); + return out; + } + Tensor Tensor::Load(const char *fname, const bool restore_device) { + return Tensor::Load(string(fname), restore_device); + } + + void Tensor::Load_(const std::string &fname, const bool restore_device) { fstream f; f.open(fname, ios::in | ios::binary); if (!f.is_open()) { cytnx_error_msg(true, "[ERROR] Cannot open file '%s'.\n", fname.c_str()); } - out.from_binary(f); + this->from_binary(f, restore_device); f.close(); - return out; } - Tensor Tensor::Load(const char *fname) { return Tensor::Load(string(fname)); } - void Tensor::from_binary(std::istream &f) { + void Tensor::Load_(const char *fname, const bool restore_device) { + this->Load(string(fname), restore_device); + } + + void Tensor::from_binary(std::istream &f, const bool restore_device) { unsigned int tmpIDDs; f.read((char *)&tmpIDDs, sizeof(unsigned int)); cytnx_error_msg(tmpIDDs != 888, "[ERROR] the object is not a cytnx tensor!%s", "\n"); @@ -522,7 +534,7 @@ namespace cytnx { f.read((char *)&this->_impl->_invmapper[0], sizeof(cytnx_uint64) * shp); // pass to storage for save: - this->_impl->_storage.from_binary(f); + this->_impl->_storage.from_binary(f, restore_device); } Tensor Tensor::real() { diff --git a/src/Type.cpp b/src/Type.cpp index 99aef3e0d..66389003b 100644 --- a/src/Type.cpp +++ b/src/Type.cpp @@ -25,11 +25,9 @@ namespace cytnx { using namespace std; // global debug flag! -namespace cytnx { - bool User_debug = false; -} namespace cytnx { + bool User_debug = false; // Construct an array of typeid(T).name() for each type in Type_list. // This is complicated by Type_list containing 'void', which means we can't use an ordinary diff --git a/src/UniTensor.cpp b/src/UniTensor.cpp index d810f3fd4..41191b751 100644 --- a/src/UniTensor.cpp +++ b/src/UniTensor.cpp @@ -90,7 +90,7 @@ namespace cytnx { // second, let dispatch to do remaining saving. this->_impl->to_binary_dispatch(f); } - void UniTensor::from_binary(std::istream &f) { + void UniTensor::from_binary(std::istream &f, const bool restore_device) { unsigned int tmpIDDs; f.read((char *)&tmpIDDs, sizeof(unsigned int)); cytnx_error_msg(tmpIDDs != 555, "[ERROR] the object is not a cytnx UniTensor!%s", "\n"); @@ -150,7 +150,7 @@ namespace cytnx { } // second, let dispatch to do remaining loading. - this->_impl->from_binary_dispatch(f); + this->_impl->from_binary_dispatch(f, restore_device); } void UniTensor::Save(const std::string &fname) const { @@ -174,18 +174,27 @@ namespace cytnx { } void UniTensor::Save(const char *fname) const { Save(string(fname)); } - UniTensor UniTensor::Load(const std::string &fname) { + UniTensor UniTensor::Load(const std::string &fname, const bool restore_device) { UniTensor out; + out.Load_(fname, restore_device); + return out; + } + UniTensor UniTensor::Load(const char *fname, const bool restore_device) { + return UniTensor::Load(string(fname), restore_device); + } + + void UniTensor::Load_(const std::string &fname, const bool restore_device) { fstream f; f.open(fname, ios::in | ios::binary); if (!f.is_open()) { cytnx_error_msg(true, "[ERROR] invalid file path for load. >> %s\n", fname.c_str()); } - out.from_binary(f); + this->from_binary(f, restore_device); f.close(); - return out; } - UniTensor UniTensor::Load(const char *fname) { return UniTensor::Load(string(fname)); } + void UniTensor::Load_(const char *fname, const bool restore_device) { + this->Load_(string(fname), restore_device); + } // Random Generators: UniTensor UniTensor::normal(const cytnx_uint64 &Nelem, const double &mean, const double &std, diff --git a/src/UniTensor_base.cpp b/src/UniTensor_base.cpp index b990a43af..c2131c4c1 100644 --- a/src/UniTensor_base.cpp +++ b/src/UniTensor_base.cpp @@ -678,7 +678,7 @@ namespace cytnx { true, "[ERROR] fatal internal, cannot call on an un-initialized UniTensor_base%s", "\n"); } - void UniTensor_base::from_binary_dispatch(std::istream &f) { + void UniTensor_base::from_binary_dispatch(std::istream &f, const bool restore_device) { cytnx_error_msg( true, "[ERROR] fatal internal, cannot call on an un-initialized UniTensor_base%s", "\n"); } diff --git a/src/backend/Storage.cpp b/src/backend/Storage.cpp index b15c98779..0edca37d3 100644 --- a/src/backend/Storage.cpp +++ b/src/backend/Storage.cpp @@ -102,13 +102,14 @@ namespace cytnx { f.close(); } void Storage::Save(const char *fname) const { this->Save(string(fname)); } + void Storage::Tofile(const std::string &fname) const { fstream f; f.open(fname, ios::out | ios::trunc | ios::binary); if (!f.is_open()) { cytnx_error_msg(true, "[ERROR] invalid file path for save.%s", "\n"); } - this->_Savebinary(f); + this->data_to_binary(f); f.close(); } void Storage::Tofile(const char *fname) const { @@ -118,14 +119,14 @@ namespace cytnx { if (!f.is_open()) { cytnx_error_msg(true, "[ERROR] invalid file path for save.%s", "\n"); } - this->_Savebinary(f); + this->data_to_binary(f); f.close(); } void Storage::Tofile(fstream &f) const { if (!f.is_open()) { cytnx_error_msg(true, "[ERROR] invalid file path for save.%s", "\n"); } - this->_Savebinary(f); + this->data_to_binary(f); } void Storage::to_binary(std::ostream &f) const { @@ -138,30 +139,9 @@ namespace cytnx { write_number(this->dtype()); write_number(this->device()); - // data: - if (this->device() == Device.cpu) { - f.write((char *)this->_impl->data(), Type.typeSize(this->dtype()) * this->size()); - } else { -#ifdef UNI_GPU - checkCudaErrors(cudaSetDevice(this->device())); - void *htmp = malloc(Type.typeSize(this->dtype()) * this->size()); - checkCudaErrors(cudaMemcpy(htmp, this->_impl->data(), - Type.typeSize(this->dtype()) * this->size(), - cudaMemcpyDeviceToHost)); - f.write((char *)htmp, Type.typeSize(this->dtype()) * this->size()); - free(htmp); - -#else - cytnx_error_msg(true, "ERROR internal fatal error in Save Storage%s", "\n"); -#endif - } + this->data_to_binary(f); } - void Storage::_Savebinary(fstream &f) const { - // header - // check: - cytnx_error_msg(!f.is_open(), "[ERROR] invalid fstream!.%s", "\n"); - - // data: + void Storage::data_to_binary(std::ostream &f) const { if (this->device() == Device.cpu) { f.write((char *)this->_impl->data(), Type.typeSize(this->dtype()) * this->size()); } else { @@ -173,19 +153,18 @@ namespace cytnx { cudaMemcpyDeviceToHost)); f.write((char *)htmp, Type.typeSize(this->dtype()) * this->size()); free(htmp); - #else cytnx_error_msg(true, "ERROR internal fatal error in Save Storage%s", "\n"); #endif } } - Storage Storage::Fromfile(const char *fname, const unsigned int &dtype, - const cytnx_int64 &count) { - return Storage::Fromfile(string(fname), dtype, count); + Storage Storage::Fromfile(const char *fname, const unsigned int &dtype, const cytnx_int64 &count, + const bool restore_device) { + return Storage::Fromfile(string(fname), dtype, count, restore_device); } Storage Storage::Fromfile(const std::string &fname, const unsigned int &dtype, - const cytnx_int64 &count) { + const cytnx_int64 &count, const bool restore_device) { cytnx_error_msg(dtype == Type.Void, "[ERROR] cannot have Void dtype.%s", "\n"); cytnx_error_msg(count == 0, "[ERROR] count cannot be zero!%s", "\n"); @@ -221,26 +200,37 @@ namespace cytnx { if (!f.is_open()) { cytnx_error_msg(true, "[ERROR] Cannot open file '%s'.\n", fname.c_str()); } - out._Loadbinary(f, dtype, Nelem); + out.data_from_binary(f, Nelem, dtype, restore_device); f.close(); return out; } - Storage Storage::Load(const std::string &fname) { + + Storage Storage::Load(const std::string &fname, const bool restore_device) { Storage out; + out.Load_(fname, restore_device); + return out; + } + Storage Storage::Load(const char *fname, const bool restore_device) { + return Storage::Load(string(fname), restore_device); + } + + void Storage::Load_(const std::string &fname, const bool restore_device) { fstream f; f.open(fname, ios::in | ios::binary); if (!f.is_open()) { cytnx_error_msg(true, "[ERROR] Cannot open file '%s'.\n", fname.c_str()); } - out.from_binary(f); + this->from_binary(f, restore_device); f.close(); - return out; } - Storage Storage::Load(const char *fname) { return Storage::Load(string(fname)); } - void Storage::from_binary(std::istream &f) { - unsigned long long sz; - unsigned int dt; - int dv; + void Storage::Load_(const char *fname, const bool restore_device) { + this->Load_(string(fname), restore_device); + } + + void Storage::from_binary(std::istream &f, const bool restore_device) { + unsigned long long Nelem; + unsigned int dtype; + int device; // checking IDD unsigned int tmpIDDs; f.read((char *)&tmpIDDs, sizeof(unsigned int)); @@ -248,55 +238,47 @@ namespace cytnx { cytnx_error_msg(true, "[ERROR] the Load file is not the Storage object!\n", "%s"); } - f.read((char *)&sz, sizeof(unsigned long long)); - f.read((char *)&dt, sizeof(unsigned int)); - f.read((char *)&dv, sizeof(int)); + f.read((char *)&Nelem, sizeof(unsigned long long)); + f.read((char *)&dtype, sizeof(unsigned int)); + f.read((char *)&device, sizeof(int)); - if (dv != Device.cpu) { - if (dv >= Device.Ngpus) { + if (restore_device) { + if (device != Device.cpu && device >= Device.Ngpus) { cytnx_warning_msg(true, "[Warning!!] the original device ID does not exists. the tensor will be " "put on CPU, please use .to() or .to_() to move to desire devices.%s", "\n"); - dv = -1; + device = Device.cpu; } + } else { + device = Device.cpu; } + this->data_from_binary(f, Nelem, dtype, device); + } - this->_impl = __SII.USIInit[dt](); - this->_impl->Init(sz, dv); - - // data: - if (dv == Device.cpu) { - f.read((char *)this->_impl->data(), Type.typeSize(dt) * sz); + void Storage::data_from_binary(std::istream &f, const cytnx_uint64 &Nelem, + const unsigned int &dtype, const int &device) { + // before enter this func, make sure + // 1. dtype is not void. + // 2. the Nelement is consistent and smaller than the file size, and should not be zero! + this->_impl = __SII.USIInit[dtype](); + this->_impl->Init(Nelem, device, false); + if (device == Device.cpu) { + f.read((char *)this->_impl->data(), Type.typeSize(dtype) * Nelem); } else { #ifdef UNI_GPU - checkCudaErrors(cudaSetDevice(dv)); - void *htmp = malloc(Type.typeSize(dt) * sz); - f.read((char *)htmp, Type.typeSize(dt) * sz); - checkCudaErrors( - cudaMemcpy(this->_impl->data(), htmp, Type.typeSize(dt) * sz, cudaMemcpyHostToDevice)); + checkCudaErrors(cudaSetDevice(device)); + void *htmp = malloc(Type.typeSize(dtype) * Nelem); + f.read((char *)htmp, Type.typeSize(dtype) * Nelem); + checkCudaErrors(cudaMemcpy(this->_impl->data(), htmp, Type.typeSize(dtype) * Nelem, + cudaMemcpyHostToDevice)); free(htmp); - #else cytnx_error_msg(true, "ERROR internal fatal error in Load Storage%s", "\n"); #endif } } - void Storage::_Loadbinary(fstream &f, const unsigned int &dtype, const cytnx_uint64 &Nelem) { - // before enter this func, makesure - // 1. dtype is not void. - // 2. the Nelement is consistent and smaller than the file size, and should not be zero! - - // check: - cytnx_error_msg(!f.is_open(), "[ERROR] invalid fstream!.%s", "\n"); - - this->_impl = __SII.USIInit[dtype](); - this->_impl->Init(Nelem, Device.cpu); - - f.read((char *)this->_impl->data(), Type.typeSize(dtype) * Nelem); - } - Scalar::Sproxy Storage::operator()(const cytnx_uint64 &idx) { Scalar::Sproxy out(this->_impl, idx); return out; diff --git a/src/tn_algo/MPS.cpp b/src/tn_algo/MPS.cpp index 774ec0ab6..6f87b7d2d 100644 --- a/src/tn_algo/MPS.cpp +++ b/src/tn_algo/MPS.cpp @@ -35,7 +35,7 @@ namespace cytnx { this->_impl->to_binary_dispatch(f); } - void MPS::from_binary(std::istream& f) { + void MPS::from_binary(std::istream& f, const bool restore_device) { unsigned int tmpIDDs; f.read((char*)&tmpIDDs, sizeof(unsigned int)); cytnx_error_msg(tmpIDDs != 109, "[ERROR] the object is not a cytnx MPS!%s", "\n"); @@ -57,7 +57,7 @@ namespace cytnx { f.read((char*)&this->_impl->S_loc, sizeof(this->_impl->S_loc)); // second, let dispatch to do remaining loading. - this->_impl->from_binary_dispatch(f); + this->_impl->from_binary_dispatch(f, restore_device); } void MPS::Save(const std::string& fname) const { @@ -91,18 +91,27 @@ namespace cytnx { f.close(); } - MPS MPS::Load(const std::string& fname) { + MPS MPS::Load(const std::string& fname, const bool restore_device) { MPS out; + out.Load_(fname, restore_device); + return out; + } + MPS MPS::Load(const char* fname, const bool restore_device) { + return MPS::Load(string(fname), restore_device); + } + + void MPS::Load_(const std::string& fname, const bool restore_device) { fstream f; f.open(fname, ios::in | ios::binary); if (!f.is_open()) { cytnx_error_msg(true, "[ERROR] Cannot open file '%s'.\n", fname.c_str()); } - out.from_binary(f); + this->from_binary(f, restore_device); f.close(); - return out; } - MPS MPS::Load(const char* fname) { return MPS::Load(string(fname)); } + void MPS::Load_(const char* fname, const bool restore_device) { + this->Load(string(fname), restore_device); + } } // namespace tn_algo diff --git a/src/tn_algo/MPS_base.cpp b/src/tn_algo/MPS_base.cpp index 8ff41f7dd..32b7a30f5 100644 --- a/src/tn_algo/MPS_base.cpp +++ b/src/tn_algo/MPS_base.cpp @@ -79,7 +79,7 @@ namespace cytnx { true, "[ERROR] MPS_Base should not be called. Please initialize the MPS first.%s", "\n"); } - void MPS_impl::from_binary_dispatch(std::istream &f) { + void MPS_impl::from_binary_dispatch(std::istream &f, const bool restore_device) { cytnx_error_msg( true, "[ERROR] MPS_Base should not be called. Please initialize the MPS first.%s", "\n"); } diff --git a/src/tn_algo/RegularMPS.cpp b/src/tn_algo/RegularMPS.cpp index eaa2b6da4..81c384680 100644 --- a/src/tn_algo/RegularMPS.cpp +++ b/src/tn_algo/RegularMPS.cpp @@ -275,7 +275,7 @@ namespace cytnx { this->_TNs[i].to_binary(f); } } - void RegularMPS::from_binary_dispatch(std::istream &f) { + void RegularMPS::from_binary_dispatch(std::istream &f, const bool restore_device) { cytnx_uint64 N; f.read((char *)&N, sizeof(cytnx_uint64)); @@ -283,7 +283,7 @@ namespace cytnx { // Load UniTensor one by one: for (cytnx_uint64 i = 0; i < N; i++) { - this->_TNs[i].from_binary(f); + this->_TNs[i].from_binary(f, restore_device); } } diff --git a/src/tn_algo/iMPS.cpp b/src/tn_algo/iMPS.cpp index cca288c68..a1fdfe1e9 100644 --- a/src/tn_algo/iMPS.cpp +++ b/src/tn_algo/iMPS.cpp @@ -69,7 +69,7 @@ namespace cytnx { this->_TNs[i].to_binary(f); } } - void iMPS::from_binary_dispatch(std::istream &f) { + void iMPS::from_binary_dispatch(std::istream &f, const bool restore_device) { cytnx_uint64 N; f.read((char *)&N, sizeof(cytnx_uint64)); @@ -77,7 +77,7 @@ namespace cytnx { // Load UniTensor one by one: for (cytnx_uint64 i = 0; i < N; i++) { - this->_TNs[i].from_binary(f); + this->_TNs[i].from_binary(f, restore_device); } } From 1899e89d123dcd854aca17609d4e8b914f547591 Mon Sep 17 00:00:00 2001 From: Manuel Schneider Date: Tue, 28 Apr 2026 17:14:57 +0800 Subject: [PATCH 05/40] added hdf5 dependency, Type translations between Cytnx and HDF5 data types --- CMakeLists.txt | 5 +++ docs/source/adv_install.rst | 4 +- include/Type.hpp | 85 +++++++++++++++++++++++++++++++++++-- 3 files changed, 88 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4d91a9d3b..ca96ff116 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -288,6 +288,11 @@ find_library(ARPACK_LIB arpack REQUIRED) message(STATUS "Found ARPACK_LIB at: ${ARPACK_LIB}") target_link_libraries(cytnx PRIVATE ${ARPACK_LIB}) +#HDF5 +find_package(HDF5 REQUIRED COMPONENTS CXX) +target_include_directories(cytnx PRIVATE ${HDF5_INCLUDE_DIRS}) +target_link_libraries(cytnx PRIVATE ${HDF5_LIBRARIES}) + # ##################################################################### # ## Get Gtest & benchmark diff --git a/docs/source/adv_install.rst b/docs/source/adv_install.rst index 971927bab..2030a5416 100644 --- a/docs/source/adv_install.rst +++ b/docs/source/adv_install.rst @@ -97,7 +97,7 @@ There are two methods how you can set-up all the dependencies before starting th .. code-block:: shell - $conda install cmake make boost libboost git compilers numpy openblas arpack pybind11 beartype + $conda install cmake make boost libboost git compilers hdf5 numpy openblas arpack pybind11 beartype .. Note:: @@ -107,7 +107,7 @@ There are two methods how you can set-up all the dependencies before starting th .. code-block:: shell - $conda install cmake make boost libboost git compilers numpy mkl mkl-include mkl-service arpack pybind11 libblas=*=*mkl beartype + $conda install cmake make boost libboost git compilers hdf5 numpy mkl mkl-include mkl-service arpack pybind11 libblas=*=*mkl beartype 3. After the installation, an automated test based on gtest and benchmark can be run. This option needs to be activated in the install script. In this case, gtest needs to be installed as well: diff --git a/include/Type.hpp b/include/Type.hpp index c8880af4c..225c7cdce 100644 --- a/include/Type.hpp +++ b/include/Type.hpp @@ -1,15 +1,17 @@ #ifndef CYTNX_TYPE_H_ #define CYTNX_TYPE_H_ +#include #include #include #include -#include #include -#include +#include #include -#include #include +#include + +#include "H5Cpp.h" #include "cytnx_error.hpp" // also brings in cuComplex.h @@ -393,6 +395,81 @@ namespace cytnx { using type_promote_from_gpu_pointer_t = typename type_promote_from_gpu_pointer::type; #endif + H5::DataType to_hdf5_type(unsigned int type_id) const { + switch (type_id) { + case Type::Double: + return H5::PredType::NATIVE_DOUBLE; + + case Type::Float: + return H5::PredType::NATIVE_FLOAT; + + case Type::Int64: + return H5::PredType::NATIVE_INT64; + + case Type::Uint64: + return H5::PredType::NATIVE_UINT64; + + case Type::Int32: + return H5::PredType::NATIVE_INT32; + + case Type::Uint32: + return H5::PredType::NATIVE_UINT32; + + case Type::Int16: + return H5::PredType::NATIVE_INT16; + + case Type::Uint16: + return H5::PredType::NATIVE_UINT16; + + case Type::Bool: + return H5::PredType::NATIVE_HBOOL; + + case Type::ComplexDouble: { + static const H5::CompType ct = [] { + H5::CompType tmp(sizeof(cytnx_complex128)); + tmp.insertMember("r", 0, H5::PredType::NATIVE_DOUBLE); + tmp.insertMember("i", sizeof(double), H5::PredType::NATIVE_DOUBLE); + return tmp; + }(); + return ct; + } + + case Type::ComplexFloat: { + static const H5::CompType ct = [] { + H5::CompType tmp(sizeof(cytnx_complex64)); + tmp.insertMember("r", 0, H5::PredType::NATIVE_FLOAT); + tmp.insertMember("i", sizeof(float), H5::PredType::NATIVE_FLOAT); + return tmp; + }(); + return ct; + } + + case Type::Void: + cytnx_error_msg(true, "[ERROR] Void dtype cannot be mapped to HDF5%s", "\n"); + + default: + cytnx_error_msg(true, "[ERROR] Unsupported Cytnx dtype: %s\n", getname(type_id).c_str()); + } + } + + unsigned int from_hdf5_type(const H5::DataType& h5_type) const { + if (h5_type == H5::PredType::NATIVE_DOUBLE) return Type::Double; + if (h5_type == H5::PredType::NATIVE_FLOAT) return Type::Float; + if (h5_type == H5::PredType::NATIVE_INT64) return Type::Int64; + if (h5_type == H5::PredType::NATIVE_UINT64) return Type::Uint64; + if (h5_type == H5::PredType::NATIVE_INT32) return Type::Int32; + if (h5_type == H5::PredType::NATIVE_UINT32) return Type::Uint32; + if (h5_type == H5::PredType::NATIVE_INT16) return Type::Int16; + if (h5_type == H5::PredType::NATIVE_UINT16) return Type::Uint16; + if (h5_type == H5::PredType::NATIVE_HBOOL) return Type::Bool; + if (h5_type.getClass() == H5T_COMPOUND) { + H5::CompType ct(h5_type.getId()); + if (ct.getSize() == sizeof(cytnx_complex128)) return Type::ComplexDouble; + if (ct.getSize() == sizeof(cytnx_complex64)) return Type::ComplexFloat; + } + cytnx_error_msg(true, "[ERROR] HDF5 DataType cannot be mapped to Cytnx dtype.%s", "\n"); + } + }; // Type_class /// @endcond @@ -402,7 +479,7 @@ namespace cytnx { * @details This is the variable about the data type of the UniTensor, Tensor, ... .\n * You can use it as following: * \code - * int type = Type.Double; + * int type = Type::Double; * \endcode * * The supported enumerations are as following: From 627acacd01ca514b7476c1839ab737177b6fe070 Mon Sep 17 00:00:00 2001 From: Manuel Schneider Date: Tue, 28 Apr 2026 21:09:16 +0800 Subject: [PATCH 06/40] fixed typos and bugs --- benchmarks/linalg/Svd_bm.cpp | 2 +- benchmarks/linalg/Svd_truncate_bm.cpp | 2 +- include/Tensor.hpp | 4 ++-- include/backend/Storage.hpp | 8 +++----- pybind/storage_py.cpp | 8 ++++---- src/Tensor.cpp | 10 +++++----- src/backend/Storage.cpp | 9 ++++----- src/tn_algo/MPS.cpp | 2 +- 8 files changed, 21 insertions(+), 24 deletions(-) diff --git a/benchmarks/linalg/Svd_bm.cpp b/benchmarks/linalg/Svd_bm.cpp index 57c4147e7..6709e172d 100644 --- a/benchmarks/linalg/Svd_bm.cpp +++ b/benchmarks/linalg/Svd_bm.cpp @@ -39,7 +39,7 @@ namespace BMTest_Svd { BENCHMARK(BM_Tensor_Svd_C128)->Args({1, 1})->Args({10, 10})->Args({100, 100})->Args({1000, 1000}); // DenseUniTensor - UniTensor ConstructDenseUT(const int D, const int dtype, const unsigned int device = Device.cpu) { + UniTensor ConstructDenseUT(const int D, const int dtype, const int device = Device.cpu) { auto bd_vi = Bond(D, BD_IN); auto bd_pi = Bond(2, BD_IN); auto bd_po = Bond(2, BD_OUT); diff --git a/benchmarks/linalg/Svd_truncate_bm.cpp b/benchmarks/linalg/Svd_truncate_bm.cpp index dffa756c6..9b95640db 100644 --- a/benchmarks/linalg/Svd_truncate_bm.cpp +++ b/benchmarks/linalg/Svd_truncate_bm.cpp @@ -5,7 +5,7 @@ using namespace cytnx; namespace BMTest_Svd_truncate { // DenseUniTensor - UniTensor ConstructDenseUT(const int D, const int dtype, const unsigned int device = Device.cpu) { + UniTensor ConstructDenseUT(const int D, const int dtype, const int device = Device.cpu) { auto bd_vi = Bond(D, BD_IN); auto bd_pi = Bond(2, BD_IN); auto bd_po = Bond(2, BD_OUT); diff --git a/include/Tensor.hpp b/include/Tensor.hpp index b151c0fa0..c02b81e94 100644 --- a/include/Tensor.hpp +++ b/include/Tensor.hpp @@ -409,9 +409,9 @@ namespace cytnx { * @see cytnx::Tensor::Tofile */ static Tensor Fromfile(const std::string &fname, const unsigned int &dtype, - const cytnx_int64 &count = -1, const bool restore_device = true); + const cytnx_int64 &count = -1, const int device = Device.cpu); static Tensor Fromfile(const char *fname, const unsigned int &dtype, - const cytnx_int64 &count = -1, const bool restore_device = true); + const cytnx_int64 &count = -1, const int device = Device.cpu); // static Tensor Frombinary(const std::string &fname); diff --git a/include/backend/Storage.hpp b/include/backend/Storage.hpp index 59b1bb98b..c978decfd 100644 --- a/include/backend/Storage.hpp +++ b/include/backend/Storage.hpp @@ -595,9 +595,7 @@ namespace cytnx { * @param[in] dtype the data type of the binary file. See cytnx.Type. * @param[in] count the number of elements you want to load from the binary file. If * \p count is -1, then it will load all the elements in the binary file. - * @param[in] restore_device whether to try restoring the device on which the data is stored; if - * false, the data will be kept on the CPU. Use .to_() to move it to the target device after - * loading. + * @param[in] device the device on which the data will be loaded. * 1. The @p dtype cannot be Type.Void. * 2. The @p dtype must be the same as the data type of the binary file. * 3. The @p count cannot be 0. @@ -607,14 +605,14 @@ namespace cytnx { * @see Tofile(const std::string &fname) const */ static Storage Fromfile(const std::string &fname, const unsigned int &dtype, - const cytnx_int64 &count = -1, const bool restore_device = true); + const cytnx_int64 &count = -1, const int device = Device.cpu); /** * @see Fromfile(const std::string &fname, const unsigned int &dtype, const cytnx_int64 &count = * -1) */ static Storage Fromfile(const char *fname, const unsigned int &dtype, - const cytnx_int64 &count = -1, const bool restore_device = true); + const cytnx_int64 &count = -1, const int device = Device.cpu); /** @brief cast the type of current Storage diff --git a/pybind/storage_py.cpp b/pybind/storage_py.cpp index 396e3b5cc..7cfc44bc9 100644 --- a/pybind/storage_py.cpp +++ b/pybind/storage_py.cpp @@ -279,10 +279,10 @@ void storage_binding(py::module &m) { py::arg("fname"), py::arg("restore_device") = true) .def_static( "Fromfile", - [](const std::string &fname, const unsigned int &dtype, const cytnx_int64 &count) { - return cytnx::Storage::Fromfile(fname, dtype, count); - }, - py::arg("fname"), py::arg("dtype"), py::arg("count") = (cytnx_int64)(-1)) + [](const std::string &fname, const unsigned int &dtype, const cytnx_int64 &count, + const int device) { return cytnx::Storage::Fromfile(fname, dtype, count, device); }, + py::arg("fname"), py::arg("dtype"), py::arg("count") = (cytnx_int64)(-1), + py::arg("device") = cytnx::Device.cpu) .def( py::pickle( diff --git a/src/Tensor.cpp b/src/Tensor.cpp index eecd508f1..bf77ac21e 100644 --- a/src/Tensor.cpp +++ b/src/Tensor.cpp @@ -485,12 +485,12 @@ namespace cytnx { } Tensor Tensor::Fromfile(const std::string &fname, const unsigned int &dtype, - const cytnx_int64 &count, const bool restore_device) { - return Tensor::from_storage(Storage::Fromfile(fname, dtype, count, restore_device)); + const cytnx_int64 &count, const int device) { + return Tensor::from_storage(Storage::Fromfile(fname, dtype, count, device)); } Tensor Tensor::Fromfile(const char *fname, const unsigned int &dtype, const cytnx_int64 &count, - const bool restore_device) { - return Tensor::from_storage(Storage::Fromfile(fname, dtype, count, restore_device)); + const int device) { + return Tensor::from_storage(Storage::Fromfile(fname, dtype, count, device)); } Tensor Tensor::Load(const std::string &fname, const bool restore_device) { @@ -512,7 +512,7 @@ namespace cytnx { f.close(); } void Tensor::Load_(const char *fname, const bool restore_device) { - this->Load(string(fname), restore_device); + this->Load_(string(fname), restore_device); } void Tensor::from_binary(std::istream &f, const bool restore_device) { diff --git a/src/backend/Storage.cpp b/src/backend/Storage.cpp index 0edca37d3..808450638 100644 --- a/src/backend/Storage.cpp +++ b/src/backend/Storage.cpp @@ -160,11 +160,11 @@ namespace cytnx { } Storage Storage::Fromfile(const char *fname, const unsigned int &dtype, const cytnx_int64 &count, - const bool restore_device) { - return Storage::Fromfile(string(fname), dtype, count, restore_device); + const int device) { + return Storage::Fromfile(string(fname), dtype, count, device); } Storage Storage::Fromfile(const std::string &fname, const unsigned int &dtype, - const cytnx_int64 &count, const bool restore_device) { + const cytnx_int64 &count, const int device) { cytnx_error_msg(dtype == Type.Void, "[ERROR] cannot have Void dtype.%s", "\n"); cytnx_error_msg(count == 0, "[ERROR] count cannot be zero!%s", "\n"); @@ -174,7 +174,6 @@ namespace cytnx { // check size: ifstream jf; - // std::cout << fname << std::endl; jf.open(fname, ios::ate | ios::binary); if (!jf.is_open()) { cytnx_error_msg(true, "[ERROR] Cannot open file '%s'.\n", fname.c_str()); @@ -200,7 +199,7 @@ namespace cytnx { if (!f.is_open()) { cytnx_error_msg(true, "[ERROR] Cannot open file '%s'.\n", fname.c_str()); } - out.data_from_binary(f, Nelem, dtype, restore_device); + out.data_from_binary(f, Nelem, dtype, device); f.close(); return out; } diff --git a/src/tn_algo/MPS.cpp b/src/tn_algo/MPS.cpp index 6f87b7d2d..dee2d2823 100644 --- a/src/tn_algo/MPS.cpp +++ b/src/tn_algo/MPS.cpp @@ -110,7 +110,7 @@ namespace cytnx { f.close(); } void MPS::Load_(const char* fname, const bool restore_device) { - this->Load(string(fname), restore_device); + this->Load_(string(fname), restore_device); } } // namespace tn_algo From f5350ef3a9392510d69b2e13d4d80b7fcbf76f1b Mon Sep 17 00:00:00 2001 From: Manuel Schneider Date: Wed, 29 Apr 2026 01:37:16 +0800 Subject: [PATCH 07/40] added hdf5 support for Storage and Tensor --- include/Tensor.hpp | 34 ++++++--- include/backend/Storage.hpp | 8 +++ include/backend/Tensor_impl.hpp | 10 +++ pybind/storage_py.cpp | 26 +++++++ pybind/tensor_py.cpp | 13 ++++ src/Tensor.cpp | 107 +++++++++++++++++++++++----- src/backend/Storage.cpp | 120 +++++++++++++++++++++++++++++--- src/backend/Tensor_impl.cpp | 53 -------------- 8 files changed, 279 insertions(+), 92 deletions(-) diff --git a/include/Tensor.hpp b/include/Tensor.hpp index b151c0fa0..ba96a29d1 100644 --- a/include/Tensor.hpp +++ b/include/Tensor.hpp @@ -1,18 +1,21 @@ #ifndef CYTNX_TENSOR_H_ #define CYTNX_TENSOR_H_ -#include "Type.hpp" -#include "cytnx_error.hpp" -#include "Device.hpp" -#include "intrusive_ptr_base.hpp" -#include #include -#include "utils/dynamic_arg_resolver.hpp" -#include "Accessor.hpp" -#include -#include #include +#include #include +#include +#include + +#include "H5Cpp.h" +#include "intrusive_ptr_base.hpp" + +#include "Accessor.hpp" +#include "cytnx_error.hpp" +#include "Device.hpp" +#include "Type.hpp" +#include "utils/dynamic_arg_resolver.hpp" #ifdef BACKEND_TORCH #else @@ -315,11 +318,15 @@ namespace cytnx { ///@endcond //------------------------------------------- + void to_hdf5(H5::Group &location, const std::string &name = "Tensor") const; + void from_hdf5(H5::Group &location, const std::string &name = "Tensor", + const bool restore_device = true); + /// @cond void to_binary(std::ostream &f) const; void from_binary(std::istream &f, const bool restore_device = true); - /// @endcond + /** @brief Save current Tensor to file @param[in] fname file name (without file extension) @@ -366,7 +373,6 @@ namespace cytnx { Load_(const std::string &fname, const bool restore_device) Load_() \endlink for loading the Tensor to the current Tensor. */ - static Tensor Load(const std::string &fname, const bool restore_device = true); /** * @see Load(const std::string &fname) @@ -619,6 +625,12 @@ namespace cytnx { */ const std::vector &shape() const { return this->_impl->shape(); } + /** + @brief the strides of the Tensor + @return [std::vector] the strides of the Tensor + */ + const std::vector strides() const { return this->_impl->strides(); } + /** @brief the rank of the Tensor @return [cytnx_uint64] the rank of the Tensor diff --git a/include/backend/Storage.hpp b/include/backend/Storage.hpp index 59b1bb98b..ef4d53549 100644 --- a/include/backend/Storage.hpp +++ b/include/backend/Storage.hpp @@ -16,6 +16,7 @@ #include "backend/Scalar.hpp" #include "cytnx_error.hpp" #include "Device.hpp" + #include "H5Cpp.h" #include "intrusive_ptr_base.hpp" #include "Type.hpp" @@ -525,6 +526,13 @@ namespace cytnx { ///@endcond /// @cond + void to_hdf5(H5::Group &location, const std::string &name = "Storage") const; + void from_hdf5(H5::Group &location, const std::string &name = "Tensor", + const bool restore_device = true); + void data_to_hdf5(H5::DataSet &dataset, H5::DataType &hdf5type) const; + void data_from_hdf5(H5::DataSet &dataset, const cytnx_uint64 &Nelem, const unsigned int &dtype, + H5::DataType &hdf5type, const int &device = Device.cpu); + void to_binary(std::ostream &f) const; void from_binary(std::istream &f, const bool restore_device = true); void data_to_binary(std::ostream &f) const; diff --git a/include/backend/Tensor_impl.hpp b/include/backend/Tensor_impl.hpp index deaf3b76a..fb381819e 100644 --- a/include/backend/Tensor_impl.hpp +++ b/include/backend/Tensor_impl.hpp @@ -78,6 +78,16 @@ namespace cytnx { const std::vector &shape() const { return _shape; } + const std::vector strides() const { + std::vector strides(this->_shape.size()); + cytnx_uint64 accu = 1; + for (cytnx_int64 i = this->_shape.size() - 1; i >= 0; i--) { + strides[this->_mapper[i]] = accu; // calculate strides here + accu *= this->_shape[this->_invmapper[i]]; + } + return strides; + } + const bool &is_contiguous() const { return this->_contiguous; } const std::vector &mapper() const { return this->_mapper; } diff --git a/pybind/storage_py.cpp b/pybind/storage_py.cpp index 396e3b5cc..0af4a115b 100644 --- a/pybind/storage_py.cpp +++ b/pybind/storage_py.cpp @@ -284,6 +284,32 @@ void storage_binding(py::module &m) { }, py::arg("fname"), py::arg("dtype"), py::arg("count") = (cytnx_int64)(-1)) + .def( + "to_hdf5", + [](cytnx::Storage &self, H5::Group &location, const std::string &name) { + self.to_hdf5(location, name); + }, + py::arg("location"), py::arg("name") = "Storage") + .def( + "from_hdf5", + [](cytnx::Storage &self, H5::Group &location, const std::string &name, + const bool restore_device) { self.from_hdf5(location, name, restore_device); }, + py::arg("location"), py::arg("name") = "Storage", py::arg("restore_device") = true) + .def( + "data_to_hdf5", + [](cytnx::Storage &self, H5::DataSet &dataset, H5::DataType &hdf5type) { + self.data_to_hdf5(dataset, hdf5type); + }, + py::arg("dataset"), py::arg("hdf5type")) + .def( + "data_from_hdf5", + [](cytnx::Storage &self, H5::DataSet &dataset, const cytnx_uint64 &Nelem, + const unsigned int &dtype, H5::DataType &hdf5type, const int &device = Device.cpu) { + self.data_from_hdf5(dataset, Nelem, dtype, hdf5type, device); + }, + py::arg("dataset"), py::arg("Nelem"), py::arg("dtype"), py::arg("hdf5type"), + py::arg("device") = (int)cytnx::Device.cpu) + .def( py::pickle( [](const cytnx::Storage &self) { // __getstate__ diff --git a/pybind/tensor_py.cpp b/pybind/tensor_py.cpp index a45f93f37..8ce0b736f 100644 --- a/pybind/tensor_py.cpp +++ b/pybind/tensor_py.cpp @@ -159,6 +159,7 @@ void tensor_binding(py::module &m) { .def("device", &cytnx::Tensor::device) .def("device_str", &cytnx::Tensor::device_str) .def("shape", &cytnx::Tensor::shape) + .def("strides", &cytnx::Tensor::strides) .def("rank", &cytnx::Tensor::rank) .def("clone", &cytnx::Tensor::clone) .def("__copy__", &cytnx::Tensor::clone) @@ -302,6 +303,18 @@ void tensor_binding(py::module &m) { }, py::arg("fname"), py::arg("restore_device") = true) + .def( + "to_hdf5", + [](cytnx::Tensor &self, H5::Group &location, const std::string &name) { + self.to_hdf5(location, name); + }, + py::arg("location"), py::arg("name") = "Tensor") + .def( + "from_hdf5", + [](cytnx::Tensor &self, H5::Group &location, const std::string &name, + const bool restore_device) { self.from_hdf5(location, name, restore_device); }, + py::arg("location"), py::arg("name") = "Tensor", py::arg("restore_device") = true) + .def(py::pickle( [](const cytnx::Tensor &self) { // __getstate__ std::ostringstream oss(std::ios::binary); diff --git a/src/Tensor.cpp b/src/Tensor.cpp index eecd508f1..8d19edb26 100644 --- a/src/Tensor.cpp +++ b/src/Tensor.cpp @@ -3,9 +3,10 @@ #include #include +#include "H5Cpp.h" +#include "Type.hpp" #include "linalg.hpp" #include "utils/is.hpp" -#include "Type.hpp" using namespace std; @@ -429,7 +430,7 @@ namespace cytnx { auto A = this->contiguous(); A.storage().Tofile(fname); } else { - this->_impl->_storage.Tofile(fname); + this->storage().Tofile(fname); } } void Tensor::Tofile(const char *fname) const { @@ -437,7 +438,7 @@ namespace cytnx { auto A = this->contiguous(); A.storage().Tofile(fname); } else { - this->_impl->_storage.Tofile(fname); + this->storage().Tofile(fname); } } void Tensor::Tofile(fstream &f) const { @@ -445,22 +446,38 @@ namespace cytnx { auto A = this->contiguous(); A.storage().Tofile(f); } else { - this->_impl->_storage.Tofile(f); + this->storage().Tofile(f); } } void Tensor::Save(const std::string &fname) const { - fstream f; + fstream f; // only for binary saving, not used for hdf5 if (std::filesystem::path(fname).has_extension()) { // filename extension is given - f.open(fname, ios::out | ios::trunc | ios::binary); - } else { - // add filename extension + auto ext = std::filesystem::path(fname).extension().string(); + if (ext == ".h5" || ext == ".hdf5" || ext == ".H5" || ext == ".HDF5" || ext == ".hdf" || + ext == ".HDF") { + // save as hdf5 + H5::H5File h5file; + try { + h5file = H5::H5File(fname, H5F_ACC_TRUNC); + } catch (H5::FileIException &error) { + error.printErrorStack(); + cytnx_error_msg(true, "[ERROR] Cannot create HDF5 file '%s'.\n", fname.c_str()); + } + this->to_hdf5(h5file); + h5file.close(); + return; + } else { // create binary file + f.open(fname, ios::out | ios::trunc | ios::binary); + } + } else { // create binary file with standard extension cytnx_warning_msg(true, "Missing file extension in fname '%s'. I am adding the extension '.cytn'. " "This is deprecated, please provide the file extension in the future.\n", fname.c_str()); f.open((fname + ".cytn"), ios::out | ios::trunc | ios::binary); } + // write binary if (!f.is_open()) { cytnx_error_msg(true, "[ERROR] invalid file path for save.%s", "\n"); } @@ -468,6 +485,23 @@ namespace cytnx { f.close(); } void Tensor::Save(const char *fname) const { this->Save(string(fname)); } + + void Tensor::to_hdf5(H5::Group &location, const std::string &name) const { + Tensor ten = this->contiguous(); + std::vector dims(this->shape().begin(), this->shape().end()); + + H5::DataSpace dataspace(dims.size(), dims.data()); + H5::DataType datatype = Type.to_hdf5_type(this->dtype()); + H5::DataSet dataset = location.createDataSet(name, datatype, dataspace); + ten.storage().data_to_hdf5(dataset, datatype); + + if (this->device() != Device.cpu) { + H5::Attribute attr = + dataset.createAttribute("device", H5::PredType::NATIVE_INT, H5::DataSpace(H5S_SCALAR)); + int device = this->device(); + attr.write(H5::PredType::NATIVE_INT, &device); + } + } void Tensor::to_binary(std::ostream &f) const { unsigned int IDDs = 888; f.write((char *)&IDDs, sizeof(unsigned int)); @@ -481,7 +515,7 @@ namespace cytnx { f.write((char *)&this->_impl->_invmapper[0], sizeof(cytnx_uint64) * shp); // pass to storage for save: - this->_impl->_storage.to_binary(f); + this->storage().to_binary(f); } Tensor Tensor::Fromfile(const std::string &fname, const unsigned int &dtype, @@ -492,7 +526,6 @@ namespace cytnx { const bool restore_device) { return Tensor::from_storage(Storage::Fromfile(fname, dtype, count, restore_device)); } - Tensor Tensor::Load(const std::string &fname, const bool restore_device) { Tensor out; out.Load_(fname, restore_device); @@ -503,18 +536,56 @@ namespace cytnx { } void Tensor::Load_(const std::string &fname, const bool restore_device) { - fstream f; - f.open(fname, ios::in | ios::binary); - if (!f.is_open()) { - cytnx_error_msg(true, "[ERROR] Cannot open file '%s'.\n", fname.c_str()); + auto ext = std::filesystem::path(fname).extension().string(); + if (ext == ".h5" || ext == ".hdf5" || ext == ".H5" || ext == ".HDF5" || ext == ".hdf" || + ext == ".HDF") { + // load hdf5 + H5::H5File h5file; + try { + h5file = H5::H5File(fname, H5F_ACC_RDONLY); + } catch (H5::FileIException &error) { + error.printErrorStack(); + cytnx_error_msg(true, "[ERROR] Cannot open HDF5 file '%s'.\n", fname.c_str()); + } + this->from_hdf5(h5file, "Tensor", restore_device); + h5file.close(); + } else { // load binary + fstream f; + f.open(fname, ios::in | ios::binary); + if (!f.is_open()) { + cytnx_error_msg(true, "[ERROR] Cannot open file '%s'.\n", fname.c_str()); + } + this->from_binary(f, restore_device); + f.close(); } - this->from_binary(f, restore_device); - f.close(); } void Tensor::Load_(const char *fname, const bool restore_device) { - this->Load(string(fname), restore_device); - } + this->Load_(string(fname), restore_device); + } + + void Tensor::from_hdf5(H5::Group &location, const std::string &name, const bool restore_device) { + H5::DataSet dataset = location.openDataSet(name); + H5::DataType datatype = dataset.getDataType(); + unsigned int dtype = Type.from_hdf5_type(datatype); + H5::DataSpace dataspace = dataset.getSpace(); + int rank = dataspace.getSimpleExtentNdims(); + hsize_t dims[rank]; + dataspace.getSimpleExtentDims(dims); + auto Nelem = dataspace.getSimpleExtentNpoints(); + + this->_impl->_shape = std::vector(dims, dims + rank); + this->_impl->_mapper = vec_range(this->_impl->_shape.size()); + this->_impl->_invmapper = this->_impl->_mapper; + this->_impl->_contiguous = true; // HDF5 data is always stored in contiguous layout + + int device = Device.cpu; + if (restore_device && dataset.attrExists("device")) { + H5::Attribute attr = dataset.openAttribute("device"); + attr.read(H5::PredType::NATIVE_INT, &device); + } + this->_impl->_storage.data_from_hdf5(dataset, Nelem, dtype, datatype, device); + } void Tensor::from_binary(std::istream &f, const bool restore_device) { unsigned int tmpIDDs; f.read((char *)&tmpIDDs, sizeof(unsigned int)); diff --git a/src/backend/Storage.cpp b/src/backend/Storage.cpp index 0edca37d3..ede4db757 100644 --- a/src/backend/Storage.cpp +++ b/src/backend/Storage.cpp @@ -3,6 +3,8 @@ #include #include +#include "H5Cpp.h" + using namespace std; namespace cytnx { @@ -86,15 +88,31 @@ namespace cytnx { fstream f; if (std::filesystem::path(fname).has_extension()) { // filename extension is given - f.open(fname, ios::out | ios::trunc | ios::binary); - } else { - // add filename extension + auto ext = std::filesystem::path(fname).extension().string(); + if (ext == ".h5" || ext == ".hdf5" || ext == ".H5" || ext == ".HDF5" || ext == ".hdf" || + ext == ".HDF") { + // save as hdf5 + H5::H5File h5file; + try { + h5file = H5::H5File(fname, H5F_ACC_TRUNC); + } catch (H5::FileIException &error) { + error.printErrorStack(); + cytnx_error_msg(true, "[ERROR] Cannot create HDF5 file '%s'.\n", fname.c_str()); + } + this->to_hdf5(h5file); + h5file.close(); + return; + } else { // create binary file + f.open(fname, ios::out | ios::trunc | ios::binary); + } + } else { // create binary file with standard extension cytnx_warning_msg(true, "Missing file extension in fname '%s'. I am adding the extension '.cyst'. " "This is deprecated, please provide the file extension in the future.\n", fname.c_str()); f.open((fname + ".cyst"), ios::out | ios::trunc | ios::binary); } + // write binary if (!f.is_open()) { cytnx_error_msg(true, "[ERROR] invalid file path for save.%s", "\n"); } @@ -129,6 +147,37 @@ namespace cytnx { this->data_to_binary(f); } + void Storage::to_hdf5(H5::Group &location, const std::string &name) const { + hsize_t Nelem = this->size(); + H5::DataSpace dataspace(1, &Nelem); + H5::DataType datatype = Type.to_hdf5_type(this->dtype()); + H5::DataSet dataset = location.createDataSet(name, datatype, dataspace); + this->data_to_hdf5(dataset, datatype); + if (this->device() != Device.cpu) { + H5::Attribute attr = + dataset.createAttribute("device", H5::PredType::NATIVE_INT, H5::DataSpace(H5S_SCALAR)); + int device = this->device(); + attr.write(H5::PredType::NATIVE_INT, &device); + } + } + void Storage::data_to_hdf5(H5::DataSet &dataset, H5::DataType &hdf5type) const { + if (this->device() == Device.cpu) { + dataset.write(this->data(), hdf5type); + } else { +#ifdef UNI_GPU + checkCudaErrors(cudaSetDevice(this->device())); + void *htmp = malloc(Type.typeSize(this->dtype()) * this->size()); + checkCudaErrors(cudaMemcpy(htmp, this->_impl->data(), + Type.typeSize(this->dtype()) * this->size(), + cudaMemcpyDeviceToHost)); + dataset.write(htmp, hdf5type); + free(htmp); +#else + cytnx_error_msg(true, "ERROR internal fatal error in Save Storage%s", "\n"); +#endif + } + } + void Storage::to_binary(std::ostream &f) const { unsigned int IDDs = 999; f.write((char *)&IDDs, sizeof(unsigned int)); @@ -174,7 +223,6 @@ namespace cytnx { // check size: ifstream jf; - // std::cout << fname << std::endl; jf.open(fname, ios::ate | ios::binary); if (!jf.is_open()) { cytnx_error_msg(true, "[ERROR] Cannot open file '%s'.\n", fname.c_str()); @@ -215,18 +263,70 @@ namespace cytnx { } void Storage::Load_(const std::string &fname, const bool restore_device) { - fstream f; - f.open(fname, ios::in | ios::binary); - if (!f.is_open()) { - cytnx_error_msg(true, "[ERROR] Cannot open file '%s'.\n", fname.c_str()); + auto ext = std::filesystem::path(fname).extension().string(); + if (ext == ".h5" || ext == ".hdf5" || ext == ".H5" || ext == ".HDF5" || ext == ".hdf" || + ext == ".HDF") { + // load hdf5 + H5::H5File h5file; + try { + h5file = H5::H5File(fname, H5F_ACC_RDONLY); + } catch (H5::FileIException &error) { + error.printErrorStack(); + cytnx_error_msg(true, "[ERROR] Cannot open HDF5 file '%s'.\n", fname.c_str()); + } + this->from_hdf5(h5file, "Storage", restore_device); + h5file.close(); + } else { // load binary + fstream f; + f.open(fname, ios::in | ios::binary); + if (!f.is_open()) { + cytnx_error_msg(true, "[ERROR] Cannot open file '%s'.\n", fname.c_str()); + } + this->from_binary(f, restore_device); + f.close(); } - this->from_binary(f, restore_device); - f.close(); } void Storage::Load_(const char *fname, const bool restore_device) { this->Load_(string(fname), restore_device); } + void Storage::from_hdf5(H5::Group &location, const std::string &name, const bool restore_device) { + H5::DataSet dataset = location.openDataSet(name); + H5::DataType datatype = dataset.getDataType(); + unsigned int dtype = Type.from_hdf5_type(datatype); + H5::DataSpace dataspace = dataset.getSpace(); + auto Nelem = dataspace.getSimpleExtentNpoints(); + + int device = Device.cpu; + if (restore_device && dataset.attrExists("device")) { + H5::Attribute attr = dataset.openAttribute("device"); + attr.read(H5::PredType::NATIVE_INT, &device); + } + + this->data_from_hdf5(dataset, Nelem, dtype, datatype, device); + } + void Storage::data_from_hdf5(H5::DataSet &dataset, const cytnx_uint64 &Nelem, + const unsigned int &dtype, H5::DataType &hdf5type, + const int &device) { + this->_impl = __SII.USIInit[dtype](); + this->_impl->Init(Nelem, device, false); + + if (device == Device.cpu) { + dataset.read(this->_impl->data(), hdf5type); + } else { +#ifdef UNI_GPU + checkCudaErrors(cudaSetDevice(device)); + void *htmp = malloc(Type.typeSize(dtype) * Nelem); + dataset.read(htmp, hdf5type); + checkCudaErrors(cudaMemcpy(this->_impl->data(), htmp, Type.typeSize(dtype) * Nelem, + cudaMemcpyHostToDevice)); + free(htmp); +#else + cytnx_error_msg(true, "ERROR internal fatal error in Load Storage%s", "\n"); +#endif + } + } + void Storage::from_binary(std::istream &f, const bool restore_device) { unsigned long long Nelem; unsigned int dtype; diff --git a/src/backend/Tensor_impl.cpp b/src/backend/Tensor_impl.cpp index 74e92b887..a9f39dd00 100644 --- a/src/backend/Tensor_impl.cpp +++ b/src/backend/Tensor_impl.cpp @@ -26,7 +26,6 @@ namespace cytnx { this->_mapper = vec_range(shape.size()); this->_invmapper = this->_mapper; this->_contiguous = true; - // cout << shape << endl; } void Tensor_impl::Init(const Storage &in) { cytnx_error_msg(in.dtype() == Type.Void, @@ -56,9 +55,6 @@ namespace cytnx { std::vector new_shape(this->_shape.size()); std::vector new_idxmap(this->_shape.size()); - // for(int i=0;i_shape.size();i++) - // std::cout << this->_mapper[i] << " " << this->_invmapper[i] << std::endl; - boost::intrusive_ptr out(new Tensor_impl()); for (cytnx_uint32 i = 0; i < rnks.size(); i++) { @@ -124,7 +120,6 @@ namespace cytnx { if (rnks[i] >= rnks.size()) { cytnx_error_msg(1, "%s", "reshape a tensor with invalid rank index."); } - // std::cout << this->_mapper[rnks[i]] << " " << i << std::endl; // new_idxmap[this->_mapper[rnks[i]]] = i; this->_invmapper[this->_mapper[rnks[i]]] = i; new_fwdmap[i] = this->_mapper[rnks[i]]; @@ -168,24 +163,10 @@ namespace cytnx { acc.push_back(Accessor::all()); } - /* - cout << "acc type bef" << endl; - for(int i=0;i_invmapper); // contiguous. - /* - cout << "acc type aft" << endl; - for(int i=0;i_shape, this->_invmapper); - // cout << "curr_shape" << endl; - // cout << curr_shape << endl; //[2] from back to front, check until last all: cytnx_uint64 Nunit = 1; @@ -199,9 +180,6 @@ namespace cytnx { break; } } - // cout << "tmpidx" << tmpidx << endl; - // cout << "Nunit" << Nunit << endl; - // cout << acc.size() << endl; // acc-> locators @@ -212,8 +190,6 @@ namespace cytnx { "[ERROR] Tensor cannot accept accessor with qnum list.%s", "\n"); acc[i].get_len_pos(curr_shape[i], get_shape[i], locators[i]); } - // cout << "get_shape" << endl; - // cout << get_shape << endl; // create Tensor: for (cytnx_uint64 i = 0; i < tmpidx; i++) { @@ -221,7 +197,6 @@ namespace cytnx { } boost::intrusive_ptr out(new Tensor_impl()); out->Init(get_shape, this->dtype(), this->device()); - // cout << get_shape << endl; if (locators.size() == 0) { locators.resize(1); @@ -242,19 +217,6 @@ namespace cytnx { new_shape.push_back(out->shape()[i]); } - // cout << "mapper" << endl; - // cout << new_mapper << endl; - // cout << "inv_mapper" << endl; - // cout << this->_invmapper << endl; - - // cout << "remove_id" << endl; - // cout << remove_id << endl; - // cout << "out shape raw" << endl; - // cout << out->shape() << endl; - - // cout << "perm" << endl; - // cout << perm << endl; - // cout << new_shape << endl; if (new_shape.size()) { // exclude the case where only single element exists! out->reshape_(new_shape); // remove size-1 axis @@ -291,13 +253,9 @@ namespace cytnx { vector get_shape(acc.size()); - // vector new_shape; std::vector> locators(this->_shape.size()); for (cytnx_uint32 i = 0; i < acc.size(); i++) { acc[i].get_len_pos(this->_shape[i], get_shape[i], locators[i]); - // std::cout << this->_shape[i] << " " << get_shape[i] << "|"; - // for(int j=0;j out(new Tensor_impl()); @@ -319,7 +277,6 @@ namespace cytnx { void Tensor_impl::set(const std::vector &accessors, const boost::intrusive_ptr &rhs) { - // cout << "calling set" << endl; cytnx_error_msg(accessors.size() > this->_shape.size(), "%s", "The input indexes rank is out of range! (>Tensor's rank)."); @@ -359,15 +316,12 @@ namespace cytnx { if (rhs->storage().size() == 1) { this->storage()._impl->SetElem_byShape_v2(rhs->storage()._impl, curr_shape, locators, Nunit, true); - // std::cout << "Scalar" << endl; } else { for (cytnx_uint64 i = 0; i < tmpidx; i++) { get_shape.push_back(curr_shape[acc.size() + i]); } - // std::cout << get_shape << endl; - // permute input to currect pos std::vector new_mapper(this->_mapper.begin(), this->_mapper.end()); std::vector new_shape; @@ -399,9 +353,7 @@ namespace cytnx { std::vector iperm(perm.size()); for (unsigned int i = 0; i < iperm.size(); i++) iperm[perm[i]] = i; - // std::cout << new_shape << endl; boost::intrusive_ptr tmp; - // std::cout << iperm << std::endl; tmp = rhs->permute(iperm)->contiguous(); cytnx_error_msg(new_shape != tmp->shape(), "[ERROR][Tensor.set_elems]%s", "inconsistent shape"); @@ -437,9 +389,6 @@ namespace cytnx { break; } } - // cout << "tmpidx" << tmpidx << endl; - // cout << "Nunit" << Nunit << endl; - // cout << acc.size() << endl; // acc-> locators @@ -450,8 +399,6 @@ namespace cytnx { "[ERROR] Tensor cannot accept accessor with qnum list.%s", "\n"); acc[i].get_len_pos(curr_shape[i], get_shape[i], locators[i]); } - // cout << "get_shape" << endl; - // cout << get_shape << endl; // call storage Scalar c = rc; From 5ff3887184845465a4b32412ae84a3dad778d57b Mon Sep 17 00:00:00 2001 From: Manuel Schneider Date: Wed, 29 Apr 2026 18:07:13 +0800 Subject: [PATCH 08/40] established hdf5 framework; some implementations still missing --- include/Bond.hpp | 107 +++++++++++++------ include/Symmetry.hpp | 113 ++++++++++++++------ include/Tensor.hpp | 153 ++++++++++++++++---------- include/Type.hpp | 6 +- include/UniTensor.hpp | 134 +++++++++++++++-------- include/backend/Storage.hpp | 208 +++++++++++++++++++++++++----------- include/tn_algo/MPS.hpp | 119 +++++++++++++++------ pybind/storage_py.cpp | 15 +-- pybind/symmetry_py.cpp | 1 + pybind/tensor_py.cpp | 39 ++++--- src/Bond.cpp | 132 +++++++++++++++++++++-- src/Symmetry.cpp | 71 +++++++++--- src/Tensor.cpp | 3 +- src/UniTensor.cpp | 129 ++++++++++++++-------- src/backend/Storage.cpp | 2 +- src/tn_algo/MPS.cpp | 71 ++++++++---- 16 files changed, 924 insertions(+), 379 deletions(-) diff --git a/include/Bond.hpp b/include/Bond.hpp index 824c23ace..a3085d591 100644 --- a/include/Bond.hpp +++ b/include/Bond.hpp @@ -1,14 +1,17 @@ #ifndef CYTNX_BOND_H_ #define CYTNX_BOND_H_ -#include "Type.hpp" -#include "cytnx_error.hpp" -#include "Symmetry.hpp" -#include -#include +#include #include +#include #include -#include +#include + +#include "H5Cpp.h" + +#include "Symmetry.hpp" +#include "Type.hpp" +#include "cytnx_error.hpp" #include "intrusive_ptr_base.hpp" #include "utils/vec_clone.hpp" @@ -36,6 +39,8 @@ namespace cytnx { BD_IN = -1, /*!< -1, same as BD_KET */ BD_OUT = 1 /*!< 1, same as BD_BRA */ }; + static const std::map bondtype_to_string = { + {BD_REG, "REG"}, {BD_IN, "IN"}, {BD_OUT, "OUT"}}; /// @cond class Bond_impl : public intrusive_ptr_base { @@ -848,48 +853,82 @@ namespace cytnx { } /** - @brief Save the Bond object to the file. - @details Save the Bond object to the file. The file extension will be automatically - added as ".cybd". - @param[in] fname the file name of the Bond object (exclude the file extension). - @see Load(const std::string &fname) - */ + * @brief Save Bond to file + * @param[in] fname file name + * @details Save the Bond to a file. The file ending should be one of ".h5", ".hdf5", ".H5", + * ".HDF5", ".hdf" to save in HDF5 file format. Otherwise, a binary file format is used. + * @note The common file ending for saving a Bond in binary format is ".cybd". + * @warning HDF5 file format is strongly recommended for compatibility with other libraries, + * readability, and future-proofing. + * @see Load(const std::string &fname, const bool restore_device) + */ void Save(const std::string &fname) const; - - /** - @see Save(const std::string &fname) const; - */ + // @see Save(const std::string &fname) const; void Save(const char *fname) const; /** - @brief Load Bond from file and create new instance - @param fname[in] file name - @pre The file must be a Bond object which is saved by cytnx::Bond::Save. - @note This function creates a new Bond and keeps the original Bond unchanged. See \link - Load_(const std::string &fname) Load_() \endlink for loading the Bond to the current Bond. - */ + * @brief Load Bond from file and create new instance + * @param fname[in] file name + * @param[in] restore_device whether to try restoring the device on which the data is stored; if + * false, the data will be kept on the CPU. Use .to_() to move it to the target device after + * loading. + * @pre The file must be a Bond object which is saved by cytnx::Bond::Save. + * @note This function creates a new Bond and keeps the original Bond unchanged. See \link + * Load_(const std::string &fname, const bool restore_device) Load_() \endlink for loading the + * Bond to the current Bond. + * @details For HDF5 file format, one of the file endings ".h5", ".hdf5", ".H5", ".HDF5", ".hdf" + * is expected. For binary format, the common file ending for a Bond is ".cybd". + */ static cytnx::Bond Load(const std::string &fname); - /** - @see Load(const std::string &fname) - */ + // @see Load(const std::string &fname) static cytnx::Bond Load(const char *fname); /** - @brief Load Bond from file and overwrite current instance - @note This function overwrites the existing Bond. See \link Load(const std::string &fname) - Load() \endlink for creating a new Bond. - @see Load(const std::string &fname) - */ + * @brief Load Bond from file and overwrite current instance + * @note This function overwrites the existing Bond. See \link Load(const std::string &fname, + * const bool restore_device) Load() \endlink for creating a new Bond. + * @see Load(const std::string &fname, const bool restore_device) + */ void Load_(const std::string &fname); + // @see Load_(const std::string &fname) + void Load_(const char *fname); + /** - * @see Load_(const std::string &fname) + * @brief Save Bond to HDF5 file + * @param[in] location the HDF5 group where the Bond will be saved. + * @param[in] save_symmetries whether to save the symmetry information in the HDF5 file. + * @warning This function is only available in C++. Use \link Save(const std::string &fname) + * Save() \endlink for saving to file in C++ or Python. + * @see from_hdf5(H5::Group &location, const std::string &name, const bool restore_device) */ - void Load_(const char *fname); + void to_hdf5(H5::Group &location, const bool save_symmetries = true) const; + /** + * @brief Load Bond from HDF5 file (inline) + * @param[in] location the HDF5 group where the Bond will be loaded from. + * @warning This function is only available in C++. Use \link Load(const std::string &fname, + * const bool restore_device) Load() \endlink for loading from file in C++ or Python. + * @see to_hdf5(H5::Group &location, const std::string &name) const + */ + void from_hdf5(H5::Group &location); - /// @cond + /** + * @brief Save Bond to binary file + * @param[in] f the output stream where the Bond will be saved. + * @warning This function is only available in C++. In Python, use pickle for the same binary + * file format. Use \link Save(const std::string &fname) Save() \endlink for saving to file in + * C++ or Python. + * @see from_binary(std::istream &f) + */ void to_binary(std::ostream &f) const; + /** + * @brief Load Bond from binary file + * @param[in] f the input stream from which the Bond will be loaded. + * @warning This function is only available in C++. In Python, use pickle for the same binary + * file format. Use \link Load(const std::string &fname, const bool restore_device) Load() + * \endlink for loading from file in C++ or Python. + * @see to_binary(std::ostream &f) const + */ void from_binary(std::istream &f); - /// @endcond /** @brief The comparison operator 'equal to'. diff --git a/include/Symmetry.hpp b/include/Symmetry.hpp index eab3f0d2c..165081c67 100644 --- a/include/Symmetry.hpp +++ b/include/Symmetry.hpp @@ -2,15 +2,17 @@ #define CYTNX_SYMMETRY_H_ #include +#include #include #include #include +#include "H5Cpp.h" #include "boost/smart_ptr/intrusive_ptr.hpp" +#include "Type.hpp" #include "cytnx_error.hpp" #include "intrusive_ptr_base.hpp" -#include "Type.hpp" #include "utils/dynamic_arg_resolver.hpp" namespace cytnx { @@ -27,7 +29,7 @@ namespace cytnx { * fPar | -2, fermionParity symmetry * fNum | -3, fermionNumber symmetry * - * @see Symmetry::stype(), Symmetry::stype_str() + * @see Symmetry::stype(), Symmetry::name(), Symmetry::stype_str() */ enum SymmetryType : int { Void = -99, U = -1, Z = 0, fPar = -2, fNum = -3 }; @@ -63,7 +65,7 @@ namespace cytnx { ///@cond class Symmetry_base : public intrusive_ptr_base { public: - int stype_id; + SymmetryType stype_id; int n; Symmetry_base() : stype_id(SymmetryType::Void){}; Symmetry_base(const int &n) : stype_id(SymmetryType::Void) { this->Init(n); }; @@ -91,6 +93,7 @@ namespace cytnx { virtual bool is_fermionic() const { return false; }; virtual void print_info() const; + virtual std::string name() const; virtual std::string stype_str() const; // virtual std::vector& combine_rule(const std::vector &inL, const // std::vector &inR); @@ -119,6 +122,7 @@ namespace cytnx { const bool &is_reverse); void reverse_rule_(cytnx_int64 &out, const cytnx_int64 &in); void print_info() const; + std::string name() const override { return "U1"; } std::string stype_str() const override { return "U1"; }; }; ///@endcond @@ -145,6 +149,7 @@ namespace cytnx { const bool &is_reverse); void reverse_rule_(cytnx_int64 &out, const cytnx_int64 &in); void print_info() const; + std::string name() const override { return "Z" + std::to_string(this->n); }; std::string stype_str() const override { return "Z" + std::to_string(this->n); }; }; ///@endcond @@ -170,6 +175,7 @@ namespace cytnx { fermionParity get_fermion_parity(const cytnx_int64 &in_qnum) const override; bool is_fermionic() const override { return true; }; void print_info() const; + std::string name() const override { return "fermion parity"; } std::string stype_str() const override { return "fP"; } }; ///@endcond @@ -195,6 +201,7 @@ namespace cytnx { fermionParity get_fermion_parity(const cytnx_int64 &in_qnum) const override; bool is_fermionic() const override { return true; }; void print_info() const; + std::string name() const override { return "fermion number"; } std::string stype_str() const override { return "f#"; } }; ///@endcond @@ -380,11 +387,13 @@ namespace cytnx { int &n() const { return this->_impl->n; } /** - @brief return the symmetry type name of current Symmetry object in string form, see - cytnx::SymmetryType:: - @return [std::string] - the symmetry type name. - + @brief Type name of the Symmetry in long form + @return [std::string] the symmetry type long name. + */ + std::string name() const { return this->_impl->name(); } + /** + @brief Type name of the Symmetry in short form + @return [std::string] the symmetry type short name. */ std::string stype_str() const { return this->_impl->stype_str(); } @@ -492,47 +501,83 @@ namespace cytnx { bool is_fermionic() const { return this->_impl->is_fermionic(); } /** - * @brief Save the current Symmetry object to a file. - * @param[in] fname the file name. - * @post the file extension will be automatically added as ".cysym". + * @brief Save Symmetry to file + * @param[in] fname file name + * @details Save the Symmetry to a file. The file ending should be one of ".h5", ".hdf5", ".H5", + * ".HDF5", ".hdf" to save in HDF5 file format. Otherwise, a binary file format is used. + * @note The common file ending for saving a Symmetry in binary format is ".cysym". + * @warning HDF5 file format is strongly recommended for compatibility with other libraries, + * readability, and future-proofing. + * @see Load(const std::string &fname, const bool restore_device) */ void Save(const std::string &fname) const; - - /** - * @brief Same as Save(const std::string &fname) const; - */ + // @brief Same as Save(const std::string &fname) const; void Save(const char *fname) const; /** - @brief Load Symmetry from file and create new instance - @param fname[in] file name - @pre The file must be a Symmetry object which is saved by cytnx::Symmetry::Save. - @note This function creates a new Symmetry and keeps the original Symmetry unchanged. See \link - Load_(const std::string &fname) Load_() \endlink for loading the Symmetry to the current - Symmetry. - */ + * @brief Load Symmetry from file and create new instance + * @param fname[in] file name + * @param[in] restore_device whether to try restoring the device on which the data is stored; if + * false, the data will be kept on the CPU. Use .to_() to move it to the target device after + * loading. + * @pre The file must be a Symmetry object which is saved by cytnx::Symmetry::Save. + * @note This function creates a new Symmetry and keeps the original Symmetry unchanged. See + * \link Load_(const std::string &fname, const bool restore_device) Load_() \endlink for loading + * the Symmetry to the current Symmetry. + * @details For HDF5 file format, one of the file endings ".h5", ".hdf5", ".H5", ".HDF5", ".hdf" + * is expected. For binary format, the common file ending for a Symmetry is ".cysym". + */ static cytnx::Symmetry Load(const std::string &fname); - /** - @see Load(const std::string &fname) - */ + // @see Load(const std::string &fname) static cytnx::Symmetry Load(const char *fname); /** - @brief Load Symmetry from file and overwrite current instance - @note This function overwrites the existing Symmetry. See \link Load(const std::string &fname) - Load() \endlink for creating a new Symmetry. - @see Load(const std::string &fname) - */ + * @brief Load Symmetry from file and overwrite current instance + * @note This function overwrites the existing Symmetry. See \link Load(const std::string + * &fname, const bool restore_device) Load() \endlink for creating a new Symmetry. + * @see Load(const std::string &fname, const bool restore_device) + */ void Load_(const std::string &fname); + // @see Load_(const std::string &fname) + void Load_(const char *fname); + /** - * @see Load_(const std::string &fname) + * @brief Save Symmetry to HDF5 file + * @param[in] location the HDF5 group where the Symmetry will be saved. + * @param[in] name the name of the Symmetry in the HDF5 file. + * @warning This function is only available in C++. Use \link Save(const std::string &fname) + * Save() \endlink for saving to file in C++ or Python. + * @see from_hdf5(H5::Group &location, const std::string &name, const bool restore_device) */ - void Load_(const char *fname); + void to_hdf5(H5::Group &location, const std::string &name = "Symmetry") const; + /** + * @brief Load Symmetry from HDF5 file (inline) + * @param[in] location the HDF5 group where the Symmetry will be loaded from. + * @param[in] name the name of the Symmetry in the HDF5 file. + * @warning This function is only available in C++. Use \link Load(const std::string &fname, + * const bool restore_device) Load() \endlink for loading from file in C++ or Python. + * @see to_hdf5(H5::Group &location, const std::string &name) const + */ + void from_hdf5(H5::Group &location, const std::string &name = "Symmetry"); - /// @cond + /** + * @brief Save Symmetry to binary file + * @param[in] f the output stream where the Symmetry will be saved. + * @warning This function is only available in C++. In Python, use pickle for the same binary + * file format. Use \link Save(const std::string &fname) Save() \endlink for saving to file in + * C++ or Python. + * @see from_binary(std::istream &f) + */ void to_binary(std::ostream &f) const; + /** + * @brief Load Symmetry from binary file + * @param[in] f the input stream from which the Symmetry will be loaded. + * @warning This function is only available in C++. In Python, use pickle for the same binary + * file format. Use \link Load(const std::string &fname, const bool restore_device) Load() + * \endlink for loading from file in C++ or Python. + * @see to_binary(std::ostream &f) const + */ void from_binary(std::istream &f); - /// @endcond /** * @brief Print the information of current Symmetry object. diff --git a/include/Tensor.hpp b/include/Tensor.hpp index ba96a29d1..27b7fc522 100644 --- a/include/Tensor.hpp +++ b/include/Tensor.hpp @@ -318,78 +318,110 @@ namespace cytnx { ///@endcond //------------------------------------------- - void to_hdf5(H5::Group &location, const std::string &name = "Tensor") const; - void from_hdf5(H5::Group &location, const std::string &name = "Tensor", - const bool restore_device = true); - - /// @cond - void to_binary(std::ostream &f) const; - void from_binary(std::istream &f, const bool restore_device = true); - /// @endcond - - /** - @brief Save current Tensor to file - @param[in] fname file name (without file extension) - - @details - save the Tensor to file with file path specify with input param \p fname with postfix - ".cytn" - @see Load(const std::string &fname, const bool restore_device) - */ - void Save(const std::string &fname) const; /** - * @see Save(const std::string &fname) const + * @brief Save Tensor to file + * @param[in] fname file name + * @details Save the Tensor to a file. The file ending should be one of ".h5", ".hdf5", ".H5", + * ".HDF5", ".hdf" to save in HDF5 file format. Otherwise, a binary file format is used. + * @note The common file ending for saving a Tensor in binary format is ".cytn". + * @warning HDF5 file format is strongly recommended for compatibility with other libraries, + * readability, and future-proofing. + * @see Load(const std::string &fname, const bool restore_device) */ + void Save(const std::string &fname) const; + // @see Save(const std::string &fname) const void Save(const char *fname) const; /** - * @brief Save current Tensor to the binary file - * @details This function will save the Tensor to the binary file with file - * name \p fname . - * @param fname[in] the file name of the binary file. - * @pre The file name @p fname must be valid. - * @see cytnx::Tensor::Fromfile + * @brief Load Tensor from file and create new instance + * @param fname[in] file name + * @param[in] restore_device whether to try restoring the device on which the data is stored; if + * false, the data will be kept on the CPU. Use .to_() to move it to the target device after + * loading. + * @pre The file must be a Tensor object which is saved by cytnx::Tensor::Save. + * @note This function creates a new Tensor and keeps the original Tensor unchanged. See \link + * Load_(const std::string &fname, const bool restore_device) Load_() \endlink for loading the + * Tensor to the current Tensor. + * @details For HDF5 file format, one of the file endings ".h5", ".hdf5", ".H5", ".HDF5", ".hdf" + * is expected. For binary format, the common file ending for a Tensor is ".cytn". */ - void Tofile(const std::string &fname) const; + static Tensor Load(const std::string &fname, const bool restore_device = true); + // @see Load(const std::string &fname, const bool restore_device) + static Tensor Load(const char *fname, const bool restore_device = true); /** - * @see Tofile(const std::string &fname) const + * @brief Load Tensor from file and overwrite current instance + * @note This function overwrites the existing Tensor. See \link Load(const std::string &fname, + * const bool restore_device) Load() \endlink for creating a new Tensor. + * @see Load(const std::string &fname, const bool restore_device) */ - void Tofile(const char *fname) const; + void Load_(const std::string &fname, const bool restore_device = true); + // @see Load_(const std::string &fname, const bool restore_device) + void Load_(const char *fname, const bool restore_device = true); /** - * @see Tofile(const std::string &fname) const + * @brief Save Tensor to HDF5 file + * @param[in] location the HDF5 group where the Tensor will be saved. + * @param[in] name the name of the Tensor in the HDF5 file. + * @warning This function is only available in C++. Use \link Save(const std::string &fname) + * Save() \endlink for saving to file in C++ or Python. + * @see from_hdf5(H5::Group &location, const std::string &name, const bool restore_device) */ - void Tofile(std::fstream &f) const; - - /** - @brief Load Tensor from file and create new instance - @param fname[in] file name - @param[in] restore_device whether to try restoring the device on which the data is stored; if - false, the data will be kept on the CPU. Use .to_() to move it to the target device after - loading. - @pre The file must be a Tensor object which is saved by cytnx::Tensor::Save. - @note This function creates a new Tensor and keeps the original Tensor unchanged. See \link - Load_(const std::string &fname, const bool restore_device) Load_() \endlink for loading the - Tensor to the current Tensor. - */ - static Tensor Load(const std::string &fname, const bool restore_device = true); + void to_hdf5(H5::Group &location, const std::string &name = "Tensor") const; /** - * @see Load(const std::string &fname) + * @brief Load Tensor from HDF5 file (inline) + * @param[in] location the HDF5 group where the Tensor will be loaded from. + * @param[in] name the name of the Tensor in the HDF5 file. + * @param[in] restore_device whether to try restoring the device on which the data is stored; if + * false, the data will be kept on the CPU. Use .to_() to move it to the target device after + * loading. + * @warning This function is only available in C++. Use \link Load(const std::string &fname, + * const bool restore_device) Load() \endlink for loading from file in C++ or Python. + * @see to_hdf5(H5::Group &location, const std::string &name) const */ - static Tensor Load(const char *fname, const bool restore_device = true); + void from_hdf5(H5::Group &location, const std::string &name = "Tensor", + const bool restore_device = true); /** - @brief Load Tensor from file and overwrite current instance - @note This function overwrites the existing Tensor. See \link Load(const std::string &fname, - const bool restore_device) Load() \endlink for creating a new Tensor. - @see Load(const std::string &fname, const bool restore_device) - */ - void Load_(const std::string &fname, const bool restore_device = true); + * @brief Save Tensor to binary file + * @param[in] f the output stream where the Tensor will be saved. + * @warning This function is only available in C++. In Python, use pickle for the same binary + * file format. Use \link Save(const std::string &fname) Save() \endlink for saving to file in + * C++ or Python. + * @see from_binary(std::istream &f, const bool restore_device) + */ + void to_binary(std::ostream &f) const; + /** + * @brief Load Tensor from binary file + * @param[in] f the input stream from which the Tensor will be loaded. + * @param[in] restore_device whether to try restoring the device on which the data is stored; if + * false, the data will be kept on the CPU. Use .to_() to move it to the target device after + * loading. + * @warning This function is only available in C++. In Python, use pickle for the same binary + * file format. Use \link Load(const std::string &fname, const bool restore_device) Load() + * \endlink for loading from file in C++ or Python. + * @see to_binary(std::ostream &f) const + */ + void from_binary(std::istream &f, const bool restore_device = true); + /** - * @see Load_(const std::string &fname, const bool restore_device) + * @brief Save current Tensor to the binary file + * @details This function will save the Tensor to the binary file with file + * name \p fname . + * @param fname[in] the file name of the binary file. + * @pre The file name @p fname must be valid. + * @see cytnx::Tensor::Fromfile + * @deprecated This function is deprecated. Please use \ref Save(const std::string &fname) + * instead for saving raw data together with metadata. */ - void Load_(const char *fname, const bool restore_device = true); + [[deprecated("Please use Save(const std::string &fname) instead.")]] void Tofile( + const std::string &fname) const; + // @see Tofile(const std::string &fname) const + [[deprecated("Please use Save(const std::string &fname) instead.")]] void Tofile( + const char *fname) const; + // @see Tofile(const std::string &fname) const + [[deprecated("Please use to_binary(std::ostream &f) instead.")]] void Tofile( + std::fstream &f) const; /** * @brief Load current Tensor from the binary file @@ -413,12 +445,15 @@ namespace cytnx { * 4. The @p Nelem cannot be larger than the number of elements in the binary file. * 5. The file name @p fname must be valid. * @see cytnx::Tensor::Tofile + * @deprecated This function is deprecated. Please use Save/Load functions instead for storing + * raw data together with metadata. */ - static Tensor Fromfile(const std::string &fname, const unsigned int &dtype, - const cytnx_int64 &count = -1, const bool restore_device = true); - static Tensor Fromfile(const char *fname, const unsigned int &dtype, - const cytnx_int64 &count = -1, const bool restore_device = true); - + [[deprecated("Please use Save/Load functions instead.")]] static Tensor Fromfile( + const std::string &fname, const unsigned int &dtype, const cytnx_int64 &count = -1, + const bool restore_device = true); + [[deprecated("Please use Save/Load functions instead.")]] static Tensor Fromfile( + const char *fname, const unsigned int &dtype, const cytnx_int64 &count = -1, + const bool restore_device = true); // static Tensor Frombinary(const std::string &fname); ///@cond diff --git a/include/Type.hpp b/include/Type.hpp index 225c7cdce..a627db87c 100644 --- a/include/Type.hpp +++ b/include/Type.hpp @@ -395,7 +395,7 @@ namespace cytnx { using type_promote_from_gpu_pointer_t = typename type_promote_from_gpu_pointer::type; #endif - H5::DataType to_hdf5_type(unsigned int type_id) const { + H5::DataType dtype_to_hdf5_type(unsigned int type_id) const { switch (type_id) { case Type::Double: return H5::PredType::NATIVE_DOUBLE; @@ -451,6 +451,10 @@ namespace cytnx { cytnx_error_msg(true, "[ERROR] Unsupported Cytnx dtype: %s\n", getname(type_id).c_str()); } } + template + H5::DataType get_hdf5_type(const T& rc) const { + return dtype_to_hdf5_type(cy_typeid(rc)); + } unsigned int from_hdf5_type(const H5::DataType& h5_type) const { if (h5_type == H5::PredType::NATIVE_DOUBLE) return Type::Double; diff --git a/include/UniTensor.hpp b/include/UniTensor.hpp index 84d6b3397..a63c5cf00 100644 --- a/include/UniTensor.hpp +++ b/include/UniTensor.hpp @@ -1,23 +1,26 @@ #ifndef CYTNX_UNITENSOR_H_ #define CYTNX_UNITENSOR_H_ -#include "Type.hpp" -#include "cytnx_error.hpp" -#include "Device.hpp" -#include "Tensor.hpp" -#include "utils/utils.hpp" -#include "intrusive_ptr_base.hpp" +#include +#include +#include #include -#include #include +#include #include -#include -#include -#include -#include "Symmetry.hpp" +#include + +#include "H5Cpp.h" + #include "Bond.hpp" +#include "Device.hpp" #include "Generator.hpp" -#include +#include "Symmetry.hpp" +#include "Tensor.hpp" +#include "Type.hpp" +#include "cytnx_error.hpp" +#include "intrusive_ptr_base.hpp" +#include "utils/utils.hpp" #ifdef BACKEND_TORCH #else @@ -5185,48 +5188,90 @@ namespace cytnx { } /** - @brief Save UniTensor to file - @param[in] fname the file name - @see Load(const std::string &fname, const bool restore_device) - */ + * @brief Save UniTensor to file + * @param[in] fname file name + * @details Save the UniTensor to a file. The file ending should be one of ".h5", ".hdf5", + * ".H5", ".HDF5", ".hdf" to save in HDF5 file format. Otherwise, a binary file format is used. + * @note The common file ending for saving a UniTensor in binary format is ".cytn". + * @warning HDF5 file format is strongly recommended for compatibility with other libraries, + * readability, and future-proofing. + * @see Load(const std::string &fname, const bool restore_device) + */ void Save(const std::string &fname) const; + // @see Save(const std::string &fname) const + void Save(const char *fname) const; /** - @brief Save UniTensor to file - @param[in] fname the file name - @see Load(const char *fname, const bool restore_device) - */ - void Save(const char *fname) const; + * @brief Load UniTensor from file and create new instance + * @param fname[in] file name + * @param[in] restore_device whether to try restoring the device on which the data is stored; if + * false, the data will be kept on the CPU. Use .to_() to move it to the target device after + * loading. + * @pre The file must be a UniTensor object which is saved by cytnx::UniTensor::Save. + * @note This function creates a new UniTensor and keeps the original UniTensor unchanged. See + * \link Load_(const std::string &fname, const bool restore_device) Load_() \endlink for loading + * the UniTensor to the current UniTensor. + * @details For HDF5 file format, one of the file endings ".h5", ".hdf5", ".H5", ".HDF5", ".hdf" + * is expected. For binary format, the common file ending for a UniTensor is ".cytn". + */ + static UniTensor Load(const std::string &fname, const bool restore_device = true); + // @see Load(const std::string &fname, const bool restore_device = true) + static UniTensor Load(const char *fname, const bool restore_device = true); /** - @brief Load UniTensor from file and create new instance - @param fname[in] file name - @param[in] restore_device whether to try restoring the device on which the data is stored; if - false, the data will be kept on the CPU. Use .to_() to move it to the target device after - loading. - @pre The file must be a UniTensor object which is saved by cytnx::UniTensor::Save. - @note This function creates a new UniTensor and keeps the original UniTensor unchanged. See - \link Load_(const std::string &fname, const bool restore_device) Load_() \endlink for loading - the UniTensor to the current UniTensor. - */ + * @brief Load UniTensor from file and overwrite current instance + * @note This function overwrites the existing UniTensor. See \link Load(const std::string + * &fname, const bool restore_device) Load() \endlink for creating a new UniTensor. + * @see Load(const std::string &fname, const bool restore_device) + */ + void Load_(const std::string &fname, const bool restore_device = true); + // @see Load_(const std::string &fname, const bool restore_device) + void Load_(const char *fname, const bool restore_device = true); - static UniTensor Load(const std::string &fname, const bool restore_device = true); /** - * @see Load(const std::string &fname) + * @brief Save UniTensor to HDF5 file + * @param[in] location the HDF5 group where the UniTensor will be saved. + * @param[in] name the name of the UniTensor in the HDF5 file. + * @warning This function is only available in C++. Use \link Save(const std::string &fname) + * Save() \endlink for saving to file in C++ or Python. + * @see from_hdf5(H5::Group &location, const std::string &name, const bool restore_device) */ - static UniTensor Load(const char *fname, const bool restore_device = true); + void to_hdf5(H5::Group &location, const std::string &name = "UniTensor") const; + /** + * @brief Load UniTensor from HDF5 file (inline) + * @param[in] location the HDF5 group where the UniTensor will be loaded from. + * @param[in] name the name of the UniTensor in the HDF5 file. + * @param[in] restore_device whether to try restoring the device on which the data is stored; if + * false, the data will be kept on the CPU. Use .to_() to move it to the target device after + * loading. + * @warning This function is only available in C++. Use \link Load(const std::string &fname, + * const bool restore_device) Load() \endlink for loading from file in C++ or Python. + * @see to_hdf5(H5::Group &location, const std::string &name) const + */ + void from_hdf5(H5::Group &location, const std::string &name = "UniTensor", + const bool restore_device = true); /** - @brief Load UniTensor from file and overwrite current instance - @note This function overwrites the existing UniTensor. See \link Load(const std::string &fname, - const bool restore_device) Load() \endlink for creating a new UniTensor. - @see Load(const std::string &fname, const bool restore_device) - */ - void Load_(const std::string &fname, const bool restore_device = true); + * @brief Save UniTensor to binary file + * @param[in] f the output stream where the UniTensor will be saved. + * @warning This function is only available in C++. In Python, use pickle for the same binary + * file format. Use \link Save(const std::string &fname) Save() \endlink for saving to file in + * C++ or Python. + * @see from_binary(std::istream &f, const bool restore_device) + */ + void to_binary(std::ostream &f) const; /** - * @see Load_(const std::string &fname, const bool restore_device) + * @brief Load UniTensor from binary file + * @param[in] f the input stream from which the UniTensor will be loaded. + * @param[in] restore_device whether to try restoring the device on which the data is stored; if + * false, the data will be kept on the CPU. Use .to_() to move it to the target device after + * loading. + * @warning This function is only available in C++. In Python, use pickle for the same binary + * file format. Use \link Load(const std::string &fname, const bool restore_device) Load() + * \endlink for loading from file in C++ or Python. + * @see to_binary(std::ostream &f) const */ - void Load_(const char *fname, const bool restore_device = true); + void from_binary(std::istream &f, const bool restore_device = true); /** * @brief truncate bond dimension of the UniTensor by the given bond label and dimension. @@ -5314,11 +5359,6 @@ namespace cytnx { const vec2d &get_itoi() const { return this->_impl->get_itoi(); } vec2d &get_itoi() { return this->_impl->get_itoi(); } - /// @cond - void from_binary(std::istream &f, const bool restore_device = true); - void to_binary(std::ostream &f) const; - /// @endcond - UniTensor &convert_from(const UniTensor &rhs, const bool &force = false, const cytnx_double &tol = 1e-14) { this->_impl->from_(rhs._impl, force, tol); diff --git a/include/backend/Storage.hpp b/include/backend/Storage.hpp index ef4d53549..3820adab8 100644 --- a/include/backend/Storage.hpp +++ b/include/backend/Storage.hpp @@ -11,14 +11,14 @@ #include #include + #include "H5Cpp.h" #include "boost/smart_ptr/intrusive_ptr.hpp" + #include "Device.hpp" + #include "Type.hpp" #include "backend/Scalar.hpp" #include "cytnx_error.hpp" - #include "Device.hpp" - #include "H5Cpp.h" #include "intrusive_ptr_base.hpp" - #include "Type.hpp" #define STORAGE_DEFT_SZ 2 @@ -525,75 +525,157 @@ namespace cytnx { ///@endcond - /// @cond + /** + * @brief Save Storage to file + * @param[in] fname file name + * @details Save the Storage to a file. The file ending should be one of ".h5", ".hdf5", ".H5", + * ".HDF5", ".hdf" to save in HDF5 file format. Otherwise, a binary file format is used. + * @note The common file ending for saving a Storage in binary format is ".cyst". + * @warning HDF5 file format is strongly recommended for compatibility with other libraries, + * readability, and future-proofing. + * @see Load(const std::string &fname, const bool restore_device) + */ + void Save(const std::string &fname) const; + // @see Save(const std::string &fname) const + void Save(const char *fname) const; + + /** + * @brief Load Storage from file and create new instance + * @param fname[in] file name + * @param[in] restore_device whether to try restoring the device on which the data is stored; if + * false, the data will be kept on the CPU. Use .to_() to move it to the target device after + * loading. + * @pre The file must be a Storage object which is saved by cytnx::Storage::Save. + * @note This function creates a new Storage and keeps the original Storage unchanged. See \link + * Load_(const std::string &fname, const bool restore_device) Load_() \endlink for loading the + * Storage to the current Storage. + * @details For HDF5 file format, one of the file endings ".h5", ".hdf5", ".H5", ".HDF5", ".hdf" + * is expected. For binary format, the common file ending for a Storage is ".cyst". + */ + static Storage Load(const std::string &fname, const bool restore_device = true); + // @see Load(const std::string &fname, const bool restore_device) + static Storage Load(const char *fname, const bool restore_device = true); + + /** + @brief Load Storage from file and overwrite current instance + @note This function overwrites the existing Storage. See \link Load(const std::string &fname, + const bool restore_device) Load() \endlink for creating a new Storage. + @see Load(const std::string &fname, const bool restore_device) + */ + void Load_(const std::string &fname, const bool restore_device = true); + // @see Load_(const std::string &fname, const bool restore_device) + void Load_(const char *fname, const bool restore_device = true); + + /** + * @brief Save Storage to HDF5 file + * @param[in] location the HDF5 group where the Storage will be saved. + * @param[in] name the name of the Storage in the HDF5 file. + * @warning This function is only available in C++. Use \link Save(const std::string &fname) + * Save() \endlink for saving to file in C++ or Python. + * @see from_hdf5(H5::Group &location, const std::string &name, const bool restore_device) + */ void to_hdf5(H5::Group &location, const std::string &name = "Storage") const; - void from_hdf5(H5::Group &location, const std::string &name = "Tensor", + /** + * @brief Load Storage from HDF5 file (inline) + * @param[in] location the HDF5 group where the Storage will be loaded from. + * @param[in] name the name of the Storage in the HDF5 file. + * @param[in] restore_device whether to try restoring the device on which the data is stored; if + * false, the data will be kept on the CPU. Use .to_() to move it to the target device after + * loading. + * @warning This function is only available in C++. Use \link Load(const std::string &fname, + * const bool restore_device) Load() \endlink for loading from file in C++ or Python. + * @see to_hdf5(H5::Group &location, const std::string &name) const + */ + void from_hdf5(H5::Group &location, const std::string &name = "Storage", const bool restore_device = true); + /** + * @brief Save only the data of the Storage to HDF5 dataset. + * @param[in] dataset the HDF5 dataset where the Storage will be saved. + * @param[in] hdf5type the HDF5 data type for the dataset, must match the data type of the + * Storage. + * @warning This function is only available in C++. Use \link Save(const std::string &fname) + * Save() \endlink for saving to file in C++ or Python. + * @see data_from_hdf5(H5::DataSet &dataset, const cytnx_uint64 &Nelem, const unsigned int + * &dtype, H5::DataType &hdf5type, const int &device) + */ void data_to_hdf5(H5::DataSet &dataset, H5::DataType &hdf5type) const; + /** + * @brief Load only the data of the Storage from an HDF5 dataset, for given Storage parameters. + * @param[in] dataset the HDF5 dataset from which the Storage will be loaded. + * @param[in] Nelem the number of elements to load from the HDF5 dataset. This should match the + * size of the Storage. + * @param[in] dtype the data type of the Storage. See cytnx.Type. This should match the data + * type of the HDF5 dataset. + * @param[in] hdf5type the HDF5 data type for the dataset, must match the data type of the + * Storage. + * @param[in] device the device on which the Storage will be loaded. + * @note This function overwrites the current Storage with a new instance. + * @warning This function is only available in C++. Use \link Save(const std::string &fname) + * Save() \endlink for saving to file in C++ or Python. + * @see data_to_hdf5(H5::DataSet &dataset, H5::DataType &hdf5type) + */ void data_from_hdf5(H5::DataSet &dataset, const cytnx_uint64 &Nelem, const unsigned int &dtype, H5::DataType &hdf5type, const int &device = Device.cpu); + /** + * @brief Save Storage to binary file + * @param[in] f the output stream where the Storage will be saved. + * @warning This function is only available in C++. In Python, use pickle for the same binary + * file format. Use \link Save(const std::string &fname) Save() \endlink for saving to file in + * C++ or Python. + * @see from_binary(std::istream &f, const bool restore_device) + */ void to_binary(std::ostream &f) const; + /** + * @brief Load Storage from binary file + * @param[in] f the input stream from which the Storage will be loaded. + * @param[in] restore_device whether to try restoring the device on which the data is stored; if + * false, the data will be kept on the CPU. Use .to_() to move it to the target device after + * loading. + * @warning This function is only available in C++. In Python, use pickle for the same binary + * file format. Use \link Load(const std::string &fname, const bool restore_device) Load() + * \endlink for loading from file in C++ or Python. + * @see to_binary(std::ostream &f) const + */ void from_binary(std::istream &f, const bool restore_device = true); + /** + * @brief Save only the data of the Storage to binary filestream. + * @param[in] f the output stream where the Storage will be saved. + * @warning This function is only available in C++. Use \link Save(const std::string &fname) + * Save() \endlink for saving to file in C++ or Python. + * @see data_from_binary(std::istream &f, const cytnx_uint64 &Nelem, const unsigned int &dtype, + * const int &device) + */ void data_to_binary(std::ostream &f) const; + /** + * @brief Load Storage from binary file + * @param[in] f the input stream from which the Storage will be loaded. + * @param[in] Nelem the number of elements to load from the dataset. + * @param[in] dtype the data type of the Storage. See cytnx.Type. + * @param[in] device the device on which the Storage will be loaded. + * @note This function overwrites the current Storage with a new instance. + * @warning This function is only available in C++. In Python, use pickle for the same binary + * file format. Use \link Load(const std::string &fname, const bool restore_device) Load() + * \endlink for loading from file in C++ or Python. + * @see data_to_binary(std::ostream &f) const + */ void data_from_binary(std::istream &f, const cytnx_uint64 &Nelem, const unsigned int &dtype, const int &device = Device.cpu); - /// @endcond - - /** - @brief Save current Storage to file - @param[in] fname file name - @details - Save the Storage to file with file path specify with input param \p fname with postfix - ".cyst" - @post The file extension will be ".cyst". - */ - void Save(const std::string &fname) const; - - /** - * @brief Save current Storage to file, same as \ref Save(const std::string &fname) - */ - void Save(const char *fname) const; /** * @brief Save current Storage to a binary file, which only contains the raw data. * @see Fromfile(const std::string &fname, const unsigned int &dtype, const cytnx_int64 &count) + * @deprecated This function is deprecated. Please use \ref Save(const std::string &fname) + * instead for saving raw data together with metadata. */ - void Tofile(const std::string &fname) const; + [[deprecated("Please use Save(const std::string &fname) instead.")]] void Tofile( + const std::string &fname) const; /// @see Tofile(const std::string &fname) const - void Tofile(const char *fname) const; + [[deprecated("Please use Save(const std::string &fname) instead.")]] void Tofile( + const char *fname) const; /// @see Tofile(const std::string &fname) const - void Tofile(std::fstream &f) const; - - /** - @brief Load Storage from file and create new instance - @param fname[in] file name - @param[in] restore_device whether to try restoring the device on which the data is stored; if - false, the data will be kept on the CPU. Use .to_() to move it to the target device after - loading. - @pre The file must be a Storage object which is saved by cytnx::Storage::Save. - @note This function creates a new Storage and keeps the original Storage unchanged. See \link - Load_(const std::string &fname, const bool restore_device) Load_() \endlink for loading the - Storage to the current Storage. - */ - - static Storage Load(const std::string &fname, const bool restore_device = true); - /** - * @see Load(const std::string &fname) - */ - static Storage Load(const char *fname, const bool restore_device = true); - - /** - @brief Load Storage from file and overwrite current instance - @note This function overwrites the existing Storage. See \link Load(const std::string &fname, - const bool restore_device) Load() \endlink for creating a new Storage. - @see Load(const std::string &fname, const bool restore_device) - */ - void Load_(const std::string &fname, const bool restore_device = true); - /** - * @see Load_(const std::string &fname, const bool restore_device) - */ - void Load_(const char *fname, const bool restore_device = true); + [[deprecated("Please use to_binary(std::ostream &f) instead.")]] void Tofile( + std::fstream &f) const; /** * @brief Load the binary file, which only contains the raw data. @@ -613,16 +695,16 @@ namespace cytnx { * 5. The file name @p fname must be valid. * * @see Tofile(const std::string &fname) const + * @deprecated This function is deprecated. Please use Save/Load functions instead for storing + * raw data together with metadata. */ - static Storage Fromfile(const std::string &fname, const unsigned int &dtype, - const cytnx_int64 &count = -1, const bool restore_device = true); - - /** - * @see Fromfile(const std::string &fname, const unsigned int &dtype, const cytnx_int64 &count = - * -1) - */ - static Storage Fromfile(const char *fname, const unsigned int &dtype, - const cytnx_int64 &count = -1, const bool restore_device = true); + [[deprecated("Please use Save/Load functions instead.")]] static Storage Fromfile( + const std::string &fname, const unsigned int &dtype, const cytnx_int64 &count = -1, + const bool restore_device = true); + // @see Fromfile(const std::string &fname, const unsigned int &dtype, const cytnx_int64 &count) + [[deprecated("Please use Save/Load functions instead.")]] static Storage Fromfile( + const char *fname, const unsigned int &dtype, const cytnx_int64 &count = -1, + const bool restore_device = true); /** @brief cast the type of current Storage diff --git a/include/tn_algo/MPS.hpp b/include/tn_algo/MPS.hpp index 5113e8161..ccad5ab00 100644 --- a/include/tn_algo/MPS.hpp +++ b/include/tn_algo/MPS.hpp @@ -1,18 +1,20 @@ #ifndef CYTNX_TN_ALGO_MPS_H_ #define CYTNX_TN_ALGO_MPS_H_ -#include "cytnx_error.hpp" -#include "Device.hpp" -#include "intrusive_ptr_base.hpp" -#include "UniTensor.hpp" -#include #include - -#include "utils/vec_clone.hpp" -#include "Accessor.hpp" -#include #include +#include #include +#include + +#include "H5Cpp.h" + +#include "Accessor.hpp" +#include "Device.hpp" +#include "UniTensor.hpp" +#include "cytnx_error.hpp" +#include "intrusive_ptr_base.hpp" +#include "utils/vec_clone.hpp" #ifdef BACKEND_TORCH #else @@ -286,42 +288,91 @@ namespace cytnx { cytnx_int64 &S_loc() { return this->_impl->S_loc; } - ///@cond - void to_binary(std::ostream &f) const; - void from_binary(std::istream &f, const bool restore_device = true); - ///@endcond - + /** + * @brief Save MPS to file + * @param[in] fname file name + * @details Save the MPS to a file. The file ending should be one of ".h5", ".hdf5", ".H5", + * ".HDF5", ".hdf" to save in HDF5 file format. Otherwise, a binary file format is used. + * @note The common file ending for saving a MPS in binary format is ".cymps". + * @warning HDF5 file format is strongly recommended for compatibility with other libraries, + * readability, and future-proofing. + * @see Load(const std::string &fname, const bool restore_device) + */ void Save(const std::string &fname) const; + // @see Save(const std::string &fname) const void Save(const char *fname) const; /** - @brief Load MPS from file and create new instance - @param fname[in] file name - @param[in] restore_device whether to try restoring the device on which the data is stored; if - false, the data will be kept on the CPU. Use .to_() to move it to the target device after - loading. - @pre The file must be an MPS object which is saved by cytnx::MPS::Save. - @note This function creates a new MPS and keeps the original MPS unchanged. See \link - Load_(const std::string &fname, const bool restore_device) Load_() \endlink for loading the - MPS to the current MPS. - */ - static MPS Load(const std::string &fname, const bool restore_device = true); - /** - * @see Load(const std::string &fname) + * @brief Load MPS from file and create new instance + * @param fname[in] file name + * @param[in] restore_device whether to try restoring the device on which the data is stored; + * if false, the data will be kept on the CPU. Use .to_() to move it to the target device + * after loading. + * @pre The file must be a MPS object which is saved by cytnx::MPS::Save. + * @note This function creates a new MPS and keeps the original MPS unchanged. See \link + * Load_(const std::string &fname, const bool restore_device) Load_() \endlink for loading the + * MPS to the current MPS. + * @details For HDF5 file format, one of the file endings ".h5", ".hdf5", ".H5", ".HDF5", + * ".hdf" is expected. For binary format, the common file ending for a MPS is ".cymps". */ + static MPS Load(const std::string &fname, const bool restore_device = true); + // @see Load(const std::string &fname) static MPS Load(const char *fname, const bool restore_device = true); /** - @brief Load MPS from file and overwrite current instance - @note This function overwrites the existing MPS. See \link Load(const std::string &fname, - const bool restore_device) Load() \endlink for creating a new MPS. - @see Load(const std::string &fname, const bool restore_device) - */ + * @brief Load MPS from file and overwrite current instance + * @note This function overwrites the existing MPS. See \link Load(const std::string &fname, + * const bool restore_device) Load() \endlink for creating a new MPS. + * @see Load(const std::string &fname, const bool restore_device) + */ void Load_(const std::string &fname, const bool restore_device = true); + // @see Load_(const std::string &fname, const bool restore_device) + void Load_(const char *fname, const bool restore_device = true); + /** - * @see Load_(const std::string &fname, const bool restore_device) + * @brief Save MPS to HDF5 file + * @param[in] location the HDF5 group where the MPS will be saved. + * @param[in] name the name of the MPS in the HDF5 file. + * @warning This function is only available in C++. Use \link Save(const std::string &fname) + * Save() \endlink for saving to file in C++ or Python. + * @see from_hdf5(H5::Group &location, const std::string &name, const bool restore_device) */ - void Load_(const char *fname, const bool restore_device = true); + void to_hdf5(H5::Group &location, const std::string &name = "MPS") const; + /** + * @brief Load MPS from HDF5 file (inline) + * @param[in] location the HDF5 group where the MPS will be loaded from. + * @param[in] name the name of the MPS in the HDF5 file. + * @param[in] restore_device whether to try restoring the device on which the data is stored; + * if false, the data will be kept on the CPU. Use .to_() to move it to the target device + * after loading. + * @warning This function is only available in C++. Use \link Load(const std::string &fname, + * const bool restore_device) Load() \endlink for loading from file in C++ or Python. + * @see to_hdf5(H5::Group &location, const std::string &name) const + */ + void from_hdf5(H5::Group &location, const std::string &name = "MPS", + const bool restore_device = true); + + /** + * @brief Save MPS to binary file + * @param[in] f the output stream where the MPS will be saved. + * @warning This function is only available in C++. In Python, use pickle for the same binary + * file format. Use \link Save(const std::string &fname) Save() \endlink for saving to file in + * C++ or Python. + * @see from_binary(std::istream &f, const bool restore_device) + */ + void to_binary(std::ostream &f) const; + /** + * @brief Load MPS from binary file + * @param[in] f the input stream from which the MPS will be loaded. + * @param[in] restore_device whether to try restoring the device on which the data is stored; + * if false, the data will be kept on the CPU. Use .to_() to move it to the target device + * after loading. + * @warning This function is only available in C++. In Python, use pickle for the same binary + * file format. Use \link Load(const std::string &fname, const bool restore_device) Load() + * \endlink for loading from file in C++ or Python. + * @see to_binary(std::ostream &f) const + */ + void from_binary(std::istream &f, const bool restore_device = true); }; std::ostream &operator<<(std::ostream &os, const MPS &in); diff --git a/pybind/storage_py.cpp b/pybind/storage_py.cpp index 0af4a115b..b63bfb977 100644 --- a/pybind/storage_py.cpp +++ b/pybind/storage_py.cpp @@ -1,19 +1,20 @@ +#include "cytnx.hpp" + #include #include #include #include -#include -#include -#include -#include -#include #include #include +#include +#include +#include +#include +#include -#include "cytnx.hpp" -// #include "../include/cytnx_error.hpp" #include "complex.h" +#include "H5Cpp.h" namespace py = pybind11; using namespace pybind11::literals; diff --git a/pybind/symmetry_py.cpp b/pybind/symmetry_py.cpp index 5711c8ee9..3f2cdc358 100644 --- a/pybind/symmetry_py.cpp +++ b/pybind/symmetry_py.cpp @@ -45,6 +45,7 @@ void symmetry_binding(py::module &m) { .def_static("FermionNumber", &Symmetry::FermionNumber) .def("clone", &Symmetry::clone) .def("stype", &Symmetry::stype) + .def("name", &Symmetry::name) .def("stype_str", &Symmetry::stype_str) .def("n", &Symmetry::n) .def("clone", &Symmetry::clone) diff --git a/pybind/tensor_py.cpp b/pybind/tensor_py.cpp index 8ce0b736f..bd1b7a310 100644 --- a/pybind/tensor_py.cpp +++ b/pybind/tensor_py.cpp @@ -1,18 +1,19 @@ -#include +#include "cytnx.hpp" + #include #include +#include -#include -#include -#include -#include -#include #include #include +#include +#include +#include +#include +#include -#include "cytnx.hpp" -// #include "../include/cytnx_error.hpp" #include "complex.h" +#include "H5Cpp.h" namespace py = pybind11; using namespace pybind11::literals; @@ -305,14 +306,28 @@ void tensor_binding(py::module &m) { .def( "to_hdf5", - [](cytnx::Tensor &self, H5::Group &location, const std::string &name) { - self.to_hdf5(location, name); + [](cytnx::Tensor &self, py::object location, const std::string &name) { + // Extract raw ID from the h5py object's .id.id attribute + hid_t raw_id = location.attr("id").attr("id").cast(); + if (H5Iinc_ref(raw_id) < 0) { + throw std::runtime_error("Failed to increment HDF5 reference count."); + } + H5::Group cpplocation(raw_id); + self.to_hdf5(cpplocation, name); }, py::arg("location"), py::arg("name") = "Tensor") .def( "from_hdf5", - [](cytnx::Tensor &self, H5::Group &location, const std::string &name, - const bool restore_device) { self.from_hdf5(location, name, restore_device); }, + [](cytnx::Tensor &self, py::object location, const std::string &name, + const bool restore_device) { + // Extract raw ID from the h5py object's .id.id attribute + hid_t raw_id = location.attr("id").attr("id").cast(); + if (H5Iinc_ref(raw_id) < 0) { + throw std::runtime_error("Failed to increment HDF5 reference count."); + } + H5::Group cpplocation(raw_id); + self.from_hdf5(cpplocation, name, restore_device); + }, py::arg("location"), py::arg("name") = "Tensor", py::arg("restore_device") = true) .def(py::pickle( diff --git a/src/Bond.cpp b/src/Bond.cpp index 59bab63d0..073a4e6c5 100644 --- a/src/Bond.cpp +++ b/src/Bond.cpp @@ -4,6 +4,7 @@ #include #include +#include "H5Cpp.h" #include "utils/utils.hpp" using namespace std; @@ -472,14 +473,34 @@ namespace cytnx { bool Bond::operator!=(const Bond &rhs) const { return !(*this == rhs); } void Bond::Save(const std::string &fname) const { - fstream f; + fstream f; // only for binary saving, not used for hdf5 if (std::filesystem::path(fname).has_extension()) { // filename extension is given - f.open(fname, ios::out | ios::trunc | ios::binary); - } else { - // add filename extension + auto ext = std::filesystem::path(fname).extension().string(); + if (ext == ".h5" || ext == ".hdf5" || ext == ".H5" || ext == ".HDF5" || ext == ".hdf" || + ext == ".HDF") { + // save as hdf5 + H5::H5File h5file; + try { + h5file = H5::H5File(fname, H5F_ACC_TRUNC); + } catch (H5::FileIException &error) { + error.printErrorStack(); + cytnx_error_msg(true, "[ERROR] Cannot create HDF5 file '%s'.\n", fname.c_str()); + } + this->to_hdf5(h5file); + h5file.close(); + return; + } else { // create binary file + f.open(fname, ios::out | ios::trunc | ios::binary); + } + } else { // create binary file with standard extension + cytnx_warning_msg(true, + "Missing file extension in fname '%s'. I am adding the extension '.cybd'. " + "This is deprecated, please provide the file extension in the future.\n", + fname.c_str()); f.open((fname + ".cybd"), ios::out | ios::trunc | ios::binary); } + // write binary if (!f.is_open()) { cytnx_error_msg(true, "[ERROR] invalid file path for save.%s", "\n"); } @@ -496,16 +517,107 @@ namespace cytnx { Bond Bond::Load(const char *fname) { return Bond::Load(string(fname)); } void Bond::Load_(const std::string &fname) { - fstream f; - f.open(fname, ios::in | ios::binary); - if (!f.is_open()) { - cytnx_error_msg(true, "[ERROR] Cannot open file '%s'.\n", fname.c_str()); + auto ext = std::filesystem::path(fname).extension().string(); + if (ext == ".h5" || ext == ".hdf5" || ext == ".H5" || ext == ".HDF5" || ext == ".hdf" || + ext == ".HDF") { + // load hdf5 + H5::H5File h5file; + try { + h5file = H5::H5File(fname, H5F_ACC_RDONLY); + } catch (H5::FileIException &error) { + error.printErrorStack(); + cytnx_error_msg(true, "[ERROR] Cannot open HDF5 file '%s'.\n", fname.c_str()); + } + this->from_hdf5(h5file); + h5file.close(); + } else { // load binary + fstream f; + f.open(fname, ios::in | ios::binary); + if (!f.is_open()) { + cytnx_error_msg(true, "[ERROR] Cannot open file '%s'.\n", fname.c_str()); + } + this->from_binary(f); + f.close(); } - this->from_binary(f); - f.close(); } void Bond::Load_(const char *fname) { this->Load_(string(fname)); } + void Bond::to_hdf5(H5::Group &location, const bool save_symmetries) const { + H5::DataType datatype; + H5::Attribute attr; + H5::DataSet dataset; + H5::DataSpace dataspace; + H5::StrType str_type; + + // // dimension, write as attribute + auto dim = this->_impl->_dim; + datatype = Type.get_hdf5_type(dim); + attr = location.createAttribute("dimension", datatype, H5::DataSpace(H5S_SCALAR)); + attr.write(H5::PredType::NATIVE_INT, &dim); + + // type, write as string + std::string typestr = bondtype_to_string.at(this->_impl->_type); + str_type = H5::StrType(H5::PredType::C_S1, typestr.length() + 1); + dataspace = H5::DataSpace(H5S_SCALAR); + attr = location.createAttribute("type", str_type, dataspace); + attr.write(str_type, typestr); + + // degs; write vector + hsize_t vecdims[1] = {this->_impl->_degs.size()}; + cytnx_error_msg(vecdims[0] < 1, + "[ERROR] Degeneracy vector has length < 1, something went wrong here!%s", "\n") + dataspace = H5::DataSpace(1, vecdims); + datatype = Type.get_hdf5_type(this->_impl->_degs[0]); + dataset = + location.createDataSet("degeneracies", Type.get_hdf5_type(this->_impl->_degs[0]), dataspace); + dataset.write(this->_impl->_degs.data(), datatype); + + // qnums; write matrix (dim x qnumdim) + size_t sectordim = this->_impl->_qnums.size(); + size_t qnumdim = this->_impl->_syms.size(); + + std::vector flat(sectordim * qnumdim); // flatten vector + for (size_t i = 0; i < sectordim; ++i) { + std::copy(this->_impl->_qnums[i].begin(), this->_impl->_qnums[i].end(), + flat.begin() + i * qnumdim); + } + hsize_t matdims[2] = {sectordim, qnumdim}; + dataspace = H5::DataSpace(2, matdims); + datatype = Type.get_hdf5_type(flat[0]); + dataset = location.createDataSet("quantum_numbers", datatype, dataspace); + dataset.write(flat.data(), datatype); + // label axes + char labels[2][9] = {"sector", "symmetry"}; + str_type = H5::StrType(H5::PredType::C_S1, 9); + hsize_t attr_dims[1] = {2}; + H5::DataSpace attr_space(1, attr_dims); + attr = dataset.createAttribute("axis_labels", str_type, attr_space); + attr.write(str_type, labels); + + // symmetries + if (save_symmetries) { + // vecdims[1] = { this->_impl->_syms.size() }; + // dataspace = H5::DataSpace(1, vecdims); + // str_type = H5::StrType(H5::PredType::C_S1, H5T_VARIABLE); + // dataset = location.createDataSet("symmetries", str_type, dataspace); + + // std::vector c_strings; + // std::string symstring; + // for (const auto& s : this->_impl->_syms) { + // symstring = s.name(); + // c_strings.push_back(symstring.c_str()); + // } + // dataset.write(c_strings.data(), str_type); + H5::Group symloc = location.createGroup("Symmetries"); + for (int sidx = 0; sidx < this->_impl->_syms.size(); sidx++) { + this->_impl->_syms[sidx].to_hdf5(symloc, "Symmetry" + std::to_string(sidx)); + } + } + } + void Bond::from_hdf5(H5::Group &location) { + cytnx_error_msg(true, "[ERROR] Loading Bond from HDF5 is not implemented yet!%s", "\n"); + } + void Bond::to_binary(std::ostream &f) const { unsigned int IDDs = 666; f.write((char *)&IDDs, sizeof(unsigned int)); diff --git a/src/Symmetry.cpp b/src/Symmetry.cpp index 41ba6defd..49b7df19a 100644 --- a/src/Symmetry.cpp +++ b/src/Symmetry.cpp @@ -6,6 +6,8 @@ #include #include +#include "H5Cpp.h" + using namespace std; namespace cytnx { @@ -64,6 +66,10 @@ namespace cytnx { cytnx_error_msg(1, "%s", "[ERROR][Internal] should not call Symmerty base!"); } + std::string cytnx::Symmetry_base::name() const { + cytnx_error_msg(1, "%s", "[ERROR][Internal] should not call Symmerty base!"); + } + std::string cytnx::Symmetry_base::stype_str() const { cytnx_error_msg(1, "%s", "[ERROR][Internal] should not call Symmerty base!"); } @@ -190,7 +196,7 @@ namespace cytnx { cout << "--------------------\n"; cout << "[Symmetry]" << endl; cout << "type : fermionic, FermionParity" << endl; - cout << "combine rule : (Q1 + Q2)\2" << endl; + cout << "combine rule : (Q1 + Q2)\%2" << endl; cout << "reverse rule : Q*(-1) " << endl; cout << "--------------------\n"; } @@ -240,18 +246,34 @@ namespace cytnx { //================================================== void cytnx::Symmetry::Save(const std::string &fname) const { - fstream f; + fstream f; // only for binary saving, not used for hdf5 if (std::filesystem::path(fname).has_extension()) { // filename extension is given - f.open(fname, ios::out | ios::trunc | ios::binary); - } else { - // add filename extension + auto ext = std::filesystem::path(fname).extension().string(); + if (ext == ".h5" || ext == ".hdf5" || ext == ".H5" || ext == ".HDF5" || ext == ".hdf" || + ext == ".HDF") { + // save as hdf5 + H5::H5File h5file; + try { + h5file = H5::H5File(fname, H5F_ACC_TRUNC); + } catch (H5::FileIException &error) { + error.printErrorStack(); + cytnx_error_msg(true, "[ERROR] Cannot create HDF5 file '%s'.\n", fname.c_str()); + } + this->to_hdf5(h5file); + h5file.close(); + return; + } else { // create binary file + f.open(fname, ios::out | ios::trunc | ios::binary); + } + } else { // create binary file with standard extension cytnx_warning_msg(true, "Missing file extension in fname '%s'. I am adding the extension '.cysym'. " "This is deprecated, please provide the file extension in the future.\n", fname.c_str()); f.open((fname + ".cysym"), ios::out | ios::trunc | ios::binary); } + // write binary if (!f.is_open()) { cytnx_error_msg(true, "[ERROR] invalid file path for save.%s", "\n"); } @@ -270,17 +292,42 @@ namespace cytnx { } void cytnx::Symmetry::Load_(const std::string &fname) { - fstream f; - f.open(fname, ios::in | ios::binary); - if (!f.is_open()) { - cytnx_error_msg(true, "[ERROR] Cannot open file '%s'.\n", fname.c_str()); + auto ext = std::filesystem::path(fname).extension().string(); + if (ext == ".h5" || ext == ".hdf5" || ext == ".H5" || ext == ".HDF5" || ext == ".hdf" || + ext == ".HDF") { + // load hdf5 + H5::H5File h5file; + try { + h5file = H5::H5File(fname, H5F_ACC_RDONLY); + } catch (H5::FileIException &error) { + error.printErrorStack(); + cytnx_error_msg(true, "[ERROR] Cannot open HDF5 file '%s'.\n", fname.c_str()); + } + this->from_hdf5(h5file); + h5file.close(); + } else { // load binary + fstream f; + f.open(fname, ios::in | ios::binary); + if (!f.is_open()) { + cytnx_error_msg(true, "[ERROR] Cannot open file '%s'.\n", fname.c_str()); + } + this->from_binary(f); + f.close(); } - this->from_binary(f); - f.close(); } void cytnx::Symmetry::Load_(const char *fname) { this->Load_(string(fname)); } - //================== + void cytnx::Symmetry::to_hdf5(H5::Group &location, const std::string &name) const { + std::string typestr = this->name(); + H5::StrType str_type(H5::PredType::C_S1, typestr.length() + 1); + H5::DataSpace dataspace = H5::DataSpace(H5S_SCALAR); + H5::Attribute attr = location.createAttribute(name, str_type, dataspace); + attr.write(str_type, typestr); + } + void cytnx::Symmetry::from_hdf5(H5::Group &location, const std::string &name) { + cytnx_error_msg(true, "[ERROR] Loading Symmetry from HDF5 is not implemented yet!%s", "\n"); + } + void cytnx::Symmetry::to_binary(std::ostream &f) const { unsigned int IDDs = 777; f.write((char *)&IDDs, sizeof(unsigned int)); diff --git a/src/Tensor.cpp b/src/Tensor.cpp index 8d19edb26..097094bf5 100644 --- a/src/Tensor.cpp +++ b/src/Tensor.cpp @@ -449,6 +449,7 @@ namespace cytnx { this->storage().Tofile(f); } } + void Tensor::Save(const std::string &fname) const { fstream f; // only for binary saving, not used for hdf5 if (std::filesystem::path(fname).has_extension()) { @@ -491,7 +492,7 @@ namespace cytnx { std::vector dims(this->shape().begin(), this->shape().end()); H5::DataSpace dataspace(dims.size(), dims.data()); - H5::DataType datatype = Type.to_hdf5_type(this->dtype()); + H5::DataType datatype = Type.dtype_to_hdf5_type(this->dtype()); H5::DataSet dataset = location.createDataSet(name, datatype, dataspace); ten.storage().data_to_hdf5(dataset, datatype); diff --git a/src/UniTensor.cpp b/src/UniTensor.cpp index 41191b751..d2213737f 100644 --- a/src/UniTensor.cpp +++ b/src/UniTensor.cpp @@ -3,9 +3,11 @@ #include #include -#include "utils/utils.hpp" +#include "H5Cpp.h" + #include "linalg.hpp" #include "random.hpp" +#include "utils/utils.hpp" using namespace std; @@ -41,6 +43,88 @@ namespace cytnx { UniTensor UniTensor::Mul(const UniTensor &rhs) const { return cytnx::linalg::Mul(*this, rhs); } UniTensor UniTensor::Mul(const Scalar &rhs) const { return cytnx::linalg::Mul(*this, rhs); } + void UniTensor::Save(const std::string &fname) const { + fstream f; // only for binary saving, not used for hdf5 + if (std::filesystem::path(fname).has_extension()) { + // filename extension is given + auto ext = std::filesystem::path(fname).extension().string(); + if (ext == ".h5" || ext == ".hdf5" || ext == ".H5" || ext == ".HDF5" || ext == ".hdf" || + ext == ".HDF") { + // save as hdf5 + H5::H5File h5file; + try { + h5file = H5::H5File(fname, H5F_ACC_TRUNC); + } catch (H5::FileIException &error) { + error.printErrorStack(); + cytnx_error_msg(true, "[ERROR] Cannot create HDF5 file '%s'.\n", fname.c_str()); + } + this->to_hdf5(h5file); + h5file.close(); + return; + } else { // create binary file + f.open(fname, ios::out | ios::trunc | ios::binary); + } + } else { // create binary file with standard extension + cytnx_warning_msg(true, + "Missing file extension in fname '%s'. I am adding the extension '.cytnx'. " + "This is deprecated, please provide the file extension in the future.\n", + fname.c_str()); + f.open((fname + ".cytnx"), ios::out | ios::trunc | ios::binary); + } + // write binary + if (!f.is_open()) { + cytnx_error_msg(true, "[ERROR] invalid file path for save.%s", "\n"); + } + this->to_binary(f); + f.close(); + } + void UniTensor::Save(const char *fname) const { Save(string(fname)); } + + UniTensor UniTensor::Load(const std::string &fname, const bool restore_device) { + UniTensor out; + out.Load_(fname, restore_device); + return out; + } + UniTensor UniTensor::Load(const char *fname, const bool restore_device) { + return UniTensor::Load(string(fname), restore_device); + } + + void UniTensor::Load_(const std::string &fname, const bool restore_device) { + auto ext = std::filesystem::path(fname).extension().string(); + if (ext == ".h5" || ext == ".hdf5" || ext == ".H5" || ext == ".HDF5" || ext == ".hdf" || + ext == ".HDF") { + // load hdf5 + H5::H5File h5file; + try { + h5file = H5::H5File(fname, H5F_ACC_RDONLY); + } catch (H5::FileIException &error) { + error.printErrorStack(); + cytnx_error_msg(true, "[ERROR] Cannot open HDF5 file '%s'.\n", fname.c_str()); + } + this->from_hdf5(h5file, "Tensor", restore_device); + h5file.close(); + } else { // load binary + fstream f; + f.open(fname, ios::in | ios::binary); + if (!f.is_open()) { + cytnx_error_msg(true, "[ERROR] Cannot open file '%s'.\n", fname.c_str()); + } + this->from_binary(f, restore_device); + f.close(); + } + } + void UniTensor::Load_(const char *fname, const bool restore_device) { + this->Load_(string(fname), restore_device); + } + + void UniTensor::to_hdf5(H5::Group &location, const std::string &name) const { + cytnx_error_msg(true, "[ERROR] Saving UniTensor to HDF5 is not implemented yet!%s", "\n"); + } + void UniTensor::from_hdf5(H5::Group &location, const std::string &name, + const bool restore_device) { + cytnx_error_msg(true, "[ERROR] Loading UniTensor from HDF5 is not implemented yet!%s", "\n"); + } + void UniTensor::to_binary(std::ostream &f) const { cytnx_error_msg(this->_impl->uten_type_id == UTenType.Void, "[ERROR][UniTensor] cannot save an uninitialized UniTensor.%s", "\n"); @@ -153,49 +237,6 @@ namespace cytnx { this->_impl->from_binary_dispatch(f, restore_device); } - void UniTensor::Save(const std::string &fname) const { - fstream f; - if (std::filesystem::path(fname).has_extension()) { - // filename extension is given - f.open(fname, ios::out | ios::trunc | ios::binary); - } else { - // add filename extension - cytnx_warning_msg(true, - "Missing file extension in fname '%s'. I am adding the extension '.cytnx'. " - "This is deprecated, please provide the file extension in the future.\n", - fname.c_str()); - f.open((fname + ".cytnx"), ios::out | ios::trunc | ios::binary); - } - if (!f.is_open()) { - cytnx_error_msg(true, "[ERROR] invalid file path for save.%s", "\n"); - } - this->to_binary(f); - f.close(); - } - void UniTensor::Save(const char *fname) const { Save(string(fname)); } - - UniTensor UniTensor::Load(const std::string &fname, const bool restore_device) { - UniTensor out; - out.Load_(fname, restore_device); - return out; - } - UniTensor UniTensor::Load(const char *fname, const bool restore_device) { - return UniTensor::Load(string(fname), restore_device); - } - - void UniTensor::Load_(const std::string &fname, const bool restore_device) { - fstream f; - f.open(fname, ios::in | ios::binary); - if (!f.is_open()) { - cytnx_error_msg(true, "[ERROR] invalid file path for load. >> %s\n", fname.c_str()); - } - this->from_binary(f, restore_device); - f.close(); - } - void UniTensor::Load_(const char *fname, const bool restore_device) { - this->Load_(string(fname), restore_device); - } - // Random Generators: UniTensor UniTensor::normal(const cytnx_uint64 &Nelem, const double &mean, const double &std, const std::vector &in_labels, const unsigned int &seed, diff --git a/src/backend/Storage.cpp b/src/backend/Storage.cpp index ede4db757..5f9ae40bf 100644 --- a/src/backend/Storage.cpp +++ b/src/backend/Storage.cpp @@ -150,7 +150,7 @@ namespace cytnx { void Storage::to_hdf5(H5::Group &location, const std::string &name) const { hsize_t Nelem = this->size(); H5::DataSpace dataspace(1, &Nelem); - H5::DataType datatype = Type.to_hdf5_type(this->dtype()); + H5::DataType datatype = Type.dtype_to_hdf5_type(this->dtype()); H5::DataSet dataset = location.createDataSet(name, datatype, dataspace); this->data_to_hdf5(dataset, datatype); if (this->device() != Device.cpu) { diff --git a/src/tn_algo/MPS.cpp b/src/tn_algo/MPS.cpp index 6f87b7d2d..5d3ba881d 100644 --- a/src/tn_algo/MPS.cpp +++ b/src/tn_algo/MPS.cpp @@ -4,6 +4,8 @@ #include #include +#include "H5Cpp.h" + using namespace std; #ifdef BACKEND_TORCH @@ -61,12 +63,27 @@ namespace cytnx { } void MPS::Save(const std::string& fname) const { - fstream f; + fstream f; // only for binary saving, not used for hdf5 if (std::filesystem::path(fname).has_extension()) { // filename extension is given - f.open(fname, ios::out | ios::trunc | ios::binary); - } else { - // add filename extension + auto ext = std::filesystem::path(fname).extension().string(); + if (ext == ".h5" || ext == ".hdf5" || ext == ".H5" || ext == ".HDF5" || ext == ".hdf" || + ext == ".HDF") { + // save as hdf5 + H5::H5File h5file; + try { + h5file = H5::H5File(fname, H5F_ACC_TRUNC); + } catch (H5::FileIException& error) { + error.printErrorStack(); + cytnx_error_msg(true, "[ERROR] Cannot create HDF5 file '%s'.\n", fname.c_str()); + } + this->to_hdf5(h5file); + h5file.close(); + return; + } else { // create binary file + f.open(fname, ios::out | ios::trunc | ios::binary); + } + } else { // create binary file with standard extension cytnx_warning_msg( true, "Missing file extension in fname '%s'. I am adding the extension '.cymps'. This is " @@ -74,22 +91,14 @@ namespace cytnx { fname.c_str()); f.open((fname + ".cymps"), ios::out | ios::trunc | ios::binary); } + // write binary if (!f.is_open()) { cytnx_error_msg(true, "[ERROR] invalid file path for save.%s", "\n"); } this->to_binary(f); f.close(); } - void MPS::Save(const char* fname) const { - fstream f; - string ffname = string(fname) + ".cymps"; - f.open((ffname), ios::out | ios::trunc | ios::binary); - if (!f.is_open()) { - cytnx_error_msg(true, "[ERROR] invalid file path for save.%s", "\n"); - } - this->to_binary(f); - f.close(); - } + void MPS::Save(const char* fname) const { this->Save(string(fname)); } MPS MPS::Load(const std::string& fname, const bool restore_device) { MPS out; @@ -101,18 +110,40 @@ namespace cytnx { } void MPS::Load_(const std::string& fname, const bool restore_device) { - fstream f; - f.open(fname, ios::in | ios::binary); - if (!f.is_open()) { - cytnx_error_msg(true, "[ERROR] Cannot open file '%s'.\n", fname.c_str()); + auto ext = std::filesystem::path(fname).extension().string(); + if (ext == ".h5" || ext == ".hdf5" || ext == ".H5" || ext == ".HDF5" || ext == ".hdf" || + ext == ".HDF") { + // load hdf5 + H5::H5File h5file; + try { + h5file = H5::H5File(fname, H5F_ACC_RDONLY); + } catch (H5::FileIException& error) { + error.printErrorStack(); + cytnx_error_msg(true, "[ERROR] Cannot open HDF5 file '%s'.\n", fname.c_str()); + } + this->from_hdf5(h5file); + h5file.close(); + } else { // load binary + fstream f; + f.open(fname, ios::in | ios::binary); + if (!f.is_open()) { + cytnx_error_msg(true, "[ERROR] Cannot open file '%s'.\n", fname.c_str()); + } + this->from_binary(f); + f.close(); } - this->from_binary(f, restore_device); - f.close(); } void MPS::Load_(const char* fname, const bool restore_device) { this->Load(string(fname), restore_device); } + void MPS::to_hdf5(H5::Group& location, const std::string& name) const { + cytnx_error_msg(true, "[ERROR] Saving MPS to HDF5 is not implemented yet!%s", "\n"); + } + void MPS::from_hdf5(H5::Group& location, const std::string& name, const bool restore_device) { + cytnx_error_msg(true, "[ERROR] Loading MPS from HDF5 is not implemented yet!%s", "\n"); + } + } // namespace tn_algo } // namespace cytnx From 3d36e25996645a606613d6d63cc4a812fedffca6 Mon Sep 17 00:00:00 2001 From: Manuel Schneider Date: Wed, 29 Apr 2026 22:58:36 +0800 Subject: [PATCH 09/40] HDF5 support for Bond implemented --- include/Bond.hpp | 6 +- include/Symmetry.hpp | 31 ++++- src/Bond.cpp | 243 +++++++++++++++++++++++++++++++--------- src/Symmetry.cpp | 14 ++- src/Tensor.cpp | 7 +- src/backend/Storage.cpp | 7 +- 6 files changed, 248 insertions(+), 60 deletions(-) diff --git a/include/Bond.hpp b/include/Bond.hpp index a3085d591..34d3bd42d 100644 --- a/include/Bond.hpp +++ b/include/Bond.hpp @@ -41,6 +41,8 @@ namespace cytnx { }; static const std::map bondtype_to_string = { {BD_REG, "REG"}, {BD_IN, "IN"}, {BD_OUT, "OUT"}}; + static const std::map string_to_bondtype = { + {"REG", BD_REG}, {"IN", BD_IN}, {"OUT", BD_OUT}}; /// @cond class Bond_impl : public intrusive_ptr_base { @@ -905,11 +907,13 @@ namespace cytnx { /** * @brief Load Bond from HDF5 file (inline) * @param[in] location the HDF5 group where the Bond will be loaded from. + * @param[in] syms vector containing the Symmetries. Leave emtpy to read the Symmetries from + * HDF5 * @warning This function is only available in C++. Use \link Load(const std::string &fname, * const bool restore_device) Load() \endlink for loading from file in C++ or Python. * @see to_hdf5(H5::Group &location, const std::string &name) const */ - void from_hdf5(H5::Group &location); + void from_hdf5(H5::Group &location, const std::vector &syms = {}); /** * @brief Save Bond to binary file diff --git a/include/Symmetry.hpp b/include/Symmetry.hpp index 165081c67..8ab14007f 100644 --- a/include/Symmetry.hpp +++ b/include/Symmetry.hpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -233,9 +234,35 @@ namespace cytnx { boost::intrusive_ptr tmp(new FermionNumberSymmetry()); this->_impl = tmp; } else { - cytnx_error_msg(1, "%s", "[ERROR] invalid symmetry type."); + cytnx_error_msg(true, "[ERROR] Invalid Symmetry type %d.\n", stype); } } + void Init(const std::string name) { + if (name == "U1") { + boost::intrusive_ptr tmp(new U1Symmetry(1)); + this->_impl = tmp; + } else if (name == "fermion parity") { + boost::intrusive_ptr tmp(new FermionParitySymmetry()); + this->_impl = tmp; + } else if (name == "fermion number") { + boost::intrusive_ptr tmp(new FermionNumberSymmetry()); + this->_impl = tmp; + } else { // check if Z(N) + std::regex pattern(R"(Z(\d+))"); + std::smatch matches; + if (std::regex_match(name, matches, pattern)) { + // matches[0] is the whole string "U(456)" + // matches[1] is the first capture group "456" + int n = std::stoi(matches[1].str()); + boost::intrusive_ptr tmp(new ZnSymmetry(n)); + this->_impl = tmp; + } else { + cytnx_error_msg(true, "[ERROR] No Symmetry type matches the string '%s'.\n", + name.c_str()); + } + } + } + Symmetry &operator=(const Symmetry &rhs) { this->_impl = rhs._impl; return *this; @@ -243,7 +270,7 @@ namespace cytnx { Symmetry(const Symmetry &rhs) { this->_impl = rhs._impl; } ///@endcond - //[genenrators] + //[generators] /** @brief create a U1 symmetry object diff --git a/src/Bond.cpp b/src/Bond.cpp index 073a4e6c5..45fa69bdc 100644 --- a/src/Bond.cpp +++ b/src/Bond.cpp @@ -330,7 +330,6 @@ namespace cytnx { } else { idx_erase.push_back(q); tmp_degs[loc] += tmp_degs[q]; - // std::cout << "add from loc" << q << " to " << cnt << std::endl; } return_order[mapper[q]] = cnt; } @@ -549,7 +548,7 @@ namespace cytnx { H5::DataSpace dataspace; H5::StrType str_type; - // // dimension, write as attribute + // dimension, write as attribute auto dim = this->_impl->_dim; datatype = Type.get_hdf5_type(dim); attr = location.createAttribute("dimension", datatype, H5::DataSpace(H5S_SCALAR)); @@ -563,59 +562,201 @@ namespace cytnx { attr.write(str_type, typestr); // degs; write vector - hsize_t vecdims[1] = {this->_impl->_degs.size()}; - cytnx_error_msg(vecdims[0] < 1, - "[ERROR] Degeneracy vector has length < 1, something went wrong here!%s", "\n") + hsize_t sectordim = this->_impl->_qnums.size(); + if (sectordim > 0) { // only with symmetries + hsize_t vecdims[1] = {sectordim}; dataspace = H5::DataSpace(1, vecdims); - datatype = Type.get_hdf5_type(this->_impl->_degs[0]); - dataset = - location.createDataSet("degeneracies", Type.get_hdf5_type(this->_impl->_degs[0]), dataspace); - dataset.write(this->_impl->_degs.data(), datatype); - - // qnums; write matrix (dim x qnumdim) - size_t sectordim = this->_impl->_qnums.size(); - size_t qnumdim = this->_impl->_syms.size(); - - std::vector flat(sectordim * qnumdim); // flatten vector - for (size_t i = 0; i < sectordim; ++i) { - std::copy(this->_impl->_qnums[i].begin(), this->_impl->_qnums[i].end(), - flat.begin() + i * qnumdim); - } - hsize_t matdims[2] = {sectordim, qnumdim}; - dataspace = H5::DataSpace(2, matdims); - datatype = Type.get_hdf5_type(flat[0]); - dataset = location.createDataSet("quantum_numbers", datatype, dataspace); - dataset.write(flat.data(), datatype); - // label axes - char labels[2][9] = {"sector", "symmetry"}; - str_type = H5::StrType(H5::PredType::C_S1, 9); - hsize_t attr_dims[1] = {2}; - H5::DataSpace attr_space(1, attr_dims); - attr = dataset.createAttribute("axis_labels", str_type, attr_space); - attr.write(str_type, labels); - - // symmetries - if (save_symmetries) { - // vecdims[1] = { this->_impl->_syms.size() }; - // dataspace = H5::DataSpace(1, vecdims); - // str_type = H5::StrType(H5::PredType::C_S1, H5T_VARIABLE); - // dataset = location.createDataSet("symmetries", str_type, dataspace); - - // std::vector c_strings; - // std::string symstring; - // for (const auto& s : this->_impl->_syms) { - // symstring = s.name(); - // c_strings.push_back(symstring.c_str()); - // } - // dataset.write(c_strings.data(), str_type); - H5::Group symloc = location.createGroup("Symmetries"); - for (int sidx = 0; sidx < this->_impl->_syms.size(); sidx++) { - this->_impl->_syms[sidx].to_hdf5(symloc, "Symmetry" + std::to_string(sidx)); + datatype = Type.get_hdf5_type(this->_impl->_degs[0]); + dataset = location.createDataSet("degeneracies", Type.get_hdf5_type(this->_impl->_degs[0]), + dataspace); + dataset.write(this->_impl->_degs.data(), datatype); + + // qnums; write matrix (dim x qnumdim) + hsize_t qnumdim = this->_impl->_syms.size(); + std::vector flat(sectordim * qnumdim); // flatten vector + for (hsize_t i = 0; i < sectordim; ++i) { + std::copy(this->_impl->_qnums[i].begin(), this->_impl->_qnums[i].end(), + flat.begin() + i * qnumdim); + } + hsize_t matdims[2] = {sectordim, qnumdim}; + dataspace = H5::DataSpace(2, matdims); + datatype = Type.get_hdf5_type(flat[0]); + dataset = location.createDataSet("quantum_numbers", datatype, dataspace); + dataset.write(flat.data(), datatype); + // label axes + char labels[2][9] = {"sector", "symmetry"}; + str_type = H5::StrType(H5::PredType::C_S1, 9); + hsize_t attr_dims[1] = {2}; + H5::DataSpace attr_space(1, attr_dims); + attr = dataset.createAttribute("axis_labels", str_type, attr_space); + attr.write(str_type, labels); + + // Symmetries + if (save_symmetries) { + // vecdims[1] = { this->_impl->_syms.size() }; + // dataspace = H5::DataSpace(1, vecdims); + // str_type = H5::StrType(H5::PredType::C_S1, H5T_VARIABLE); + // dataset = location.createDataSet("symmetries", str_type, dataspace); + + // std::vector c_strings; + // std::string symstring; + // for (const auto& s : this->_impl->_syms) { + // symstring = s.name(); + // c_strings.push_back(symstring.c_str()); + // } + // dataset.write(c_strings.data(), str_type); + H5::Group symloc = location.createGroup("Symmetries"); + for (int sidx = 0; sidx < this->_impl->_syms.size(); sidx++) { + this->_impl->_syms[sidx].to_hdf5(symloc, "Symmetry" + std::to_string(sidx)); + } } } } - void Bond::from_hdf5(H5::Group &location) { - cytnx_error_msg(true, "[ERROR] Loading Bond from HDF5 is not implemented yet!%s", "\n"); + void Bond::from_hdf5(H5::Group &location, const std::vector &syms) { + H5::DataType datatype; + H5::Attribute attr; + H5::DataSet dataset; + H5::DataSpace dataspace; + H5::StrType str_type; + + // type from string + attr = location.openAttribute("type"); + str_type = attr.getStrType(); + size_t size = str_type.getSize() - 1; // not including the null terminator + std::string typestr; + typestr.resize(size); + attr.read(str_type, &typestr[0]); + this->_impl->_type = string_to_bondtype.at(typestr); + + // degs; read vector + bool symmetric = false; + hssize_t sectordim; + if (location.exists("degeneracies")) { + symmetric = true; + dataset = location.openDataSet("degeneracies"); + dataspace = dataset.getSpace(); + sectordim = dataspace.getSimpleExtentNpoints(); + this->_impl->_degs.resize(sectordim); + datatype = dataset.getDataType(); + cytnx_error_msg( + datatype.getSize() != sizeof(std::size_t), + "[ERROR] 'degeneracies' bit-length mismatch. File: %zu bytes, expected: %zu bytes.\n", + datatype.getSize(), sizeof(std::size_t)); + dataset.read(this->_impl->_degs.data(), datatype); + this->_impl->_dim = + std::accumulate(this->_impl->_degs.begin(), this->_impl->_degs.end(), cytnx_uint64(0)); + } else { + this->_impl->_degs = {}; + } + + // qnums; read matrix (dim x qnumdim) + hsize_t qnumdim; + if (location.exists("quantum_numbers")) { + cytnx_error_msg(!symmetric, + "[ERROR] 'degeneracies' were not found in HDF5 location, but " + "'quantum_numbers' existn. The HDF5 data seems corrupt!%s", + "\n"); + dataset = location.openDataSet("quantum_numbers"); + dataspace = dataset.getSpace(); + cytnx_error_msg(dataspace.getSimpleExtentNdims() != 2, + "[ERROR] 'quantum_numbers' should be a two-dimensional array. The HDF5 data " + "seems corrupt!%s", + "\n"); + hsize_t dims[2]; + dataspace.getSimpleExtentDims(dims); + cytnx_error_msg(dims[0] != sectordim, + "[ERROR] Length of 'degeneracies' = %d, but first dimension of " + "'quantum_numbers' is %d. The HDF5 data seems corrupt!\n", + sectordim, dims[0]); + qnumdim = dims[1]; + // Read HDF5 data into a flattened temporary vector + std::vector flat(sectordim * qnumdim); + datatype = dataset.getDataType(); + cytnx_error_msg( + datatype.getSize() != sizeof(cytnx_int64), + "[ERROR] 'quantum_numbers' bit-length mismatch. File: %zu bytes, expected: %zu bytes.\n", + datatype.getSize(), sizeof(cytnx_int64)); + dataset.read(flat.data(), datatype); + // Reconstruct the vector of vectors + this->_impl->_qnums.assign(sectordim, std::vector(qnumdim)); + for (hsize_t i = 0; i < sectordim; ++i) { + std::copy(flat.begin() + i * qnumdim, flat.begin() + (i + 1) * qnumdim, + this->_impl->_qnums[i].begin()); + } + } else { + this->_impl->_qnums = {}; + } + + // Symmetries + if (syms.empty()) { + if (location.exists("Symmetries")) { + H5::Group symloc = location.openGroup("Symmetries"); + this->_impl->_syms.clear(); + hsize_t sidx = 0; + while (true) { + std::string name = "Symmetry" + std::to_string(sidx); + if (!symloc.attrExists(name) && !symloc.exists(name)) { + break; + } + Symmetry sym; + sym.from_hdf5(symloc, name); + this->_impl->_syms.push_back(sym); + sidx++; + } + if (symmetric) { + cytnx_error_msg(sidx != qnumdim, + "[ERROR] %d Symmetries were found, but second dimension of " + "'quantum_numbers' is %d. The HDF5 data seems corrupt!\n", + sidx, qnumdim); + } else { + cytnx_error_msg(sidx > 0, + "[ERROR] 'degeneracies' and 'quantum_numbers' were not found in HDF5 " + "location, but 'Symmetries' exist. The HDF5 data seems corrupt!%s", + "\n"); + this->_impl->_syms = {}; + } + } else { + cytnx_error_msg(symmetric, + "[ERROR] 'degeneracies' and 'quantum_numbers' exist in HDF5 location, but " + "'Symmetries' are missing. The HDF5 data seems corrupt!%s", + "\n"); + } + } else { + cytnx_error_msg(!symmetric, + "[ERROR] 'degeneracies' and 'quantum_numbers' not found in HDF5 location, " + "but Symmetries are passed as arguments.%s", + "\n"); + cytnx_error_msg(syms.size() != qnumdim, + "[ERROR] %d Symmetries are passed, but second dimension of 'quantum_numbers' " + "is %d. The HDF5 data seems corrupt!\n", + syms.size(), qnumdim); + this->_impl->_syms = syms; + } + + // dim; from attribute + if (location.attrExists("dimension")) { + attr = location.openAttribute("dimension"); + cytnx_uint64 dimension; + datatype = attr.getDataType(); + cytnx_error_msg( + datatype.getSize() != sizeof(cytnx_uint64), + "[ERROR] 'dimension' bit-length mismatch. File: %zu bytes, expected: %zu bytes.\n", + datatype.getSize(), sizeof(cytnx_uint64)); + attr.read(datatype, &dimension); + if (symmetric) { + cytnx_error_msg(dimension != this->_impl->_dim, + "[ERROR] 'dimension' read from HDF5 file is %d, but the sum of all " + "degeneracies is %d. The HDF5 data seems corrupt!\n", + dimension, this->_impl->_dim); + } else { + this->_impl->_dim = dimension; + } + } else { + cytnx_error_msg(!symmetric, + "[ERROR] Could not find 'dimension' or 'degeneracies' in HDF5 file. The HDF5 " + "data seems corrupt!%s", + "\n"); + } } void Bond::to_binary(std::ostream &f) const { diff --git a/src/Symmetry.cpp b/src/Symmetry.cpp index 49b7df19a..a9635c9b5 100644 --- a/src/Symmetry.cpp +++ b/src/Symmetry.cpp @@ -318,14 +318,20 @@ namespace cytnx { void cytnx::Symmetry::Load_(const char *fname) { this->Load_(string(fname)); } void cytnx::Symmetry::to_hdf5(H5::Group &location, const std::string &name) const { - std::string typestr = this->name(); - H5::StrType str_type(H5::PredType::C_S1, typestr.length() + 1); + std::string symname = this->name(); + H5::StrType str_type(H5::PredType::C_S1, symname.length() + 1); H5::DataSpace dataspace = H5::DataSpace(H5S_SCALAR); H5::Attribute attr = location.createAttribute(name, str_type, dataspace); - attr.write(str_type, typestr); + attr.write(str_type, symname); } void cytnx::Symmetry::from_hdf5(H5::Group &location, const std::string &name) { - cytnx_error_msg(true, "[ERROR] Loading Symmetry from HDF5 is not implemented yet!%s", "\n"); + H5::Attribute attr = location.openAttribute(name); + H5::StrType str_type = attr.getStrType(); + size_t size = str_type.getSize() - 1; // remove the null terminator + std::string symname; + symname.resize(size); + attr.read(str_type, &symname[0]); + this->Init(symname); } void cytnx::Symmetry::to_binary(std::ostream &f) const { diff --git a/src/Tensor.cpp b/src/Tensor.cpp index 097094bf5..5b700957f 100644 --- a/src/Tensor.cpp +++ b/src/Tensor.cpp @@ -582,7 +582,12 @@ namespace cytnx { int device = Device.cpu; if (restore_device && dataset.attrExists("device")) { H5::Attribute attr = dataset.openAttribute("device"); - attr.read(H5::PredType::NATIVE_INT, &device); + datatype = dataset.getDataType(); + cytnx_error_msg( + datatype.getSize() != sizeof(int), + "[ERROR] 'device' bit-length mismatch. File: %zu bytes, expected: %zu bytes.\n", + datatype.getSize(), sizeof(int)); + attr.read(datatype, &device); } this->_impl->_storage.data_from_hdf5(dataset, Nelem, dtype, datatype, device); diff --git a/src/backend/Storage.cpp b/src/backend/Storage.cpp index 5f9ae40bf..6e29c1c94 100644 --- a/src/backend/Storage.cpp +++ b/src/backend/Storage.cpp @@ -300,7 +300,12 @@ namespace cytnx { int device = Device.cpu; if (restore_device && dataset.attrExists("device")) { H5::Attribute attr = dataset.openAttribute("device"); - attr.read(H5::PredType::NATIVE_INT, &device); + datatype = dataset.getDataType(); + cytnx_error_msg( + datatype.getSize() != sizeof(int), + "[ERROR] 'device' bit-length mismatch. File: %zu bytes, expected: %zu bytes.\n", + datatype.getSize(), sizeof(int)); + attr.read(datatype, &device); } this->data_from_hdf5(dataset, Nelem, dtype, datatype, device); From a589b5b4c915323bde9eea549bbd08f7d4624e4a Mon Sep 17 00:00:00 2001 From: Manuel Schneider Date: Wed, 29 Apr 2026 23:53:36 +0800 Subject: [PATCH 10/40] fixed merge issues, removed pybindings for to_hdf5 and from_hdf5 because they cannot be used in python directly --- include/Tensor.hpp | 18 +++++++++--------- include/backend/Storage.hpp | 4 ++-- pybind/random_py.cpp | 16 ++++++++-------- pybind/storage_py.cpp | 32 +++----------------------------- pybind/tensor_py.cpp | 30 ++---------------------------- 5 files changed, 24 insertions(+), 76 deletions(-) diff --git a/include/Tensor.hpp b/include/Tensor.hpp index 6d13f5f48..7b91d8d77 100644 --- a/include/Tensor.hpp +++ b/include/Tensor.hpp @@ -334,7 +334,7 @@ namespace cytnx { /** * @brief Load Tensor from file and create new instance - * @param fname[in] file name + * @param[in] fname file name * @param[in] restore_device whether to try restoring the device on which the data is stored; if * false, the data will be kept on the CPU. Use .to_() to move it to the target device after * loading. @@ -429,14 +429,13 @@ namespace cytnx { * cytnx::Tensor::Tofile. Given the file name \p fname , data type \p dtype and * number of elements \p count, this function will load the first \p count elements * from the binary file \p fname with data type \p dtype. - * @param fname[in] the file name of the binary file. - * @param dtype[in] the data type of the binary file. This can be any of the type defined in + * @param[in] fname the file name of the binary file. + * @param[in] dtype the data type of the binary file. This can be any of the type defined in * cytnx::Type. - * @param count[in] the number of elements to be loaded from the binary file. If set to -1, + * @param[in] count the number of elements to be loaded from the binary file. If set to -1, * all elements in the binary file will be loaded. - * @param[in] restore_device whether to try restoring the device on which the data is stored; if - * false, the data will be kept on the CPU. Use .to_() to move it to the target device after - * loading. + * @param[in] device the device that tensor to be created. This can be cytnx::Device.cpu or + * cytnx::Device.cuda+, see cytnx::Device for more detail. * @return Tensor * @pre * 1. The @p dtype cannot be Type.Void. @@ -450,10 +449,10 @@ namespace cytnx { */ [[deprecated("Please use Save/Load functions instead.")]] static Tensor Fromfile( const std::string &fname, const unsigned int &dtype, const cytnx_int64 &count = -1, - const bool restore_device = true); + const int device = Device.cpu); [[deprecated("Please use Save/Load functions instead.")]] static Tensor Fromfile( const char *fname, const unsigned int &dtype, const cytnx_int64 &count = -1, - const bool restore_device = true); + const int device = Device.cpu); // static Tensor Frombinary(const std::string &fname); ///@cond @@ -488,6 +487,7 @@ namespace cytnx { @param[in] shape the shape of tensor. @param[in] dtype the dtype of tensor. This can be any of type defined in cytnx::Type @param[in] device the device that tensor to be created. This can be cytnx::Device.cpu or + cytnx::Device.cuda+, see cytnx::Device for more detail. @param[in] init_zero if true, the content of Tensor will be initialized to zero. if false, the content of Tensor will be un-initialize. cytnx::Device.cuda+, see cytnx::Device for more detail. diff --git a/include/backend/Storage.hpp b/include/backend/Storage.hpp index d97bae8be..1205d58bb 100644 --- a/include/backend/Storage.hpp +++ b/include/backend/Storage.hpp @@ -698,11 +698,11 @@ namespace cytnx { */ [[deprecated("Please use Save/Load functions instead.")]] static Storage Fromfile( const std::string &fname, const unsigned int &dtype, const cytnx_int64 &count = -1, - const bool restore_device = true); + const int device = Device.cpu); // @see Fromfile(const std::string &fname, const unsigned int &dtype, const cytnx_int64 &count) [[deprecated("Please use Save/Load functions instead.")]] static Storage Fromfile( const char *fname, const unsigned int &dtype, const cytnx_int64 &count = -1, - const bool restore_device = true); + const int device = Device.cpu); /** @brief cast the type of current Storage diff --git a/pybind/random_py.cpp b/pybind/random_py.cpp index db484b75e..10b8506d8 100644 --- a/pybind/random_py.cpp +++ b/pybind/random_py.cpp @@ -104,8 +104,8 @@ void random_binding(py::module &m) { } return cytnx::random::normal(Nelem, mean, std, device, seed, dtype); }, - py::arg("Nelem"), py::arg("mean"), py::arg("std"), py::arg("device") = -1, py::arg("seed") = -1, - py::arg("dtype") = (unsigned int)(Type.Double)); + py::arg("Nelem"), py::arg("mean"), py::arg("std"), py::arg("device") = (int)cytnx::Device.cpu, + py::arg("seed") = -1, py::arg("dtype") = (unsigned int)(Type.Double)); m_random.def( "normal", [](const std::vector &Nelem, const double &mean, const double &std, @@ -116,8 +116,8 @@ void random_binding(py::module &m) { } return cytnx::random::normal(Nelem, mean, std, device, seed, dtype); }, - py::arg("Nelem"), py::arg("mean"), py::arg("std"), py::arg("device") = -1, py::arg("seed") = -1, - py::arg("dtype") = (unsigned int)(Type.Double)); + py::arg("Nelem"), py::arg("mean"), py::arg("std"), py::arg("device") = (int)cytnx::Device.cpu, + py::arg("seed") = -1, py::arg("dtype") = (unsigned int)(Type.Double)); m_random.def( "uniform", [](const cytnx_uint64 &Nelem, const double &low, const double &high, const int &device, @@ -128,8 +128,8 @@ void random_binding(py::module &m) { } return cytnx::random::uniform(Nelem, low, high, device, seed, dtype); }, - py::arg("Nelem"), py::arg("low"), py::arg("high"), py::arg("device") = -1, py::arg("seed") = -1, - py::arg("dtype") = (unsigned int)(Type.Double)); + py::arg("Nelem"), py::arg("low"), py::arg("high"), py::arg("device") = (int)cytnx::Device.cpu, + py::arg("seed") = -1, py::arg("dtype") = (unsigned int)(Type.Double)); m_random.def( "uniform", [](const std::vector &Nelem, const double &low, const double &high, @@ -140,7 +140,7 @@ void random_binding(py::module &m) { } return cytnx::random::uniform(Nelem, low, high, device, seed, dtype); }, - py::arg("Nelem"), py::arg("low"), py::arg("high"), py::arg("device") = -1, py::arg("seed") = -1, - py::arg("dtype") = (unsigned int)(Type.Double)); + py::arg("Nelem"), py::arg("low"), py::arg("high"), py::arg("device") = (int)cytnx::Device.cpu, + py::arg("seed") = -1, py::arg("dtype") = (unsigned int)(Type.Double)); } #endif diff --git a/pybind/storage_py.cpp b/pybind/storage_py.cpp index d84550424..56bf32686 100644 --- a/pybind/storage_py.cpp +++ b/pybind/storage_py.cpp @@ -77,10 +77,10 @@ void storage_binding(py::module &m) { .def(py::init()) .def(py::init>()) .def(py::init(), - py::arg("size"), py::arg("dtype") = (cytnx_uint64)Type.Double, py::arg("device") = -1, - py::arg("init_zero") = true) + py::arg("size"), py::arg("dtype") = (cytnx_uint64)Type.Double, + py::arg("device") = (int)cytnx::Device.cpu, py::arg("init_zero") = true) .def("Init", &cytnx::Storage::Init, py::arg("size"), - py::arg("dtype") = (cytnx_uint64)Type.Double, py::arg("device") = -1, + py::arg("dtype") = (cytnx_uint64)Type.Double, py::arg("device") = (int)cytnx::Device.cpu, py::arg("init_zero") = true) .def("dtype", &cytnx::Storage::dtype) @@ -283,32 +283,6 @@ void storage_binding(py::module &m) { [](const std::string &fname, const unsigned int &dtype, const cytnx_int64 &count, const int device) { return cytnx::Storage::Fromfile(fname, dtype, count, device); }, py::arg("fname"), py::arg("dtype"), py::arg("count") = (cytnx_int64)(-1), - py::arg("device") = cytnx::Device.cpu) - - .def( - "to_hdf5", - [](cytnx::Storage &self, H5::Group &location, const std::string &name) { - self.to_hdf5(location, name); - }, - py::arg("location"), py::arg("name") = "Storage") - .def( - "from_hdf5", - [](cytnx::Storage &self, H5::Group &location, const std::string &name, - const bool restore_device) { self.from_hdf5(location, name, restore_device); }, - py::arg("location"), py::arg("name") = "Storage", py::arg("restore_device") = true) - .def( - "data_to_hdf5", - [](cytnx::Storage &self, H5::DataSet &dataset, H5::DataType &hdf5type) { - self.data_to_hdf5(dataset, hdf5type); - }, - py::arg("dataset"), py::arg("hdf5type")) - .def( - "data_from_hdf5", - [](cytnx::Storage &self, H5::DataSet &dataset, const cytnx_uint64 &Nelem, - const unsigned int &dtype, H5::DataType &hdf5type, const int &device = Device.cpu) { - self.data_from_hdf5(dataset, Nelem, dtype, hdf5type, device); - }, - py::arg("dataset"), py::arg("Nelem"), py::arg("dtype"), py::arg("hdf5type"), py::arg("device") = (int)cytnx::Device.cpu) .def( diff --git a/pybind/tensor_py.cpp b/pybind/tensor_py.cpp index bd1b7a310..bcfc139a3 100644 --- a/pybind/tensor_py.cpp +++ b/pybind/tensor_py.cpp @@ -304,32 +304,6 @@ void tensor_binding(py::module &m) { }, py::arg("fname"), py::arg("restore_device") = true) - .def( - "to_hdf5", - [](cytnx::Tensor &self, py::object location, const std::string &name) { - // Extract raw ID from the h5py object's .id.id attribute - hid_t raw_id = location.attr("id").attr("id").cast(); - if (H5Iinc_ref(raw_id) < 0) { - throw std::runtime_error("Failed to increment HDF5 reference count."); - } - H5::Group cpplocation(raw_id); - self.to_hdf5(cpplocation, name); - }, - py::arg("location"), py::arg("name") = "Tensor") - .def( - "from_hdf5", - [](cytnx::Tensor &self, py::object location, const std::string &name, - const bool restore_device) { - // Extract raw ID from the h5py object's .id.id attribute - hid_t raw_id = location.attr("id").attr("id").cast(); - if (H5Iinc_ref(raw_id) < 0) { - throw std::runtime_error("Failed to increment HDF5 reference count."); - } - H5::Group cpplocation(raw_id); - self.from_hdf5(cpplocation, name, restore_device); - }, - py::arg("location"), py::arg("name") = "Tensor", py::arg("restore_device") = true) - .def(py::pickle( [](const cytnx::Tensor &self) { // __getstate__ std::ostringstream oss(std::ios::binary); @@ -350,9 +324,9 @@ void tensor_binding(py::module &m) { .def_static( "Fromfile", [](const std::string &fname, const unsigned int &dtype, const cytnx::cytnx_int64 &count, - const bool restore_device) { return cytnx::Tensor::Load(fname, restore_device); }, + const int device) { return cytnx::Tensor::Fromfile(fname, dtype, count, device); }, py::arg("fname"), py::arg("dtype"), py::arg("count") = cytnx::cytnx_int64(-1), - py::arg("restore_device") = true) + py::arg("device") = (int)cytnx::Device.cpu) .def_static( "from_storage", From cf39fca1e4ac2b820a203e41ddd89ad2021559bc Mon Sep 17 00:00:00 2001 From: Manuel Schneider Date: Thu, 30 Apr 2026 17:49:03 +0800 Subject: [PATCH 11/40] implemented file paths for Bond and Symmetry; Save and Load accept path instead of string for file names --- include/Bond.hpp | 64 ++++++++++++++--------- include/Gncon.hpp | 34 ++++++------ include/Network.hpp | 34 ++++++------ include/Symmetry.hpp | 62 ++++++++++++++-------- include/Tensor.hpp | 60 ++++++++++----------- include/UniTensor.hpp | 37 ++++++------- include/backend/Storage.hpp | 81 +++++++++++++++-------------- include/tn_algo/MPS.hpp | 41 ++++++++------- pybind/algo_py.cpp | 1 + pybind/bond_py.cpp | 38 +++++++++----- pybind/cytnx.cpp | 1 + pybind/generator_py.cpp | 1 + pybind/linalg_py.cpp | 1 + pybind/linop_py.cpp | 1 + pybind/ncon_py.cpp | 1 + pybind/network_py.cpp | 1 + pybind/physics_related_py.cpp | 1 + pybind/random_py.cpp | 1 + pybind/scalar_py.cpp | 1 + pybind/storage_py.cpp | 13 +++-- pybind/symmetry_py.cpp | 36 +++++++++---- pybind/tensor_py.cpp | 13 +++-- pybind/tnalgo_py.cpp | 27 ++++++---- pybind/unitensor_py.cpp | 24 +++++---- src/Bond.cpp | 93 ++++++++++++++++++++++++++------- src/Gncon_base.cpp | 7 +-- src/Network_base.cpp | 8 +-- src/RegularGncon.cpp | 14 ++--- src/RegularNetwork.cpp | 6 +-- src/Symmetry.cpp | 98 ++++++++++++++++++++++++++++------- src/Tensor.cpp | 18 +++---- src/UniTensor.cpp | 14 ++--- src/backend/Storage.cpp | 18 +++---- src/tn_algo/MPS.cpp | 14 ++--- 34 files changed, 542 insertions(+), 322 deletions(-) diff --git a/include/Bond.hpp b/include/Bond.hpp index e87aa7cd1..2a4239c32 100644 --- a/include/Bond.hpp +++ b/include/Bond.hpp @@ -2,6 +2,7 @@ #define CYTNX_BOND_H_ #include +#include #include #include #include @@ -857,60 +858,75 @@ namespace cytnx { /** * @brief Save Bond to file * @param[in] fname file name + * @param[in] path path inside the file. Only used for HDF5 files. A path '/foo/bar/' will + * write the Bond to the group '/foo/bar' in the file. + * @param[in] mode the write mode:\n + * `w` Creates a new file. If the given file exists, its contents are destroyed.\n + * `x` Creates a new file. Fails if the given file exists already.\n + * `a` Opens for writing without overwriting any existing content. Creates the file if it + * doesn't exist. Only available for HDF5 files.\n + * `u` Opens for writing. Existing content will be updated(overwritten). + * Creates the file if it doesn't exist. Only available for HDF5 files. * @details Save the Bond to a file. The file ending should be one of ".h5", ".hdf5", ".H5", * ".HDF5", ".hdf" to save in HDF5 file format. Otherwise, a binary file format is used. * @note The common file ending for saving a Bond in binary format is ".cybd". * @warning HDF5 file format is strongly recommended for compatibility with other libraries, * readability, and future-proofing. - * @see Load(const std::string &fname, const bool restore_device) + * @see Load(const std::filesystem::path &fname, const bool restore_device) */ - void Save(const std::string &fname) const; - // @see Save(const std::string &fname) const; - void Save(const char *fname) const; + void Save(const std::filesystem::path &fname, const std::string &path = "/", + const char mode = 'w') const; + // @see Save(const std::filesystem::path &fname, const std::string &path, const char mode) + // const; + void Save(const char *fname, const std::string &path = "/", const char mode = 'w') const; /** * @brief Load Bond from file and create new instance * @param fname[in] file name + * @param[in] path path inside the file. Only used for HDF5 files. A path '/foo/bar/' will + * read the Bond from the group '/foo/bar' in the file. * @param[in] restore_device whether to try restoring the device on which the data is stored; if * false, the data will be kept on the CPU. Use .to_() to move it to the target device after * loading. * @pre The file must be a Bond object which is saved by cytnx::Bond::Save. * @note This function creates a new Bond and keeps the original Bond unchanged. See \link - * Load_(const std::string &fname, const bool restore_device) Load_() \endlink for loading the - * Bond to the current Bond. + * Load_(const std::filesystem::path &fname, const bool restore_device) Load_() \endlink for + * loading the Bond to the current Bond. * @details For HDF5 file format, one of the file endings ".h5", ".hdf5", ".H5", ".HDF5", ".hdf" * is expected. For binary format, the common file ending for a Bond is ".cybd". */ - static cytnx::Bond Load(const std::string &fname); - // @see Load(const std::string &fname) - static cytnx::Bond Load(const char *fname); + static cytnx::Bond Load(const std::filesystem::path &fname, const std::string &path = "/"); + // @see Load(const std::filesystem::path &fname, const std::string &path) + static cytnx::Bond Load(const char *fname, const std::string &path = "/"); /** * @brief Load Bond from file and overwrite current instance - * @note This function overwrites the existing Bond. See \link Load(const std::string &fname, - * const bool restore_device) Load() \endlink for creating a new Bond. - * @see Load(const std::string &fname, const bool restore_device) + * @note This function overwrites the existing Bond. See \link Load(const std::filesystem::path + * &fname, const bool restore_device) Load() \endlink for creating a new Bond. + * @see Load(const std::filesystem::path &fname, const bool restore_device) */ - void Load_(const std::string &fname); - // @see Load_(const std::string &fname) - void Load_(const char *fname); + void Load_(const std::filesystem::path &fname, const std::string &path = "/"); + // @see Load_(const std::filesystem::path &fname, const std::string &path) + void Load_(const char *fname, const std::string &path = "/"); /** * @brief Save Bond to HDF5 file * @param[in] location the HDF5 group where the Bond will be saved. + * @param[in] overwrite overwrite previous Bond information in the location. * @param[in] save_symmetries whether to save the symmetry information in the HDF5 file. - * @warning This function is only available in C++. Use \link Save(const std::string &fname) - * Save() \endlink for saving to file in C++ or Python. + * @warning This function is only available in C++. Use \link Save(const std::filesystem::path + * &fname) Save() \endlink for saving to file in C++ or Python. * @see from_hdf5(H5::Group &location, const std::string &name, const bool restore_device) */ - void to_hdf5(H5::Group &location, const bool save_symmetries = true) const; + void to_hdf5(H5::Group &location, const bool overwrite = false, + const bool save_symmetries = true) const; /** * @brief Load Bond from HDF5 file (inline) * @param[in] location the HDF5 group where the Bond will be loaded from. * @param[in] syms vector containing the Symmetries. Leave emtpy to read the Symmetries from * HDF5 - * @warning This function is only available in C++. Use \link Load(const std::string &fname, - * const bool restore_device) Load() \endlink for loading from file in C++ or Python. + * @warning This function is only available in C++. Use \link Load(const std::filesystem::path + * &fname, const bool restore_device) Load() \endlink for loading from file in C++ or Python. * @see to_hdf5(H5::Group &location, const std::string &name) const */ void from_hdf5(H5::Group &location, const std::vector &syms = {}); @@ -919,8 +935,8 @@ namespace cytnx { * @brief Save Bond to binary file * @param[in] f the output stream where the Bond will be saved. * @warning This function is only available in C++. In Python, use pickle for the same binary - * file format. Use \link Save(const std::string &fname) Save() \endlink for saving to file in - * C++ or Python. + * file format. Use \link Save(const std::filesystem::path &fname) Save() \endlink for saving to + * file in C++ or Python. * @see from_binary(std::istream &f) */ void to_binary(std::ostream &f) const; @@ -928,8 +944,8 @@ namespace cytnx { * @brief Load Bond from binary file * @param[in] f the input stream from which the Bond will be loaded. * @warning This function is only available in C++. In Python, use pickle for the same binary - * file format. Use \link Load(const std::string &fname, const bool restore_device) Load() - * \endlink for loading from file in C++ or Python. + * file format. Use \link Load(const std::filesystem::path &fname, const bool restore_device) + * Load() \endlink for loading from file in C++ or Python. * @see to_binary(std::ostream &f) const */ void from_binary(std::istream &f); diff --git a/include/Gncon.hpp b/include/Gncon.hpp index d83c1c14f..438f8aee6 100644 --- a/include/Gncon.hpp +++ b/include/Gncon.hpp @@ -1,16 +1,18 @@ #ifndef CYTNX_GNCON_H_ #define CYTNX_GNCON_H_ -#include "Type.hpp" -#include "cytnx_error.hpp" +#include +#include #include -#include #include -#include -#include "intrusive_ptr_base.hpp" -#include "utils/utils.hpp" +#include + +#include "Type.hpp" #include "UniTensor.hpp" #include "contraction_tree.hpp" +#include "cytnx_error.hpp" +#include "intrusive_ptr_base.hpp" +#include "utils/utils.hpp" #ifdef BACKEND_TORCH #else @@ -88,14 +90,14 @@ namespace cytnx { const std::vector &alias, const std::string &contract_order); - virtual void Fromfile(const std::string &fname); + virtual void Fromfile(const std::filesystem::path &fname); virtual void FromString(const std::vector &content); virtual void clear(); virtual std::string getOptimalOrder(); virtual UniTensor Launch(const bool &optimal = false, const std::string &contract_order = ""); virtual void PrintNet(std::ostream &os); virtual boost::intrusive_ptr clone(); - virtual void Savefile(const std::string &fname); + virtual void Savefile(const std::filesystem::path &fname); virtual ~Gncon_base(){}; }; // Gncon_base @@ -103,7 +105,7 @@ namespace cytnx { class RegularGncon : public Gncon_base { public: RegularGncon() { this->nwrktype_id = GNType.Regular; }; - void Fromfile(const std::string &fname); + void Fromfile(const std::filesystem::path &fname); void FromString(const std::vector &contents); void PutUniTensor(const std::string &name, const UniTensor &utensor); void PutUniTensor(const cytnx_uint64 &idx, const UniTensor &utensor); @@ -138,7 +140,7 @@ namespace cytnx { return out; } void PrintNet(std::ostream &os); - void Savefile(const std::string &fname); + void Savefile(const std::filesystem::path &fname); ~RegularGncon(){}; }; @@ -149,7 +151,7 @@ namespace cytnx { public: FermionGncon() { this->nwrktype_id = GNType.Fermion; }; - void Fromfile(const std::string &fname){}; + void Fromfile(const std::filesystem::path &fname){}; void FromString(const std::vector &contents){}; void PutUniTensor(const std::string &name, const UniTensor &utensor){}; void PutUniTensor(const cytnx_uint64 &idx, const UniTensor &utensor){}; @@ -183,7 +185,7 @@ namespace cytnx { return out; } void PrintNet(std::ostream &os){}; - void Savefile(const std::string &fname){}; + void Savefile(const std::filesystem::path &fname){}; ~FermionGncon(){}; }; @@ -254,7 +256,7 @@ namespace cytnx { */ - void Fromfile(const std::string &fname, const int &Gncon_type = GNType.Regular) { + void Fromfile(const std::filesystem::path &fname, const int &Gncon_type = GNType.Regular) { if (Gncon_type == GNType.Regular) { boost::intrusive_ptr tmp(new RegularGncon()); this->_impl = tmp; @@ -301,7 +303,7 @@ namespace cytnx { } this->_impl->FromString(contents); } - // void Savefile(const std::string &fname); + // void Savefile(const std::filesystem::path &fname); static Gncon Contract(const std::vector &tensors, const std::string &Tout, const std::vector &alias = {}, @@ -313,7 +315,7 @@ namespace cytnx { return out; } - Gncon(const std::string &fname, const int &Gncon_type = GNType.Regular) { + Gncon(const std::filesystem::path &fname, const int &Gncon_type = GNType.Regular) { this->Fromfile(fname, Gncon_type); } @@ -354,7 +356,7 @@ namespace cytnx { } void PrintNet() { this->_impl->PrintNet(std::cout); } - void Savefile(const std::string &fname) { this->_impl->Savefile(fname); } + void Savefile(const std::filesystem::path &fname) { this->_impl->Savefile(fname); } }; ///@cond diff --git a/include/Network.hpp b/include/Network.hpp index 75aa82db1..dc31f9129 100644 --- a/include/Network.hpp +++ b/include/Network.hpp @@ -1,16 +1,18 @@ #ifndef CYTNX_NETWORK_H_ #define CYTNX_NETWORK_H_ -#include "Type.hpp" -#include "cytnx_error.hpp" +#include +#include #include -#include #include -#include -#include "intrusive_ptr_base.hpp" -#include "utils/utils.hpp" +#include + +#include "Type.hpp" #include "UniTensor.hpp" #include "contraction_tree.hpp" +#include "cytnx_error.hpp" +#include "intrusive_ptr_base.hpp" +#include "utils/utils.hpp" #ifdef BACKEND_TORCH #else @@ -119,7 +121,7 @@ namespace cytnx { const std::vector &alias, const std::string &contract_order); - virtual void Fromfile(const std::string &fname); + virtual void Fromfile(const std::filesystem::path &fname); virtual void FromString(const std::vector &content); virtual void clear(); virtual std::string getOptimalOrder(); @@ -137,7 +139,7 @@ namespace cytnx { const std::string &order, const bool optim); virtual void PrintNet(std::ostream &os); virtual boost::intrusive_ptr clone(); - virtual void Savefile(const std::string &fname); + virtual void Savefile(const std::filesystem::path &fname); virtual ~Network_base(){}; }; // Network_base @@ -145,7 +147,7 @@ namespace cytnx { class RegularNetwork : public Network_base { public: RegularNetwork() { this->nwrktype_id = NtType.Regular; }; - void Fromfile(const std::string &fname); + void Fromfile(const std::filesystem::path &fname); void FromString(const std::vector &contents); void PutUniTensor(const std::string &name, const UniTensor &utensor); void PutUniTensor(const cytnx_uint64 &idx, const UniTensor &utensor); @@ -195,7 +197,7 @@ namespace cytnx { return out; } void PrintNet(std::ostream &os); - void Savefile(const std::string &fname); + void Savefile(const std::filesystem::path &fname); ~RegularNetwork(){}; }; @@ -206,7 +208,7 @@ namespace cytnx { public: FermionNetwork() { this->nwrktype_id = NtType.Fermion; }; - void Fromfile(const std::string &fname){}; + void Fromfile(const std::filesystem::path &fname){}; void FromString(const std::vector &contents){}; void RmUniTensor(const cytnx_uint64 &idx){}; void RmUniTensor(const std::string &name){}; @@ -246,7 +248,7 @@ namespace cytnx { return out; } void PrintNet(std::ostream &os){}; - void Savefile(const std::string &fname){}; + void Savefile(const std::filesystem::path &fname){}; ~FermionNetwork(){}; }; @@ -317,7 +319,7 @@ namespace cytnx { */ - void Fromfile(const std::string &fname, const int &network_type = NtType.Regular) { + void Fromfile(const std::filesystem::path &fname, const int &network_type = NtType.Regular) { if (network_type == NtType.Regular) { boost::intrusive_ptr tmp(new RegularNetwork()); this->_impl = tmp; @@ -364,7 +366,7 @@ namespace cytnx { } this->_impl->FromString(contents); } - // void Savefile(const std::string &fname); + // void Savefile(const std::filesystem::path &fname); static Network Contract(const std::vector &tensors, const std::string &Tout, const std::vector &alias = {}, @@ -376,7 +378,7 @@ namespace cytnx { return out; } - Network(const std::string &fname, const int &network_type = NtType.Regular) { + Network(const std::filesystem::path &fname, const int &network_type = NtType.Regular) { this->Fromfile(fname, network_type); } @@ -452,7 +454,7 @@ namespace cytnx { } void PrintNet() { this->_impl->PrintNet(std::cout); } - void Savefile(const std::string &fname) { this->_impl->Savefile(fname); } + void Savefile(const std::filesystem::path &fname) { this->_impl->Savefile(fname); } }; ///@cond diff --git a/include/Symmetry.hpp b/include/Symmetry.hpp index 8ab14007f..c05f80f9e 100644 --- a/include/Symmetry.hpp +++ b/include/Symmetry.hpp @@ -1,6 +1,7 @@ #ifndef CYTNX_SYMMETRY_H_ #define CYTNX_SYMMETRY_H_ +#include #include #include #include @@ -530,59 +531,76 @@ namespace cytnx { /** * @brief Save Symmetry to file * @param[in] fname file name + * @param[in] path path inside the file. Only available for HDF5 files. A path '/foo/bar/Symm' + * will write the Symmetry to the attribute 'Symm' the group '/foo/bar' in the file. + * @param[in] mode the write mode:\n + * `w` Creates a new file. If the given file exists, its contents are destroyed.\n + * `x` Creates a new file. Fails if the given file exists already.\n + * `a` Opens for writing without overwriting any existing content. Creates the file if it + * doesn't exist. Only available for HDF5 files.\n + * `u` Opens for writing. Existing content will be updated(overwritten). + * Creates the file if it doesn't exist. Only available for HDF5 files. * @details Save the Symmetry to a file. The file ending should be one of ".h5", ".hdf5", ".H5", * ".HDF5", ".hdf" to save in HDF5 file format. Otherwise, a binary file format is used. * @note The common file ending for saving a Symmetry in binary format is ".cysym". * @warning HDF5 file format is strongly recommended for compatibility with other libraries, * readability, and future-proofing. - * @see Load(const std::string &fname, const bool restore_device) + * @see Load(const std::filesystem::path &fname, const bool restore_device) */ - void Save(const std::string &fname) const; - // @brief Same as Save(const std::string &fname) const; - void Save(const char *fname) const; + void Save(const std::filesystem::path &fname, const std::string &path = "/Symmetry", + const char mode = 'w') const; + // @brief Same as Save(const std::filesystem::path &fname, const std::string &path, const char + // mode) const; + void Save(const char *fname, const std::string &path = "/Symmetry", + const char mode = 'w') const; /** * @brief Load Symmetry from file and create new instance * @param fname[in] file name + * @param[in] path path inside the file. Only available for HDF5 files. A path /foo/bar/Symm + * will write the Symmetry to the attribute 'Symm' the group '/foo/bar' in the file. * @param[in] restore_device whether to try restoring the device on which the data is stored; if * false, the data will be kept on the CPU. Use .to_() to move it to the target device after * loading. * @pre The file must be a Symmetry object which is saved by cytnx::Symmetry::Save. * @note This function creates a new Symmetry and keeps the original Symmetry unchanged. See - * \link Load_(const std::string &fname, const bool restore_device) Load_() \endlink for loading - * the Symmetry to the current Symmetry. + * \link Load_(const std::filesystem::path &fname, const bool restore_device) Load_() \endlink + * for loading the Symmetry to the current Symmetry. * @details For HDF5 file format, one of the file endings ".h5", ".hdf5", ".H5", ".HDF5", ".hdf" * is expected. For binary format, the common file ending for a Symmetry is ".cysym". */ - static cytnx::Symmetry Load(const std::string &fname); - // @see Load(const std::string &fname) - static cytnx::Symmetry Load(const char *fname); + static cytnx::Symmetry Load(const std::filesystem::path &fname, + const std::string &path = "/Symmetry"); + // @see Load(const std::filesystem::path &fname, const std::string &path) + static cytnx::Symmetry Load(const char *fname, const std::string &path = "/Symmetry"); /** * @brief Load Symmetry from file and overwrite current instance * @note This function overwrites the existing Symmetry. See \link Load(const std::string * &fname, const bool restore_device) Load() \endlink for creating a new Symmetry. - * @see Load(const std::string &fname, const bool restore_device) + * @see Load(const std::filesystem::path &fname, const bool restore_device) */ - void Load_(const std::string &fname); - // @see Load_(const std::string &fname) - void Load_(const char *fname); + void Load_(const std::filesystem::path &fname, const std::string &path = "/Symmetry"); + // @see Load_(const std::filesystem::path &fname, const std::string &path) + void Load_(const char *fname, const std::string &path = "/Symmetry"); /** * @brief Save Symmetry to HDF5 file * @param[in] location the HDF5 group where the Symmetry will be saved. + * @param[in] overwrite overwrite previous Bond information in the location. * @param[in] name the name of the Symmetry in the HDF5 file. - * @warning This function is only available in C++. Use \link Save(const std::string &fname) - * Save() \endlink for saving to file in C++ or Python. + * @warning This function is only available in C++. Use \link Save(const std::filesystem::path + * &fname) Save() \endlink for saving to file in C++ or Python. * @see from_hdf5(H5::Group &location, const std::string &name, const bool restore_device) */ - void to_hdf5(H5::Group &location, const std::string &name = "Symmetry") const; + void to_hdf5(H5::Group &location, const bool overwrite = false, + const std::string &name = "Symmetry") const; /** * @brief Load Symmetry from HDF5 file (inline) * @param[in] location the HDF5 group where the Symmetry will be loaded from. * @param[in] name the name of the Symmetry in the HDF5 file. - * @warning This function is only available in C++. Use \link Load(const std::string &fname, - * const bool restore_device) Load() \endlink for loading from file in C++ or Python. + * @warning This function is only available in C++. Use \link Load(const std::filesystem::path + * &fname, const bool restore_device) Load() \endlink for loading from file in C++ or Python. * @see to_hdf5(H5::Group &location, const std::string &name) const */ void from_hdf5(H5::Group &location, const std::string &name = "Symmetry"); @@ -591,8 +609,8 @@ namespace cytnx { * @brief Save Symmetry to binary file * @param[in] f the output stream where the Symmetry will be saved. * @warning This function is only available in C++. In Python, use pickle for the same binary - * file format. Use \link Save(const std::string &fname) Save() \endlink for saving to file in - * C++ or Python. + * file format. Use \link Save(const std::filesystem::path &fname) Save() \endlink for saving to + * file in C++ or Python. * @see from_binary(std::istream &f) */ void to_binary(std::ostream &f) const; @@ -600,8 +618,8 @@ namespace cytnx { * @brief Load Symmetry from binary file * @param[in] f the input stream from which the Symmetry will be loaded. * @warning This function is only available in C++. In Python, use pickle for the same binary - * file format. Use \link Load(const std::string &fname, const bool restore_device) Load() - * \endlink for loading from file in C++ or Python. + * file format. Use \link Load(const std::filesystem::path &fname, const bool restore_device) + * Load() \endlink for loading from file in C++ or Python. * @see to_binary(std::ostream &f) const */ void from_binary(std::istream &f); diff --git a/include/Tensor.hpp b/include/Tensor.hpp index 7b91d8d77..aaa12ec38 100644 --- a/include/Tensor.hpp +++ b/include/Tensor.hpp @@ -1,6 +1,7 @@ #ifndef CYTNX_TENSOR_H_ #define CYTNX_TENSOR_H_ +#include #include #include #include @@ -326,10 +327,10 @@ namespace cytnx { * @note The common file ending for saving a Tensor in binary format is ".cytn". * @warning HDF5 file format is strongly recommended for compatibility with other libraries, * readability, and future-proofing. - * @see Load(const std::string &fname, const bool restore_device) + * @see Load(const std::filesystem::path &fname, const bool restore_device) */ - void Save(const std::string &fname) const; - // @see Save(const std::string &fname) const + void Save(const std::filesystem::path &fname) const; + // @see Save(const std::filesystem::path &fname) const void Save(const char *fname) const; /** @@ -340,31 +341,32 @@ namespace cytnx { * loading. * @pre The file must be a Tensor object which is saved by cytnx::Tensor::Save. * @note This function creates a new Tensor and keeps the original Tensor unchanged. See \link - * Load_(const std::string &fname, const bool restore_device) Load_() \endlink for loading the - * Tensor to the current Tensor. + * Load_(const std::filesystem::path &fname, const bool restore_device) Load_() \endlink for + * loading the Tensor to the current Tensor. * @details For HDF5 file format, one of the file endings ".h5", ".hdf5", ".H5", ".HDF5", ".hdf" * is expected. For binary format, the common file ending for a Tensor is ".cytn". */ - static Tensor Load(const std::string &fname, const bool restore_device = true); - // @see Load(const std::string &fname, const bool restore_device) + static Tensor Load(const std::filesystem::path &fname, const bool restore_device = true); + // @see Load(const std::filesystem::path &fname, const bool restore_device) static Tensor Load(const char *fname, const bool restore_device = true); /** * @brief Load Tensor from file and overwrite current instance - * @note This function overwrites the existing Tensor. See \link Load(const std::string &fname, - * const bool restore_device) Load() \endlink for creating a new Tensor. - * @see Load(const std::string &fname, const bool restore_device) + * @note This function overwrites the existing Tensor. See \link Load(const + * std::filesystem::path &fname, const bool restore_device) Load() \endlink for creating a new + * Tensor. + * @see Load(const std::filesystem::path &fname, const bool restore_device) */ - void Load_(const std::string &fname, const bool restore_device = true); - // @see Load_(const std::string &fname, const bool restore_device) + void Load_(const std::filesystem::path &fname, const bool restore_device = true); + // @see Load_(const std::filesystem::path &fname, const bool restore_device) void Load_(const char *fname, const bool restore_device = true); /** * @brief Save Tensor to HDF5 file * @param[in] location the HDF5 group where the Tensor will be saved. * @param[in] name the name of the Tensor in the HDF5 file. - * @warning This function is only available in C++. Use \link Save(const std::string &fname) - * Save() \endlink for saving to file in C++ or Python. + * @warning This function is only available in C++. Use \link Save(const std::filesystem::path + * &fname) Save() \endlink for saving to file in C++ or Python. * @see from_hdf5(H5::Group &location, const std::string &name, const bool restore_device) */ void to_hdf5(H5::Group &location, const std::string &name = "Tensor") const; @@ -375,8 +377,8 @@ namespace cytnx { * @param[in] restore_device whether to try restoring the device on which the data is stored; if * false, the data will be kept on the CPU. Use .to_() to move it to the target device after * loading. - * @warning This function is only available in C++. Use \link Load(const std::string &fname, - * const bool restore_device) Load() \endlink for loading from file in C++ or Python. + * @warning This function is only available in C++. Use \link Load(const std::filesystem::path + * &fname, const bool restore_device) Load() \endlink for loading from file in C++ or Python. * @see to_hdf5(H5::Group &location, const std::string &name) const */ void from_hdf5(H5::Group &location, const std::string &name = "Tensor", @@ -386,8 +388,8 @@ namespace cytnx { * @brief Save Tensor to binary file * @param[in] f the output stream where the Tensor will be saved. * @warning This function is only available in C++. In Python, use pickle for the same binary - * file format. Use \link Save(const std::string &fname) Save() \endlink for saving to file in - * C++ or Python. + * file format. Use \link Save(const std::filesystem::path &fname) Save() \endlink for saving to + * file in C++ or Python. * @see from_binary(std::istream &f, const bool restore_device) */ void to_binary(std::ostream &f) const; @@ -398,8 +400,8 @@ namespace cytnx { * false, the data will be kept on the CPU. Use .to_() to move it to the target device after * loading. * @warning This function is only available in C++. In Python, use pickle for the same binary - * file format. Use \link Load(const std::string &fname, const bool restore_device) Load() - * \endlink for loading from file in C++ or Python. + * file format. Use \link Load(const std::filesystem::path &fname, const bool restore_device) + * Load() \endlink for loading from file in C++ or Python. * @see to_binary(std::ostream &f) const */ void from_binary(std::istream &f, const bool restore_device = true); @@ -411,15 +413,15 @@ namespace cytnx { * @param fname[in] the file name of the binary file. * @pre The file name @p fname must be valid. * @see cytnx::Tensor::Fromfile - * @deprecated This function is deprecated. Please use \ref Save(const std::string &fname) - * instead for saving raw data together with metadata. + * @deprecated This function is deprecated. Please use \ref Save(const std::filesystem::path + * &fname) instead for saving raw data together with metadata. */ - [[deprecated("Please use Save(const std::string &fname) instead.")]] void Tofile( - const std::string &fname) const; - // @see Tofile(const std::string &fname) const - [[deprecated("Please use Save(const std::string &fname) instead.")]] void Tofile( + [[deprecated("Please use Save(const std::filesystem::path &fname) instead.")]] void Tofile( + const std::filesystem::path &fname) const; + // @see Tofile(const std::filesystem::path &fname) const + [[deprecated("Please use Save(const std::filesystem::path &fname) instead.")]] void Tofile( const char *fname) const; - // @see Tofile(const std::string &fname) const + // @see Tofile(const std::filesystem::path &fname) const [[deprecated("Please use to_binary(std::ostream &f) instead.")]] void Tofile( std::fstream &f) const; @@ -448,12 +450,12 @@ namespace cytnx { * raw data together with metadata. */ [[deprecated("Please use Save/Load functions instead.")]] static Tensor Fromfile( - const std::string &fname, const unsigned int &dtype, const cytnx_int64 &count = -1, + const std::filesystem::path &fname, const unsigned int &dtype, const cytnx_int64 &count = -1, const int device = Device.cpu); [[deprecated("Please use Save/Load functions instead.")]] static Tensor Fromfile( const char *fname, const unsigned int &dtype, const cytnx_int64 &count = -1, const int device = Device.cpu); - // static Tensor Frombinary(const std::string &fname); + // static Tensor Frombinary(const std::filesystem::path &fname); ///@cond boost::intrusive_ptr _impl; diff --git a/include/UniTensor.hpp b/include/UniTensor.hpp index 647f75d85..f190df5a8 100644 --- a/include/UniTensor.hpp +++ b/include/UniTensor.hpp @@ -2,6 +2,7 @@ #define CYTNX_UNITENSOR_H_ #include +#include #include #include #include @@ -5465,10 +5466,10 @@ namespace cytnx { * @note The common file ending for saving a UniTensor in binary format is ".cytn". * @warning HDF5 file format is strongly recommended for compatibility with other libraries, * readability, and future-proofing. - * @see Load(const std::string &fname, const bool restore_device) + * @see Load(const std::filesystem::path &fname, const bool restore_device) */ - void Save(const std::string &fname) const; - // @see Save(const std::string &fname) const + void Save(const std::filesystem::path &fname) const; + // @see Save(const std::filesystem::path &fname) const void Save(const char *fname) const; /** @@ -5479,31 +5480,31 @@ namespace cytnx { * loading. * @pre The file must be a UniTensor object which is saved by cytnx::UniTensor::Save. * @note This function creates a new UniTensor and keeps the original UniTensor unchanged. See - * \link Load_(const std::string &fname, const bool restore_device) Load_() \endlink for loading - * the UniTensor to the current UniTensor. + * \link Load_(const std::filesystem::path &fname, const bool restore_device) Load_() \endlink + * for loading the UniTensor to the current UniTensor. * @details For HDF5 file format, one of the file endings ".h5", ".hdf5", ".H5", ".HDF5", ".hdf" * is expected. For binary format, the common file ending for a UniTensor is ".cytn". */ - static UniTensor Load(const std::string &fname, const bool restore_device = true); - // @see Load(const std::string &fname, const bool restore_device = true) + static UniTensor Load(const std::filesystem::path &fname, const bool restore_device = true); + // @see Load(const std::filesystem::path &fname, const bool restore_device = true) static UniTensor Load(const char *fname, const bool restore_device = true); /** * @brief Load UniTensor from file and overwrite current instance * @note This function overwrites the existing UniTensor. See \link Load(const std::string * &fname, const bool restore_device) Load() \endlink for creating a new UniTensor. - * @see Load(const std::string &fname, const bool restore_device) + * @see Load(const std::filesystem::path &fname, const bool restore_device) */ - void Load_(const std::string &fname, const bool restore_device = true); - // @see Load_(const std::string &fname, const bool restore_device) + void Load_(const std::filesystem::path &fname, const bool restore_device = true); + // @see Load_(const std::filesystem::path &fname, const bool restore_device) void Load_(const char *fname, const bool restore_device = true); /** * @brief Save UniTensor to HDF5 file * @param[in] location the HDF5 group where the UniTensor will be saved. * @param[in] name the name of the UniTensor in the HDF5 file. - * @warning This function is only available in C++. Use \link Save(const std::string &fname) - * Save() \endlink for saving to file in C++ or Python. + * @warning This function is only available in C++. Use \link Save(const std::filesystem::path + * &fname) Save() \endlink for saving to file in C++ or Python. * @see from_hdf5(H5::Group &location, const std::string &name, const bool restore_device) */ void to_hdf5(H5::Group &location, const std::string &name = "UniTensor") const; @@ -5514,8 +5515,8 @@ namespace cytnx { * @param[in] restore_device whether to try restoring the device on which the data is stored; if * false, the data will be kept on the CPU. Use .to_() to move it to the target device after * loading. - * @warning This function is only available in C++. Use \link Load(const std::string &fname, - * const bool restore_device) Load() \endlink for loading from file in C++ or Python. + * @warning This function is only available in C++. Use \link Load(const std::filesystem::path + * &fname, const bool restore_device) Load() \endlink for loading from file in C++ or Python. * @see to_hdf5(H5::Group &location, const std::string &name) const */ void from_hdf5(H5::Group &location, const std::string &name = "UniTensor", @@ -5525,8 +5526,8 @@ namespace cytnx { * @brief Save UniTensor to binary file * @param[in] f the output stream where the UniTensor will be saved. * @warning This function is only available in C++. In Python, use pickle for the same binary - * file format. Use \link Save(const std::string &fname) Save() \endlink for saving to file in - * C++ or Python. + * file format. Use \link Save(const std::filesystem::path &fname) Save() \endlink for saving to + * file in C++ or Python. * @see from_binary(std::istream &f, const bool restore_device) */ void to_binary(std::ostream &f) const; @@ -5537,8 +5538,8 @@ namespace cytnx { * false, the data will be kept on the CPU. Use .to_() to move it to the target device after * loading. * @warning This function is only available in C++. In Python, use pickle for the same binary - * file format. Use \link Load(const std::string &fname, const bool restore_device) Load() - * \endlink for loading from file in C++ or Python. + * file format. Use \link Load(const std::filesystem::path &fname, const bool restore_device) + * Load() \endlink for loading from file in C++ or Python. * @see to_binary(std::ostream &f) const */ void from_binary(std::istream &f, const bool restore_device = true); diff --git a/include/backend/Storage.hpp b/include/backend/Storage.hpp index 1205d58bb..f92c042ae 100644 --- a/include/backend/Storage.hpp +++ b/include/backend/Storage.hpp @@ -4,6 +4,7 @@ #ifndef BACKEND_TORCH #include + #include #include #include #include @@ -533,10 +534,10 @@ namespace cytnx { * @note The common file ending for saving a Storage in binary format is ".cyst". * @warning HDF5 file format is strongly recommended for compatibility with other libraries, * readability, and future-proofing. - * @see Load(const std::string &fname, const bool restore_device) + * @see Load(const std::filesystem::path &fname, const bool restore_device) */ - void Save(const std::string &fname) const; - // @see Save(const std::string &fname) const + void Save(const std::filesystem::path &fname) const; + // @see Save(const std::filesystem::path &fname) const void Save(const char *fname) const; /** @@ -547,31 +548,31 @@ namespace cytnx { * loading. * @pre The file must be a Storage object which is saved by cytnx::Storage::Save. * @note This function creates a new Storage and keeps the original Storage unchanged. See \link - * Load_(const std::string &fname, const bool restore_device) Load_() \endlink for loading the - * Storage to the current Storage. + * Load_(const std::filesystem::path &fname, const bool restore_device) Load_() \endlink for + * loading the Storage to the current Storage. * @details For HDF5 file format, one of the file endings ".h5", ".hdf5", ".H5", ".HDF5", ".hdf" * is expected. For binary format, the common file ending for a Storage is ".cyst". */ - static Storage Load(const std::string &fname, const bool restore_device = true); - // @see Load(const std::string &fname, const bool restore_device) + static Storage Load(const std::filesystem::path &fname, const bool restore_device = true); + // @see Load(const std::filesystem::path &fname, const bool restore_device) static Storage Load(const char *fname, const bool restore_device = true); /** @brief Load Storage from file and overwrite current instance - @note This function overwrites the existing Storage. See \link Load(const std::string &fname, - const bool restore_device) Load() \endlink for creating a new Storage. - @see Load(const std::string &fname, const bool restore_device) + @note This function overwrites the existing Storage. See \link Load(const std::filesystem::path + &fname, const bool restore_device) Load() \endlink for creating a new Storage. + @see Load(const std::filesystem::path &fname, const bool restore_device) */ - void Load_(const std::string &fname, const bool restore_device = true); - // @see Load_(const std::string &fname, const bool restore_device) + void Load_(const std::filesystem::path &fname, const bool restore_device = true); + // @see Load_(const std::filesystem::path &fname, const bool restore_device) void Load_(const char *fname, const bool restore_device = true); /** * @brief Save Storage to HDF5 file * @param[in] location the HDF5 group where the Storage will be saved. * @param[in] name the name of the Storage in the HDF5 file. - * @warning This function is only available in C++. Use \link Save(const std::string &fname) - * Save() \endlink for saving to file in C++ or Python. + * @warning This function is only available in C++. Use \link Save(const std::filesystem::path + * &fname) Save() \endlink for saving to file in C++ or Python. * @see from_hdf5(H5::Group &location, const std::string &name, const bool restore_device) */ void to_hdf5(H5::Group &location, const std::string &name = "Storage") const; @@ -582,8 +583,8 @@ namespace cytnx { * @param[in] restore_device whether to try restoring the device on which the data is stored; if * false, the data will be kept on the CPU. Use .to_() to move it to the target device after * loading. - * @warning This function is only available in C++. Use \link Load(const std::string &fname, - * const bool restore_device) Load() \endlink for loading from file in C++ or Python. + * @warning This function is only available in C++. Use \link Load(const std::filesystem::path + * &fname, const bool restore_device) Load() \endlink for loading from file in C++ or Python. * @see to_hdf5(H5::Group &location, const std::string &name) const */ void from_hdf5(H5::Group &location, const std::string &name = "Storage", @@ -593,8 +594,8 @@ namespace cytnx { * @param[in] dataset the HDF5 dataset where the Storage will be saved. * @param[in] hdf5type the HDF5 data type for the dataset, must match the data type of the * Storage. - * @warning This function is only available in C++. Use \link Save(const std::string &fname) - * Save() \endlink for saving to file in C++ or Python. + * @warning This function is only available in C++. Use \link Save(const std::filesystem::path + * &fname) Save() \endlink for saving to file in C++ or Python. * @see data_from_hdf5(H5::DataSet &dataset, const cytnx_uint64 &Nelem, const unsigned int * &dtype, H5::DataType &hdf5type, const int &device) */ @@ -610,8 +611,8 @@ namespace cytnx { * Storage. * @param[in] device the device on which the Storage will be loaded. * @note This function overwrites the current Storage with a new instance. - * @warning This function is only available in C++. Use \link Save(const std::string &fname) - * Save() \endlink for saving to file in C++ or Python. + * @warning This function is only available in C++. Use \link Save(const std::filesystem::path + * &fname) Save() \endlink for saving to file in C++ or Python. * @see data_to_hdf5(H5::DataSet &dataset, H5::DataType &hdf5type) */ void data_from_hdf5(H5::DataSet &dataset, const cytnx_uint64 &Nelem, const unsigned int &dtype, @@ -621,8 +622,8 @@ namespace cytnx { * @brief Save Storage to binary file * @param[in] f the output stream where the Storage will be saved. * @warning This function is only available in C++. In Python, use pickle for the same binary - * file format. Use \link Save(const std::string &fname) Save() \endlink for saving to file in - * C++ or Python. + * file format. Use \link Save(const std::filesystem::path &fname) Save() \endlink for saving to + * file in C++ or Python. * @see from_binary(std::istream &f, const bool restore_device) */ void to_binary(std::ostream &f) const; @@ -633,16 +634,16 @@ namespace cytnx { * false, the data will be kept on the CPU. Use .to_() to move it to the target device after * loading. * @warning This function is only available in C++. In Python, use pickle for the same binary - * file format. Use \link Load(const std::string &fname, const bool restore_device) Load() - * \endlink for loading from file in C++ or Python. + * file format. Use \link Load(const std::filesystem::path &fname, const bool restore_device) + * Load() \endlink for loading from file in C++ or Python. * @see to_binary(std::ostream &f) const */ void from_binary(std::istream &f, const bool restore_device = true); /** * @brief Save only the data of the Storage to binary filestream. * @param[in] f the output stream where the Storage will be saved. - * @warning This function is only available in C++. Use \link Save(const std::string &fname) - * Save() \endlink for saving to file in C++ or Python. + * @warning This function is only available in C++. Use \link Save(const std::filesystem::path + * &fname) Save() \endlink for saving to file in C++ or Python. * @see data_from_binary(std::istream &f, const cytnx_uint64 &Nelem, const unsigned int &dtype, * const int &device) */ @@ -655,8 +656,8 @@ namespace cytnx { * @param[in] device the device on which the Storage will be loaded. * @note This function overwrites the current Storage with a new instance. * @warning This function is only available in C++. In Python, use pickle for the same binary - * file format. Use \link Load(const std::string &fname, const bool restore_device) Load() - * \endlink for loading from file in C++ or Python. + * file format. Use \link Load(const std::filesystem::path &fname, const bool restore_device) + * Load() \endlink for loading from file in C++ or Python. * @see data_to_binary(std::ostream &f) const */ void data_from_binary(std::istream &f, const cytnx_uint64 &Nelem, const unsigned int &dtype, @@ -664,16 +665,17 @@ namespace cytnx { /** * @brief Save current Storage to a binary file, which only contains the raw data. - * @see Fromfile(const std::string &fname, const unsigned int &dtype, const cytnx_int64 &count) - * @deprecated This function is deprecated. Please use \ref Save(const std::string &fname) - * instead for saving raw data together with metadata. + * @see Fromfile(const std::filesystem::path &fname, const unsigned int &dtype, const + * cytnx_int64 &count) + * @deprecated This function is deprecated. Please use \ref Save(const std::filesystem::path + * &fname) instead for saving raw data together with metadata. */ - [[deprecated("Please use Save(const std::string &fname) instead.")]] void Tofile( - const std::string &fname) const; - /// @see Tofile(const std::string &fname) const - [[deprecated("Please use Save(const std::string &fname) instead.")]] void Tofile( + [[deprecated("Please use Save(const std::filesystem::path &fname) instead.")]] void Tofile( + const std::filesystem::path &fname) const; + /// @see Tofile(const std::filesystem::path &fname) const + [[deprecated("Please use Save(const std::filesystem::path &fname) instead.")]] void Tofile( const char *fname) const; - /// @see Tofile(const std::string &fname) const + /// @see Tofile(const std::filesystem::path &fname) const [[deprecated("Please use to_binary(std::ostream &f) instead.")]] void Tofile( std::fstream &f) const; @@ -692,14 +694,15 @@ namespace cytnx { * 4. The @p count cannot be larger than the number of elements in the binary file. * 5. The file name @p fname must be valid. * - * @see Tofile(const std::string &fname) const + * @see Tofile(const std::filesystem::path &fname) const * @deprecated This function is deprecated. Please use Save/Load functions instead for storing * raw data together with metadata. */ [[deprecated("Please use Save/Load functions instead.")]] static Storage Fromfile( - const std::string &fname, const unsigned int &dtype, const cytnx_int64 &count = -1, + const std::filesystem::path &fname, const unsigned int &dtype, const cytnx_int64 &count = -1, const int device = Device.cpu); - // @see Fromfile(const std::string &fname, const unsigned int &dtype, const cytnx_int64 &count) + // @see Fromfile(const std::filesystem::path &fname, const unsigned int &dtype, const + // cytnx_int64 &count) [[deprecated("Please use Save/Load functions instead.")]] static Storage Fromfile( const char *fname, const unsigned int &dtype, const cytnx_int64 &count = -1, const int device = Device.cpu); diff --git a/include/tn_algo/MPS.hpp b/include/tn_algo/MPS.hpp index 95b039fc7..580eada20 100644 --- a/include/tn_algo/MPS.hpp +++ b/include/tn_algo/MPS.hpp @@ -1,6 +1,7 @@ #ifndef CYTNX_TN_ALGO_MPS_H_ #define CYTNX_TN_ALGO_MPS_H_ +#include #include #include #include @@ -296,10 +297,10 @@ namespace cytnx { * @note The common file ending for saving a MPS in binary format is ".cymps". * @warning HDF5 file format is strongly recommended for compatibility with other libraries, * readability, and future-proofing. - * @see Load(const std::string &fname, const bool restore_device) + * @see Load(const std::filesystem::path &fname, const bool restore_device) */ - void Save(const std::string &fname) const; - // @see Save(const std::string &fname) const + void Save(const std::filesystem::path &fname) const; + // @see Save(const std::filesystem::path &fname) const void Save(const char *fname) const; /** @@ -310,31 +311,31 @@ namespace cytnx { * after loading. * @pre The file must be a MPS object which is saved by cytnx::MPS::Save. * @note This function creates a new MPS and keeps the original MPS unchanged. See \link - * Load_(const std::string &fname, const bool restore_device) Load_() \endlink for loading the - * MPS to the current MPS. + * Load_(const std::filesystem::path &fname, const bool restore_device) Load_() \endlink for + * loading the MPS to the current MPS. * @details For HDF5 file format, one of the file endings ".h5", ".hdf5", ".H5", ".HDF5", * ".hdf" is expected. For binary format, the common file ending for a MPS is ".cymps". */ - static MPS Load(const std::string &fname, const bool restore_device = true); - // @see Load(const std::string &fname) + static MPS Load(const std::filesystem::path &fname, const bool restore_device = true); + // @see Load(const std::filesystem::path &fname) static MPS Load(const char *fname, const bool restore_device = true); /** * @brief Load MPS from file and overwrite current instance - * @note This function overwrites the existing MPS. See \link Load(const std::string &fname, - * const bool restore_device) Load() \endlink for creating a new MPS. - * @see Load(const std::string &fname, const bool restore_device) + * @note This function overwrites the existing MPS. See \link Load(const std::filesystem::path + * &fname, const bool restore_device) Load() \endlink for creating a new MPS. + * @see Load(const std::filesystem::path &fname, const bool restore_device) */ - void Load_(const std::string &fname, const bool restore_device = true); - // @see Load_(const std::string &fname, const bool restore_device) + void Load_(const std::filesystem::path &fname, const bool restore_device = true); + // @see Load_(const std::filesystem::path &fname, const bool restore_device) void Load_(const char *fname, const bool restore_device = true); /** * @brief Save MPS to HDF5 file * @param[in] location the HDF5 group where the MPS will be saved. * @param[in] name the name of the MPS in the HDF5 file. - * @warning This function is only available in C++. Use \link Save(const std::string &fname) - * Save() \endlink for saving to file in C++ or Python. + * @warning This function is only available in C++. Use \link Save(const std::filesystem::path + * &fname) Save() \endlink for saving to file in C++ or Python. * @see from_hdf5(H5::Group &location, const std::string &name, const bool restore_device) */ void to_hdf5(H5::Group &location, const std::string &name = "MPS") const; @@ -345,8 +346,8 @@ namespace cytnx { * @param[in] restore_device whether to try restoring the device on which the data is stored; * if false, the data will be kept on the CPU. Use .to_() to move it to the target device * after loading. - * @warning This function is only available in C++. Use \link Load(const std::string &fname, - * const bool restore_device) Load() \endlink for loading from file in C++ or Python. + * @warning This function is only available in C++. Use \link Load(const std::filesystem::path + * &fname, const bool restore_device) Load() \endlink for loading from file in C++ or Python. * @see to_hdf5(H5::Group &location, const std::string &name) const */ void from_hdf5(H5::Group &location, const std::string &name = "MPS", @@ -356,8 +357,8 @@ namespace cytnx { * @brief Save MPS to binary file * @param[in] f the output stream where the MPS will be saved. * @warning This function is only available in C++. In Python, use pickle for the same binary - * file format. Use \link Save(const std::string &fname) Save() \endlink for saving to file in - * C++ or Python. + * file format. Use \link Save(const std::filesystem::path &fname) Save() \endlink for saving + * to file in C++ or Python. * @see from_binary(std::istream &f, const bool restore_device) */ void to_binary(std::ostream &f) const; @@ -368,8 +369,8 @@ namespace cytnx { * if false, the data will be kept on the CPU. Use .to_() to move it to the target device * after loading. * @warning This function is only available in C++. In Python, use pickle for the same binary - * file format. Use \link Load(const std::string &fname, const bool restore_device) Load() - * \endlink for loading from file in C++ or Python. + * file format. Use \link Load(const std::filesystem::path &fname, const bool restore_device) + * Load() \endlink for loading from file in C++ or Python. * @see to_binary(std::ostream &f) const */ void from_binary(std::istream &f, const bool restore_device = true); diff --git a/pybind/algo_py.cpp b/pybind/algo_py.cpp index 53ceb4eaf..9cefd2127 100644 --- a/pybind/algo_py.cpp +++ b/pybind/algo_py.cpp @@ -4,6 +4,7 @@ #include #include +#include #include #include #include diff --git a/pybind/bond_py.cpp b/pybind/bond_py.cpp index e1af5ca63..b0f7dbc2d 100644 --- a/pybind/bond_py.cpp +++ b/pybind/bond_py.cpp @@ -1,18 +1,21 @@ -#include +#include "cytnx.hpp" + +#include #include #include +#include -#include -#include -#include -#include -#include #include #include +#include +#include +#include +#include +#include +#include -#include "cytnx.hpp" -// #include "../include/cytnx_error.hpp" #include "complex.h" +#include "H5Cpp.h" namespace py = pybind11; using namespace pybind11::literals; @@ -179,12 +182,23 @@ void bond_binding(py::module &m) { .def("calc_reverse_qnums", &Bond::calc_reverse_qnums) .def( - "Save", [](Bond &self, const std::string &fname) { self.Save(fname); }, py::arg("fname")) + "Save", + [](Bond &self, const std::filesystem::path &fname, const std::string &path, const char mode) { + self.Save(fname, path, mode); + }, + py::arg("fname"), py::arg("path") = "/", py::arg("mode") = 'w') .def_static( - "Load", [](const std::string &fname) { return Bond::Load(fname); }, py::arg("fname")) + "Load", + [](const std::filesystem::path &fname, const std::string &path) { + return Bond::Load(fname, path); + }, + py::arg("fname"), py::arg("path") = "/") .def( - "Load_", [](cytnx::Bond &self, const std::string &fname) { return self.Load_(fname); }, - py::arg("fname")) + "Load_", + [](cytnx::Bond &self, const std::filesystem::path &fname, const std::string &path) { + return self.Load_(fname, path); + }, + py::arg("fname"), py::arg("path") = "/") .def(py::pickle( [](const Bond &self) { // __getstate__ diff --git a/pybind/cytnx.cpp b/pybind/cytnx.cpp index 603d52454..774f4db4f 100644 --- a/pybind/cytnx.cpp +++ b/pybind/cytnx.cpp @@ -4,6 +4,7 @@ #include #include +#include #include #include #include diff --git a/pybind/generator_py.cpp b/pybind/generator_py.cpp index 06b26d9e9..a716eda6c 100644 --- a/pybind/generator_py.cpp +++ b/pybind/generator_py.cpp @@ -4,6 +4,7 @@ #include #include +#include #include #include #include diff --git a/pybind/linalg_py.cpp b/pybind/linalg_py.cpp index fac480e25..ab5b208f7 100644 --- a/pybind/linalg_py.cpp +++ b/pybind/linalg_py.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include diff --git a/pybind/linop_py.cpp b/pybind/linop_py.cpp index 822a555cf..3c320d1a5 100644 --- a/pybind/linop_py.cpp +++ b/pybind/linop_py.cpp @@ -4,6 +4,7 @@ #include #include +#include #include #include #include diff --git a/pybind/ncon_py.cpp b/pybind/ncon_py.cpp index 77a96d183..df48516fb 100644 --- a/pybind/ncon_py.cpp +++ b/pybind/ncon_py.cpp @@ -4,6 +4,7 @@ #include #include +#include #include #include #include diff --git a/pybind/network_py.cpp b/pybind/network_py.cpp index 5f442d19f..aa7796827 100644 --- a/pybind/network_py.cpp +++ b/pybind/network_py.cpp @@ -4,6 +4,7 @@ #include #include +#include #include #include #include diff --git a/pybind/physics_related_py.cpp b/pybind/physics_related_py.cpp index 8bf5e6717..0255c47a2 100644 --- a/pybind/physics_related_py.cpp +++ b/pybind/physics_related_py.cpp @@ -4,6 +4,7 @@ #include #include +#include #include #include #include diff --git a/pybind/random_py.cpp b/pybind/random_py.cpp index 10b8506d8..6bc27c31b 100644 --- a/pybind/random_py.cpp +++ b/pybind/random_py.cpp @@ -4,6 +4,7 @@ #include #include +#include #include #include #include diff --git a/pybind/scalar_py.cpp b/pybind/scalar_py.cpp index 6cd648864..5f0daa258 100644 --- a/pybind/scalar_py.cpp +++ b/pybind/scalar_py.cpp @@ -4,6 +4,7 @@ #include #include +#include #include #include #include diff --git a/pybind/storage_py.cpp b/pybind/storage_py.cpp index 56bf32686..1e946ab1e 100644 --- a/pybind/storage_py.cpp +++ b/pybind/storage_py.cpp @@ -1,5 +1,6 @@ #include "cytnx.hpp" +#include #include #include #include @@ -12,6 +13,7 @@ #include #include #include +#include #include "complex.h" #include "H5Cpp.h" @@ -261,26 +263,27 @@ void storage_binding(py::module &m) { .def("c_pylist_bool", &cytnx::Storage::vector) .def( - "Save", [](cytnx::Storage &self, const std::string &fname) { self.Save(fname); }, + "Save", [](cytnx::Storage &self, const std::filesystem::path &fname) { self.Save(fname); }, py::arg("fname")) .def( - "Tofile", [](cytnx::Storage &self, const std::string &fname) { self.Tofile(fname); }, + "Tofile", + [](cytnx::Storage &self, const std::filesystem::path &fname) { self.Tofile(fname); }, py::arg("fname")) .def_static( "Load", - [](const std::string &fname, const bool restore_device) { + [](const std::filesystem::path &fname, const bool restore_device) { return cytnx::Storage::Load(fname, restore_device); }, py::arg("fname"), py::arg("restore_device") = true) .def( "Load_", - [](cytnx::Storage &self, const std::string &fname, const bool restore_device) { + [](cytnx::Storage &self, const std::filesystem::path &fname, const bool restore_device) { return self.Load_(fname, restore_device); }, py::arg("fname"), py::arg("restore_device") = true) .def_static( "Fromfile", - [](const std::string &fname, const unsigned int &dtype, const cytnx_int64 &count, + [](const std::filesystem::path &fname, const unsigned int &dtype, const cytnx_int64 &count, const int device) { return cytnx::Storage::Fromfile(fname, dtype, count, device); }, py::arg("fname"), py::arg("dtype"), py::arg("count") = (cytnx_int64)(-1), py::arg("device") = (int)cytnx::Device.cpu) diff --git a/pybind/symmetry_py.cpp b/pybind/symmetry_py.cpp index 3f2cdc358..b4335d6ec 100644 --- a/pybind/symmetry_py.cpp +++ b/pybind/symmetry_py.cpp @@ -1,17 +1,21 @@ -#include +#include "cytnx.hpp" + +#include #include #include +#include -#include -#include -#include -#include -#include #include #include +#include +#include +#include +#include +#include +#include -#include "cytnx.hpp" #include "complex.h" +#include "H5Cpp.h" namespace py = pybind11; using namespace pybind11::literals; @@ -67,12 +71,22 @@ void symmetry_binding(py::module &m) { .def("is_fermionic", &Symmetry::is_fermionic) .def( - "Save", [](Symmetry &self, const std::string &fname) { self.Save(fname); }, py::arg("fname")) + "Save", + [](Symmetry &self, const std::filesystem::path &fname, const std::string &path, + const char mode) { self.Save(fname, path, mode); }, + py::arg("fname"), py::arg("path") = "/Symmetry", py::arg("mode") = 'w') .def_static( - "Load", [](const std::string &fname) { return Symmetry::Load(fname); }, py::arg("fname")) + "Load", + [](const std::filesystem::path &fname, const std::string &path) { + return Symmetry::Load(fname, path); + }, + py::arg("fname"), py::arg("path") = "/Symmetry") .def( - "Load_", [](cytnx::Symmetry &self, const std::string &fname) { return self.Load_(fname); }, - py::arg("fname")) + "Load_", + [](cytnx::Symmetry &self, const std::filesystem::path &fname, const std::string &path) { + return self.Load_(fname, path); + }, + py::arg("fname"), py::arg("path") = "/Symmetry") .def(py::pickle( [](const Symmetry &self) { // __getstate__ diff --git a/pybind/tensor_py.cpp b/pybind/tensor_py.cpp index bcfc139a3..0c9b91cd0 100644 --- a/pybind/tensor_py.cpp +++ b/pybind/tensor_py.cpp @@ -1,5 +1,6 @@ #include "cytnx.hpp" +#include #include #include #include @@ -11,6 +12,7 @@ #include #include #include +#include #include "complex.h" #include "H5Cpp.h" @@ -289,17 +291,17 @@ void tensor_binding(py::module &m) { py::arg("val")) .def( - "Save", [](cytnx::Tensor &self, const std::string &fname) { self.Save(fname); }, + "Save", [](cytnx::Tensor &self, const std::filesystem::path &fname) { self.Save(fname); }, py::arg("fname")) .def_static( "Load", - [](const std::string &fname, const bool restore_device) { + [](const std::filesystem::path &fname, const bool restore_device) { return cytnx::Tensor::Load(fname, restore_device); }, py::arg("fname"), py::arg("restore_device") = true) .def( "Load_", - [](cytnx::Tensor &self, const std::string &fname, const bool restore_device) { + [](cytnx::Tensor &self, const std::filesystem::path &fname, const bool restore_device) { return self.Load_(fname, restore_device); }, py::arg("fname"), py::arg("restore_device") = true) @@ -319,11 +321,12 @@ void tensor_binding(py::module &m) { })) .def( - "Tofile", [](cytnx::Tensor &self, const std::string &fname) { self.Tofile(fname); }, + "Tofile", [](cytnx::Tensor &self, const std::filesystem::path &fname) { self.Tofile(fname); }, py::arg("fname")) .def_static( "Fromfile", - [](const std::string &fname, const unsigned int &dtype, const cytnx::cytnx_int64 &count, + [](const std::filesystem::path &fname, const unsigned int &dtype, + const cytnx::cytnx_int64 &count, const int device) { return cytnx::Tensor::Fromfile(fname, dtype, count, device); }, py::arg("fname"), py::arg("dtype"), py::arg("count") = cytnx::cytnx_int64(-1), py::arg("device") = (int)cytnx::Device.cpu) diff --git a/pybind/tnalgo_py.cpp b/pybind/tnalgo_py.cpp index 5896f9686..462730091 100644 --- a/pybind/tnalgo_py.cpp +++ b/pybind/tnalgo_py.cpp @@ -1,18 +1,21 @@ -#include +#include "cytnx.hpp" + +#include #include #include +#include -#include -#include -#include -#include -#include #include #include +#include +#include +#include +#include +#include +#include -#include "cytnx.hpp" -// #include "../include/cytnx_error.hpp" #include "complex.h" +#include "H5Cpp.h" namespace py = pybind11; using namespace pybind11::literals; @@ -52,14 +55,16 @@ void tnalgo_binding(py::module &m) { .def("c_S_mvleft", &tn_algo::MPS::S_mvleft) .def("c_S_mvright", &tn_algo::MPS::S_mvright) .def( - "Save", [](tn_algo::MPS &self, const std::string &fname) { self.Save(fname); }, + "Save", [](tn_algo::MPS &self, const std::filesystem::path &fname) { self.Save(fname); }, py::arg("fname")) .def_static( - "Load", [](const std::string &fname) { return cytnx::tn_algo::MPS::Load(fname); }, + "Load", [](const std::filesystem::path &fname) { return cytnx::tn_algo::MPS::Load(fname); }, py::arg("fname")) .def( "Load_", - [](cytnx::tn_algo::MPS &self, const std::string &fname) { return self.Load_(fname); }, + [](cytnx::tn_algo::MPS &self, const std::filesystem::path &fname) { + return self.Load_(fname); + }, py::arg("fname")) .def(py::pickle( diff --git a/pybind/unitensor_py.cpp b/pybind/unitensor_py.cpp index a14ccd594..ba3c41052 100644 --- a/pybind/unitensor_py.cpp +++ b/pybind/unitensor_py.cpp @@ -1,19 +1,23 @@ +#include "cytnx.hpp" + +#include #include -#include #include #include #include +#include -#include -#include -#include -#include -#include #include #include +#include +#include +#include +#include +#include +#include #include -#include "cytnx.hpp" +#include "H5Cpp.h" namespace py = pybind11; using namespace pybind11::literals; @@ -516,16 +520,16 @@ void unitensor_binding(py::module &m) { .def("__copy__", &UniTensor::clone) .def("__deepcopy__", &UniTensor::clone) .def( - "Save", [](UniTensor &self, const std::string &fname) { self.Save(fname); }, py::arg("fname")) + "Save", [](UniTensor &self, const std::filesystem::path &fname) { self.Save(fname); }, py::arg("fname")) .def_static( "Load", - [](const std::string &fname, const bool restore_device) { + [](const std::filesystem::path &fname, const bool restore_device) { return cytnx::UniTensor::Load(fname, restore_device); }, py::arg("fname"), py::arg("restore_device") = true) .def( "Load_", - [](cytnx::UniTensor &self, const std::string &fname, const bool restore_device) { + [](cytnx::UniTensor &self, const std::filesystem::path &fname, const bool restore_device) { return self.Load_(fname, restore_device); }, py::arg("fname"), py::arg("restore_device") = true) diff --git a/src/Bond.cpp b/src/Bond.cpp index 2752ab321..f99c11ec5 100644 --- a/src/Bond.cpp +++ b/src/Bond.cpp @@ -471,33 +471,70 @@ namespace cytnx { bool Bond::operator!=(const Bond &rhs) const { return !(*this == rhs); } - void Bond::Save(const std::string &fname) const { + void Bond::Save(const std::filesystem::path &fname, const std::string &path, + const char mode) const { fstream f; // only for binary saving, not used for hdf5 - if (std::filesystem::path(fname).has_extension()) { + if (fname.has_extension()) { // filename extension is given - auto ext = std::filesystem::path(fname).extension().string(); + std::string ext = fname.extension().string(); if (ext == ".h5" || ext == ".hdf5" || ext == ".H5" || ext == ".HDF5" || ext == ".hdf" || ext == ".HDF") { // save as hdf5 H5::H5File h5file; - try { - h5file = H5::H5File(fname, H5F_ACC_TRUNC); + try { // overwrite file + if (mode == 'w') { + h5file = H5::H5File(fname, H5F_ACC_TRUNC); + } else if (mode == 'a' || mode == 'u') { + if (std::filesystem::exists(fname)) + h5file = H5::H5File(fname, H5F_ACC_RDWR); + else + h5file = H5::H5File(fname, H5F_ACC_EXCL); + } else if (mode == 'x') { // create a new file + h5file = H5::H5File(fname, H5F_ACC_EXCL); + } } catch (H5::FileIException &error) { error.printErrorStack(); cytnx_error_msg(true, "[ERROR] Cannot create HDF5 file '%s'.\n", fname.c_str()); } - this->to_hdf5(h5file); + // create group + H5::Group location = h5file; + if (h5file.exists(path)) { + location = h5file.openGroup(path); + } else { + H5::LinkCreatPropList lcpl; + lcpl.setCreateIntermediateGroup(1); + location = h5file.createGroup(path, lcpl); + } + if (mode == 'u') + this->to_hdf5(location, true); + else + this->to_hdf5(location, false); h5file.close(); return; } else { // create binary file - f.open(fname, ios::out | ios::trunc | ios::binary); + if (mode == 'x') { + cytnx_error_msg(std::filesystem::exists(fname), + "[ERROR] File %s already exists. Use mode 'w' to overwrite.", fname); + } else { + cytnx_error_msg((mode != 'x'), "[ERROR] Unknown mode '%c' for writing to binary file.", + mode); + } + f.open(fname, std::ios::out | std::ios::trunc | std::ios::binary); } } else { // create binary file with standard extension cytnx_warning_msg(true, "Missing file extension in fname '%s'. I am adding the extension '.cybd'. " "This is deprecated, please provide the file extension in the future.\n", fname.c_str()); - f.open((fname + ".cybd"), ios::out | ios::trunc | ios::binary); + if (mode == 'x') { + cytnx_error_msg(std::filesystem::exists(fname), + "[ERROR] File %s already exists. Use mode 'w' to overwrite.", fname); + } else { + cytnx_error_msg((mode != 'x'), "[ERROR] Unknown mode '%c' for writing to binary file.", + mode); + } + f.open(std::filesystem::path(fname) += ".cybd", + std::ios::out | std::ios::trunc | std::ios::binary); } // write binary if (!f.is_open()) { @@ -506,17 +543,21 @@ namespace cytnx { this->to_binary(f); f.close(); } - void Bond::Save(const char *fname) const { this->Save(string(fname)); } + void Bond::Save(const char *fname, const std::string &path, const char mode) const { + this->Save(string(fname), path, mode); + } - Bond Bond::Load(const std::string &fname) { + Bond Bond::Load(const std::filesystem::path &fname, const std::string &path) { Bond out; - out.Load_(fname); + out.Load_(fname, path); return out; } - Bond Bond::Load(const char *fname) { return Bond::Load(string(fname)); } + Bond Bond::Load(const char *fname, const std::string &path) { + return Bond::Load(string(fname), path); + } - void Bond::Load_(const std::string &fname) { - auto ext = std::filesystem::path(fname).extension().string(); + void Bond::Load_(const std::filesystem::path &fname, const std::string &path) { + std::string ext = fname.extension().string(); if (ext == ".h5" || ext == ".hdf5" || ext == ".H5" || ext == ".HDF5" || ext == ".hdf" || ext == ".HDF") { // load hdf5 @@ -527,11 +568,14 @@ namespace cytnx { error.printErrorStack(); cytnx_error_msg(true, "[ERROR] Cannot open HDF5 file '%s'.\n", fname.c_str()); } - this->from_hdf5(h5file); + cytnx_error_msg(!h5file.exists(path), "[ERROR] Path '%s' does not exist in HDF5 file '%s'", + path, fname); + H5::Group location = h5file.openGroup(path); + this->from_hdf5(location); h5file.close(); } else { // load binary fstream f; - f.open(fname, ios::in | ios::binary); + f.open(fname, std::ios::in | std::ios::binary); if (!f.is_open()) { cytnx_error_msg(true, "[ERROR] Cannot open file '%s'.\n", fname.c_str()); } @@ -539,15 +583,26 @@ namespace cytnx { f.close(); } } - void Bond::Load_(const char *fname) { this->Load_(string(fname)); } + void Bond::Load_(const char *fname, const std::string &path) { this->Load_(string(fname), path); } - void Bond::to_hdf5(H5::Group &location, const bool save_symmetries) const { + void Bond::to_hdf5(H5::Group &location, const bool overwrite, const bool save_symmetries) const { H5::DataType datatype; H5::Attribute attr; H5::DataSet dataset; H5::DataSpace dataspace; H5::StrType str_type; + if (overwrite) { // delete previous data + // remove attributes + if (location.attrExists("dimension")) location.removeAttr("dimension"); + if (location.attrExists("type")) location.removeAttr("type"); + // remove datasets + if (location.nameExists("degeneracies")) location.unlink("degeneracies"); + if (location.nameExists("quantum_numbers")) location.unlink("quantum_numbers"); + // remove groups and its contents recursively + if (location.nameExists("Symmetries")) location.unlink("Symmetries"); + } + // dimension, write as attribute auto dim = this->_impl->_dim; datatype = Type.get_hdf5_type(dim); @@ -607,7 +662,7 @@ namespace cytnx { // dataset.write(c_strings.data(), str_type); H5::Group symloc = location.createGroup("Symmetries"); for (int sidx = 0; sidx < this->_impl->_syms.size(); sidx++) { - this->_impl->_syms[sidx].to_hdf5(symloc, "Symmetry" + std::to_string(sidx)); + this->_impl->_syms[sidx].to_hdf5(symloc, overwrite, "Symmetry" + std::to_string(sidx)); } } } diff --git a/src/Gncon_base.cpp b/src/Gncon_base.cpp index 334783a0b..6766231a4 100644 --- a/src/Gncon_base.cpp +++ b/src/Gncon_base.cpp @@ -1,6 +1,7 @@ +#include #include -#include "Gncon.hpp" +#include "Gncon.hpp" #include "linalg.hpp" using namespace std; @@ -14,13 +15,13 @@ namespace cytnx { const std::string &contract_order) { cytnx_error_msg(true, "[ERROR][Gncon][Contract_plan] call from uninitialize Gncon.%s", "\n"); } - void Gncon_base::Fromfile(const std::string &fname) { + void Gncon_base::Fromfile(const std::filesystem::path &fname) { cytnx_error_msg(true, "[ERROR][Gncon][Fromfile] call from uninitialize Gncon.%s", "\n"); } void Gncon_base::FromString(const std::vector &fname) { cytnx_error_msg(true, "[ERROR][Gncon][FromString] call from uninitialize Gncon.%s", "\n"); } - void Gncon_base::Savefile(const std::string &fname) { + void Gncon_base::Savefile(const std::filesystem::path &fname) { cytnx_error_msg(true, "[ERROR][Gncon][Savefile] call from uninitialize Gncon.%s", "\n"); } void Gncon_base::PutUniTensor(const std::string &name, const UniTensor &utensor) { diff --git a/src/Network_base.cpp b/src/Network_base.cpp index 6877fc8c7..e04cd7b85 100644 --- a/src/Network_base.cpp +++ b/src/Network_base.cpp @@ -1,6 +1,8 @@ -#include #include "Network.hpp" +#include +#include + #include "linalg.hpp" using namespace std; @@ -15,13 +17,13 @@ namespace cytnx { cytnx_error_msg(true, "[ERROR][Network][Contract_plan] call from uninitialized network.%s", "\n"); } - void Network_base::Fromfile(const std::string &fname) { + void Network_base::Fromfile(const std::filesystem::path &fname) { cytnx_error_msg(true, "[ERROR][Network][Fromfile] call from uninitialized network.%s", "\n"); } void Network_base::FromString(const std::vector &fname) { cytnx_error_msg(true, "[ERROR][Network][FromString] call from uninitialized network.%s", "\n"); } - void Network_base::Savefile(const std::string &fname) { + void Network_base::Savefile(const std::filesystem::path &fname) { cytnx_error_msg(true, "[ERROR][Network][Savefile] call from uninitialized network.%s", "\n"); } void Network_base::PutUniTensor(const std::string &name, const UniTensor &utensor) { diff --git a/src/RegularGncon.cpp b/src/RegularGncon.cpp index bf23de315..557d1692b 100644 --- a/src/RegularGncon.cpp +++ b/src/RegularGncon.cpp @@ -1,10 +1,12 @@ -#include #include "Gncon.hpp" -#include "search_tree.hpp" -#include #include +#include #include +#include +#include + +#include "search_tree.hpp" using namespace std; @@ -402,7 +404,7 @@ namespace cytnx { // print_gn(this->table, this->names, this->name2pos); } - void RegularGncon::Fromfile(const std::string &fname) { + void RegularGncon::Fromfile(const std::filesystem::path &fname) { const cytnx_uint64 MAXLINES = 1024; // empty all @@ -482,13 +484,13 @@ namespace cytnx { } } - void RegularGncon::Savefile(const std::string &fname) { + void RegularGncon::Savefile(const std::filesystem::path &fname) { cytnx_error_msg(this->label_arr.size() == 0, "[ERROR][RegularGncon][Savefile] Cannot save empty Gncon to Gncon file!%s", "\n"); fstream fo; - fo.open(fname + ".net", ios::out | ios::trunc); + fo.open(std::filesystem::path(fname) += ".net", ios::out | ios::trunc); if (!fo.is_open()) { cytnx_error_msg(true, "[ERROR][RegularGncon][Savefile] Cannot open/create file:%s\n", fname.c_str()); diff --git a/src/RegularNetwork.cpp b/src/RegularNetwork.cpp index 0d5a1d111..e9ee798a8 100644 --- a/src/RegularNetwork.cpp +++ b/src/RegularNetwork.cpp @@ -650,7 +650,7 @@ namespace cytnx { this->einsum_path = CtTree_to_eisumpath(CtTree, names); } // end of FromString - void RegularNetwork::Fromfile(const string &fname) { + void RegularNetwork::Fromfile(const std::filesystem::path &fname) { const cytnx_uint64 MAXLINES = 1024; // empty all @@ -738,13 +738,13 @@ namespace cytnx { } } - void RegularNetwork::Savefile(const string &fname) { + void RegularNetwork::Savefile(const std::filesystem::path &fname) { cytnx_error_msg( this->label_arr.size() == 0, "[ERROR][RegularNetwork][Savefile] Cannot save empty network to network file!%s", "\n"); fstream fo; - fo.open(fname + ".net", ios::out | ios::trunc); + fo.open(std::filesystem::path(fname) += ".net", ios::out | ios::trunc); if (!fo.is_open()) { cytnx_error_msg(true, "[ERROR][RegularNetwork][Savefile] Cannot open/create file:%s\n", fname.c_str()); diff --git a/src/Symmetry.cpp b/src/Symmetry.cpp index a9635c9b5..f98331d57 100644 --- a/src/Symmetry.cpp +++ b/src/Symmetry.cpp @@ -245,33 +245,75 @@ namespace cytnx { //================================================== - void cytnx::Symmetry::Save(const std::string &fname) const { + void cytnx::Symmetry::Save(const std::filesystem::path &fname, const std::string &path, + const char mode) const { fstream f; // only for binary saving, not used for hdf5 - if (std::filesystem::path(fname).has_extension()) { + if (fname.has_extension()) { // filename extension is given - auto ext = std::filesystem::path(fname).extension().string(); + std::string ext = fname.extension().string(); if (ext == ".h5" || ext == ".hdf5" || ext == ".H5" || ext == ".HDF5" || ext == ".hdf" || ext == ".HDF") { // save as hdf5 H5::H5File h5file; - try { - h5file = H5::H5File(fname, H5F_ACC_TRUNC); + try { // overwrite file + if (mode == 'w') { + h5file = H5::H5File(fname, H5F_ACC_TRUNC); + } else if (mode == 'a' || mode == 'u') { + if (std::filesystem::exists(fname)) + h5file = H5::H5File(fname, H5F_ACC_RDWR); + else + h5file = H5::H5File(fname, H5F_ACC_EXCL); + } else if (mode == 'x') { // create a new file + h5file = H5::H5File(fname, H5F_ACC_EXCL); + } } catch (H5::FileIException &error) { error.printErrorStack(); cytnx_error_msg(true, "[ERROR] Cannot create HDF5 file '%s'.\n", fname.c_str()); } - this->to_hdf5(h5file); + // split path into group and name + std::filesystem::path p(path); + std::string grouppath = p.parent_path().generic_string(); + std::string datasetname = p.filename().string(); + if (datasetname.empty()) datasetname = "Symmetry"; + // create group + H5::Group location = h5file; + if (h5file.exists(grouppath)) { + location = h5file.openGroup(grouppath); + } else { + H5::LinkCreatPropList lcpl; + lcpl.setCreateIntermediateGroup(1); + location = h5file.createGroup(grouppath, lcpl); + } + if (mode == 'u') + this->to_hdf5(location, true, datasetname); + else + this->to_hdf5(location, false, datasetname); h5file.close(); return; } else { // create binary file - f.open(fname, ios::out | ios::trunc | ios::binary); + if (mode == 'x') { + cytnx_error_msg(std::filesystem::exists(fname), + "[ERROR] File %s already exists. Use mode 'w' to overwrite.", fname); + } else { + cytnx_error_msg((mode != 'x'), "[ERROR] Unknown mode '%c' for writing to binary file.", + mode); + } + f.open(fname, std::ios::out | std::ios::trunc | std::ios::binary); } } else { // create binary file with standard extension cytnx_warning_msg(true, "Missing file extension in fname '%s'. I am adding the extension '.cysym'. " "This is deprecated, please provide the file extension in the future.\n", fname.c_str()); - f.open((fname + ".cysym"), ios::out | ios::trunc | ios::binary); + if (mode == 'x') { + cytnx_error_msg(std::filesystem::exists(fname), + "[ERROR] File %s already exists. Use mode 'w' to overwrite.", fname); + } else { + cytnx_error_msg((mode != 'x'), "[ERROR] Unknown mode '%c' for writing to binary file.", + mode); + } + f.open(std::filesystem::path(fname) += ".cysym", + std::ios::out | std::ios::trunc | std::ios::binary); } // write binary if (!f.is_open()) { @@ -280,19 +322,22 @@ namespace cytnx { this->to_binary(f); f.close(); } - void cytnx::Symmetry::Save(const char *fname) const { this->Save(string(fname)); } + void cytnx::Symmetry::Save(const char *fname, const std::string &path, const char mode) const { + this->Save(string(fname), path, mode); + } - cytnx::Symmetry cytnx::Symmetry::Load(const std::string &fname) { + cytnx::Symmetry cytnx::Symmetry::Load(const std::filesystem::path &fname, + const std::string &path) { Symmetry out; - out.Load_(fname); + out.Load_(fname, path); return out; } - cytnx::Symmetry cytnx::Symmetry::Load(const char *fname) { - return cytnx::Symmetry::Load(string(fname)); + cytnx::Symmetry cytnx::Symmetry::Load(const char *fname, const std::string &path) { + return cytnx::Symmetry::Load(string(fname), path); } - void cytnx::Symmetry::Load_(const std::string &fname) { - auto ext = std::filesystem::path(fname).extension().string(); + void cytnx::Symmetry::Load_(const std::filesystem::path &fname, const std::string &path) { + std::string ext = fname.extension().string(); if (ext == ".h5" || ext == ".hdf5" || ext == ".H5" || ext == ".HDF5" || ext == ".hdf" || ext == ".HDF") { // load hdf5 @@ -303,7 +348,17 @@ namespace cytnx { error.printErrorStack(); cytnx_error_msg(true, "[ERROR] Cannot open HDF5 file '%s'.\n", fname.c_str()); } - this->from_hdf5(h5file); + + // split path into group and name + std::filesystem::path p(path); + std::string grouppath = p.parent_path().generic_string(); + std::string datasetname = p.filename().string(); + if (datasetname.empty()) datasetname = "Symmetry"; + // open group + cytnx_error_msg(!h5file.exists(grouppath), + "[ERROR] Path '%s' does not exist in HDF5 file '%s'", grouppath, fname); + H5::Group location = h5file.openGroup(grouppath); + this->from_hdf5(location); h5file.close(); } else { // load binary fstream f; @@ -315,9 +370,16 @@ namespace cytnx { f.close(); } } - void cytnx::Symmetry::Load_(const char *fname) { this->Load_(string(fname)); } + void cytnx::Symmetry::Load_(const char *fname, const std::string &path) { + this->Load_(string(fname), path); + } + + void cytnx::Symmetry::to_hdf5(H5::Group &location, const bool overwrite, + const std::string &name) const { + if (overwrite) { // delete previous data + if (location.attrExists(name)) location.removeAttr(name); + } - void cytnx::Symmetry::to_hdf5(H5::Group &location, const std::string &name) const { std::string symname = this->name(); H5::StrType str_type(H5::PredType::C_S1, symname.length() + 1); H5::DataSpace dataspace = H5::DataSpace(H5S_SCALAR); diff --git a/src/Tensor.cpp b/src/Tensor.cpp index 769e92bcf..aa9ce1113 100644 --- a/src/Tensor.cpp +++ b/src/Tensor.cpp @@ -425,7 +425,7 @@ namespace cytnx { //=================================================================== // wrapper - void Tensor::Tofile(const std::string &fname) const { + void Tensor::Tofile(const std::filesystem::path &fname) const { if (!this->is_contiguous()) { auto A = this->contiguous(); A.storage().Tofile(fname); @@ -450,11 +450,11 @@ namespace cytnx { } } - void Tensor::Save(const std::string &fname) const { + void Tensor::Save(const std::filesystem::path &fname) const { fstream f; // only for binary saving, not used for hdf5 - if (std::filesystem::path(fname).has_extension()) { + if (fname.has_extension()) { // filename extension is given - auto ext = std::filesystem::path(fname).extension().string(); + std::string ext = fname.extension().string(); if (ext == ".h5" || ext == ".hdf5" || ext == ".H5" || ext == ".HDF5" || ext == ".hdf" || ext == ".HDF") { // save as hdf5 @@ -476,7 +476,7 @@ namespace cytnx { "Missing file extension in fname '%s'. I am adding the extension '.cytn'. " "This is deprecated, please provide the file extension in the future.\n", fname.c_str()); - f.open((fname + ".cytn"), ios::out | ios::trunc | ios::binary); + f.open((std::filesystem::path(fname) += ".cytn"), ios::out | ios::trunc | ios::binary); } // write binary if (!f.is_open()) { @@ -519,7 +519,7 @@ namespace cytnx { this->storage().to_binary(f); } - Tensor Tensor::Fromfile(const std::string &fname, const unsigned int &dtype, + Tensor Tensor::Fromfile(const std::filesystem::path &fname, const unsigned int &dtype, const cytnx_int64 &count, const int device) { return Tensor::from_storage(Storage::Fromfile(fname, dtype, count, device)); } @@ -527,7 +527,7 @@ namespace cytnx { const int device) { return Tensor::from_storage(Storage::Fromfile(fname, dtype, count, device)); } - Tensor Tensor::Load(const std::string &fname, const bool restore_device) { + Tensor Tensor::Load(const std::filesystem::path &fname, const bool restore_device) { Tensor out; out.Load_(fname, restore_device); return out; @@ -536,8 +536,8 @@ namespace cytnx { return Tensor::Load(string(fname), restore_device); } - void Tensor::Load_(const std::string &fname, const bool restore_device) { - auto ext = std::filesystem::path(fname).extension().string(); + void Tensor::Load_(const std::filesystem::path &fname, const bool restore_device) { + std::string ext = fname.extension().string(); if (ext == ".h5" || ext == ".hdf5" || ext == ".H5" || ext == ".HDF5" || ext == ".hdf" || ext == ".HDF") { // load hdf5 diff --git a/src/UniTensor.cpp b/src/UniTensor.cpp index eddf78564..60f635305 100644 --- a/src/UniTensor.cpp +++ b/src/UniTensor.cpp @@ -43,11 +43,11 @@ namespace cytnx { UniTensor UniTensor::Mul(const UniTensor &rhs) const { return cytnx::linalg::Mul(*this, rhs); } UniTensor UniTensor::Mul(const Scalar &rhs) const { return cytnx::linalg::Mul(*this, rhs); } - void UniTensor::Save(const std::string &fname) const { + void UniTensor::Save(const std::filesystem::path &fname) const { fstream f; // only for binary saving, not used for hdf5 - if (std::filesystem::path(fname).has_extension()) { + if (fname.has_extension()) { // filename extension is given - auto ext = std::filesystem::path(fname).extension().string(); + std::string ext = fname.extension().string(); if (ext == ".h5" || ext == ".hdf5" || ext == ".H5" || ext == ".HDF5" || ext == ".hdf" || ext == ".HDF") { // save as hdf5 @@ -69,7 +69,7 @@ namespace cytnx { "Missing file extension in fname '%s'. I am adding the extension '.cytnx'. " "This is deprecated, please provide the file extension in the future.\n", fname.c_str()); - f.open((fname + ".cytnx"), ios::out | ios::trunc | ios::binary); + f.open((std::filesystem::path(fname) += ".cytnx"), ios::out | ios::trunc | ios::binary); } // write binary if (!f.is_open()) { @@ -80,7 +80,7 @@ namespace cytnx { } void UniTensor::Save(const char *fname) const { Save(string(fname)); } - UniTensor UniTensor::Load(const std::string &fname, const bool restore_device) { + UniTensor UniTensor::Load(const std::filesystem::path &fname, const bool restore_device) { UniTensor out; out.Load_(fname, restore_device); return out; @@ -89,8 +89,8 @@ namespace cytnx { return UniTensor::Load(string(fname), restore_device); } - void UniTensor::Load_(const std::string &fname, const bool restore_device) { - auto ext = std::filesystem::path(fname).extension().string(); + void UniTensor::Load_(const std::filesystem::path &fname, const bool restore_device) { + std::string ext = fname.extension().string(); if (ext == ".h5" || ext == ".hdf5" || ext == ".H5" || ext == ".HDF5" || ext == ".hdf" || ext == ".HDF") { // load hdf5 diff --git a/src/backend/Storage.cpp b/src/backend/Storage.cpp index 26f74db81..028327c64 100644 --- a/src/backend/Storage.cpp +++ b/src/backend/Storage.cpp @@ -84,11 +84,11 @@ namespace cytnx { } bool Storage::operator!=(const Storage &rhs) { return !(*this == rhs); } - void Storage::Save(const std::string &fname) const { + void Storage::Save(const std::filesystem::path &fname) const { fstream f; - if (std::filesystem::path(fname).has_extension()) { + if (fname.has_extension()) { // filename extension is given - auto ext = std::filesystem::path(fname).extension().string(); + std::string ext = fname.extension().string(); if (ext == ".h5" || ext == ".hdf5" || ext == ".H5" || ext == ".HDF5" || ext == ".hdf" || ext == ".HDF") { // save as hdf5 @@ -110,7 +110,7 @@ namespace cytnx { "Missing file extension in fname '%s'. I am adding the extension '.cyst'. " "This is deprecated, please provide the file extension in the future.\n", fname.c_str()); - f.open((fname + ".cyst"), ios::out | ios::trunc | ios::binary); + f.open((std::filesystem::path(fname) += ".cyst"), ios::out | ios::trunc | ios::binary); } // write binary if (!f.is_open()) { @@ -121,7 +121,7 @@ namespace cytnx { } void Storage::Save(const char *fname) const { this->Save(string(fname)); } - void Storage::Tofile(const std::string &fname) const { + void Storage::Tofile(const std::filesystem::path &fname) const { fstream f; f.open(fname, ios::out | ios::trunc | ios::binary); if (!f.is_open()) { @@ -212,7 +212,7 @@ namespace cytnx { const int device) { return Storage::Fromfile(string(fname), dtype, count, device); } - Storage Storage::Fromfile(const std::string &fname, const unsigned int &dtype, + Storage Storage::Fromfile(const std::filesystem::path &fname, const unsigned int &dtype, const cytnx_int64 &count, const int device) { cytnx_error_msg(dtype == Type.Void, "[ERROR] Cannot have Void dtype.%s", "\n"); cytnx_error_msg(count == 0, "[ERROR] count cannot be zero!%s", "\n"); @@ -253,7 +253,7 @@ namespace cytnx { return out; } - Storage Storage::Load(const std::string &fname, const bool restore_device) { + Storage Storage::Load(const std::filesystem::path &fname, const bool restore_device) { Storage out; out.Load_(fname, restore_device); return out; @@ -262,8 +262,8 @@ namespace cytnx { return Storage::Load(string(fname), restore_device); } - void Storage::Load_(const std::string &fname, const bool restore_device) { - auto ext = std::filesystem::path(fname).extension().string(); + void Storage::Load_(const std::filesystem::path &fname, const bool restore_device) { + std::string ext = fname.extension().string(); if (ext == ".h5" || ext == ".hdf5" || ext == ".H5" || ext == ".HDF5" || ext == ".hdf" || ext == ".HDF") { // load hdf5 diff --git a/src/tn_algo/MPS.cpp b/src/tn_algo/MPS.cpp index fe92e727c..9b0410cea 100644 --- a/src/tn_algo/MPS.cpp +++ b/src/tn_algo/MPS.cpp @@ -62,11 +62,11 @@ namespace cytnx { this->_impl->from_binary_dispatch(f, restore_device); } - void MPS::Save(const std::string& fname) const { + void MPS::Save(const std::filesystem::path& fname) const { fstream f; // only for binary saving, not used for hdf5 - if (std::filesystem::path(fname).has_extension()) { + if (fname.has_extension()) { // filename extension is given - auto ext = std::filesystem::path(fname).extension().string(); + std::string ext = fname.extension().string(); if (ext == ".h5" || ext == ".hdf5" || ext == ".H5" || ext == ".HDF5" || ext == ".hdf" || ext == ".HDF") { // save as hdf5 @@ -89,7 +89,7 @@ namespace cytnx { "Missing file extension in fname '%s'. I am adding the extension '.cymps'. This is " "deprecated, please provide the file extension in the future.\n", fname.c_str()); - f.open((fname + ".cymps"), ios::out | ios::trunc | ios::binary); + f.open((std::filesystem::path(fname) += ".cymps"), ios::out | ios::trunc | ios::binary); } // write binary if (!f.is_open()) { @@ -100,7 +100,7 @@ namespace cytnx { } void MPS::Save(const char* fname) const { this->Save(string(fname)); } - MPS MPS::Load(const std::string& fname, const bool restore_device) { + MPS MPS::Load(const std::filesystem::path& fname, const bool restore_device) { MPS out; out.Load_(fname, restore_device); return out; @@ -109,8 +109,8 @@ namespace cytnx { return MPS::Load(string(fname), restore_device); } - void MPS::Load_(const std::string& fname, const bool restore_device) { - auto ext = std::filesystem::path(fname).extension().string(); + void MPS::Load_(const std::filesystem::path& fname, const bool restore_device) { + std::string ext = fname.extension().string(); if (ext == ".h5" || ext == ".hdf5" || ext == ".H5" || ext == ".HDF5" || ext == ".hdf" || ext == ".HDF") { // load hdf5 From b39226558e3f5946ea428acb134c30d70a1b4a3c Mon Sep 17 00:00:00 2001 From: Manuel Schneider Date: Fri, 1 May 2026 02:20:54 +0800 Subject: [PATCH 12/40] fixed bugs, implemented Save/Load path for Tensor --- include/Bond.hpp | 82 ++++++-------- include/Symmetry.hpp | 82 ++++++-------- include/Tensor.hpp | 99 ++++++++--------- pybind/tensor_py.cpp | 18 +-- src/Bond.cpp | 91 +++++++-------- src/Symmetry.cpp | 88 +++++++-------- src/Tensor.cpp | 241 ++++++++++++++++++++++++---------------- src/UniTensor.cpp | 6 +- src/backend/Storage.cpp | 8 +- src/tn_algo/MPS.cpp | 6 +- 10 files changed, 357 insertions(+), 364 deletions(-) diff --git a/include/Bond.hpp b/include/Bond.hpp index 2a4239c32..8e8f5b283 100644 --- a/include/Bond.hpp +++ b/include/Bond.hpp @@ -858,8 +858,7 @@ namespace cytnx { /** * @brief Save Bond to file * @param[in] fname file name - * @param[in] path path inside the file. Only used for HDF5 files. A path '/foo/bar/' will - * write the Bond to the group '/foo/bar' in the file. + * @param[in] path path inside the file. Only used for HDF5 files. A path '/foo/bar/' will write the Bond to the group '/foo/bar' in the file. * @param[in] mode the write mode:\n * `w` Creates a new file. If the given file exists, its contents are destroyed.\n * `x` Creates a new file. Fails if the given file exists already.\n @@ -867,46 +866,42 @@ namespace cytnx { * doesn't exist. Only available for HDF5 files.\n * `u` Opens for writing. Existing content will be updated(overwritten). * Creates the file if it doesn't exist. Only available for HDF5 files. - * @details Save the Bond to a file. The file ending should be one of ".h5", ".hdf5", ".H5", - * ".HDF5", ".hdf" to save in HDF5 file format. Otherwise, a binary file format is used. + * @details Save the Bond to a file. The file ending should be one of ".h5", ".hdf5", ".H5", ".HDF5", ".hdf" to save in HDF5 file format. Otherwise, a binary file format is used. * @note The common file ending for saving a Bond in binary format is ".cybd". - * @warning HDF5 file format is strongly recommended for compatibility with other libraries, - * readability, and future-proofing. - * @see Load(const std::filesystem::path &fname, const bool restore_device) + * @warning HDF5 file format is strongly recommended for compatibility with other libraries, readability, and future-proofing. + * @see Load() */ void Save(const std::filesystem::path &fname, const std::string &path = "/", const char mode = 'w') const; - // @see Save(const std::filesystem::path &fname, const std::string &path, const char mode) - // const; + /** + * @see Save(const std::filesystem::path &fname, const std::string &path, const char mode) const; + */ void Save(const char *fname, const std::string &path = "/", const char mode = 'w') const; /** * @brief Load Bond from file and create new instance * @param fname[in] file name - * @param[in] path path inside the file. Only used for HDF5 files. A path '/foo/bar/' will - * read the Bond from the group '/foo/bar' in the file. - * @param[in] restore_device whether to try restoring the device on which the data is stored; if - * false, the data will be kept on the CPU. Use .to_() to move it to the target device after - * loading. - * @pre The file must be a Bond object which is saved by cytnx::Bond::Save. - * @note This function creates a new Bond and keeps the original Bond unchanged. See \link - * Load_(const std::filesystem::path &fname, const bool restore_device) Load_() \endlink for - * loading the Bond to the current Bond. - * @details For HDF5 file format, one of the file endings ".h5", ".hdf5", ".H5", ".HDF5", ".hdf" - * is expected. For binary format, the common file ending for a Bond is ".cybd". + * @param[in] path path inside the file. Only used for HDF5 files. A path '/foo/bar/' will read the Bond from the group '/foo/bar' in the file. + * @param[in] restore_device whether to try restoring the device on which the data is stored; if false, the data will be kept on the CPU. Use .to_() to move it to the target device after loading. + * @pre The file must be a Bond object which is saved by Save(). + * @note This function creates a new Bond and keeps the original Bond unchanged. See Load_() for loading the Bond to the current Bond. + * @note For HDF5 file format, one of the file endings ".h5", ".hdf5", ".H5", ".HDF5", ".hdf" is expected. For binary format, the common file ending for a Bond is ".cybd". */ static cytnx::Bond Load(const std::filesystem::path &fname, const std::string &path = "/"); - // @see Load(const std::filesystem::path &fname, const std::string &path) + /** + * @see Load(const std::filesystem::path &fname, const std::string &path) + */ static cytnx::Bond Load(const char *fname, const std::string &path = "/"); /** * @brief Load Bond from file and overwrite current instance - * @note This function overwrites the existing Bond. See \link Load(const std::filesystem::path - * &fname, const bool restore_device) Load() \endlink for creating a new Bond. - * @see Load(const std::filesystem::path &fname, const bool restore_device) + * @note This function overwrites the existing Bond. See Load() for creating a new Bond. + * @see Load() */ void Load_(const std::filesystem::path &fname, const std::string &path = "/"); - // @see Load_(const std::filesystem::path &fname, const std::string &path) + /** + * @see Load_(const std::filesystem::path &fname, const std::string &path) + */ void Load_(const char *fname, const std::string &path = "/"); /** @@ -914,57 +909,46 @@ namespace cytnx { * @param[in] location the HDF5 group where the Bond will be saved. * @param[in] overwrite overwrite previous Bond information in the location. * @param[in] save_symmetries whether to save the symmetry information in the HDF5 file. - * @warning This function is only available in C++. Use \link Save(const std::filesystem::path - * &fname) Save() \endlink for saving to file in C++ or Python. - * @see from_hdf5(H5::Group &location, const std::string &name, const bool restore_device) + * @warning This function is only available in C++. Use Save() for saving to file in C++ or Python. + * @see from_hdf5() */ void to_hdf5(H5::Group &location, const bool overwrite = false, const bool save_symmetries = true) const; /** * @brief Load Bond from HDF5 file (inline) * @param[in] location the HDF5 group where the Bond will be loaded from. - * @param[in] syms vector containing the Symmetries. Leave emtpy to read the Symmetries from - * HDF5 - * @warning This function is only available in C++. Use \link Load(const std::filesystem::path - * &fname, const bool restore_device) Load() \endlink for loading from file in C++ or Python. - * @see to_hdf5(H5::Group &location, const std::string &name) const + * @param[in] syms vector containing the Symmetries. Leave emtpy to read the Symmetries from HDF5 + * @warning This function is only available in C++. Use Load() for loading from file in C++ or Python. + * @see to_hdf5() const */ void from_hdf5(H5::Group &location, const std::vector &syms = {}); /** * @brief Save Bond to binary file * @param[in] f the output stream where the Bond will be saved. - * @warning This function is only available in C++. In Python, use pickle for the same binary - * file format. Use \link Save(const std::filesystem::path &fname) Save() \endlink for saving to - * file in C++ or Python. - * @see from_binary(std::istream &f) + * @warning This function is only available in C++. In Python, use pickle for the same binary file format. Use Save() for saving to file in C++ or Python. + * @see from_binary() */ void to_binary(std::ostream &f) const; /** * @brief Load Bond from binary file * @param[in] f the input stream from which the Bond will be loaded. - * @warning This function is only available in C++. In Python, use pickle for the same binary - * file format. Use \link Load(const std::filesystem::path &fname, const bool restore_device) - * Load() \endlink for loading from file in C++ or Python. - * @see to_binary(std::ostream &f) const + * @warning This function is only available in C++. In Python, use pickle for the same binary file format. Use Load() for loading from file in C++ or Python. + * @see to_binary() const */ void from_binary(std::istream &f); /** @brief The comparison operator 'equal to'. - @details The comparison operators to compare two Bonds. If two Bond object are - same, return true. Otherwise, return false. This equal to operator will - compare all the "value" of the Bond object. Even the Bond object are different - object (different address), but they are same "value", it will return true. - @see operator!=(const Bond &rhs) const + @details The comparison operators to compare two Bonds. If two Bond object are same, return true. Otherwise, return false. This equal to operator will compare all the "value" of the Bond object. Even the Bond object are different object (different address), but they are same "value", it will return true. + @see operator!=(const Bond &rhs) const */ bool operator==(const Bond &rhs) const; /** @brief The comparison operator 'not equal to'. - @details The comparison operators to compare two Bonds. More precisely, it is - the opposite result of the operator==(const Bond &rhs) const. - @see operator==(const Bond &rhs) const + @details The comparison operators to compare two Bonds. More precisely, it is the opposite result of the operator==(const Bond &rhs) const. + @see operator==(const Bond &rhs) const */ bool operator!=(const Bond &rhs) const; diff --git a/include/Symmetry.hpp b/include/Symmetry.hpp index c05f80f9e..fb362522c 100644 --- a/include/Symmetry.hpp +++ b/include/Symmetry.hpp @@ -531,8 +531,7 @@ namespace cytnx { /** * @brief Save Symmetry to file * @param[in] fname file name - * @param[in] path path inside the file. Only available for HDF5 files. A path '/foo/bar/Symm' - * will write the Symmetry to the attribute 'Symm' the group '/foo/bar' in the file. + * @param[in] path path inside the file. Only used for HDF5 files. A path '/foo/bar/Symm' will write the Symmetry to the attribute 'Symm' the group '/foo/bar' in the file. * @param[in] mode the write mode:\n * `w` Creates a new file. If the given file exists, its contents are destroyed.\n * `x` Creates a new file. Fails if the given file exists already.\n @@ -540,87 +539,72 @@ namespace cytnx { * doesn't exist. Only available for HDF5 files.\n * `u` Opens for writing. Existing content will be updated(overwritten). * Creates the file if it doesn't exist. Only available for HDF5 files. - * @details Save the Symmetry to a file. The file ending should be one of ".h5", ".hdf5", ".H5", - * ".HDF5", ".hdf" to save in HDF5 file format. Otherwise, a binary file format is used. + * @details Save the Symmetry to a file. The file ending should be one of ".h5", ".hdf5", ".H5", ".HDF5", ".hdf" to save in HDF5 file format. Otherwise, a binary file format is used. * @note The common file ending for saving a Symmetry in binary format is ".cysym". - * @warning HDF5 file format is strongly recommended for compatibility with other libraries, - * readability, and future-proofing. - * @see Load(const std::filesystem::path &fname, const bool restore_device) + * @warning HDF5 file format is strongly recommended for compatibility with other libraries, readability, and future-proofing. + * @see Load() */ - void Save(const std::filesystem::path &fname, const std::string &path = "/Symmetry", - const char mode = 'w') const; - // @brief Same as Save(const std::filesystem::path &fname, const std::string &path, const char - // mode) const; - void Save(const char *fname, const std::string &path = "/Symmetry", - const char mode = 'w') const; + void Save(const std::filesystem::path &fname, const std::string &path = "/Symmetry", const char mode = 'w') const; + /** + * @see Save(const std::filesystem::path &fname, const std::string &path, const char mode) const; + */ + void Save(const char *fname, const std::string &path = "/Symmetry", const char mode = 'w') const; /** * @brief Load Symmetry from file and create new instance * @param fname[in] file name - * @param[in] path path inside the file. Only available for HDF5 files. A path /foo/bar/Symm - * will write the Symmetry to the attribute 'Symm' the group '/foo/bar' in the file. - * @param[in] restore_device whether to try restoring the device on which the data is stored; if - * false, the data will be kept on the CPU. Use .to_() to move it to the target device after - * loading. - * @pre The file must be a Symmetry object which is saved by cytnx::Symmetry::Save. - * @note This function creates a new Symmetry and keeps the original Symmetry unchanged. See - * \link Load_(const std::filesystem::path &fname, const bool restore_device) Load_() \endlink - * for loading the Symmetry to the current Symmetry. - * @details For HDF5 file format, one of the file endings ".h5", ".hdf5", ".H5", ".HDF5", ".hdf" - * is expected. For binary format, the common file ending for a Symmetry is ".cysym". + * @param[in] path path inside the file. Only used for HDF5 files. A path /foo/bar/Symm will read the Symmetry from the attribute 'Symm' the group '/foo/bar' in the file. + * @pre The file must be a Symmetry object which is saved by Save(). + * @note This function creates a new Symmetry and keeps the original Symmetry unchanged. See Load_() for loading the Symmetry to the current Symmetry. + * @note For HDF5 file format, one of the file endings ".h5", ".hdf5", ".H5", ".HDF5", ".hdf" is expected. For binary format, the common file ending for a Symmetry is ".cysym". + */ + static cytnx::Symmetry Load(const std::filesystem::path &fname, const std::string &path = "/Symmetry"); + /** + * @see Load(const std::filesystem::path &fname, const std::string &path) */ - static cytnx::Symmetry Load(const std::filesystem::path &fname, - const std::string &path = "/Symmetry"); - // @see Load(const std::filesystem::path &fname, const std::string &path) static cytnx::Symmetry Load(const char *fname, const std::string &path = "/Symmetry"); /** * @brief Load Symmetry from file and overwrite current instance - * @note This function overwrites the existing Symmetry. See \link Load(const std::string - * &fname, const bool restore_device) Load() \endlink for creating a new Symmetry. - * @see Load(const std::filesystem::path &fname, const bool restore_device) + * @note This function overwrites the existing Symmetry. See Load() for creating a new Symmetry. + * @see Load() */ void Load_(const std::filesystem::path &fname, const std::string &path = "/Symmetry"); - // @see Load_(const std::filesystem::path &fname, const std::string &path) + /** + * @see Load_(const std::filesystem::path &fname, const std::string &path) + */ void Load_(const char *fname, const std::string &path = "/Symmetry"); /** * @brief Save Symmetry to HDF5 file * @param[in] location the HDF5 group where the Symmetry will be saved. * @param[in] overwrite overwrite previous Bond information in the location. - * @param[in] name the name of the Symmetry in the HDF5 file. - * @warning This function is only available in C++. Use \link Save(const std::filesystem::path - * &fname) Save() \endlink for saving to file in C++ or Python. - * @see from_hdf5(H5::Group &location, const std::string &name, const bool restore_device) + * @param[in] name the name of the attribute in the HDF5 file. + * @warning This function is only available in C++. Use Save() for saving to file in C++ or Python. + * @see from_hdf5() */ - void to_hdf5(H5::Group &location, const bool overwrite = false, - const std::string &name = "Symmetry") const; + void to_hdf5(H5::Group &location, const bool overwrite = false, const std::string &name = "Symmetry") const; /** * @brief Load Symmetry from HDF5 file (inline) * @param[in] location the HDF5 group where the Symmetry will be loaded from. - * @param[in] name the name of the Symmetry in the HDF5 file. - * @warning This function is only available in C++. Use \link Load(const std::filesystem::path - * &fname, const bool restore_device) Load() \endlink for loading from file in C++ or Python. - * @see to_hdf5(H5::Group &location, const std::string &name) const + * @param[in] name the name of the attribute in the HDF5 file. + * @warning This function is only available in C++. Use Load() for loading from file in C++ or Python. + * @see to_hdf5() */ void from_hdf5(H5::Group &location, const std::string &name = "Symmetry"); /** * @brief Save Symmetry to binary file * @param[in] f the output stream where the Symmetry will be saved. - * @warning This function is only available in C++. In Python, use pickle for the same binary - * file format. Use \link Save(const std::filesystem::path &fname) Save() \endlink for saving to - * file in C++ or Python. - * @see from_binary(std::istream &f) + * @warning This function is only available in C++. In Python, use pickle for the same binary file format. Use Save() for saving to file in C++ or Python. + * @see from_binary() */ void to_binary(std::ostream &f) const; /** * @brief Load Symmetry from binary file * @param[in] f the input stream from which the Symmetry will be loaded. - * @warning This function is only available in C++. In Python, use pickle for the same binary - * file format. Use \link Load(const std::filesystem::path &fname, const bool restore_device) - * Load() \endlink for loading from file in C++ or Python. - * @see to_binary(std::ostream &f) const + * @warning This function is only available in C++. In Python, use pickle for the same binary file format. Use Load() for loading from file in C++ or Python. + * @see to_binary() */ void from_binary(std::istream &f); diff --git a/include/Tensor.hpp b/include/Tensor.hpp index aaa12ec38..d259ce488 100644 --- a/include/Tensor.hpp +++ b/include/Tensor.hpp @@ -322,87 +322,80 @@ namespace cytnx { /** * @brief Save Tensor to file * @param[in] fname file name - * @details Save the Tensor to a file. The file ending should be one of ".h5", ".hdf5", ".H5", - * ".HDF5", ".hdf" to save in HDF5 file format. Otherwise, a binary file format is used. + * @param[in] path path inside the file. Only used for HDF5 files. A path '/foo/bar/Ten' will write the Tensor to the dataset 'Ten' the group '/foo/bar' in the file. + * @param[in] mode the write mode:\n + * `w` Creates a new file. If the given file exists, its contents are destroyed.\n + * `x` Creates a new file. Fails if the given file exists already.\n + * `a` Opens for writing without overwriting any existing content. Creates the file if it + * doesn't exist. Only available for HDF5 files.\n + * `u` Opens for writing. Existing content will be updated(overwritten). + * Creates the file if it doesn't exist. Only available for HDF5 files. + * @details Save the Tensor to a file. The file ending should be one of ".h5", ".hdf5", ".H5", ".HDF5", ".hdf" to save in HDF5 file format. Otherwise, a binary file format is used. * @note The common file ending for saving a Tensor in binary format is ".cytn". - * @warning HDF5 file format is strongly recommended for compatibility with other libraries, - * readability, and future-proofing. - * @see Load(const std::filesystem::path &fname, const bool restore_device) + * @warning HDF5 file format is strongly recommended for compatibility with other libraries, readability, and future-proofing. + * @see Load() */ - void Save(const std::filesystem::path &fname) const; - // @see Save(const std::filesystem::path &fname) const - void Save(const char *fname) const; + void Save(const std::filesystem::path &fname, const std::string &path = "/Tensor", + const char mode = 'w') const; + // @see Save(const std::filesystem::path &fname, const std::string &path, const char mode) const; + void Save(const char *fname, const std::string &path = "/Tensor", const char mode = 'w') const; /** * @brief Load Tensor from file and create new instance - * @param[in] fname file name - * @param[in] restore_device whether to try restoring the device on which the data is stored; if - * false, the data will be kept on the CPU. Use .to_() to move it to the target device after - * loading. - * @pre The file must be a Tensor object which is saved by cytnx::Tensor::Save. - * @note This function creates a new Tensor and keeps the original Tensor unchanged. See \link - * Load_(const std::filesystem::path &fname, const bool restore_device) Load_() \endlink for - * loading the Tensor to the current Tensor. - * @details For HDF5 file format, one of the file endings ".h5", ".hdf5", ".H5", ".HDF5", ".hdf" - * is expected. For binary format, the common file ending for a Tensor is ".cytn". + * @param fname[in] file name + * @param[in] path path inside the file. Only used for HDF5 files. A path /foo/bar/Ten will read the Tensor from the dataset 'Ten' the group '/foo/bar' in the file. + * @param[in] restore_device whether to try restoring the device on which the data is stored; if false, the data will be kept on the CPU. Use .to_() to move it to the target device after loading. + * @pre The file must be a Tensor object which is saved by Save(). + * @note This function creates a new Tensor and keeps the original Tensor unchanged. See Load_() for loading the Tensor to the current Tensor. + * @note For HDF5 file format, one of the file endings ".h5", ".hdf5", ".H5", ".HDF5", ".hdf" is expected. For binary format, the common file ending for a Tensor is ".cytn". */ - static Tensor Load(const std::filesystem::path &fname, const bool restore_device = true); - // @see Load(const std::filesystem::path &fname, const bool restore_device) - static Tensor Load(const char *fname, const bool restore_device = true); + static cytnx::Tensor Load(const std::filesystem::path &fname, const std::string &path = "/Tensor", const bool restore_device = true); + // @see Load(const std::filesystem::path &fname, const std::string &path, const bool restore_device) + static cytnx::Tensor Load(const char *fname, const std::string &path = "/Tensor", const bool restore_device = true); /** * @brief Load Tensor from file and overwrite current instance - * @note This function overwrites the existing Tensor. See \link Load(const - * std::filesystem::path &fname, const bool restore_device) Load() \endlink for creating a new - * Tensor. - * @see Load(const std::filesystem::path &fname, const bool restore_device) + * @note This function overwrites the existing Tensor. See Load() for creating a new Tensor. + * @see Load() + */ + void Load_(const std::filesystem::path &fname, const std::string &path = "/Tensor", const bool restore_device = true); + /** + * @see Load_(const std::filesystem::path &fname, const std::string &path, const bool restore_device) */ - void Load_(const std::filesystem::path &fname, const bool restore_device = true); - // @see Load_(const std::filesystem::path &fname, const bool restore_device) - void Load_(const char *fname, const bool restore_device = true); + void Load_(const char *fname, const std::string &path = "/Tensor", const bool restore_device = true); /** * @brief Save Tensor to HDF5 file * @param[in] location the HDF5 group where the Tensor will be saved. - * @param[in] name the name of the Tensor in the HDF5 file. - * @warning This function is only available in C++. Use \link Save(const std::filesystem::path - * &fname) Save() \endlink for saving to file in C++ or Python. - * @see from_hdf5(H5::Group &location, const std::string &name, const bool restore_device) + * @param[in] overwrite overwrite previous Bond information in the location. + * @param[in] name the name of the dataset in the HDF5 file.d in the + * @warning This function is only available in C++. Use Save() for saving to file in C++ or Python. + * @see from_hdf5() */ - void to_hdf5(H5::Group &location, const std::string &name = "Tensor") const; + void to_hdf5(H5::Group &location, const bool overwrite = false, const std::string &name = "Tensor") const; /** * @brief Load Tensor from HDF5 file (inline) * @param[in] location the HDF5 group where the Tensor will be loaded from. - * @param[in] name the name of the Tensor in the HDF5 file. - * @param[in] restore_device whether to try restoring the device on which the data is stored; if - * false, the data will be kept on the CPU. Use .to_() to move it to the target device after - * loading. - * @warning This function is only available in C++. Use \link Load(const std::filesystem::path - * &fname, const bool restore_device) Load() \endlink for loading from file in C++ or Python. - * @see to_hdf5(H5::Group &location, const std::string &name) const + * @param[in] name the name of the dataset in the HDF5 file. + * @param[in] restore_device whether to try restoring the device on which the data is stored; if false, the data will be kept on the CPU. Use .to_() to move it to the target device after loading. + * @warning This function is only available in C++. Use Load() for loading from file in C++ or Python. + * @see to_hdf5() */ - void from_hdf5(H5::Group &location, const std::string &name = "Tensor", - const bool restore_device = true); + void from_hdf5(H5::Group &location, const std::string &name = "Tensor", const bool restore_device = true); /** * @brief Save Tensor to binary file * @param[in] f the output stream where the Tensor will be saved. - * @warning This function is only available in C++. In Python, use pickle for the same binary - * file format. Use \link Save(const std::filesystem::path &fname) Save() \endlink for saving to - * file in C++ or Python. - * @see from_binary(std::istream &f, const bool restore_device) + * @warning This function is only available in C++. In Python, use pickle for the same binary file format. Use Save() for saving to file in C++ or Python. + * @see from_binary() */ void to_binary(std::ostream &f) const; /** * @brief Load Tensor from binary file * @param[in] f the input stream from which the Tensor will be loaded. - * @param[in] restore_device whether to try restoring the device on which the data is stored; if - * false, the data will be kept on the CPU. Use .to_() to move it to the target device after - * loading. - * @warning This function is only available in C++. In Python, use pickle for the same binary - * file format. Use \link Load(const std::filesystem::path &fname, const bool restore_device) - * Load() \endlink for loading from file in C++ or Python. - * @see to_binary(std::ostream &f) const + * @param[in] restore_device whether to try restoring the device on which the data is stored; if false, the data will be kept on the CPU. Use .to_() to move it to the target device after loading. + * @warning This function is only available in C++. In Python, use pickle for the same binary file format. Use Load() for loading from file in C++ or Python. + * @see to_binary() */ void from_binary(std::istream &f, const bool restore_device = true); diff --git a/pybind/tensor_py.cpp b/pybind/tensor_py.cpp index 0c9b91cd0..1ed50a799 100644 --- a/pybind/tensor_py.cpp +++ b/pybind/tensor_py.cpp @@ -291,20 +291,22 @@ void tensor_binding(py::module &m) { py::arg("val")) .def( - "Save", [](cytnx::Tensor &self, const std::filesystem::path &fname) { self.Save(fname); }, - py::arg("fname")) + "Save", + [](cytnx::Tensor &self, const std::filesystem::path &fname, const std::string &path, + const char mode) { self.Save(fname, path, mode); }, + py::arg("fname"), py::arg("path") = "/Tensor", py::arg("mode") = 'w') .def_static( "Load", - [](const std::filesystem::path &fname, const bool restore_device) { - return cytnx::Tensor::Load(fname, restore_device); + [](const std::filesystem::path &fname, const std::string &path, const bool restore_device) { + return cytnx::Tensor::Load(fname, path, restore_device); }, - py::arg("fname"), py::arg("restore_device") = true) + py::arg("fname"), py::arg("path") = "/Tensor", py::arg("restore_device") = true) .def( "Load_", - [](cytnx::Tensor &self, const std::filesystem::path &fname, const bool restore_device) { - return self.Load_(fname, restore_device); + [](cytnx::Tensor &self, const std::filesystem::path &fname, const std::string &path, const bool restore_device) { + return self.Load_(fname, path, restore_device); }, - py::arg("fname"), py::arg("restore_device") = true) + py::arg("fname"), py::arg("path") = "/Tensor", py::arg("restore_device") = true) .def(py::pickle( [](const cytnx::Tensor &self) { // __getstate__ diff --git a/src/Bond.cpp b/src/Bond.cpp index f99c11ec5..de015333d 100644 --- a/src/Bond.cpp +++ b/src/Bond.cpp @@ -471,30 +471,35 @@ namespace cytnx { bool Bond::operator!=(const Bond &rhs) const { return !(*this == rhs); } - void Bond::Save(const std::filesystem::path &fname, const std::string &path, - const char mode) const { - fstream f; // only for binary saving, not used for hdf5 + void Bond::Save(const std::filesystem::path &fname, const std::string &path, const char mode) const { + fstream f; // only for binary saving, not used for HDF5 if (fname.has_extension()) { // filename extension is given std::string ext = fname.extension().string(); if (ext == ".h5" || ext == ".hdf5" || ext == ".H5" || ext == ".HDF5" || ext == ".hdf" || ext == ".HDF") { - // save as hdf5 + // save as HDF5 H5::H5File h5file; - try { // overwrite file - if (mode == 'w') { - h5file = H5::H5File(fname, H5F_ACC_TRUNC); - } else if (mode == 'a' || mode == 'u') { - if (std::filesystem::exists(fname)) - h5file = H5::H5File(fname, H5F_ACC_RDWR); - else - h5file = H5::H5File(fname, H5F_ACC_EXCL); - } else if (mode == 'x') { // create a new file + bool overwrite = false; + // open file + if (mode == 'w') { // Write new file + h5file = H5::H5File(fname, H5F_ACC_TRUNC); + } else if (mode == 'x') { // eXclusive create + h5file = H5::H5File(fname, H5F_ACC_EXCL); + } else if (mode == 'a') { // Append data + if (std::filesystem::exists(fname)) + h5file = H5::H5File(fname, H5F_ACC_RDWR); + else + h5file = H5::H5File(fname, H5F_ACC_EXCL); + } else if (mode == 'u') { // Update data + if (std::filesystem::exists(fname)) { + h5file = H5::H5File(fname, H5F_ACC_RDWR); + overwrite = true; + } else { h5file = H5::H5File(fname, H5F_ACC_EXCL); } - } catch (H5::FileIException &error) { - error.printErrorStack(); - cytnx_error_msg(true, "[ERROR] Cannot create HDF5 file '%s'.\n", fname.c_str()); + } else { + cytnx_error_msg(true, "[ERROR] Unknown mode '%c' for writing to HDF5 file.", mode); } // create group H5::Group location = h5file; @@ -505,10 +510,7 @@ namespace cytnx { lcpl.setCreateIntermediateGroup(1); location = h5file.createGroup(path, lcpl); } - if (mode == 'u') - this->to_hdf5(location, true); - else - this->to_hdf5(location, false); + this->to_hdf5(location, overwrite); h5file.close(); return; } else { // create binary file @@ -516,25 +518,20 @@ namespace cytnx { cytnx_error_msg(std::filesystem::exists(fname), "[ERROR] File %s already exists. Use mode 'w' to overwrite.", fname); } else { - cytnx_error_msg((mode != 'x'), "[ERROR] Unknown mode '%c' for writing to binary file.", - mode); + cytnx_error_msg(mode != 'w', "[ERROR] Unknown mode '%c' for writing to binary file.", mode); } f.open(fname, std::ios::out | std::ios::trunc | std::ios::binary); } } else { // create binary file with standard extension - cytnx_warning_msg(true, - "Missing file extension in fname '%s'. I am adding the extension '.cybd'. " - "This is deprecated, please provide the file extension in the future.\n", - fname.c_str()); + std::filesystem::path fnameext = fname; + fnameext += ".cybd"; + cytnx_warning_msg(true, "Missing file extension in fname '%s'. I am adding the extension '.cybc'. This is deprecated, please provide the file extension in the future.\n", fname.c_str()); if (mode == 'x') { - cytnx_error_msg(std::filesystem::exists(fname), - "[ERROR] File %s already exists. Use mode 'w' to overwrite.", fname); + cytnx_error_msg(std::filesystem::exists(fnameext), "[ERROR] File %s already exists. Use mode 'w' to overwrite.", fnameext.c_str()); } else { - cytnx_error_msg((mode != 'x'), "[ERROR] Unknown mode '%c' for writing to binary file.", - mode); + cytnx_error_msg(mode != 'w', "[ERROR] Unknown mode '%c' for writing to binary file.", mode); } - f.open(std::filesystem::path(fname) += ".cybd", - std::ios::out | std::ios::trunc | std::ios::binary); + f.open(fnameext, std::ios::out | std::ios::trunc | std::ios::binary); } // write binary if (!f.is_open()) { @@ -544,7 +541,7 @@ namespace cytnx { f.close(); } void Bond::Save(const char *fname, const std::string &path, const char mode) const { - this->Save(string(fname), path, mode); + this->Save(std::filesystem::path(fname), path, mode); } Bond Bond::Load(const std::filesystem::path &fname, const std::string &path) { @@ -553,23 +550,17 @@ namespace cytnx { return out; } Bond Bond::Load(const char *fname, const std::string &path) { - return Bond::Load(string(fname), path); + return Bond::Load(std::filesystem::path(fname), path); } void Bond::Load_(const std::filesystem::path &fname, const std::string &path) { std::string ext = fname.extension().string(); if (ext == ".h5" || ext == ".hdf5" || ext == ".H5" || ext == ".HDF5" || ext == ".hdf" || ext == ".HDF") { - // load hdf5 - H5::H5File h5file; - try { - h5file = H5::H5File(fname, H5F_ACC_RDONLY); - } catch (H5::FileIException &error) { - error.printErrorStack(); - cytnx_error_msg(true, "[ERROR] Cannot open HDF5 file '%s'.\n", fname.c_str()); - } + // load HDF5 + H5::H5File h5file(fname, H5F_ACC_RDONLY); cytnx_error_msg(!h5file.exists(path), "[ERROR] Path '%s' does not exist in HDF5 file '%s'", - path, fname); + path, fname.c_str()); H5::Group location = h5file.openGroup(path); this->from_hdf5(location); h5file.close(); @@ -583,15 +574,9 @@ namespace cytnx { f.close(); } } - void Bond::Load_(const char *fname, const std::string &path) { this->Load_(string(fname), path); } + void Bond::Load_(const char *fname, const std::string &path) { this->Load_(std::filesystem::path(fname), path); } void Bond::to_hdf5(H5::Group &location, const bool overwrite, const bool save_symmetries) const { - H5::DataType datatype; - H5::Attribute attr; - H5::DataSet dataset; - H5::DataSpace dataspace; - H5::StrType str_type; - if (overwrite) { // delete previous data // remove attributes if (location.attrExists("dimension")) location.removeAttr("dimension"); @@ -603,6 +588,12 @@ namespace cytnx { if (location.nameExists("Symmetries")) location.unlink("Symmetries"); } + H5::DataType datatype; + H5::Attribute attr; + H5::DataSet dataset; + H5::DataSpace dataspace; + H5::StrType str_type; + // dimension, write as attribute auto dim = this->_impl->_dim; datatype = Type.get_hdf5_type(dim); diff --git a/src/Symmetry.cpp b/src/Symmetry.cpp index f98331d57..73e324f92 100644 --- a/src/Symmetry.cpp +++ b/src/Symmetry.cpp @@ -245,30 +245,35 @@ namespace cytnx { //================================================== - void cytnx::Symmetry::Save(const std::filesystem::path &fname, const std::string &path, - const char mode) const { - fstream f; // only for binary saving, not used for hdf5 + void cytnx::Symmetry::Save(const std::filesystem::path &fname, const std::string &path, const char mode) const { + fstream f; // only for binary saving, not used for HDF5 if (fname.has_extension()) { // filename extension is given std::string ext = fname.extension().string(); if (ext == ".h5" || ext == ".hdf5" || ext == ".H5" || ext == ".HDF5" || ext == ".hdf" || ext == ".HDF") { - // save as hdf5 + // save as HDF5 H5::H5File h5file; - try { // overwrite file - if (mode == 'w') { - h5file = H5::H5File(fname, H5F_ACC_TRUNC); - } else if (mode == 'a' || mode == 'u') { - if (std::filesystem::exists(fname)) - h5file = H5::H5File(fname, H5F_ACC_RDWR); - else - h5file = H5::H5File(fname, H5F_ACC_EXCL); - } else if (mode == 'x') { // create a new file + bool overwrite = false; + // open file + if (mode == 'w') { // Write new file + h5file = H5::H5File(fname, H5F_ACC_TRUNC); + } else if (mode == 'x') { // eXclusive create + h5file = H5::H5File(fname, H5F_ACC_EXCL); + } else if (mode == 'a') { // Append data + if (std::filesystem::exists(fname)) + h5file = H5::H5File(fname, H5F_ACC_RDWR); + else + h5file = H5::H5File(fname, H5F_ACC_EXCL); + } else if (mode == 'u') { // Update data + if (std::filesystem::exists(fname)) { + h5file = H5::H5File(fname, H5F_ACC_RDWR); + overwrite = true; + } else { h5file = H5::H5File(fname, H5F_ACC_EXCL); } - } catch (H5::FileIException &error) { - error.printErrorStack(); - cytnx_error_msg(true, "[ERROR] Cannot create HDF5 file '%s'.\n", fname.c_str()); + } else { + cytnx_error_msg(true, "[ERROR] Unknown mode '%c' for writing to HDF5 file.", mode); } // split path into group and name std::filesystem::path p(path); @@ -284,10 +289,7 @@ namespace cytnx { lcpl.setCreateIntermediateGroup(1); location = h5file.createGroup(grouppath, lcpl); } - if (mode == 'u') - this->to_hdf5(location, true, datasetname); - else - this->to_hdf5(location, false, datasetname); + this->to_hdf5(location, overwrite, datasetname); h5file.close(); return; } else { // create binary file @@ -295,25 +297,20 @@ namespace cytnx { cytnx_error_msg(std::filesystem::exists(fname), "[ERROR] File %s already exists. Use mode 'w' to overwrite.", fname); } else { - cytnx_error_msg((mode != 'x'), "[ERROR] Unknown mode '%c' for writing to binary file.", - mode); + cytnx_error_msg(mode != 'w', "[ERROR] Unknown mode '%c' for writing to binary file.", mode); } f.open(fname, std::ios::out | std::ios::trunc | std::ios::binary); } } else { // create binary file with standard extension - cytnx_warning_msg(true, - "Missing file extension in fname '%s'. I am adding the extension '.cysym'. " - "This is deprecated, please provide the file extension in the future.\n", - fname.c_str()); + std::filesystem::path fnameext = fname; + fnameext += ".cysym"; + cytnx_warning_msg(true, "Missing file extension in fname '%s'. I am adding the extension '.cysym'. This is deprecated, please provide the file extension in the future.\n", fname.c_str()); if (mode == 'x') { - cytnx_error_msg(std::filesystem::exists(fname), - "[ERROR] File %s already exists. Use mode 'w' to overwrite.", fname); + cytnx_error_msg(std::filesystem::exists(fnameext), "[ERROR] File %s already exists. Use mode 'w' to overwrite.", fnameext.c_str()); } else { - cytnx_error_msg((mode != 'x'), "[ERROR] Unknown mode '%c' for writing to binary file.", - mode); + cytnx_error_msg(mode != 'w', "[ERROR] Unknown mode '%c' for writing to binary file.", mode); } - f.open(std::filesystem::path(fname) += ".cysym", - std::ios::out | std::ios::trunc | std::ios::binary); + f.open(fnameext, std::ios::out | std::ios::trunc | std::ios::binary); } // write binary if (!f.is_open()) { @@ -323,32 +320,24 @@ namespace cytnx { f.close(); } void cytnx::Symmetry::Save(const char *fname, const std::string &path, const char mode) const { - this->Save(string(fname), path, mode); + this->Save(std::filesystem::path(fname), path, mode); } - cytnx::Symmetry cytnx::Symmetry::Load(const std::filesystem::path &fname, - const std::string &path) { + cytnx::Symmetry cytnx::Symmetry::Load(const std::filesystem::path &fname, const std::string &path) { Symmetry out; out.Load_(fname, path); return out; } cytnx::Symmetry cytnx::Symmetry::Load(const char *fname, const std::string &path) { - return cytnx::Symmetry::Load(string(fname), path); + return cytnx::Symmetry::Load(std::filesystem::path(fname), path); } void cytnx::Symmetry::Load_(const std::filesystem::path &fname, const std::string &path) { std::string ext = fname.extension().string(); if (ext == ".h5" || ext == ".hdf5" || ext == ".H5" || ext == ".HDF5" || ext == ".hdf" || ext == ".HDF") { - // load hdf5 - H5::H5File h5file; - try { - h5file = H5::H5File(fname, H5F_ACC_RDONLY); - } catch (H5::FileIException &error) { - error.printErrorStack(); - cytnx_error_msg(true, "[ERROR] Cannot open HDF5 file '%s'.\n", fname.c_str()); - } - + // load HDF5 + H5::H5File h5file(fname, H5F_ACC_RDONLY); // split path into group and name std::filesystem::path p(path); std::string grouppath = p.parent_path().generic_string(); @@ -356,9 +345,9 @@ namespace cytnx { if (datasetname.empty()) datasetname = "Symmetry"; // open group cytnx_error_msg(!h5file.exists(grouppath), - "[ERROR] Path '%s' does not exist in HDF5 file '%s'", grouppath, fname); + "[ERROR] Path '%s' does not exist in HDF5 file '%s'", grouppath, fname.c_str()); H5::Group location = h5file.openGroup(grouppath); - this->from_hdf5(location); + this->from_hdf5(location, datasetname); h5file.close(); } else { // load binary fstream f; @@ -371,11 +360,10 @@ namespace cytnx { } } void cytnx::Symmetry::Load_(const char *fname, const std::string &path) { - this->Load_(string(fname), path); + this->Load_(std::filesystem::path(fname), path); } - void cytnx::Symmetry::to_hdf5(H5::Group &location, const bool overwrite, - const std::string &name) const { + void cytnx::Symmetry::to_hdf5(H5::Group &location, const bool overwrite, const std::string &name) const { if (overwrite) { // delete previous data if (location.attrExists(name)) location.removeAttr(name); } diff --git a/src/Tensor.cpp b/src/Tensor.cpp index aa9ce1113..b91e544e0 100644 --- a/src/Tensor.cpp +++ b/src/Tensor.cpp @@ -425,58 +425,72 @@ namespace cytnx { //=================================================================== // wrapper - void Tensor::Tofile(const std::filesystem::path &fname) const { - if (!this->is_contiguous()) { - auto A = this->contiguous(); - A.storage().Tofile(fname); - } else { - this->storage().Tofile(fname); - } - } - void Tensor::Tofile(const char *fname) const { - if (!this->is_contiguous()) { - auto A = this->contiguous(); - A.storage().Tofile(fname); - } else { - this->storage().Tofile(fname); - } - } - void Tensor::Tofile(fstream &f) const { - if (!this->is_contiguous()) { - auto A = this->contiguous(); - A.storage().Tofile(f); - } else { - this->storage().Tofile(f); - } - } - - void Tensor::Save(const std::filesystem::path &fname) const { - fstream f; // only for binary saving, not used for hdf5 + void cytnx::Tensor::Save(const std::filesystem::path &fname, const std::string &path, const char mode) const { + fstream f; // only for binary saving, not used for HDF5 if (fname.has_extension()) { // filename extension is given std::string ext = fname.extension().string(); if (ext == ".h5" || ext == ".hdf5" || ext == ".H5" || ext == ".HDF5" || ext == ".hdf" || ext == ".HDF") { - // save as hdf5 + // save as HDF5 H5::H5File h5file; - try { + bool overwrite = false; + // open file + if (mode == 'w') { // Write new file h5file = H5::H5File(fname, H5F_ACC_TRUNC); - } catch (H5::FileIException &error) { - error.printErrorStack(); - cytnx_error_msg(true, "[ERROR] Cannot create HDF5 file '%s'.\n", fname.c_str()); + } else if (mode == 'x') { // eXclusive create + h5file = H5::H5File(fname, H5F_ACC_EXCL); + } else if (mode == 'a') { // Append data + if (std::filesystem::exists(fname)) + h5file = H5::H5File(fname, H5F_ACC_RDWR); + else + h5file = H5::H5File(fname, H5F_ACC_EXCL); + } else if (mode == 'u') { // Update data + if (std::filesystem::exists(fname)) { + h5file = H5::H5File(fname, H5F_ACC_RDWR); + overwrite = true; + } else { + h5file = H5::H5File(fname, H5F_ACC_EXCL); + } + } else { + cytnx_error_msg(true, "[ERROR] Unknown mode '%c' for writing to HDF5 file.", mode); + } + // split path into group and name + std::filesystem::path p(path); + std::string grouppath = p.parent_path().generic_string(); + std::string datasetname = p.filename().string(); + if (datasetname.empty()) datasetname = "Tensor"; + // create group + H5::Group location = h5file; + if (h5file.exists(grouppath)) { + location = h5file.openGroup(grouppath); + } else { + H5::LinkCreatPropList lcpl; + lcpl.setCreateIntermediateGroup(1); + location = h5file.createGroup(grouppath, lcpl); } - this->to_hdf5(h5file); + this->to_hdf5(location, overwrite, datasetname); h5file.close(); return; } else { // create binary file - f.open(fname, ios::out | ios::trunc | ios::binary); + if (mode == 'x') { + cytnx_error_msg(std::filesystem::exists(fname), + "[ERROR] File %s already exists. Use mode 'w' to overwrite.", fname); + } else { + cytnx_error_msg(mode != 'w', "[ERROR] Unknown mode '%c' for writing to binary file.", mode); + } + f.open(fname, std::ios::out | std::ios::trunc | std::ios::binary); } } else { // create binary file with standard extension - cytnx_warning_msg(true, - "Missing file extension in fname '%s'. I am adding the extension '.cytn'. " - "This is deprecated, please provide the file extension in the future.\n", - fname.c_str()); - f.open((std::filesystem::path(fname) += ".cytn"), ios::out | ios::trunc | ios::binary); + std::filesystem::path fnameext = fname; + fnameext += ".cytn"; + cytnx_warning_msg(true, "Missing file extension in fname '%s'. I am adding the extension '.cytn'. This is deprecated, please provide the file extension in the future.\n", fname.c_str()); + if (mode == 'x') { + cytnx_error_msg(std::filesystem::exists(fnameext), "[ERROR] File %s already exists. Use mode 'w' to overwrite.", fnameext.c_str()); + } else { + cytnx_error_msg(mode != 'w', "[ERROR] Unknown mode '%c' for writing to binary file.", mode); + } + f.open(fnameext, std::ios::out | std::ios::trunc | std::ios::binary); } // write binary if (!f.is_open()) { @@ -485,70 +499,35 @@ namespace cytnx { this->to_binary(f); f.close(); } - void Tensor::Save(const char *fname) const { this->Save(string(fname)); } - - void Tensor::to_hdf5(H5::Group &location, const std::string &name) const { - Tensor ten = this->contiguous(); - std::vector dims(this->shape().begin(), this->shape().end()); - - H5::DataSpace dataspace(dims.size(), dims.data()); - H5::DataType datatype = Type.dtype_to_hdf5_type(this->dtype()); - H5::DataSet dataset = location.createDataSet(name, datatype, dataspace); - ten.storage().data_to_hdf5(dataset, datatype); - - if (this->device() != Device.cpu) { - H5::Attribute attr = - dataset.createAttribute("device", H5::PredType::NATIVE_INT, H5::DataSpace(H5S_SCALAR)); - int device = this->device(); - attr.write(H5::PredType::NATIVE_INT, &device); - } - } - void Tensor::to_binary(std::ostream &f) const { - unsigned int IDDs = 888; - f.write((char *)&IDDs, sizeof(unsigned int)); - cytnx_uint64 shp = this->shape().size(); - cytnx_uint64 Conti = this->is_contiguous(); - f.write((char *)&shp, sizeof(cytnx_uint64)); - - f.write((char *)&Conti, sizeof(cytnx_uint64)); - f.write((char *)&this->_impl->_shape[0], sizeof(cytnx_uint64) * shp); - f.write((char *)&this->_impl->_mapper[0], sizeof(cytnx_uint64) * shp); - f.write((char *)&this->_impl->_invmapper[0], sizeof(cytnx_uint64) * shp); - - // pass to storage for save: - this->storage().to_binary(f); + void cytnx::Tensor::Save(const char *fname, const std::string &path, const char mode) const { + this->Save(std::filesystem::path(fname), path, mode); } - Tensor Tensor::Fromfile(const std::filesystem::path &fname, const unsigned int &dtype, - const cytnx_int64 &count, const int device) { - return Tensor::from_storage(Storage::Fromfile(fname, dtype, count, device)); - } - Tensor Tensor::Fromfile(const char *fname, const unsigned int &dtype, const cytnx_int64 &count, - const int device) { - return Tensor::from_storage(Storage::Fromfile(fname, dtype, count, device)); - } - Tensor Tensor::Load(const std::filesystem::path &fname, const bool restore_device) { + cytnx::Tensor cytnx::Tensor::Load(const std::filesystem::path &fname, const std::string &path, const bool restore_device) { Tensor out; - out.Load_(fname, restore_device); + out.Load_(fname, path, restore_device); return out; } - Tensor Tensor::Load(const char *fname, const bool restore_device) { - return Tensor::Load(string(fname), restore_device); + cytnx::Tensor cytnx::Tensor::Load(const char *fname, const std::string &path, const bool restore_device) { + return cytnx::Tensor::Load(std::filesystem::path(fname), path, restore_device); } - void Tensor::Load_(const std::filesystem::path &fname, const bool restore_device) { + void cytnx::Tensor::Load_(const std::filesystem::path &fname, const std::string &path, const bool restore_device) { std::string ext = fname.extension().string(); if (ext == ".h5" || ext == ".hdf5" || ext == ".H5" || ext == ".HDF5" || ext == ".hdf" || ext == ".HDF") { - // load hdf5 - H5::H5File h5file; - try { - h5file = H5::H5File(fname, H5F_ACC_RDONLY); - } catch (H5::FileIException &error) { - error.printErrorStack(); - cytnx_error_msg(true, "[ERROR] Cannot open HDF5 file '%s'.\n", fname.c_str()); - } - this->from_hdf5(h5file, "Tensor", restore_device); + // load HDF5 + H5::H5File h5file(fname, H5F_ACC_RDONLY); + // split path into group and name + std::filesystem::path p(path); + std::string grouppath = p.parent_path().generic_string(); + std::string datasetname = p.filename().string(); + if (datasetname.empty()) datasetname = "Tensor"; + // open group + cytnx_error_msg(!h5file.exists(grouppath), + "[ERROR] Path '%s' does not exist in HDF5 file '%s'", grouppath, fname.c_str()); + H5::Group location = h5file.openGroup(grouppath); + this->from_hdf5(location, datasetname, restore_device); h5file.close(); } else { // load binary fstream f; @@ -560,8 +539,29 @@ namespace cytnx { f.close(); } } - void Tensor::Load_(const char *fname, const bool restore_device) { - this->Load_(string(fname), restore_device); + void cytnx::Tensor::Load_(const char *fname, const std::string &path, const bool restore_device) { + this->Load_(std::filesystem::path(fname), path, restore_device); + } + + void Tensor::to_hdf5(H5::Group &location, const bool overwrite, const std::string &name) const { + if (overwrite) { // delete previous data + if (location.nameExists(name)) location.unlink(name); + } + + Tensor ten = this->contiguous(); + std::vector dims(this->shape().begin(), this->shape().end()); + + H5::DataSpace dataspace(dims.size(), dims.data()); + H5::DataType datatype = Type.dtype_to_hdf5_type(this->dtype()); + H5::DataSet dataset = location.createDataSet(name, datatype, dataspace); + ten.storage().data_to_hdf5(dataset, datatype); + + if (this->device() != Device.cpu) { + H5::Attribute attr = + dataset.createAttribute("device", H5::PredType::NATIVE_INT, H5::DataSpace(H5S_SCALAR)); + int device = this->device(); + attr.write(H5::PredType::NATIVE_INT, &device); + } } void Tensor::from_hdf5(H5::Group &location, const std::string &name, const bool restore_device) { @@ -592,6 +592,23 @@ namespace cytnx { this->_impl->_storage.data_from_hdf5(dataset, Nelem, dtype, datatype, device); } + + void Tensor::to_binary(std::ostream &f) const { + unsigned int IDDs = 888; + f.write((char *)&IDDs, sizeof(unsigned int)); + cytnx_uint64 shp = this->shape().size(); + cytnx_uint64 Conti = this->is_contiguous(); + f.write((char *)&shp, sizeof(cytnx_uint64)); + + f.write((char *)&Conti, sizeof(cytnx_uint64)); + f.write((char *)&this->_impl->_shape[0], sizeof(cytnx_uint64) * shp); + f.write((char *)&this->_impl->_mapper[0], sizeof(cytnx_uint64) * shp); + f.write((char *)&this->_impl->_invmapper[0], sizeof(cytnx_uint64) * shp); + + // pass to storage for save: + this->storage().to_binary(f); + } + void Tensor::from_binary(std::istream &f, const bool restore_device) { unsigned int tmpIDDs; f.read((char *)&tmpIDDs, sizeof(unsigned int)); @@ -614,6 +631,40 @@ namespace cytnx { this->_impl->_storage.from_binary(f, restore_device); } + void Tensor::Tofile(const std::filesystem::path &fname) const { + if (!this->is_contiguous()) { + auto A = this->contiguous(); + A.storage().Tofile(fname); + } else { + this->storage().Tofile(fname); + } + } + void Tensor::Tofile(const char *fname) const { + if (!this->is_contiguous()) { + auto A = this->contiguous(); + A.storage().Tofile(fname); + } else { + this->storage().Tofile(fname); + } + } + void Tensor::Tofile(fstream &f) const { + if (!this->is_contiguous()) { + auto A = this->contiguous(); + A.storage().Tofile(f); + } else { + this->storage().Tofile(f); + } + } + + Tensor Tensor::Fromfile(const std::filesystem::path &fname, const unsigned int &dtype, + const cytnx_int64 &count, const int device) { + return Tensor::from_storage(Storage::Fromfile(fname, dtype, count, device)); + } + Tensor Tensor::Fromfile(const char *fname, const unsigned int &dtype, const cytnx_int64 &count, + const int device) { + return Tensor::from_storage(Storage::Fromfile(fname, dtype, count, device)); + } + Tensor Tensor::real() { Tensor out; out._impl = this->_impl->_clone_meta_only(); diff --git a/src/UniTensor.cpp b/src/UniTensor.cpp index 60f635305..93635bc9b 100644 --- a/src/UniTensor.cpp +++ b/src/UniTensor.cpp @@ -78,7 +78,7 @@ namespace cytnx { this->to_binary(f); f.close(); } - void UniTensor::Save(const char *fname) const { Save(string(fname)); } + void UniTensor::Save(const char *fname) const { Save(std::filesystem::path(fname)); } UniTensor UniTensor::Load(const std::filesystem::path &fname, const bool restore_device) { UniTensor out; @@ -86,7 +86,7 @@ namespace cytnx { return out; } UniTensor UniTensor::Load(const char *fname, const bool restore_device) { - return UniTensor::Load(string(fname), restore_device); + return UniTensor::Load(std::filesystem::path(fname), restore_device); } void UniTensor::Load_(const std::filesystem::path &fname, const bool restore_device) { @@ -114,7 +114,7 @@ namespace cytnx { } } void UniTensor::Load_(const char *fname, const bool restore_device) { - this->Load_(string(fname), restore_device); + this->Load_(std::filesystem::path(fname), restore_device); } void UniTensor::to_hdf5(H5::Group &location, const std::string &name) const { diff --git a/src/backend/Storage.cpp b/src/backend/Storage.cpp index 028327c64..54098e4cc 100644 --- a/src/backend/Storage.cpp +++ b/src/backend/Storage.cpp @@ -119,7 +119,7 @@ namespace cytnx { this->to_binary(f); f.close(); } - void Storage::Save(const char *fname) const { this->Save(string(fname)); } + void Storage::Save(const char *fname) const { this->Save(std::filesystem::path(fname)); } void Storage::Tofile(const std::filesystem::path &fname) const { fstream f; @@ -210,7 +210,7 @@ namespace cytnx { Storage Storage::Fromfile(const char *fname, const unsigned int &dtype, const cytnx_int64 &count, const int device) { - return Storage::Fromfile(string(fname), dtype, count, device); + return Storage::Fromfile(std::filesystem::path(fname), dtype, count, device); } Storage Storage::Fromfile(const std::filesystem::path &fname, const unsigned int &dtype, const cytnx_int64 &count, const int device) { @@ -259,7 +259,7 @@ namespace cytnx { return out; } Storage Storage::Load(const char *fname, const bool restore_device) { - return Storage::Load(string(fname), restore_device); + return Storage::Load(std::filesystem::path(fname), restore_device); } void Storage::Load_(const std::filesystem::path &fname, const bool restore_device) { @@ -287,7 +287,7 @@ namespace cytnx { } } void Storage::Load_(const char *fname, const bool restore_device) { - this->Load_(string(fname), restore_device); + this->Load_(std::filesystem::path(fname), restore_device); } void Storage::from_hdf5(H5::Group &location, const std::string &name, const bool restore_device) { diff --git a/src/tn_algo/MPS.cpp b/src/tn_algo/MPS.cpp index 9b0410cea..9522cc0d1 100644 --- a/src/tn_algo/MPS.cpp +++ b/src/tn_algo/MPS.cpp @@ -98,7 +98,7 @@ namespace cytnx { this->to_binary(f); f.close(); } - void MPS::Save(const char* fname) const { this->Save(string(fname)); } + void MPS::Save(const char* fname) const { this->Save(std::filesystem::path(fname)); } MPS MPS::Load(const std::filesystem::path& fname, const bool restore_device) { MPS out; @@ -106,7 +106,7 @@ namespace cytnx { return out; } MPS MPS::Load(const char* fname, const bool restore_device) { - return MPS::Load(string(fname), restore_device); + return MPS::Load(std::filesystem::path(fname), restore_device); } void MPS::Load_(const std::filesystem::path& fname, const bool restore_device) { @@ -134,7 +134,7 @@ namespace cytnx { } } void MPS::Load_(const char* fname, const bool restore_device) { - this->Load_(string(fname), restore_device); + this->Load_(std::filesystem::path(fname), restore_device); } void MPS::to_hdf5(H5::Group& location, const std::string& name) const { From 2406482cd99d77934c5126cbb2f3fa8e6905ffaf Mon Sep 17 00:00:00 2001 From: Manuel Schneider Date: Fri, 1 May 2026 04:57:00 +0800 Subject: [PATCH 13/40] bug fixes when opening groups implemented Save/Load with path for UniTensor and MPS (but to/from_hdf5 still missing) H5 output is currently disabled for using try-catch statements without output to std::err --- include/Bond.hpp | 72 ++++--- include/Symmetry.hpp | 50 +++-- include/Tensor.hpp | 86 +++++--- include/UniTensor.hpp | 111 ++++++----- include/backend/Storage.hpp | 115 +++++++---- include/tn_algo/MPS.hpp | 93 +++++---- pybind/bond_py.cpp | 6 +- pybind/storage_py.cpp | 30 +-- pybind/tensor_py.cpp | 5 +- pybind/tnalgo_py.cpp | 21 +- pybind/unitensor_py.cpp | 18 +- src/Bond.cpp | 37 +++- src/Symmetry.cpp | 38 ++-- src/Tensor.cpp | 41 ++-- src/UniTensor.cpp | 107 +++++++--- src/backend/Storage.cpp | 379 +++++++++++++++++++++--------------- src/tn_algo/MPS.cpp | 195 ++++++++++++------- 17 files changed, 883 insertions(+), 521 deletions(-) diff --git a/include/Bond.hpp b/include/Bond.hpp index 8e8f5b283..af91dea6d 100644 --- a/include/Bond.hpp +++ b/include/Bond.hpp @@ -857,8 +857,11 @@ namespace cytnx { /** * @brief Save Bond to file + * @details Save the Bond to a file. The file ending should be one of ".h5", ".hdf5", ".H5", + * ".HDF5", ".hdf" to save in HDF5 file format. Otherwise, a binary file format is used. * @param[in] fname file name - * @param[in] path path inside the file. Only used for HDF5 files. A path '/foo/bar/' will write the Bond to the group '/foo/bar' in the file. + * @param[in] path path inside the file. Only used for HDF5 files. A path '/foo/bar/' will write + * the Bond to the group '/foo/bar' in the file. * @param[in] mode the write mode:\n * `w` Creates a new file. If the given file exists, its contents are destroyed.\n * `x` Creates a new file. Fails if the given file exists already.\n @@ -866,50 +869,57 @@ namespace cytnx { * doesn't exist. Only available for HDF5 files.\n * `u` Opens for writing. Existing content will be updated(overwritten). * Creates the file if it doesn't exist. Only available for HDF5 files. - * @details Save the Bond to a file. The file ending should be one of ".h5", ".hdf5", ".H5", ".HDF5", ".hdf" to save in HDF5 file format. Otherwise, a binary file format is used. * @note The common file ending for saving a Bond in binary format is ".cybd". - * @warning HDF5 file format is strongly recommended for compatibility with other libraries, readability, and future-proofing. + * @warning HDF5 file format is strongly recommended for compatibility with other libraries, + * readability, and future-proofing. * @see Load() */ - void Save(const std::filesystem::path &fname, const std::string &path = "/", + void Save(const std::filesystem::path &fname, const std::string &path = "/Bond/", const char mode = 'w') const; /** - * @see Save(const std::filesystem::path &fname, const std::string &path, const char mode) const; - */ - void Save(const char *fname, const std::string &path = "/", const char mode = 'w') const; + * @see Save(const std::filesystem::path &fname, const std::string &path, const char mode) + * const; + */ + void Save(const char *fname, const std::string &path = "/Bond/", const char mode = 'w') const; /** * @brief Load Bond from file and create new instance + * @details This function creates a new Bond and keeps the original Bond unchanged. See Load_() + * for loading the Bond to the current Bond. * @param fname[in] file name - * @param[in] path path inside the file. Only used for HDF5 files. A path '/foo/bar/' will read the Bond from the group '/foo/bar' in the file. - * @param[in] restore_device whether to try restoring the device on which the data is stored; if false, the data will be kept on the CPU. Use .to_() to move it to the target device after loading. + * @param[in] path path inside the file. Only used for HDF5 files. A path '/foo/bar/' will read + * the Bond from the group '/foo/bar' in the file. + * @param[in] restore_device whether to try restoring the device on which the data is stored; if + * false, the data will be kept on the CPU. Use .to_() to move it to the target device after + * loading. * @pre The file must be a Bond object which is saved by Save(). - * @note This function creates a new Bond and keeps the original Bond unchanged. See Load_() for loading the Bond to the current Bond. - * @note For HDF5 file format, one of the file endings ".h5", ".hdf5", ".H5", ".HDF5", ".hdf" is expected. For binary format, the common file ending for a Bond is ".cybd". + * @note For HDF5 file format, one of the file endings ".h5", ".hdf5", ".H5", ".HDF5", ".hdf" is + * expected. For binary format, the common file ending for a Bond is ".cybd". */ - static cytnx::Bond Load(const std::filesystem::path &fname, const std::string &path = "/"); + static cytnx::Bond Load(const std::filesystem::path &fname, const std::string &path = "/Bond/"); /** - * @see Load(const std::filesystem::path &fname, const std::string &path) - */ - static cytnx::Bond Load(const char *fname, const std::string &path = "/"); + * @see Load(const std::filesystem::path &fname, const std::string &path) + */ + static cytnx::Bond Load(const char *fname, const std::string &path = "/Bond/"); /** * @brief Load Bond from file and overwrite current instance - * @note This function overwrites the existing Bond. See Load() for creating a new Bond. + * @details This function overwrites the existing Bond. See Load() for creating a new Bond. * @see Load() */ - void Load_(const std::filesystem::path &fname, const std::string &path = "/"); + void Load_(const std::filesystem::path &fname, const std::string &path = "/Bond/"); /** - * @see Load_(const std::filesystem::path &fname, const std::string &path) - */ - void Load_(const char *fname, const std::string &path = "/"); + * @see Load_(const std::filesystem::path &fname, const std::string &path) + */ + void Load_(const char *fname, const std::string &path = "/Bond/"); /** * @brief Save Bond to HDF5 file * @param[in] location the HDF5 group where the Bond will be saved. * @param[in] overwrite overwrite previous Bond information in the location. * @param[in] save_symmetries whether to save the symmetry information in the HDF5 file. - * @warning This function is only available in C++. Use Save() for saving to file in C++ or Python. + * @warning This function is only available in C++. Use Save() for saving to file in C++ or + * Python. * @see from_hdf5() */ void to_hdf5(H5::Group &location, const bool overwrite = false, @@ -917,8 +927,10 @@ namespace cytnx { /** * @brief Load Bond from HDF5 file (inline) * @param[in] location the HDF5 group where the Bond will be loaded from. - * @param[in] syms vector containing the Symmetries. Leave emtpy to read the Symmetries from HDF5 - * @warning This function is only available in C++. Use Load() for loading from file in C++ or Python. + * @param[in] syms vector containing the Symmetries. Leave emtpy to read the Symmetries from + * HDF5 + * @warning This function is only available in C++. Use Load() for loading from file in C++ or + * Python. * @see to_hdf5() const */ void from_hdf5(H5::Group &location, const std::vector &syms = {}); @@ -926,28 +938,34 @@ namespace cytnx { /** * @brief Save Bond to binary file * @param[in] f the output stream where the Bond will be saved. - * @warning This function is only available in C++. In Python, use pickle for the same binary file format. Use Save() for saving to file in C++ or Python. + * @warning This function is only available in C++. In Python, use pickle for the same binary + * file format. Use Save() for saving to file in C++ or Python. * @see from_binary() */ void to_binary(std::ostream &f) const; /** * @brief Load Bond from binary file * @param[in] f the input stream from which the Bond will be loaded. - * @warning This function is only available in C++. In Python, use pickle for the same binary file format. Use Load() for loading from file in C++ or Python. + * @warning This function is only available in C++. In Python, use pickle for the same binary + * file format. Use Load() for loading from file in C++ or Python. * @see to_binary() const */ void from_binary(std::istream &f); /** @brief The comparison operator 'equal to'. - @details The comparison operators to compare two Bonds. If two Bond object are same, return true. Otherwise, return false. This equal to operator will compare all the "value" of the Bond object. Even the Bond object are different object (different address), but they are same "value", it will return true. + @details The comparison operators to compare two Bonds. If two Bond object are same, return + true. Otherwise, return false. This equal to operator will compare all the "value" of the Bond + object. Even the Bond object are different object (different address), but they are same + "value", it will return true. @see operator!=(const Bond &rhs) const */ bool operator==(const Bond &rhs) const; /** @brief The comparison operator 'not equal to'. - @details The comparison operators to compare two Bonds. More precisely, it is the opposite result of the operator==(const Bond &rhs) const. + @details The comparison operators to compare two Bonds. More precisely, it is the opposite + result of the operator==(const Bond &rhs) const. @see operator==(const Bond &rhs) const */ bool operator!=(const Bond &rhs) const; diff --git a/include/Symmetry.hpp b/include/Symmetry.hpp index fb362522c..fd3cba7e4 100644 --- a/include/Symmetry.hpp +++ b/include/Symmetry.hpp @@ -530,8 +530,11 @@ namespace cytnx { /** * @brief Save Symmetry to file + * @details Save the Symmetry to a file. The file ending should be one of ".h5", ".hdf5", ".H5", + * ".HDF5", ".hdf" to save in HDF5 file format. Otherwise, a binary file format is used. * @param[in] fname file name - * @param[in] path path inside the file. Only used for HDF5 files. A path '/foo/bar/Symm' will write the Symmetry to the attribute 'Symm' the group '/foo/bar' in the file. + * @param[in] path path inside the file. Only used for HDF5 files. A path '/foo/bar/Symm' will + * write the Symmetry to the attribute 'Symm' the group '/foo/bar' in the file. * @param[in] mode the write mode:\n * `w` Creates a new file. If the given file exists, its contents are destroyed.\n * `x` Creates a new file. Fails if the given file exists already.\n @@ -539,26 +542,33 @@ namespace cytnx { * doesn't exist. Only available for HDF5 files.\n * `u` Opens for writing. Existing content will be updated(overwritten). * Creates the file if it doesn't exist. Only available for HDF5 files. - * @details Save the Symmetry to a file. The file ending should be one of ".h5", ".hdf5", ".H5", ".HDF5", ".hdf" to save in HDF5 file format. Otherwise, a binary file format is used. * @note The common file ending for saving a Symmetry in binary format is ".cysym". - * @warning HDF5 file format is strongly recommended for compatibility with other libraries, readability, and future-proofing. + * @warning HDF5 file format is strongly recommended for compatibility with other libraries, + * readability, and future-proofing. * @see Load() */ - void Save(const std::filesystem::path &fname, const std::string &path = "/Symmetry", const char mode = 'w') const; + void Save(const std::filesystem::path &fname, const std::string &path = "/Symmetry", + const char mode = 'w') const; /** - * @see Save(const std::filesystem::path &fname, const std::string &path, const char mode) const; - */ - void Save(const char *fname, const std::string &path = "/Symmetry", const char mode = 'w') const; + * @see Save(const std::filesystem::path &fname, const std::string &path, const char mode) + * const; + */ + void Save(const char *fname, const std::string &path = "/Symmetry", + const char mode = 'w') const; /** * @brief Load Symmetry from file and create new instance + * @details This function creates a new Symmetry and keeps the original Symmetry unchanged. See + * Load_() for loading the Symmetry to the current Symmetry. * @param fname[in] file name - * @param[in] path path inside the file. Only used for HDF5 files. A path /foo/bar/Symm will read the Symmetry from the attribute 'Symm' the group '/foo/bar' in the file. + * @param[in] path path inside the file. Only used for HDF5 files. A path /foo/bar/Symm will + * read the Symmetry from the attribute 'Symm' the group '/foo/bar' in the file. * @pre The file must be a Symmetry object which is saved by Save(). - * @note This function creates a new Symmetry and keeps the original Symmetry unchanged. See Load_() for loading the Symmetry to the current Symmetry. - * @note For HDF5 file format, one of the file endings ".h5", ".hdf5", ".H5", ".HDF5", ".hdf" is expected. For binary format, the common file ending for a Symmetry is ".cysym". + * @note For HDF5 file format, one of the file endings ".h5", ".hdf5", ".H5", ".HDF5", ".hdf" is + * expected. For binary format, the common file ending for a Symmetry is ".cysym". */ - static cytnx::Symmetry Load(const std::filesystem::path &fname, const std::string &path = "/Symmetry"); + static cytnx::Symmetry Load(const std::filesystem::path &fname, + const std::string &path = "/Symmetry"); /** * @see Load(const std::filesystem::path &fname, const std::string &path) */ @@ -566,7 +576,8 @@ namespace cytnx { /** * @brief Load Symmetry from file and overwrite current instance - * @note This function overwrites the existing Symmetry. See Load() for creating a new Symmetry. + * @details This function overwrites the existing Symmetry. See Load() for creating a new + * Symmetry. * @see Load() */ void Load_(const std::filesystem::path &fname, const std::string &path = "/Symmetry"); @@ -580,15 +591,18 @@ namespace cytnx { * @param[in] location the HDF5 group where the Symmetry will be saved. * @param[in] overwrite overwrite previous Bond information in the location. * @param[in] name the name of the attribute in the HDF5 file. - * @warning This function is only available in C++. Use Save() for saving to file in C++ or Python. + * @warning This function is only available in C++. Use Save() for saving to file in C++ or + * Python. * @see from_hdf5() */ - void to_hdf5(H5::Group &location, const bool overwrite = false, const std::string &name = "Symmetry") const; + void to_hdf5(H5::Group &location, const bool overwrite = false, + const std::string &name = "Symmetry") const; /** * @brief Load Symmetry from HDF5 file (inline) * @param[in] location the HDF5 group where the Symmetry will be loaded from. * @param[in] name the name of the attribute in the HDF5 file. - * @warning This function is only available in C++. Use Load() for loading from file in C++ or Python. + * @warning This function is only available in C++. Use Load() for loading from file in C++ or + * Python. * @see to_hdf5() */ void from_hdf5(H5::Group &location, const std::string &name = "Symmetry"); @@ -596,14 +610,16 @@ namespace cytnx { /** * @brief Save Symmetry to binary file * @param[in] f the output stream where the Symmetry will be saved. - * @warning This function is only available in C++. In Python, use pickle for the same binary file format. Use Save() for saving to file in C++ or Python. + * @warning This function is only available in C++. In Python, use pickle for the same binary + * file format. Use Save() for saving to file in C++ or Python. * @see from_binary() */ void to_binary(std::ostream &f) const; /** * @brief Load Symmetry from binary file * @param[in] f the input stream from which the Symmetry will be loaded. - * @warning This function is only available in C++. In Python, use pickle for the same binary file format. Use Load() for loading from file in C++ or Python. + * @warning This function is only available in C++. In Python, use pickle for the same binary + * file format. Use Load() for loading from file in C++ or Python. * @see to_binary() */ void from_binary(std::istream &f); diff --git a/include/Tensor.hpp b/include/Tensor.hpp index d259ce488..f3e1e187f 100644 --- a/include/Tensor.hpp +++ b/include/Tensor.hpp @@ -321,8 +321,11 @@ namespace cytnx { /** * @brief Save Tensor to file + * @details Save the Tensor to a file. The file ending should be one of ".h5", ".hdf5", ".H5", + * ".HDF5", ".hdf" to save in HDF5 file format. Otherwise, a binary file format is used. * @param[in] fname file name - * @param[in] path path inside the file. Only used for HDF5 files. A path '/foo/bar/Ten' will write the Tensor to the dataset 'Ten' the group '/foo/bar' in the file. + * @param[in] path path inside the file. Only used for HDF5 files. A path '/foo/bar/Ten' will + * write the Tensor to the dataset 'Ten' the group '/foo/bar' in the file. * @param[in] mode the write mode:\n * `w` Creates a new file. If the given file exists, its contents are destroyed.\n * `x` Creates a new file. Fails if the given file exists already.\n @@ -330,71 +333,98 @@ namespace cytnx { * doesn't exist. Only available for HDF5 files.\n * `u` Opens for writing. Existing content will be updated(overwritten). * Creates the file if it doesn't exist. Only available for HDF5 files. - * @details Save the Tensor to a file. The file ending should be one of ".h5", ".hdf5", ".H5", ".HDF5", ".hdf" to save in HDF5 file format. Otherwise, a binary file format is used. * @note The common file ending for saving a Tensor in binary format is ".cytn". - * @warning HDF5 file format is strongly recommended for compatibility with other libraries, readability, and future-proofing. + * @warning HDF5 file format is strongly recommended for compatibility with other libraries, + * readability, and future-proofing. * @see Load() */ void Save(const std::filesystem::path &fname, const std::string &path = "/Tensor", const char mode = 'w') const; - // @see Save(const std::filesystem::path &fname, const std::string &path, const char mode) const; + /** + * @see Save(const std::filesystem::path &fname, const std::string &path, const char mode) + * const; + */ void Save(const char *fname, const std::string &path = "/Tensor", const char mode = 'w') const; /** * @brief Load Tensor from file and create new instance + * @details This function creates a new Tensor and keeps the original Tensor unchanged. See + * Load_() for loading the Tensor to the current Tensor. * @param fname[in] file name - * @param[in] path path inside the file. Only used for HDF5 files. A path /foo/bar/Ten will read the Tensor from the dataset 'Ten' the group '/foo/bar' in the file. - * @param[in] restore_device whether to try restoring the device on which the data is stored; if false, the data will be kept on the CPU. Use .to_() to move it to the target device after loading. + * @param[in] path path inside the file. Only used for HDF5 files. A path /foo/bar/Ten will read + * the Tensor from the dataset 'Ten' the group '/foo/bar' in the file. + * @param[in] restore_device whether to try restoring the device on which the data is stored; if + * false, the data will be kept on the CPU. Use .to_() to move it to the target device after + * loading. * @pre The file must be a Tensor object which is saved by Save(). - * @note This function creates a new Tensor and keeps the original Tensor unchanged. See Load_() for loading the Tensor to the current Tensor. - * @note For HDF5 file format, one of the file endings ".h5", ".hdf5", ".H5", ".HDF5", ".hdf" is expected. For binary format, the common file ending for a Tensor is ".cytn". + * @note For HDF5 file format, one of the file endings ".h5", ".hdf5", ".H5", ".HDF5", ".hdf" is + * expected. For binary format, the common file ending for a Tensor is ".cytn". + */ + static cytnx::Tensor Load(const std::filesystem::path &fname, + const std::string &path = "/Tensor", + const bool restore_device = true); + /** + * @see Load(const std::filesystem::path &fname, const std::string &path, const bool + * restore_device) */ - static cytnx::Tensor Load(const std::filesystem::path &fname, const std::string &path = "/Tensor", const bool restore_device = true); - // @see Load(const std::filesystem::path &fname, const std::string &path, const bool restore_device) - static cytnx::Tensor Load(const char *fname, const std::string &path = "/Tensor", const bool restore_device = true); + static cytnx::Tensor Load(const char *fname, const std::string &path = "/Tensor", + const bool restore_device = true); /** * @brief Load Tensor from file and overwrite current instance - * @note This function overwrites the existing Tensor. See Load() for creating a new Tensor. + * @details This function overwrites the existing Tensor. See Load() for creating a new Tensor. * @see Load() */ - void Load_(const std::filesystem::path &fname, const std::string &path = "/Tensor", const bool restore_device = true); + void Load_(const std::filesystem::path &fname, const std::string &path = "/Tensor", + const bool restore_device = true); /** - * @see Load_(const std::filesystem::path &fname, const std::string &path, const bool restore_device) + * @see Load_(const std::filesystem::path &fname, const std::string &path, const bool + * restore_device) */ - void Load_(const char *fname, const std::string &path = "/Tensor", const bool restore_device = true); + void Load_(const char *fname, const std::string &path = "/Tensor", + const bool restore_device = true); /** * @brief Save Tensor to HDF5 file * @param[in] location the HDF5 group where the Tensor will be saved. * @param[in] overwrite overwrite previous Bond information in the location. - * @param[in] name the name of the dataset in the HDF5 file.d in the - * @warning This function is only available in C++. Use Save() for saving to file in C++ or Python. + * @param[in] name the name of the dataset in the HDF5 file.d in the + * @warning This function is only available in C++. Use Save() for saving to file in C++ or + * Python. * @see from_hdf5() */ - void to_hdf5(H5::Group &location, const bool overwrite = false, const std::string &name = "Tensor") const; + void to_hdf5(H5::Group &location, const bool overwrite = false, + const std::string &name = "Tensor") const; /** * @brief Load Tensor from HDF5 file (inline) * @param[in] location the HDF5 group where the Tensor will be loaded from. * @param[in] name the name of the dataset in the HDF5 file. - * @param[in] restore_device whether to try restoring the device on which the data is stored; if false, the data will be kept on the CPU. Use .to_() to move it to the target device after loading. - * @warning This function is only available in C++. Use Load() for loading from file in C++ or Python. + * @param[in] restore_device whether to try restoring the device on which the data is stored; if + * false, the data will be kept on the CPU. Use .to_() to move it to the target device after + * loading. + * @warning This function is only available in C++. Use Load() for loading from file in C++ or + * Python. * @see to_hdf5() */ - void from_hdf5(H5::Group &location, const std::string &name = "Tensor", const bool restore_device = true); + void from_hdf5(H5::Group &location, const std::string &name = "Tensor", + const bool restore_device = true); /** * @brief Save Tensor to binary file * @param[in] f the output stream where the Tensor will be saved. - * @warning This function is only available in C++. In Python, use pickle for the same binary file format. Use Save() for saving to file in C++ or Python. + * @warning This function is only available in C++. In Python, use pickle for the same binary + * file format. Use Save() for saving to file in C++ or Python. * @see from_binary() */ void to_binary(std::ostream &f) const; /** * @brief Load Tensor from binary file * @param[in] f the input stream from which the Tensor will be loaded. - * @param[in] restore_device whether to try restoring the device on which the data is stored; if false, the data will be kept on the CPU. Use .to_() to move it to the target device after loading. - * @warning This function is only available in C++. In Python, use pickle for the same binary file format. Use Load() for loading from file in C++ or Python. + * @param[in] restore_device whether to try restoring the device on which the data is stored; if + * false, the data will be kept on the CPU. Use .to_() to move it to the target device after + * loading. + * @warning This function is only available in C++. In Python, use pickle for the same binary + * file format. Use Load() for loading from file in C++ or Python. * @see to_binary() */ void from_binary(std::istream &f, const bool restore_device = true); @@ -411,10 +441,14 @@ namespace cytnx { */ [[deprecated("Please use Save(const std::filesystem::path &fname) instead.")]] void Tofile( const std::filesystem::path &fname) const; - // @see Tofile(const std::filesystem::path &fname) const + /** + * @see Tofile(const std::filesystem::path &fname) const + */ [[deprecated("Please use Save(const std::filesystem::path &fname) instead.")]] void Tofile( const char *fname) const; - // @see Tofile(const std::filesystem::path &fname) const + /** + * @see Tofile(const std::filesystem::path &fname) const + */ [[deprecated("Please use to_binary(std::ostream &f) instead.")]] void Tofile( std::fstream &f) const; diff --git a/include/UniTensor.hpp b/include/UniTensor.hpp index f190df5a8..37cbe5d4d 100644 --- a/include/UniTensor.hpp +++ b/include/UniTensor.hpp @@ -5460,75 +5460,98 @@ namespace cytnx { /** * @brief Save UniTensor to file - * @param[in] fname file name * @details Save the UniTensor to a file. The file ending should be one of ".h5", ".hdf5", * ".H5", ".HDF5", ".hdf" to save in HDF5 file format. Otherwise, a binary file format is used. - * @note The common file ending for saving a UniTensor in binary format is ".cytn". + * @param[in] fname file name + * @param[in] path path inside the file. Only used for HDF5 files. A path '/foo/bar/' will write + * the UniTensor to the group '/foo/bar' in the file. + * @param[in] mode the write mode:\n + * `w` Creates a new file. If the given file exists, its contents are destroyed.\n + * `x` Creates a new file. Fails if the given file exists already.\n + * `a` Opens for writing without overwriting any existing content. Creates the file if it + * doesn't exist. Only available for HDF5 files.\n + * `u` Opens for writing. Existing content will be updated(overwritten). + * Creates the file if it doesn't exist. Only available for HDF5 files. + * @note The common file ending for saving a UniTensor in binary format is ".cytnx". * @warning HDF5 file format is strongly recommended for compatibility with other libraries, * readability, and future-proofing. - * @see Load(const std::filesystem::path &fname, const bool restore_device) + * @see Load() + */ + void Save(const std::filesystem::path &fname, const std::string &path = "/UniTensor/", + const char mode = 'w') const; + /** + * @see Save(const std::filesystem::path &fname, const std::string &path, const char mode) + * const; */ - void Save(const std::filesystem::path &fname) const; - // @see Save(const std::filesystem::path &fname) const - void Save(const char *fname) const; + void Save(const char *fname, const std::string &path = "/UniTensor/", + const char mode = 'w') const; /** * @brief Load UniTensor from file and create new instance + * @details This function creates a new UniTensor and keeps the original UniTensor unchanged. + * See Load_() for loading the UniTensor to the current UniTensor. * @param fname[in] file name + * @param[in] path path inside the file. Only used for HDF5 files. A path '/foo/bar/' will read + * the UniTensor from the group '/foo/bar' in the file. * @param[in] restore_device whether to try restoring the device on which the data is stored; if * false, the data will be kept on the CPU. Use .to_() to move it to the target device after * loading. - * @pre The file must be a UniTensor object which is saved by cytnx::UniTensor::Save. - * @note This function creates a new UniTensor and keeps the original UniTensor unchanged. See - * \link Load_(const std::filesystem::path &fname, const bool restore_device) Load_() \endlink - * for loading the UniTensor to the current UniTensor. - * @details For HDF5 file format, one of the file endings ".h5", ".hdf5", ".H5", ".HDF5", ".hdf" - * is expected. For binary format, the common file ending for a UniTensor is ".cytn". + * @pre The file must be a UniTensor object which is saved by Save(). + * @note For HDF5 file format, one of the file endings ".h5", ".hdf5", ".H5", ".HDF5", ".hdf" is + * expected. For binary format, the common file ending for a UniTensor is ".cytnx". */ - static UniTensor Load(const std::filesystem::path &fname, const bool restore_device = true); - // @see Load(const std::filesystem::path &fname, const bool restore_device = true) - static UniTensor Load(const char *fname, const bool restore_device = true); + static UniTensor Load(const std::filesystem::path &fname, + const std::string &path = "/UniTensor/", + const bool restore_device = true); + /** + * @see Load(const std::filesystem::path &fname, const std::string &path, const bool + * restore_device) + */ + static UniTensor Load(const char *fname, const std::string &path = "/UniTensor/", + const bool restore_device = true); /** * @brief Load UniTensor from file and overwrite current instance - * @note This function overwrites the existing UniTensor. See \link Load(const std::string - * &fname, const bool restore_device) Load() \endlink for creating a new UniTensor. - * @see Load(const std::filesystem::path &fname, const bool restore_device) + * @details This function overwrites the existing UniTensor. See Load() for creating a new + * UniTensor. + * @see Load() */ - void Load_(const std::filesystem::path &fname, const bool restore_device = true); - // @see Load_(const std::filesystem::path &fname, const bool restore_device) - void Load_(const char *fname, const bool restore_device = true); + void Load_(const std::filesystem::path &fname, const std::string &path = "/UniTensor/", + const bool restore_device = true); + /** + * @see Load_(const std::filesystem::path &fname, const std::string &path, const bool + * restore_device) + */ + void Load_(const char *fname, const std::string &path = "/UniTensor/", + const bool restore_device = true); /** * @brief Save UniTensor to HDF5 file * @param[in] location the HDF5 group where the UniTensor will be saved. - * @param[in] name the name of the UniTensor in the HDF5 file. - * @warning This function is only available in C++. Use \link Save(const std::filesystem::path - * &fname) Save() \endlink for saving to file in C++ or Python. - * @see from_hdf5(H5::Group &location, const std::string &name, const bool restore_device) + * @param[in] overwrite overwrite previous Bond information in the location. + * @warning This function is only available in C++. Use Save() for saving to file in C++ or + * Python. + * @see from_hdf5() */ - void to_hdf5(H5::Group &location, const std::string &name = "UniTensor") const; + void to_hdf5(H5::Group &location, const bool overwrite = false) const; /** * @brief Load UniTensor from HDF5 file (inline) * @param[in] location the HDF5 group where the UniTensor will be loaded from. - * @param[in] name the name of the UniTensor in the HDF5 file. * @param[in] restore_device whether to try restoring the device on which the data is stored; if * false, the data will be kept on the CPU. Use .to_() to move it to the target device after * loading. - * @warning This function is only available in C++. Use \link Load(const std::filesystem::path - * &fname, const bool restore_device) Load() \endlink for loading from file in C++ or Python. - * @see to_hdf5(H5::Group &location, const std::string &name) const + * @warning This function is only available in C++. Use Load() for loading from file in C++ or + * Python. + * @see to_hdf5() */ - void from_hdf5(H5::Group &location, const std::string &name = "UniTensor", - const bool restore_device = true); + void from_hdf5(H5::Group &location, const bool restore_device = true); /** * @brief Save UniTensor to binary file * @param[in] f the output stream where the UniTensor will be saved. * @warning This function is only available in C++. In Python, use pickle for the same binary - * file format. Use \link Save(const std::filesystem::path &fname) Save() \endlink for saving to - * file in C++ or Python. - * @see from_binary(std::istream &f, const bool restore_device) + * file format. Use Save() for saving to file in C++ or Python. + * @see from_binary() */ void to_binary(std::ostream &f) const; /** @@ -5538,9 +5561,8 @@ namespace cytnx { * false, the data will be kept on the CPU. Use .to_() to move it to the target device after * loading. * @warning This function is only available in C++. In Python, use pickle for the same binary - * file format. Use \link Load(const std::filesystem::path &fname, const bool restore_device) - * Load() \endlink for loading from file in C++ or Python. - * @see to_binary(std::ostream &f) const + * file format. Use Load() for loading from file in C++ or Python. + * @see to_binary() */ void from_binary(std::istream &f, const bool restore_device = true); @@ -5730,7 +5752,7 @@ namespace cytnx { @note 2-bond if not diagonal. 1-bond if diagonal. @see identity(Nelem, in_labels, is_diag, dtype, device, name) Note: - This function is a alias of cytnx::UniTensor::identity(). + This function is a alias of identity(). */ static UniTensor eye(const cytnx_uint64 &dim, const std::vector &in_labels = {}, const cytnx_bool &is_diag = false, const unsigned int &dtype = Type.Double, @@ -5989,8 +6011,7 @@ namespace cytnx { @param[in] cacheR if the inR should be contiguous align after calling @return [UniTensor] - - @see cytnx::UniTensor::contract + @see cytnx::Contract() */ UniTensor Contract(const UniTensor &inL, const UniTensor &inR, const bool &cacheL = false, @@ -6004,9 +6025,7 @@ namespace cytnx { @param[in] optimal wheather to find the optimal contraction order automatically. @return [UniTensor] - - See also \link cytnx::UniTensor::contract UniTensor.contract \endlink - + @see cytnx::Contract() */ UniTensor Contract(const std::vector &TNs, const std::string &order, const bool &optimal); @@ -6039,9 +6058,7 @@ namespace cytnx { @param args the Tensors. @return [UniTensor] - - See also \link cytnx::UniTensor::contract UniTensor.contract \endlink - + @see cytnx::Contract() */ template UniTensor Contract(const UniTensor &in, const T &...args, const std::string &order, diff --git a/include/backend/Storage.hpp b/include/backend/Storage.hpp index f92c042ae..9ccb1fb50 100644 --- a/include/backend/Storage.hpp +++ b/include/backend/Storage.hpp @@ -528,67 +528,95 @@ namespace cytnx { /** * @brief Save Storage to file - * @param[in] fname file name * @details Save the Storage to a file. The file ending should be one of ".h5", ".hdf5", ".H5", * ".HDF5", ".hdf" to save in HDF5 file format. Otherwise, a binary file format is used. + * @param[in] fname file name + * @param[in] path path inside the file. Only used for HDF5 files. A path '/foo/bar/Ten' will + * write the Storage to the dataset 'Ten' the group '/foo/bar' in the file. + * @param[in] mode the write mode:\n + * `w` Creates a new file. If the given file exists, its contents are destroyed.\n + * `x` Creates a new file. Fails if the given file exists already.\n + * `a` Opens for writing without overwriting any existing content. Creates the file if it + * doesn't exist. Only available for HDF5 files.\n + * `u` Opens for writing. Existing content will be updated(overwritten). + * Creates the file if it doesn't exist. Only available for HDF5 files. * @note The common file ending for saving a Storage in binary format is ".cyst". * @warning HDF5 file format is strongly recommended for compatibility with other libraries, * readability, and future-proofing. - * @see Load(const std::filesystem::path &fname, const bool restore_device) + * @see Load() + */ + void Save(const std::filesystem::path &fname, const std::string &path = "/Storage", + const char mode = 'w') const; + /** + * @see Save(const std::filesystem::path &fname, const std::string &path, const char mode) + * const; */ - void Save(const std::filesystem::path &fname) const; - // @see Save(const std::filesystem::path &fname) const - void Save(const char *fname) const; + void Save(const char *fname, const std::string &path = "/Storage", const char mode = 'w') const; /** * @brief Load Storage from file and create new instance + * @details This function creates a new Storage and keeps the original Storage unchanged. See + * Load_() for loading the Storage to the current Storage. * @param fname[in] file name + * @param[in] path path inside the file. Only used for HDF5 files. A path /foo/bar/Ten will read + * the Storage from the dataset 'Ten' the group '/foo/bar' in the file. * @param[in] restore_device whether to try restoring the device on which the data is stored; if * false, the data will be kept on the CPU. Use .to_() to move it to the target device after * loading. - * @pre The file must be a Storage object which is saved by cytnx::Storage::Save. - * @note This function creates a new Storage and keeps the original Storage unchanged. See \link - * Load_(const std::filesystem::path &fname, const bool restore_device) Load_() \endlink for - * loading the Storage to the current Storage. - * @details For HDF5 file format, one of the file endings ".h5", ".hdf5", ".H5", ".HDF5", ".hdf" - * is expected. For binary format, the common file ending for a Storage is ".cyst". + * @pre The file must be a Storage object which is saved by Save(). + * @note For HDF5 file format, one of the file endings ".h5", ".hdf5", ".H5", ".HDF5", ".hdf" is + * expected. For binary format, the common file ending for a Storage is ".cyst". */ - static Storage Load(const std::filesystem::path &fname, const bool restore_device = true); - // @see Load(const std::filesystem::path &fname, const bool restore_device) - static Storage Load(const char *fname, const bool restore_device = true); + static cytnx::Storage Load(const std::filesystem::path &fname, + const std::string &path = "/Storage", + const bool restore_device = true); + /** + * @see Load(const std::filesystem::path &fname, const std::string &path, const bool + * restore_device) + */ + static cytnx::Storage Load(const char *fname, const std::string &path = "/Storage", + const bool restore_device = true); /** - @brief Load Storage from file and overwrite current instance - @note This function overwrites the existing Storage. See \link Load(const std::filesystem::path - &fname, const bool restore_device) Load() \endlink for creating a new Storage. - @see Load(const std::filesystem::path &fname, const bool restore_device) - */ - void Load_(const std::filesystem::path &fname, const bool restore_device = true); - // @see Load_(const std::filesystem::path &fname, const bool restore_device) - void Load_(const char *fname, const bool restore_device = true); + * @brief Load Storage from file and overwrite current instance + * @details This function overwrites the existing Storage. See Load() for creating a new + * Storage. + * @see Load() + */ + void Load_(const std::filesystem::path &fname, const std::string &path = "/Storage", + const bool restore_device = true); + /** + * @see Load_(const std::filesystem::path &fname, const std::string &path, const bool + * restore_device) + */ + void Load_(const char *fname, const std::string &path = "/Storage", + const bool restore_device = true); /** * @brief Save Storage to HDF5 file * @param[in] location the HDF5 group where the Storage will be saved. - * @param[in] name the name of the Storage in the HDF5 file. - * @warning This function is only available in C++. Use \link Save(const std::filesystem::path - * &fname) Save() \endlink for saving to file in C++ or Python. - * @see from_hdf5(H5::Group &location, const std::string &name, const bool restore_device) + * @param[in] overwrite overwrite previous Bond information in the location. + * @param[in] name the name of the dataset in the HDF5 file.d in the + * @warning This function is only available in C++. Use Save() for saving to file in C++ or + * Python. + * @see from_hdf5() */ - void to_hdf5(H5::Group &location, const std::string &name = "Storage") const; + void to_hdf5(H5::Group &location, const bool overwrite = false, + const std::string &name = "Storage") const; /** * @brief Load Storage from HDF5 file (inline) * @param[in] location the HDF5 group where the Storage will be loaded from. - * @param[in] name the name of the Storage in the HDF5 file. + * @param[in] name the name of the dataset in the HDF5 file. * @param[in] restore_device whether to try restoring the device on which the data is stored; if * false, the data will be kept on the CPU. Use .to_() to move it to the target device after * loading. - * @warning This function is only available in C++. Use \link Load(const std::filesystem::path - * &fname, const bool restore_device) Load() \endlink for loading from file in C++ or Python. - * @see to_hdf5(H5::Group &location, const std::string &name) const + * @warning This function is only available in C++. Use Load() for loading from file in C++ or + * Python. + * @see to_hdf5() */ void from_hdf5(H5::Group &location, const std::string &name = "Storage", const bool restore_device = true); + /** * @brief Save only the data of the Storage to HDF5 dataset. * @param[in] dataset the HDF5 dataset where the Storage will be saved. @@ -622,9 +650,8 @@ namespace cytnx { * @brief Save Storage to binary file * @param[in] f the output stream where the Storage will be saved. * @warning This function is only available in C++. In Python, use pickle for the same binary - * file format. Use \link Save(const std::filesystem::path &fname) Save() \endlink for saving to - * file in C++ or Python. - * @see from_binary(std::istream &f, const bool restore_device) + * file format. Use Save() for saving to file in C++ or Python. + * @see from_binary() */ void to_binary(std::ostream &f) const; /** @@ -634,11 +661,11 @@ namespace cytnx { * false, the data will be kept on the CPU. Use .to_() to move it to the target device after * loading. * @warning This function is only available in C++. In Python, use pickle for the same binary - * file format. Use \link Load(const std::filesystem::path &fname, const bool restore_device) - * Load() \endlink for loading from file in C++ or Python. - * @see to_binary(std::ostream &f) const + * file format. Use Load() for loading from file in C++ or Python. + * @see to_binary() */ void from_binary(std::istream &f, const bool restore_device = true); + /** * @brief Save only the data of the Storage to binary filestream. * @param[in] f the output stream where the Storage will be saved. @@ -672,10 +699,14 @@ namespace cytnx { */ [[deprecated("Please use Save(const std::filesystem::path &fname) instead.")]] void Tofile( const std::filesystem::path &fname) const; - /// @see Tofile(const std::filesystem::path &fname) const + /** + * @see Tofile(const std::filesystem::path &fname) const + */ [[deprecated("Please use Save(const std::filesystem::path &fname) instead.")]] void Tofile( const char *fname) const; - /// @see Tofile(const std::filesystem::path &fname) const + /** + * @see Tofile(const std::filesystem::path &fname) const + */ [[deprecated("Please use to_binary(std::ostream &f) instead.")]] void Tofile( std::fstream &f) const; @@ -701,8 +732,10 @@ namespace cytnx { [[deprecated("Please use Save/Load functions instead.")]] static Storage Fromfile( const std::filesystem::path &fname, const unsigned int &dtype, const cytnx_int64 &count = -1, const int device = Device.cpu); - // @see Fromfile(const std::filesystem::path &fname, const unsigned int &dtype, const - // cytnx_int64 &count) + /** + * @see Fromfile(const std::filesystem::path &fname, const unsigned int &dtype, const + * cytnx_int64 &count) + */ [[deprecated("Please use Save/Load functions instead.")]] static Storage Fromfile( const char *fname, const unsigned int &dtype, const cytnx_int64 &count = -1, const int device = Device.cpu); diff --git a/include/tn_algo/MPS.hpp b/include/tn_algo/MPS.hpp index 580eada20..53145bc6e 100644 --- a/include/tn_algo/MPS.hpp +++ b/include/tn_algo/MPS.hpp @@ -291,75 +291,95 @@ namespace cytnx { /** * @brief Save MPS to file - * @param[in] fname file name * @details Save the MPS to a file. The file ending should be one of ".h5", ".hdf5", ".H5", * ".HDF5", ".hdf" to save in HDF5 file format. Otherwise, a binary file format is used. + * @param[in] fname file name + * @param[in] path path inside the file. Only used for HDF5 files. A path '/foo/bar/' will + * write the MPS to the group '/foo/bar' in the file. + * @param[in] mode the write mode:\n + * `w` Creates a new file. If the given file exists, its contents are destroyed.\n + * `x` Creates a new file. Fails if the given file exists already.\n + * `a` Opens for writing without overwriting any existing content. Creates the file if it + * doesn't exist. Only available for HDF5 files.\n + * `u` Opens for writing. Existing content will be updated(overwritten). + * Creates the file if it doesn't exist. Only available for HDF5 files. * @note The common file ending for saving a MPS in binary format is ".cymps". * @warning HDF5 file format is strongly recommended for compatibility with other libraries, * readability, and future-proofing. - * @see Load(const std::filesystem::path &fname, const bool restore_device) + * @see Load() + */ + void Save(const std::filesystem::path &fname, const std::string &path = "/MPS/", + const char mode = 'w') const; + /** + * @see Save(const std::filesystem::path &fname, const std::string &path, const char mode) + * const; */ - void Save(const std::filesystem::path &fname) const; - // @see Save(const std::filesystem::path &fname) const - void Save(const char *fname) const; + void Save(const char *fname, const std::string &path = "/MPS/", const char mode = 'w') const; /** * @brief Load MPS from file and create new instance + * @details This function creates a new MPS and keeps the original MPS unchanged. See Load_() + * for loading the MPS to the current MPS. * @param fname[in] file name + * @param[in] path path inside the file. Only used for HDF5 files. A path '/foo/bar/' will + * read the MPS from the group '/foo/bar' in the file. * @param[in] restore_device whether to try restoring the device on which the data is stored; * if false, the data will be kept on the CPU. Use .to_() to move it to the target device * after loading. - * @pre The file must be a MPS object which is saved by cytnx::MPS::Save. - * @note This function creates a new MPS and keeps the original MPS unchanged. See \link - * Load_(const std::filesystem::path &fname, const bool restore_device) Load_() \endlink for - * loading the MPS to the current MPS. - * @details For HDF5 file format, one of the file endings ".h5", ".hdf5", ".H5", ".HDF5", - * ".hdf" is expected. For binary format, the common file ending for a MPS is ".cymps". + * @pre The file must be a MPS object which is saved by Save(). + * @note For HDF5 file format, one of the file endings ".h5", ".hdf5", ".H5", ".HDF5", ".hdf" + * is expected. For binary format, the common file ending for a MPS is ".cymps". */ - static MPS Load(const std::filesystem::path &fname, const bool restore_device = true); - // @see Load(const std::filesystem::path &fname) - static MPS Load(const char *fname, const bool restore_device = true); + static MPS Load(const std::filesystem::path &fname, const std::string &path = "/MPS/", + const bool restore_device = true); + /** + * @see Load(const std::filesystem::path &fname, const std::string &path, const bool + * restore_device) + */ + static MPS Load(const char *fname, const std::string &path = "/MPS/", + const bool restore_device = true); /** * @brief Load MPS from file and overwrite current instance - * @note This function overwrites the existing MPS. See \link Load(const std::filesystem::path - * &fname, const bool restore_device) Load() \endlink for creating a new MPS. - * @see Load(const std::filesystem::path &fname, const bool restore_device) + * @details This function overwrites the existing MPS. See Load() for creating a new MPS. + * @see Load() + */ + void Load_(const std::filesystem::path &fname, const std::string &path = "/MPS/", + const bool restore_device = true); + /** + * @see Load_(const std::filesystem::path &fname, const std::string &path, const bool + * restore_device) */ - void Load_(const std::filesystem::path &fname, const bool restore_device = true); - // @see Load_(const std::filesystem::path &fname, const bool restore_device) - void Load_(const char *fname, const bool restore_device = true); + void Load_(const char *fname, const std::string &path = "/MPS/", + const bool restore_device = true); /** * @brief Save MPS to HDF5 file * @param[in] location the HDF5 group where the MPS will be saved. - * @param[in] name the name of the MPS in the HDF5 file. - * @warning This function is only available in C++. Use \link Save(const std::filesystem::path - * &fname) Save() \endlink for saving to file in C++ or Python. - * @see from_hdf5(H5::Group &location, const std::string &name, const bool restore_device) + * @param[in] overwrite overwrite previous Bond information in the location. + * @warning This function is only available in C++. Use Save() for saving to file in C++ or + * Python. + * @see from_hdf5() */ - void to_hdf5(H5::Group &location, const std::string &name = "MPS") const; + void to_hdf5(H5::Group &location, const bool overwrite = false) const; /** * @brief Load MPS from HDF5 file (inline) * @param[in] location the HDF5 group where the MPS will be loaded from. - * @param[in] name the name of the MPS in the HDF5 file. * @param[in] restore_device whether to try restoring the device on which the data is stored; * if false, the data will be kept on the CPU. Use .to_() to move it to the target device * after loading. - * @warning This function is only available in C++. Use \link Load(const std::filesystem::path - * &fname, const bool restore_device) Load() \endlink for loading from file in C++ or Python. - * @see to_hdf5(H5::Group &location, const std::string &name) const + * @warning This function is only available in C++. Use Load() for loading from file in C++ or + * Python. + * @see to_hdf5() */ - void from_hdf5(H5::Group &location, const std::string &name = "MPS", - const bool restore_device = true); + void from_hdf5(H5::Group &location, const bool restore_device = true); /** * @brief Save MPS to binary file * @param[in] f the output stream where the MPS will be saved. * @warning This function is only available in C++. In Python, use pickle for the same binary - * file format. Use \link Save(const std::filesystem::path &fname) Save() \endlink for saving - * to file in C++ or Python. - * @see from_binary(std::istream &f, const bool restore_device) + * file format. Use Save() for saving to file in C++ or Python. + * @see from_binary() */ void to_binary(std::ostream &f) const; /** @@ -369,9 +389,8 @@ namespace cytnx { * if false, the data will be kept on the CPU. Use .to_() to move it to the target device * after loading. * @warning This function is only available in C++. In Python, use pickle for the same binary - * file format. Use \link Load(const std::filesystem::path &fname, const bool restore_device) - * Load() \endlink for loading from file in C++ or Python. - * @see to_binary(std::ostream &f) const + * file format. Use Load() for loading from file in C++ or Python. + * @see to_binary() */ void from_binary(std::istream &f, const bool restore_device = true); }; diff --git a/pybind/bond_py.cpp b/pybind/bond_py.cpp index b0f7dbc2d..5c27b2322 100644 --- a/pybind/bond_py.cpp +++ b/pybind/bond_py.cpp @@ -186,19 +186,19 @@ void bond_binding(py::module &m) { [](Bond &self, const std::filesystem::path &fname, const std::string &path, const char mode) { self.Save(fname, path, mode); }, - py::arg("fname"), py::arg("path") = "/", py::arg("mode") = 'w') + py::arg("fname"), py::arg("path") = "/Bond/", py::arg("mode") = 'w') .def_static( "Load", [](const std::filesystem::path &fname, const std::string &path) { return Bond::Load(fname, path); }, - py::arg("fname"), py::arg("path") = "/") + py::arg("fname"), py::arg("path") = "/Bond/") .def( "Load_", [](cytnx::Bond &self, const std::filesystem::path &fname, const std::string &path) { return self.Load_(fname, path); }, - py::arg("fname"), py::arg("path") = "/") + py::arg("fname"), py::arg("path") = "/Bond/") .def(py::pickle( [](const Bond &self) { // __getstate__ diff --git a/pybind/storage_py.cpp b/pybind/storage_py.cpp index 1e946ab1e..dd5ec8a14 100644 --- a/pybind/storage_py.cpp +++ b/pybind/storage_py.cpp @@ -63,7 +63,7 @@ void storage_binding(py::module &m) { } else if (tmpIN.dtype() == Type.Bool) { chr_dtype = py::format_descriptor::format(); } else { - cytnx_error_msg(true, "[ERROR] Void Type Tensor cannot convert to numpy ndarray%s", + cytnx_error_msg(true, "[ERROR] Void Type Storage cannot convert to numpy ndarray%s", "\n"); } @@ -263,24 +263,26 @@ void storage_binding(py::module &m) { .def("c_pylist_bool", &cytnx::Storage::vector) .def( - "Save", [](cytnx::Storage &self, const std::filesystem::path &fname) { self.Save(fname); }, - py::arg("fname")) - .def( - "Tofile", - [](cytnx::Storage &self, const std::filesystem::path &fname) { self.Tofile(fname); }, - py::arg("fname")) + "Save", + [](cytnx::Storage &self, const std::filesystem::path &fname, const std::string &path, + const char mode) { self.Save(fname, path, mode); }, + py::arg("fname"), py::arg("path") = "/Storage", py::arg("mode") = 'w') .def_static( "Load", - [](const std::filesystem::path &fname, const bool restore_device) { - return cytnx::Storage::Load(fname, restore_device); + [](const std::filesystem::path &fname, const std::string &path, const bool restore_device) { + return cytnx::Storage::Load(fname, path, restore_device); }, - py::arg("fname"), py::arg("restore_device") = true) + py::arg("fname"), py::arg("path") = "/Storage", py::arg("restore_device") = true) .def( "Load_", - [](cytnx::Storage &self, const std::filesystem::path &fname, const bool restore_device) { - return self.Load_(fname, restore_device); - }, - py::arg("fname"), py::arg("restore_device") = true) + [](cytnx::Storage &self, const std::filesystem::path &fname, const std::string &path, + const bool restore_device) { return self.Load_(fname, path, restore_device); }, + py::arg("fname"), py::arg("path") = "/Storage", py::arg("restore_device") = true) + + .def( + "Tofile", + [](cytnx::Storage &self, const std::filesystem::path &fname) { self.Tofile(fname); }, + py::arg("fname")) .def_static( "Fromfile", [](const std::filesystem::path &fname, const unsigned int &dtype, const cytnx_int64 &count, diff --git a/pybind/tensor_py.cpp b/pybind/tensor_py.cpp index 1ed50a799..72dd51cab 100644 --- a/pybind/tensor_py.cpp +++ b/pybind/tensor_py.cpp @@ -303,9 +303,8 @@ void tensor_binding(py::module &m) { py::arg("fname"), py::arg("path") = "/Tensor", py::arg("restore_device") = true) .def( "Load_", - [](cytnx::Tensor &self, const std::filesystem::path &fname, const std::string &path, const bool restore_device) { - return self.Load_(fname, path, restore_device); - }, + [](cytnx::Tensor &self, const std::filesystem::path &fname, const std::string &path, + const bool restore_device) { return self.Load_(fname, path, restore_device); }, py::arg("fname"), py::arg("path") = "/Tensor", py::arg("restore_device") = true) .def(py::pickle( diff --git a/pybind/tnalgo_py.cpp b/pybind/tnalgo_py.cpp index 462730091..ccfc765b6 100644 --- a/pybind/tnalgo_py.cpp +++ b/pybind/tnalgo_py.cpp @@ -54,18 +54,23 @@ void tnalgo_binding(py::module &m) { .def("c_Into_Lortho", &tn_algo::MPS::Into_Lortho) .def("c_S_mvleft", &tn_algo::MPS::S_mvleft) .def("c_S_mvright", &tn_algo::MPS::S_mvright) + .def( - "Save", [](tn_algo::MPS &self, const std::filesystem::path &fname) { self.Save(fname); }, - py::arg("fname")) + "Save", + [](tn_algo::MPS &self, const std::filesystem::path &fname, const std::string &path, + const char mode) { self.Save(fname, path, mode); }, + py::arg("fname"), py::arg("path") = "/MPS/", py::arg("mode") = 'w') .def_static( - "Load", [](const std::filesystem::path &fname) { return cytnx::tn_algo::MPS::Load(fname); }, - py::arg("fname")) + "Load", + [](const std::filesystem::path &fname, const std::string &path, const bool restore_device) { + return tn_algo::MPS::Load(fname, path, restore_device); + }, + py::arg("fname"), py::arg("path") = "/MPS/", py::arg("restore_device") = true) .def( "Load_", - [](cytnx::tn_algo::MPS &self, const std::filesystem::path &fname) { - return self.Load_(fname); - }, - py::arg("fname")) + [](tn_algo::MPS &self, const std::filesystem::path &fname, const std::string &path, + const bool restore_device) { return self.Load_(fname, path, restore_device); }, + py::arg("fname"), py::arg("path") = "/MPS/", py::arg("restore_device") = true) .def(py::pickle( [](const tn_algo::MPS &self) { // __getstate__ diff --git a/pybind/unitensor_py.cpp b/pybind/unitensor_py.cpp index ba3c41052..f35dc54ea 100644 --- a/pybind/unitensor_py.cpp +++ b/pybind/unitensor_py.cpp @@ -519,20 +519,24 @@ void unitensor_binding(py::module &m) { .def("clone", &UniTensor::clone) .def("__copy__", &UniTensor::clone) .def("__deepcopy__", &UniTensor::clone) + .def( - "Save", [](UniTensor &self, const std::filesystem::path &fname) { self.Save(fname); }, py::arg("fname")) + "Save", + [](UniTensor &self, const std::filesystem::path &fname, const std::string &path, + const char mode) { self.Save(fname, path, mode); }, + py::arg("fname"), py::arg("path") = "/UniTensor/", py::arg("mode") = 'w') .def_static( "Load", - [](const std::filesystem::path &fname, const bool restore_device) { - return cytnx::UniTensor::Load(fname, restore_device); + [](const std::filesystem::path &fname, const std::string &path, const bool restore_device) { + return UniTensor::Load(fname, path, restore_device); }, - py::arg("fname"), py::arg("restore_device") = true) + py::arg("fname"), py::arg("path") = "/UniTensor/", py::arg("restore_device") = true) .def( "Load_", - [](cytnx::UniTensor &self, const std::filesystem::path &fname, const bool restore_device) { - return self.Load_(fname, restore_device); + [](UniTensor &self, const std::filesystem::path &fname, const std::string &path, const bool restore_device) { + return self.Load_(fname, path, restore_device); }, - py::arg("fname"), py::arg("restore_device") = true) + py::arg("fname"), py::arg("path") = "/UniTensor/", py::arg("restore_device") = true) .def(py::pickle( [](const UniTensor &self) { // __getstate__ diff --git a/src/Bond.cpp b/src/Bond.cpp index de015333d..2da5d261d 100644 --- a/src/Bond.cpp +++ b/src/Bond.cpp @@ -471,7 +471,8 @@ namespace cytnx { bool Bond::operator!=(const Bond &rhs) const { return !(*this == rhs); } - void Bond::Save(const std::filesystem::path &fname, const std::string &path, const char mode) const { + void Bond::Save(const std::filesystem::path &fname, const std::string &path, + const char mode) const { fstream f; // only for binary saving, not used for HDF5 if (fname.has_extension()) { // filename extension is given @@ -503,9 +504,10 @@ namespace cytnx { } // create group H5::Group location = h5file; - if (h5file.exists(path)) { + try { + H5::Exception::dontPrint(); location = h5file.openGroup(path); - } else { + } catch (const H5::Exception &e) { H5::LinkCreatPropList lcpl; lcpl.setCreateIntermediateGroup(1); location = h5file.createGroup(path, lcpl); @@ -518,16 +520,22 @@ namespace cytnx { cytnx_error_msg(std::filesystem::exists(fname), "[ERROR] File %s already exists. Use mode 'w' to overwrite.", fname); } else { - cytnx_error_msg(mode != 'w', "[ERROR] Unknown mode '%c' for writing to binary file.", mode); + cytnx_error_msg(mode != 'w', "[ERROR] Unknown mode '%c' for writing to binary file.", + mode); } f.open(fname, std::ios::out | std::ios::trunc | std::ios::binary); } } else { // create binary file with standard extension std::filesystem::path fnameext = fname; fnameext += ".cybd"; - cytnx_warning_msg(true, "Missing file extension in fname '%s'. I am adding the extension '.cybc'. This is deprecated, please provide the file extension in the future.\n", fname.c_str()); + cytnx_warning_msg(true, + "Missing file extension in fname '%s'. I am adding the extension '.cybd'. " + "This is deprecated, please provide the file extension in the future.\n", + fname.c_str()); if (mode == 'x') { - cytnx_error_msg(std::filesystem::exists(fnameext), "[ERROR] File %s already exists. Use mode 'w' to overwrite.", fnameext.c_str()); + cytnx_error_msg(std::filesystem::exists(fnameext), + "[ERROR] File %s already exists. Use mode 'w' to overwrite.", + fnameext.c_str()); } else { cytnx_error_msg(mode != 'w', "[ERROR] Unknown mode '%c' for writing to binary file.", mode); } @@ -559,9 +567,15 @@ namespace cytnx { ext == ".HDF") { // load HDF5 H5::H5File h5file(fname, H5F_ACC_RDONLY); - cytnx_error_msg(!h5file.exists(path), "[ERROR] Path '%s' does not exist in HDF5 file '%s'", - path, fname.c_str()); - H5::Group location = h5file.openGroup(path); + H5::Group location; + try { + H5::Exception::dontPrint(); + location = h5file.openGroup(path); + } catch (const H5::Exception &e) { + std::cerr << e.getDetailMsg() << std::endl; + cytnx_error_msg(true, "[ERROR] HDF5 path '%s' not found or is not a group in file '%s'.", + path.c_str(), fname.c_str()); + } this->from_hdf5(location); h5file.close(); } else { // load binary @@ -574,7 +588,9 @@ namespace cytnx { f.close(); } } - void Bond::Load_(const char *fname, const std::string &path) { this->Load_(std::filesystem::path(fname), path); } + void Bond::Load_(const char *fname, const std::string &path) { + this->Load_(std::filesystem::path(fname), path); + } void Bond::to_hdf5(H5::Group &location, const bool overwrite, const bool save_symmetries) const { if (overwrite) { // delete previous data @@ -749,6 +765,7 @@ namespace cytnx { this->_impl->_syms.push_back(sym); sidx++; } + symloc.close(); if (symmetric) { cytnx_error_msg(sidx != qnumdim, "[ERROR] %d Symmetries were found, but second dimension of " diff --git a/src/Symmetry.cpp b/src/Symmetry.cpp index 73e324f92..9fbe22487 100644 --- a/src/Symmetry.cpp +++ b/src/Symmetry.cpp @@ -245,7 +245,8 @@ namespace cytnx { //================================================== - void cytnx::Symmetry::Save(const std::filesystem::path &fname, const std::string &path, const char mode) const { + void cytnx::Symmetry::Save(const std::filesystem::path &fname, const std::string &path, + const char mode) const { fstream f; // only for binary saving, not used for HDF5 if (fname.has_extension()) { // filename extension is given @@ -282,9 +283,10 @@ namespace cytnx { if (datasetname.empty()) datasetname = "Symmetry"; // create group H5::Group location = h5file; - if (h5file.exists(grouppath)) { + try { + H5::Exception::dontPrint(); location = h5file.openGroup(grouppath); - } else { + } catch (const H5::Exception &e) { H5::LinkCreatPropList lcpl; lcpl.setCreateIntermediateGroup(1); location = h5file.createGroup(grouppath, lcpl); @@ -297,16 +299,22 @@ namespace cytnx { cytnx_error_msg(std::filesystem::exists(fname), "[ERROR] File %s already exists. Use mode 'w' to overwrite.", fname); } else { - cytnx_error_msg(mode != 'w', "[ERROR] Unknown mode '%c' for writing to binary file.", mode); + cytnx_error_msg(mode != 'w', "[ERROR] Unknown mode '%c' for writing to binary file.", + mode); } f.open(fname, std::ios::out | std::ios::trunc | std::ios::binary); } } else { // create binary file with standard extension std::filesystem::path fnameext = fname; fnameext += ".cysym"; - cytnx_warning_msg(true, "Missing file extension in fname '%s'. I am adding the extension '.cysym'. This is deprecated, please provide the file extension in the future.\n", fname.c_str()); + cytnx_warning_msg(true, + "Missing file extension in fname '%s'. I am adding the extension '.cysym'. " + "This is deprecated, please provide the file extension in the future.\n", + fname.c_str()); if (mode == 'x') { - cytnx_error_msg(std::filesystem::exists(fnameext), "[ERROR] File %s already exists. Use mode 'w' to overwrite.", fnameext.c_str()); + cytnx_error_msg(std::filesystem::exists(fnameext), + "[ERROR] File %s already exists. Use mode 'w' to overwrite.", + fnameext.c_str()); } else { cytnx_error_msg(mode != 'w', "[ERROR] Unknown mode '%c' for writing to binary file.", mode); } @@ -323,7 +331,8 @@ namespace cytnx { this->Save(std::filesystem::path(fname), path, mode); } - cytnx::Symmetry cytnx::Symmetry::Load(const std::filesystem::path &fname, const std::string &path) { + cytnx::Symmetry cytnx::Symmetry::Load(const std::filesystem::path &fname, + const std::string &path) { Symmetry out; out.Load_(fname, path); return out; @@ -344,9 +353,15 @@ namespace cytnx { std::string datasetname = p.filename().string(); if (datasetname.empty()) datasetname = "Symmetry"; // open group - cytnx_error_msg(!h5file.exists(grouppath), - "[ERROR] Path '%s' does not exist in HDF5 file '%s'", grouppath, fname.c_str()); - H5::Group location = h5file.openGroup(grouppath); + H5::Group location; + try { + H5::Exception::dontPrint(); + location = h5file.openGroup(grouppath); + } catch (const H5::Exception &e) { + std::cerr << e.getDetailMsg() << std::endl; + cytnx_error_msg(true, "[ERROR] HDF5 path '%s' not found or is not a group in file '%s'.", + grouppath.c_str(), fname.c_str()); + } this->from_hdf5(location, datasetname); h5file.close(); } else { // load binary @@ -363,7 +378,8 @@ namespace cytnx { this->Load_(std::filesystem::path(fname), path); } - void cytnx::Symmetry::to_hdf5(H5::Group &location, const bool overwrite, const std::string &name) const { + void cytnx::Symmetry::to_hdf5(H5::Group &location, const bool overwrite, + const std::string &name) const { if (overwrite) { // delete previous data if (location.attrExists(name)) location.removeAttr(name); } diff --git a/src/Tensor.cpp b/src/Tensor.cpp index b91e544e0..72f42e32f 100644 --- a/src/Tensor.cpp +++ b/src/Tensor.cpp @@ -425,7 +425,8 @@ namespace cytnx { //=================================================================== // wrapper - void cytnx::Tensor::Save(const std::filesystem::path &fname, const std::string &path, const char mode) const { + void cytnx::Tensor::Save(const std::filesystem::path &fname, const std::string &path, + const char mode) const { fstream f; // only for binary saving, not used for HDF5 if (fname.has_extension()) { // filename extension is given @@ -462,9 +463,10 @@ namespace cytnx { if (datasetname.empty()) datasetname = "Tensor"; // create group H5::Group location = h5file; - if (h5file.exists(grouppath)) { + try { + H5::Exception::dontPrint(); location = h5file.openGroup(grouppath); - } else { + } catch (const H5::Exception &e) { H5::LinkCreatPropList lcpl; lcpl.setCreateIntermediateGroup(1); location = h5file.createGroup(grouppath, lcpl); @@ -477,16 +479,22 @@ namespace cytnx { cytnx_error_msg(std::filesystem::exists(fname), "[ERROR] File %s already exists. Use mode 'w' to overwrite.", fname); } else { - cytnx_error_msg(mode != 'w', "[ERROR] Unknown mode '%c' for writing to binary file.", mode); + cytnx_error_msg(mode != 'w', "[ERROR] Unknown mode '%c' for writing to binary file.", + mode); } f.open(fname, std::ios::out | std::ios::trunc | std::ios::binary); } } else { // create binary file with standard extension std::filesystem::path fnameext = fname; fnameext += ".cytn"; - cytnx_warning_msg(true, "Missing file extension in fname '%s'. I am adding the extension '.cytn'. This is deprecated, please provide the file extension in the future.\n", fname.c_str()); + cytnx_warning_msg(true, + "Missing file extension in fname '%s'. I am adding the extension '.cytn'. " + "This is deprecated, please provide the file extension in the future.\n", + fname.c_str()); if (mode == 'x') { - cytnx_error_msg(std::filesystem::exists(fnameext), "[ERROR] File %s already exists. Use mode 'w' to overwrite.", fnameext.c_str()); + cytnx_error_msg(std::filesystem::exists(fnameext), + "[ERROR] File %s already exists. Use mode 'w' to overwrite.", + fnameext.c_str()); } else { cytnx_error_msg(mode != 'w', "[ERROR] Unknown mode '%c' for writing to binary file.", mode); } @@ -503,16 +511,19 @@ namespace cytnx { this->Save(std::filesystem::path(fname), path, mode); } - cytnx::Tensor cytnx::Tensor::Load(const std::filesystem::path &fname, const std::string &path, const bool restore_device) { + cytnx::Tensor cytnx::Tensor::Load(const std::filesystem::path &fname, const std::string &path, + const bool restore_device) { Tensor out; out.Load_(fname, path, restore_device); return out; } - cytnx::Tensor cytnx::Tensor::Load(const char *fname, const std::string &path, const bool restore_device) { + cytnx::Tensor cytnx::Tensor::Load(const char *fname, const std::string &path, + const bool restore_device) { return cytnx::Tensor::Load(std::filesystem::path(fname), path, restore_device); } - void cytnx::Tensor::Load_(const std::filesystem::path &fname, const std::string &path, const bool restore_device) { + void cytnx::Tensor::Load_(const std::filesystem::path &fname, const std::string &path, + const bool restore_device) { std::string ext = fname.extension().string(); if (ext == ".h5" || ext == ".hdf5" || ext == ".H5" || ext == ".HDF5" || ext == ".hdf" || ext == ".HDF") { @@ -524,9 +535,15 @@ namespace cytnx { std::string datasetname = p.filename().string(); if (datasetname.empty()) datasetname = "Tensor"; // open group - cytnx_error_msg(!h5file.exists(grouppath), - "[ERROR] Path '%s' does not exist in HDF5 file '%s'", grouppath, fname.c_str()); - H5::Group location = h5file.openGroup(grouppath); + H5::Group location; + try { + H5::Exception::dontPrint(); + location = h5file.openGroup(grouppath); + } catch (const H5::Exception &e) { + std::cerr << e.getDetailMsg() << std::endl; + cytnx_error_msg(true, "[ERROR] HDF5 path '%s' not found or is not a group in file '%s'.", + grouppath.c_str(), fname.c_str()); + } this->from_hdf5(location, datasetname, restore_device); h5file.close(); } else { // load binary diff --git a/src/UniTensor.cpp b/src/UniTensor.cpp index 93635bc9b..2ecf6a47e 100644 --- a/src/UniTensor.cpp +++ b/src/UniTensor.cpp @@ -43,33 +43,75 @@ namespace cytnx { UniTensor UniTensor::Mul(const UniTensor &rhs) const { return cytnx::linalg::Mul(*this, rhs); } UniTensor UniTensor::Mul(const Scalar &rhs) const { return cytnx::linalg::Mul(*this, rhs); } - void UniTensor::Save(const std::filesystem::path &fname) const { - fstream f; // only for binary saving, not used for hdf5 + void UniTensor::Save(const std::filesystem::path &fname, const std::string &path, + const char mode) const { + fstream f; // only for binary saving, not used for HDF5 if (fname.has_extension()) { // filename extension is given std::string ext = fname.extension().string(); if (ext == ".h5" || ext == ".hdf5" || ext == ".H5" || ext == ".HDF5" || ext == ".hdf" || ext == ".HDF") { - // save as hdf5 + // save as HDF5 H5::H5File h5file; - try { + bool overwrite = false; + // open file + if (mode == 'w') { // Write new file h5file = H5::H5File(fname, H5F_ACC_TRUNC); - } catch (H5::FileIException &error) { - error.printErrorStack(); - cytnx_error_msg(true, "[ERROR] Cannot create HDF5 file '%s'.\n", fname.c_str()); + } else if (mode == 'x') { // eXclusive create + h5file = H5::H5File(fname, H5F_ACC_EXCL); + } else if (mode == 'a') { // Append data + if (std::filesystem::exists(fname)) + h5file = H5::H5File(fname, H5F_ACC_RDWR); + else + h5file = H5::H5File(fname, H5F_ACC_EXCL); + } else if (mode == 'u') { // Update data + if (std::filesystem::exists(fname)) { + h5file = H5::H5File(fname, H5F_ACC_RDWR); + overwrite = true; + } else { + h5file = H5::H5File(fname, H5F_ACC_EXCL); + } + } else { + cytnx_error_msg(true, "[ERROR] Unknown mode '%c' for writing to HDF5 file.", mode); + } + // create group + H5::Group location = h5file; + try { + H5::Exception::dontPrint(); + location = h5file.openGroup(path); + } catch (const H5::Exception &e) { + H5::LinkCreatPropList lcpl; + lcpl.setCreateIntermediateGroup(1); + location = h5file.createGroup(path, lcpl); } - this->to_hdf5(h5file); + this->to_hdf5(location, overwrite); h5file.close(); return; } else { // create binary file - f.open(fname, ios::out | ios::trunc | ios::binary); + if (mode == 'x') { + cytnx_error_msg(std::filesystem::exists(fname), + "[ERROR] File %s already exists. Use mode 'w' to overwrite.", fname); + } else { + cytnx_error_msg(mode != 'w', "[ERROR] Unknown mode '%c' for writing to binary file.", + mode); + } + f.open(fname, std::ios::out | std::ios::trunc | std::ios::binary); } } else { // create binary file with standard extension + std::filesystem::path fnameext = fname; + fnameext += ".cytnx"; cytnx_warning_msg(true, "Missing file extension in fname '%s'. I am adding the extension '.cytnx'. " "This is deprecated, please provide the file extension in the future.\n", fname.c_str()); - f.open((std::filesystem::path(fname) += ".cytnx"), ios::out | ios::trunc | ios::binary); + if (mode == 'x') { + cytnx_error_msg(std::filesystem::exists(fnameext), + "[ERROR] File %s already exists. Use mode 'w' to overwrite.", + fnameext.c_str()); + } else { + cytnx_error_msg(mode != 'w', "[ERROR] Unknown mode '%c' for writing to binary file.", mode); + } + f.open(fnameext, std::ios::out | std::ios::trunc | std::ios::binary); } // write binary if (!f.is_open()) { @@ -78,34 +120,41 @@ namespace cytnx { this->to_binary(f); f.close(); } - void UniTensor::Save(const char *fname) const { Save(std::filesystem::path(fname)); } + void UniTensor::Save(const char *fname, const std::string &path, const char mode) const { + this->Save(std::filesystem::path(fname), path, mode); + } - UniTensor UniTensor::Load(const std::filesystem::path &fname, const bool restore_device) { + UniTensor UniTensor::Load(const std::filesystem::path &fname, const std::string &path, + const bool restore_device) { UniTensor out; - out.Load_(fname, restore_device); + out.Load_(fname, path, restore_device); return out; } - UniTensor UniTensor::Load(const char *fname, const bool restore_device) { - return UniTensor::Load(std::filesystem::path(fname), restore_device); + UniTensor UniTensor::Load(const char *fname, const std::string &path, const bool restore_device) { + return UniTensor::Load(std::filesystem::path(fname), path, restore_device); } - void UniTensor::Load_(const std::filesystem::path &fname, const bool restore_device) { + void UniTensor::Load_(const std::filesystem::path &fname, const std::string &path, + const bool restore_device) { std::string ext = fname.extension().string(); if (ext == ".h5" || ext == ".hdf5" || ext == ".H5" || ext == ".HDF5" || ext == ".hdf" || ext == ".HDF") { - // load hdf5 - H5::H5File h5file; + // load HDF5 + H5::H5File h5file(fname, H5F_ACC_RDONLY); + H5::Group location; try { - h5file = H5::H5File(fname, H5F_ACC_RDONLY); - } catch (H5::FileIException &error) { - error.printErrorStack(); - cytnx_error_msg(true, "[ERROR] Cannot open HDF5 file '%s'.\n", fname.c_str()); + H5::Exception::dontPrint(); + location = h5file.openGroup(path); + } catch (const H5::Exception &e) { + std::cerr << e.getDetailMsg() << std::endl; + cytnx_error_msg(true, "[ERROR] HDF5 path '%s' not found or is not a group in file '%s'.", + path.c_str(), fname.c_str()); } - this->from_hdf5(h5file, "Tensor", restore_device); + this->from_hdf5(location, restore_device); h5file.close(); } else { // load binary fstream f; - f.open(fname, ios::in | ios::binary); + f.open(fname, std::ios::in | std::ios::binary); if (!f.is_open()) { cytnx_error_msg(true, "[ERROR] Cannot open file '%s'.\n", fname.c_str()); } @@ -113,15 +162,14 @@ namespace cytnx { f.close(); } } - void UniTensor::Load_(const char *fname, const bool restore_device) { - this->Load_(std::filesystem::path(fname), restore_device); + void UniTensor::Load_(const char *fname, const std::string &path, const bool restore_device) { + this->Load_(std::filesystem::path(fname), path, restore_device); } - void UniTensor::to_hdf5(H5::Group &location, const std::string &name) const { + void UniTensor::to_hdf5(H5::Group &location, const bool overwrite) const { cytnx_error_msg(true, "[ERROR] Saving UniTensor to HDF5 is not implemented yet!%s", "\n"); } - void UniTensor::from_hdf5(H5::Group &location, const std::string &name, - const bool restore_device) { + void UniTensor::from_hdf5(H5::Group &location, const bool restore_device) { cytnx_error_msg(true, "[ERROR] Loading UniTensor from HDF5 is not implemented yet!%s", "\n"); } @@ -174,6 +222,7 @@ namespace cytnx { // second, let dispatch to do remaining saving. this->_impl->to_binary_dispatch(f); } + void UniTensor::from_binary(std::istream &f, const bool restore_device) { unsigned int tmpIDDs; f.read((char *)&tmpIDDs, sizeof(unsigned int)); diff --git a/src/backend/Storage.cpp b/src/backend/Storage.cpp index 54098e4cc..d3ac43576 100644 --- a/src/backend/Storage.cpp +++ b/src/backend/Storage.cpp @@ -84,33 +84,80 @@ namespace cytnx { } bool Storage::operator!=(const Storage &rhs) { return !(*this == rhs); } - void Storage::Save(const std::filesystem::path &fname) const { - fstream f; + void Storage::Save(const std::filesystem::path &fname, const std::string &path, + const char mode) const { + fstream f; // only for binary saving, not used for HDF5 if (fname.has_extension()) { // filename extension is given std::string ext = fname.extension().string(); if (ext == ".h5" || ext == ".hdf5" || ext == ".H5" || ext == ".HDF5" || ext == ".hdf" || ext == ".HDF") { - // save as hdf5 + // save as HDF5 H5::H5File h5file; - try { + bool overwrite = false; + // open file + if (mode == 'w') { // Write new file h5file = H5::H5File(fname, H5F_ACC_TRUNC); - } catch (H5::FileIException &error) { - error.printErrorStack(); - cytnx_error_msg(true, "[ERROR] Cannot create HDF5 file '%s'.\n", fname.c_str()); + } else if (mode == 'x') { // eXclusive create + h5file = H5::H5File(fname, H5F_ACC_EXCL); + } else if (mode == 'a') { // Append data + if (std::filesystem::exists(fname)) + h5file = H5::H5File(fname, H5F_ACC_RDWR); + else + h5file = H5::H5File(fname, H5F_ACC_EXCL); + } else if (mode == 'u') { // Update data + if (std::filesystem::exists(fname)) { + h5file = H5::H5File(fname, H5F_ACC_RDWR); + overwrite = true; + } else { + h5file = H5::H5File(fname, H5F_ACC_EXCL); + } + } else { + cytnx_error_msg(true, "[ERROR] Unknown mode '%c' for writing to HDF5 file.", mode); } - this->to_hdf5(h5file); + // split path into group and name + std::filesystem::path p(path); + std::string grouppath = p.parent_path().generic_string(); + std::string datasetname = p.filename().string(); + if (datasetname.empty()) datasetname = "Storage"; + // create group + H5::Group location = h5file; + try { + H5::Exception::dontPrint(); + location = h5file.openGroup(grouppath); + } catch (const H5::Exception &e) { + H5::LinkCreatPropList lcpl; + lcpl.setCreateIntermediateGroup(1); + location = h5file.createGroup(grouppath, lcpl); + } + this->to_hdf5(location, overwrite, datasetname); h5file.close(); return; } else { // create binary file - f.open(fname, ios::out | ios::trunc | ios::binary); + if (mode == 'x') { + cytnx_error_msg(std::filesystem::exists(fname), + "[ERROR] File %s already exists. Use mode 'w' to overwrite.", fname); + } else { + cytnx_error_msg(mode != 'w', "[ERROR] Unknown mode '%c' for writing to binary file.", + mode); + } + f.open(fname, std::ios::out | std::ios::trunc | std::ios::binary); } } else { // create binary file with standard extension + std::filesystem::path fnameext = fname; + fnameext += ".cyst"; cytnx_warning_msg(true, "Missing file extension in fname '%s'. I am adding the extension '.cyst'. " "This is deprecated, please provide the file extension in the future.\n", fname.c_str()); - f.open((std::filesystem::path(fname) += ".cyst"), ios::out | ios::trunc | ios::binary); + if (mode == 'x') { + cytnx_error_msg(std::filesystem::exists(fnameext), + "[ERROR] File %s already exists. Use mode 'w' to overwrite.", + fnameext.c_str()); + } else { + cytnx_error_msg(mode != 'w', "[ERROR] Unknown mode '%c' for writing to binary file.", mode); + } + f.open(fnameext, std::ios::out | std::ios::trunc | std::ios::binary); } // write binary if (!f.is_open()) { @@ -119,162 +166,43 @@ namespace cytnx { this->to_binary(f); f.close(); } - void Storage::Save(const char *fname) const { this->Save(std::filesystem::path(fname)); } - - void Storage::Tofile(const std::filesystem::path &fname) const { - fstream f; - f.open(fname, ios::out | ios::trunc | ios::binary); - if (!f.is_open()) { - cytnx_error_msg(true, "[ERROR] invalid file path for save.%s", "\n"); - } - this->data_to_binary(f); - f.close(); - } - void Storage::Tofile(const char *fname) const { - fstream f; - string ffname = string(fname); - f.open(ffname, ios::out | ios::trunc | ios::binary); - if (!f.is_open()) { - cytnx_error_msg(true, "[ERROR] invalid file path for save.%s", "\n"); - } - this->data_to_binary(f); - f.close(); - } - void Storage::Tofile(fstream &f) const { - if (!f.is_open()) { - cytnx_error_msg(true, "[ERROR] invalid file path for save.%s", "\n"); - } - this->data_to_binary(f); - } - - void Storage::to_hdf5(H5::Group &location, const std::string &name) const { - hsize_t Nelem = this->size(); - H5::DataSpace dataspace(1, &Nelem); - H5::DataType datatype = Type.dtype_to_hdf5_type(this->dtype()); - H5::DataSet dataset = location.createDataSet(name, datatype, dataspace); - this->data_to_hdf5(dataset, datatype); - if (this->device() != Device.cpu) { - H5::Attribute attr = - dataset.createAttribute("device", H5::PredType::NATIVE_INT, H5::DataSpace(H5S_SCALAR)); - int device = this->device(); - attr.write(H5::PredType::NATIVE_INT, &device); - } - } - void Storage::data_to_hdf5(H5::DataSet &dataset, H5::DataType &hdf5type) const { - if (this->device() == Device.cpu) { - dataset.write(this->data(), hdf5type); - } else { -#ifdef UNI_GPU - checkCudaErrors(cudaSetDevice(this->device())); - void *htmp = malloc(Type.typeSize(this->dtype()) * this->size()); - checkCudaErrors(cudaMemcpy(htmp, this->_impl->data(), - Type.typeSize(this->dtype()) * this->size(), - cudaMemcpyDeviceToHost)); - dataset.write(htmp, hdf5type); - free(htmp); -#else - cytnx_error_msg(true, "ERROR internal fatal error in Save Storage%s", "\n"); -#endif - } - } - - void Storage::to_binary(std::ostream &f) const { - unsigned int IDDs = 999; - f.write((char *)&IDDs, sizeof(unsigned int)); - auto write_number = [&f](auto number) { - f.write(reinterpret_cast(&number), sizeof(number)); - }; - write_number(this->size()); - write_number(this->dtype()); - write_number(this->device()); - - this->data_to_binary(f); - } - void Storage::data_to_binary(std::ostream &f) const { - if (this->device() == Device.cpu) { - f.write((char *)this->_impl->data(), Type.typeSize(this->dtype()) * this->size()); - } else { -#ifdef UNI_GPU - checkCudaErrors(cudaSetDevice(this->device())); - void *htmp = malloc(Type.typeSize(this->dtype()) * this->size()); - checkCudaErrors(cudaMemcpy(htmp, this->_impl->data(), - Type.typeSize(this->dtype()) * this->size(), - cudaMemcpyDeviceToHost)); - f.write((char *)htmp, Type.typeSize(this->dtype()) * this->size()); - free(htmp); -#else - cytnx_error_msg(true, "ERROR internal fatal error in Save Storage%s", "\n"); -#endif - } - } - - Storage Storage::Fromfile(const char *fname, const unsigned int &dtype, const cytnx_int64 &count, - const int device) { - return Storage::Fromfile(std::filesystem::path(fname), dtype, count, device); - } - Storage Storage::Fromfile(const std::filesystem::path &fname, const unsigned int &dtype, - const cytnx_int64 &count, const int device) { - cytnx_error_msg(dtype == Type.Void, "[ERROR] Cannot have Void dtype.%s", "\n"); - cytnx_error_msg(count == 0, "[ERROR] count cannot be zero!%s", "\n"); - - Storage out; - cytnx_uint64 Nbytes; - cytnx_uint64 Nelem; - - // check size: - ifstream jf; - jf.open(fname, ios::ate | ios::binary); - if (!jf.is_open()) { - cytnx_error_msg(true, "[ERROR] Cannot open file '%s'.\n", fname.c_str()); - } - Nbytes = jf.tellg(); - jf.close(); - - fstream f; - // check if type match? - cytnx_error_msg(Nbytes % Type.typeSize(dtype), - "[ERROR] the total size of file is not an interval of assigned dtype.%s", "\n"); - - // check count smaller than Nelem: - if (count < 0) - Nelem = Nbytes / Type.typeSize(dtype); - else { - cytnx_error_msg(count > Nelem, "[ERROR] count exceed the total # of elements %d in file.\n", - Nelem); - Nelem = count; - } - - f.open(fname, ios::in | ios::binary); - if (!f.is_open()) { - cytnx_error_msg(true, "[ERROR] Cannot open file '%s'.\n", fname.c_str()); - } - out.data_from_binary(f, Nelem, dtype, device); - f.close(); - return out; + void Storage::Save(const char *fname, const std::string &path, const char mode) const { + this->Save(std::filesystem::path(fname), path, mode); } - Storage Storage::Load(const std::filesystem::path &fname, const bool restore_device) { + Storage Storage::Load(const std::filesystem::path &fname, const std::string &path, + const bool restore_device) { Storage out; - out.Load_(fname, restore_device); + out.Load_(fname, path, restore_device); return out; } - Storage Storage::Load(const char *fname, const bool restore_device) { - return Storage::Load(std::filesystem::path(fname), restore_device); + Storage Storage::Load(const char *fname, const std::string &path, const bool restore_device) { + return Storage::Load(std::filesystem::path(fname), path, restore_device); } - void Storage::Load_(const std::filesystem::path &fname, const bool restore_device) { + void Storage::Load_(const std::filesystem::path &fname, const std::string &path, + const bool restore_device) { std::string ext = fname.extension().string(); if (ext == ".h5" || ext == ".hdf5" || ext == ".H5" || ext == ".HDF5" || ext == ".hdf" || ext == ".HDF") { - // load hdf5 - H5::H5File h5file; + // load HDF5 + H5::H5File h5file(fname, H5F_ACC_RDONLY); + // split path into group and name + std::filesystem::path p(path); + std::string grouppath = p.parent_path().generic_string(); + std::string datasetname = p.filename().string(); + if (datasetname.empty()) datasetname = "Storage"; + // open group + H5::Group location; try { - h5file = H5::H5File(fname, H5F_ACC_RDONLY); - } catch (H5::FileIException &error) { - error.printErrorStack(); - cytnx_error_msg(true, "[ERROR] Cannot open HDF5 file '%s'.\n", fname.c_str()); + H5::Exception::dontPrint(); + location = h5file.openGroup(grouppath); + } catch (const H5::Exception &e) { + std::cerr << e.getDetailMsg() << std::endl; + cytnx_error_msg(true, "[ERROR] HDF5 path '%s' not found or is not a group in file '%s'.", + grouppath.c_str(), fname.c_str()); } - this->from_hdf5(h5file, "Storage", restore_device); + this->from_hdf5(location, datasetname, restore_device); h5file.close(); } else { // load binary fstream f; @@ -286,8 +214,26 @@ namespace cytnx { f.close(); } } - void Storage::Load_(const char *fname, const bool restore_device) { - this->Load_(std::filesystem::path(fname), restore_device); + void Storage::Load_(const char *fname, const std::string &path, const bool restore_device) { + this->Load_(std::filesystem::path(fname), path, restore_device); + } + + void Storage::to_hdf5(H5::Group &location, const bool overwrite, const std::string &name) const { + if (overwrite) { // delete previous data + if (location.nameExists(name)) location.unlink(name); + } + + hsize_t Nelem = this->size(); + H5::DataSpace dataspace(1, &Nelem); + H5::DataType datatype = Type.dtype_to_hdf5_type(this->dtype()); + H5::DataSet dataset = location.createDataSet(name, datatype, dataspace); + this->data_to_hdf5(dataset, datatype); + if (this->device() != Device.cpu) { + H5::Attribute attr = + dataset.createAttribute("device", H5::PredType::NATIVE_INT, H5::DataSpace(H5S_SCALAR)); + int device = this->device(); + attr.write(H5::PredType::NATIVE_INT, &device); + } } void Storage::from_hdf5(H5::Group &location, const std::string &name, const bool restore_device) { @@ -310,6 +256,25 @@ namespace cytnx { this->data_from_hdf5(dataset, Nelem, dtype, datatype, device); } + + void Storage::data_to_hdf5(H5::DataSet &dataset, H5::DataType &hdf5type) const { + if (this->device() == Device.cpu) { + dataset.write(this->data(), hdf5type); + } else { +#ifdef UNI_GPU + checkCudaErrors(cudaSetDevice(this->device())); + void *htmp = malloc(Type.typeSize(this->dtype()) * this->size()); + checkCudaErrors(cudaMemcpy(htmp, this->_impl->data(), + Type.typeSize(this->dtype()) * this->size(), + cudaMemcpyDeviceToHost)); + dataset.write(htmp, hdf5type); + free(htmp); +#else + cytnx_error_msg(true, "ERROR internal fatal error in Save Storage%s", "\n"); +#endif + } + } + void Storage::data_from_hdf5(H5::DataSet &dataset, const cytnx_uint64 &Nelem, const unsigned int &dtype, H5::DataType &hdf5type, const int &device) { @@ -332,6 +297,19 @@ namespace cytnx { } } + void Storage::to_binary(std::ostream &f) const { + unsigned int IDDs = 999; + f.write((char *)&IDDs, sizeof(unsigned int)); + auto write_number = [&f](auto number) { + f.write(reinterpret_cast(&number), sizeof(number)); + }; + write_number(this->size()); + write_number(this->dtype()); + write_number(this->device()); + + this->data_to_binary(f); + } + void Storage::from_binary(std::istream &f, const bool restore_device) { unsigned long long Nelem; unsigned int dtype; @@ -361,6 +339,24 @@ namespace cytnx { this->data_from_binary(f, Nelem, dtype, device); } + void Storage::data_to_binary(std::ostream &f) const { + if (this->device() == Device.cpu) { + f.write((char *)this->_impl->data(), Type.typeSize(this->dtype()) * this->size()); + } else { +#ifdef UNI_GPU + checkCudaErrors(cudaSetDevice(this->device())); + void *htmp = malloc(Type.typeSize(this->dtype()) * this->size()); + checkCudaErrors(cudaMemcpy(htmp, this->_impl->data(), + Type.typeSize(this->dtype()) * this->size(), + cudaMemcpyDeviceToHost)); + f.write((char *)htmp, Type.typeSize(this->dtype()) * this->size()); + free(htmp); +#else + cytnx_error_msg(true, "ERROR internal fatal error in Save Storage%s", "\n"); +#endif + } + } + void Storage::data_from_binary(std::istream &f, const cytnx_uint64 &Nelem, const unsigned int &dtype, const int &device) { // before enter this func, make sure @@ -384,6 +380,77 @@ namespace cytnx { } } + void Storage::Tofile(const std::filesystem::path &fname) const { + fstream f; + f.open(fname, ios::out | ios::trunc | ios::binary); + if (!f.is_open()) { + cytnx_error_msg(true, "[ERROR] invalid file path for save.%s", "\n"); + } + this->data_to_binary(f); + f.close(); + } + void Storage::Tofile(const char *fname) const { + fstream f; + string ffname = string(fname); + f.open(ffname, ios::out | ios::trunc | ios::binary); + if (!f.is_open()) { + cytnx_error_msg(true, "[ERROR] invalid file path for save.%s", "\n"); + } + this->data_to_binary(f); + f.close(); + } + void Storage::Tofile(fstream &f) const { + if (!f.is_open()) { + cytnx_error_msg(true, "[ERROR] invalid file path for save.%s", "\n"); + } + this->data_to_binary(f); + } + + Storage Storage::Fromfile(const char *fname, const unsigned int &dtype, const cytnx_int64 &count, + const int device) { + return Storage::Fromfile(std::filesystem::path(fname), dtype, count, device); + } + Storage Storage::Fromfile(const std::filesystem::path &fname, const unsigned int &dtype, + const cytnx_int64 &count, const int device) { + cytnx_error_msg(dtype == Type.Void, "[ERROR] Cannot have Void dtype.%s", "\n"); + cytnx_error_msg(count == 0, "[ERROR] count cannot be zero!%s", "\n"); + + Storage out; + cytnx_uint64 Nbytes; + cytnx_uint64 Nelem; + + // check size: + ifstream jf; + jf.open(fname, ios::ate | ios::binary); + if (!jf.is_open()) { + cytnx_error_msg(true, "[ERROR] Cannot open file '%s'.\n", fname.c_str()); + } + Nbytes = jf.tellg(); + jf.close(); + + fstream f; + // check if type match? + cytnx_error_msg(Nbytes % Type.typeSize(dtype), + "[ERROR] the total size of file is not an interval of assigned dtype.%s", "\n"); + + // check count smaller than Nelem: + if (count < 0) + Nelem = Nbytes / Type.typeSize(dtype); + else { + cytnx_error_msg(count > Nelem, "[ERROR] count exceed the total # of elements %d in file.\n", + Nelem); + Nelem = count; + } + + f.open(fname, ios::in | ios::binary); + if (!f.is_open()) { + cytnx_error_msg(true, "[ERROR] Cannot open file '%s'.\n", fname.c_str()); + } + out.data_from_binary(f, Nelem, dtype, device); + f.close(); + return out; + } + Scalar::Sproxy Storage::operator()(const cytnx_uint64 &idx) { Scalar::Sproxy out(this->_impl, idx); return out; diff --git a/src/tn_algo/MPS.cpp b/src/tn_algo/MPS.cpp index 9522cc0d1..3ee1c939d 100644 --- a/src/tn_algo/MPS.cpp +++ b/src/tn_algo/MPS.cpp @@ -14,82 +14,82 @@ using namespace std; namespace cytnx { namespace tn_algo { - std::ostream& operator<<(std::ostream& os, const MPS& in) { + std::ostream &operator<<(std::ostream &os, const MPS &in) { in._impl->Print(os); return os; } - void MPS::to_binary(std::ostream& f) const { - cytnx_error_msg(this->_impl->mps_type_id == MPSType.Void, - "[ERROR][MPS] Cannot save an uninitialize MPS.%s", "\n"); - - unsigned int IDDs = 109; - f.write((char*)&IDDs, sizeof(unsigned int)); - - f.write((char*)&this->_impl->mps_type_id, - sizeof(int)); // mps type, this is used to determine Regular/iMPS upon load - - // first, save common meta data: - f.write((char*)&this->_impl->virt_dim, sizeof(this->_impl->virt_dim)); - f.write((char*)&this->_impl->S_loc, sizeof(this->_impl->S_loc)); - - // second, dispatch to do remaining saving: - this->_impl->to_binary_dispatch(f); - } - - void MPS::from_binary(std::istream& f, const bool restore_device) { - unsigned int tmpIDDs; - f.read((char*)&tmpIDDs, sizeof(unsigned int)); - cytnx_error_msg(tmpIDDs != 109, "[ERROR] the object is not a cytnx MPS!%s", "\n"); - - int mpstype; - f.read((char*)&mpstype, - sizeof(int)); // mps type, this is used to determine Sparse/Dense upon load - - if (mpstype == MPSType.RegularMPS) { - this->_impl = boost::intrusive_ptr(new RegularMPS()); - } else if (mpstype == MPSType.iMPS) { - this->_impl = boost::intrusive_ptr(new iMPS()); - } else { - cytnx_error_msg(true, "[ERROR] Unknown MPS type!%s", "\n"); - } - - // first, load common meta data: - f.read((char*)&this->_impl->virt_dim, sizeof(this->_impl->virt_dim)); - f.read((char*)&this->_impl->S_loc, sizeof(this->_impl->S_loc)); - - // second, let dispatch to do remaining loading. - this->_impl->from_binary_dispatch(f, restore_device); - } - - void MPS::Save(const std::filesystem::path& fname) const { - fstream f; // only for binary saving, not used for hdf5 + void MPS::Save(const std::filesystem::path &fname, const std::string &path, + const char mode) const { + fstream f; // only for binary saving, not used for HDF5 if (fname.has_extension()) { // filename extension is given std::string ext = fname.extension().string(); if (ext == ".h5" || ext == ".hdf5" || ext == ".H5" || ext == ".HDF5" || ext == ".hdf" || ext == ".HDF") { - // save as hdf5 + // save as HDF5 H5::H5File h5file; - try { + bool overwrite = false; + // open file + if (mode == 'w') { // Write new file h5file = H5::H5File(fname, H5F_ACC_TRUNC); - } catch (H5::FileIException& error) { - error.printErrorStack(); - cytnx_error_msg(true, "[ERROR] Cannot create HDF5 file '%s'.\n", fname.c_str()); + } else if (mode == 'x') { // eXclusive create + h5file = H5::H5File(fname, H5F_ACC_EXCL); + } else if (mode == 'a') { // Append data + if (std::filesystem::exists(fname)) + h5file = H5::H5File(fname, H5F_ACC_RDWR); + else + h5file = H5::H5File(fname, H5F_ACC_EXCL); + } else if (mode == 'u') { // Update data + if (std::filesystem::exists(fname)) { + h5file = H5::H5File(fname, H5F_ACC_RDWR); + overwrite = true; + } else { + h5file = H5::H5File(fname, H5F_ACC_EXCL); + } + } else { + cytnx_error_msg(true, "[ERROR] Unknown mode '%c' for writing to HDF5 file.", mode); + } + // create group + H5::Group location = h5file; + try { + H5::Exception::dontPrint(); + location = h5file.openGroup(path); + } catch (const H5::Exception &e) { + H5::LinkCreatPropList lcpl; + lcpl.setCreateIntermediateGroup(1); + location = h5file.createGroup(path, lcpl); } - this->to_hdf5(h5file); + this->to_hdf5(location, overwrite); h5file.close(); return; } else { // create binary file - f.open(fname, ios::out | ios::trunc | ios::binary); + if (mode == 'x') { + cytnx_error_msg(std::filesystem::exists(fname), + "[ERROR] File %s already exists. Use mode 'w' to overwrite.", fname); + } else { + cytnx_error_msg(mode != 'w', "[ERROR] Unknown mode '%c' for writing to binary file.", + mode); + } + f.open(fname, std::ios::out | std::ios::trunc | std::ios::binary); } } else { // create binary file with standard extension + std::filesystem::path fnameext = fname; + fnameext += ".cymps"; cytnx_warning_msg( true, "Missing file extension in fname '%s'. I am adding the extension '.cymps'. This is " "deprecated, please provide the file extension in the future.\n", fname.c_str()); - f.open((std::filesystem::path(fname) += ".cymps"), ios::out | ios::trunc | ios::binary); + if (mode == 'x') { + cytnx_error_msg(std::filesystem::exists(fnameext), + "[ERROR] File %s already exists. Use mode 'w' to overwrite.", + fnameext.c_str()); + } else { + cytnx_error_msg(mode != 'w', "[ERROR] Unknown mode '%c' for writing to binary file.", + mode); + } + f.open(fnameext, std::ios::out | std::ios::trunc | std::ios::binary); } // write binary if (!f.is_open()) { @@ -98,52 +98,101 @@ namespace cytnx { this->to_binary(f); f.close(); } - void MPS::Save(const char* fname) const { this->Save(std::filesystem::path(fname)); } + void MPS::Save(const char *fname, const std::string &path, const char mode) const { + this->Save(std::filesystem::path(fname), path, mode); + } - MPS MPS::Load(const std::filesystem::path& fname, const bool restore_device) { + MPS MPS::Load(const std::filesystem::path &fname, const std::string &path, + const bool restore_device) { MPS out; - out.Load_(fname, restore_device); + out.Load_(fname, path, restore_device); return out; } - MPS MPS::Load(const char* fname, const bool restore_device) { - return MPS::Load(std::filesystem::path(fname), restore_device); + MPS MPS::Load(const char *fname, const std::string &path, const bool restore_device) { + return MPS::Load(std::filesystem::path(fname), path, restore_device); } - void MPS::Load_(const std::filesystem::path& fname, const bool restore_device) { + void MPS::Load_(const std::filesystem::path &fname, const std::string &path, + const bool restore_device) { std::string ext = fname.extension().string(); if (ext == ".h5" || ext == ".hdf5" || ext == ".H5" || ext == ".HDF5" || ext == ".hdf" || ext == ".HDF") { - // load hdf5 - H5::H5File h5file; + // load HDF5 + H5::H5File h5file(fname, H5F_ACC_RDONLY); + H5::Group location; try { - h5file = H5::H5File(fname, H5F_ACC_RDONLY); - } catch (H5::FileIException& error) { - error.printErrorStack(); - cytnx_error_msg(true, "[ERROR] Cannot open HDF5 file '%s'.\n", fname.c_str()); + H5::Exception::dontPrint(); + location = h5file.openGroup(path); + } catch (const H5::Exception &e) { + std::cerr << e.getDetailMsg() << std::endl; + cytnx_error_msg(true, "[ERROR] HDF5 path '%s' not found or is not a group in file '%s'.", + path.c_str(), fname.c_str()); } - this->from_hdf5(h5file); + this->from_hdf5(location, restore_device); h5file.close(); } else { // load binary fstream f; - f.open(fname, ios::in | ios::binary); + f.open(fname, std::ios::in | std::ios::binary); if (!f.is_open()) { cytnx_error_msg(true, "[ERROR] Cannot open file '%s'.\n", fname.c_str()); } - this->from_binary(f); + this->from_binary(f, restore_device); f.close(); } } - void MPS::Load_(const char* fname, const bool restore_device) { - this->Load_(std::filesystem::path(fname), restore_device); + void MPS::Load_(const char *fname, const std::string &path, const bool restore_device) { + this->Load_(std::filesystem::path(fname), path, restore_device); } - void MPS::to_hdf5(H5::Group& location, const std::string& name) const { + void MPS::to_hdf5(H5::Group &location, const bool overwrite) const { cytnx_error_msg(true, "[ERROR] Saving MPS to HDF5 is not implemented yet!%s", "\n"); } - void MPS::from_hdf5(H5::Group& location, const std::string& name, const bool restore_device) { + void MPS::from_hdf5(H5::Group &location, const bool restore_device) { cytnx_error_msg(true, "[ERROR] Loading MPS from HDF5 is not implemented yet!%s", "\n"); } + void MPS::to_binary(std::ostream &f) const { + cytnx_error_msg(this->_impl->mps_type_id == MPSType.Void, + "[ERROR][MPS] Cannot save an uninitialize MPS.%s", "\n"); + + unsigned int IDDs = 109; + f.write((char *)&IDDs, sizeof(unsigned int)); + + f.write((char *)&this->_impl->mps_type_id, + sizeof(int)); // mps type, this is used to determine Regular/iMPS upon load + + // first, save common meta data: + f.write((char *)&this->_impl->virt_dim, sizeof(this->_impl->virt_dim)); + f.write((char *)&this->_impl->S_loc, sizeof(this->_impl->S_loc)); + + // second, dispatch to do remaining saving: + this->_impl->to_binary_dispatch(f); + } + + void MPS::from_binary(std::istream &f, const bool restore_device) { + unsigned int tmpIDDs; + f.read((char *)&tmpIDDs, sizeof(unsigned int)); + cytnx_error_msg(tmpIDDs != 109, "[ERROR] the object is not a cytnx MPS!%s", "\n"); + + int mpstype; + f.read((char *)&mpstype, + sizeof(int)); // mps type, this is used to determine Sparse/Dense upon load + + if (mpstype == MPSType.RegularMPS) { + this->_impl = boost::intrusive_ptr(new RegularMPS()); + } else if (mpstype == MPSType.iMPS) { + this->_impl = boost::intrusive_ptr(new iMPS()); + } else { + cytnx_error_msg(true, "[ERROR] Unknown MPS type!%s", "\n"); + } + + // first, load common meta data: + f.read((char *)&this->_impl->virt_dim, sizeof(this->_impl->virt_dim)); + f.read((char *)&this->_impl->S_loc, sizeof(this->_impl->S_loc)); + + // second, let dispatch to do remaining loading. + this->_impl->from_binary_dispatch(f, restore_device); + } } // namespace tn_algo } // namespace cytnx From 7b968728ec7d04a792f64bdfad4380f0773e5a97 Mon Sep 17 00:00:00 2001 From: Manuel Schneider Date: Fri, 1 May 2026 14:07:14 +0800 Subject: [PATCH 14/40] groups are created without switching off HDF5 error messages --- src/Bond.cpp | 26 ++++++++++++++------------ src/Symmetry.cpp | 26 +++++++++++++------------- src/Tensor.cpp | 26 +++++++++++++------------- src/UniTensor.cpp | 26 ++++++++++++++------------ src/backend/Storage.cpp | 26 +++++++++++++------------- src/tn_algo/MPS.cpp | 26 ++++++++++++++------------ 6 files changed, 81 insertions(+), 75 deletions(-) diff --git a/src/Bond.cpp b/src/Bond.cpp index 2da5d261d..d8098e178 100644 --- a/src/Bond.cpp +++ b/src/Bond.cpp @@ -503,15 +503,17 @@ namespace cytnx { cytnx_error_msg(true, "[ERROR] Unknown mode '%c' for writing to HDF5 file.", mode); } // create group - H5::Group location = h5file; - try { - H5::Exception::dontPrint(); - location = h5file.openGroup(path); - } catch (const H5::Exception &e) { - H5::LinkCreatPropList lcpl; - lcpl.setCreateIntermediateGroup(1); - location = h5file.createGroup(path, lcpl); + std::filesystem::path grouppath(path); + std::filesystem::path subpath; + std::string groupfolder = "/"; + for (const auto &part : grouppath) { + if (part.empty()) continue; + subpath /= part; + groupfolder = subpath.generic_string(); + if (!h5file.exists(groupfolder)) h5file.createGroup(groupfolder); } + H5::Group location = h5file.openGroup(groupfolder); + // write data this->to_hdf5(location, overwrite); h5file.close(); return; @@ -564,18 +566,18 @@ namespace cytnx { void Bond::Load_(const std::filesystem::path &fname, const std::string &path) { std::string ext = fname.extension().string(); if (ext == ".h5" || ext == ".hdf5" || ext == ".H5" || ext == ".HDF5" || ext == ".hdf" || - ext == ".HDF") { - // load HDF5 + ext == ".HDF") { // load HDF5 H5::H5File h5file(fname, H5F_ACC_RDONLY); + // open group H5::Group location; try { - H5::Exception::dontPrint(); - location = h5file.openGroup(path); + location = h5file.openGroup(path.empty() ? "/" : path); } catch (const H5::Exception &e) { std::cerr << e.getDetailMsg() << std::endl; cytnx_error_msg(true, "[ERROR] HDF5 path '%s' not found or is not a group in file '%s'.", path.c_str(), fname.c_str()); } + // read data this->from_hdf5(location); h5file.close(); } else { // load binary diff --git a/src/Symmetry.cpp b/src/Symmetry.cpp index 9fbe22487..a271ccd9b 100644 --- a/src/Symmetry.cpp +++ b/src/Symmetry.cpp @@ -278,19 +278,20 @@ namespace cytnx { } // split path into group and name std::filesystem::path p(path); - std::string grouppath = p.parent_path().generic_string(); + std::filesystem::path grouppath = p.parent_path(); std::string datasetname = p.filename().string(); if (datasetname.empty()) datasetname = "Symmetry"; // create group - H5::Group location = h5file; - try { - H5::Exception::dontPrint(); - location = h5file.openGroup(grouppath); - } catch (const H5::Exception &e) { - H5::LinkCreatPropList lcpl; - lcpl.setCreateIntermediateGroup(1); - location = h5file.createGroup(grouppath, lcpl); + std::filesystem::path subpath; + std::string groupfolder = "/"; + for (const auto &part : grouppath) { + if (part.empty()) continue; + subpath /= part; + groupfolder = subpath.generic_string(); + if (!h5file.exists(groupfolder)) h5file.createGroup(groupfolder); } + H5::Group location = h5file.openGroup(groupfolder); + // write data this->to_hdf5(location, overwrite, datasetname); h5file.close(); return; @@ -344,8 +345,7 @@ namespace cytnx { void cytnx::Symmetry::Load_(const std::filesystem::path &fname, const std::string &path) { std::string ext = fname.extension().string(); if (ext == ".h5" || ext == ".hdf5" || ext == ".H5" || ext == ".HDF5" || ext == ".hdf" || - ext == ".HDF") { - // load HDF5 + ext == ".HDF") { // load HDF5 H5::H5File h5file(fname, H5F_ACC_RDONLY); // split path into group and name std::filesystem::path p(path); @@ -355,13 +355,13 @@ namespace cytnx { // open group H5::Group location; try { - H5::Exception::dontPrint(); - location = h5file.openGroup(grouppath); + location = h5file.openGroup(grouppath.empty() ? "/" : grouppath); } catch (const H5::Exception &e) { std::cerr << e.getDetailMsg() << std::endl; cytnx_error_msg(true, "[ERROR] HDF5 path '%s' not found or is not a group in file '%s'.", grouppath.c_str(), fname.c_str()); } + // read data this->from_hdf5(location, datasetname); h5file.close(); } else { // load binary diff --git a/src/Tensor.cpp b/src/Tensor.cpp index 72f42e32f..c36f9ac82 100644 --- a/src/Tensor.cpp +++ b/src/Tensor.cpp @@ -458,19 +458,20 @@ namespace cytnx { } // split path into group and name std::filesystem::path p(path); - std::string grouppath = p.parent_path().generic_string(); + std::filesystem::path grouppath = p.parent_path(); std::string datasetname = p.filename().string(); if (datasetname.empty()) datasetname = "Tensor"; // create group - H5::Group location = h5file; - try { - H5::Exception::dontPrint(); - location = h5file.openGroup(grouppath); - } catch (const H5::Exception &e) { - H5::LinkCreatPropList lcpl; - lcpl.setCreateIntermediateGroup(1); - location = h5file.createGroup(grouppath, lcpl); + std::filesystem::path subpath; + std::string groupfolder = "/"; + for (const auto &part : grouppath) { + if (part.empty()) continue; + subpath /= part; + groupfolder = subpath.generic_string(); + if (!h5file.exists(groupfolder)) h5file.createGroup(groupfolder); } + H5::Group location = h5file.openGroup(groupfolder); + // write data this->to_hdf5(location, overwrite, datasetname); h5file.close(); return; @@ -526,8 +527,7 @@ namespace cytnx { const bool restore_device) { std::string ext = fname.extension().string(); if (ext == ".h5" || ext == ".hdf5" || ext == ".H5" || ext == ".HDF5" || ext == ".hdf" || - ext == ".HDF") { - // load HDF5 + ext == ".HDF") { // load HDF5 H5::H5File h5file(fname, H5F_ACC_RDONLY); // split path into group and name std::filesystem::path p(path); @@ -537,13 +537,13 @@ namespace cytnx { // open group H5::Group location; try { - H5::Exception::dontPrint(); - location = h5file.openGroup(grouppath); + location = h5file.openGroup(grouppath.empty() ? "/" : grouppath); } catch (const H5::Exception &e) { std::cerr << e.getDetailMsg() << std::endl; cytnx_error_msg(true, "[ERROR] HDF5 path '%s' not found or is not a group in file '%s'.", grouppath.c_str(), fname.c_str()); } + // read data this->from_hdf5(location, datasetname, restore_device); h5file.close(); } else { // load binary diff --git a/src/UniTensor.cpp b/src/UniTensor.cpp index 2ecf6a47e..b4fa86b92 100644 --- a/src/UniTensor.cpp +++ b/src/UniTensor.cpp @@ -75,15 +75,17 @@ namespace cytnx { cytnx_error_msg(true, "[ERROR] Unknown mode '%c' for writing to HDF5 file.", mode); } // create group - H5::Group location = h5file; - try { - H5::Exception::dontPrint(); - location = h5file.openGroup(path); - } catch (const H5::Exception &e) { - H5::LinkCreatPropList lcpl; - lcpl.setCreateIntermediateGroup(1); - location = h5file.createGroup(path, lcpl); + std::filesystem::path grouppath(path); + std::filesystem::path subpath; + std::string groupfolder = "/"; + for (const auto &part : grouppath) { + if (part.empty()) continue; + subpath /= part; + groupfolder = subpath.generic_string(); + if (!h5file.exists(groupfolder)) h5file.createGroup(groupfolder); } + H5::Group location = h5file.openGroup(groupfolder); + // write data this->to_hdf5(location, overwrite); h5file.close(); return; @@ -138,18 +140,18 @@ namespace cytnx { const bool restore_device) { std::string ext = fname.extension().string(); if (ext == ".h5" || ext == ".hdf5" || ext == ".H5" || ext == ".HDF5" || ext == ".hdf" || - ext == ".HDF") { - // load HDF5 + ext == ".HDF") { // load HDF5 H5::H5File h5file(fname, H5F_ACC_RDONLY); + // open group H5::Group location; try { - H5::Exception::dontPrint(); - location = h5file.openGroup(path); + location = h5file.openGroup(path.empty() ? "/" : path); } catch (const H5::Exception &e) { std::cerr << e.getDetailMsg() << std::endl; cytnx_error_msg(true, "[ERROR] HDF5 path '%s' not found or is not a group in file '%s'.", path.c_str(), fname.c_str()); } + // read data this->from_hdf5(location, restore_device); h5file.close(); } else { // load binary diff --git a/src/backend/Storage.cpp b/src/backend/Storage.cpp index d3ac43576..e1d4f3c17 100644 --- a/src/backend/Storage.cpp +++ b/src/backend/Storage.cpp @@ -117,19 +117,20 @@ namespace cytnx { } // split path into group and name std::filesystem::path p(path); - std::string grouppath = p.parent_path().generic_string(); + std::filesystem::path grouppath = p.parent_path(); std::string datasetname = p.filename().string(); if (datasetname.empty()) datasetname = "Storage"; // create group - H5::Group location = h5file; - try { - H5::Exception::dontPrint(); - location = h5file.openGroup(grouppath); - } catch (const H5::Exception &e) { - H5::LinkCreatPropList lcpl; - lcpl.setCreateIntermediateGroup(1); - location = h5file.createGroup(grouppath, lcpl); + std::filesystem::path subpath; + std::string groupfolder = "/"; + for (const auto &part : grouppath) { + if (part.empty()) continue; + subpath /= part; + groupfolder = subpath.generic_string(); + if (!h5file.exists(groupfolder)) h5file.createGroup(groupfolder); } + H5::Group location = h5file.openGroup(groupfolder); + // write data this->to_hdf5(location, overwrite, datasetname); h5file.close(); return; @@ -184,8 +185,7 @@ namespace cytnx { const bool restore_device) { std::string ext = fname.extension().string(); if (ext == ".h5" || ext == ".hdf5" || ext == ".H5" || ext == ".HDF5" || ext == ".hdf" || - ext == ".HDF") { - // load HDF5 + ext == ".HDF") { // load HDF5 H5::H5File h5file(fname, H5F_ACC_RDONLY); // split path into group and name std::filesystem::path p(path); @@ -195,13 +195,13 @@ namespace cytnx { // open group H5::Group location; try { - H5::Exception::dontPrint(); - location = h5file.openGroup(grouppath); + location = h5file.openGroup(grouppath.empty() ? "/" : grouppath); } catch (const H5::Exception &e) { std::cerr << e.getDetailMsg() << std::endl; cytnx_error_msg(true, "[ERROR] HDF5 path '%s' not found or is not a group in file '%s'.", grouppath.c_str(), fname.c_str()); } + // read data this->from_hdf5(location, datasetname, restore_device); h5file.close(); } else { // load binary diff --git a/src/tn_algo/MPS.cpp b/src/tn_algo/MPS.cpp index 3ee1c939d..0e9b6a876 100644 --- a/src/tn_algo/MPS.cpp +++ b/src/tn_algo/MPS.cpp @@ -51,15 +51,17 @@ namespace cytnx { cytnx_error_msg(true, "[ERROR] Unknown mode '%c' for writing to HDF5 file.", mode); } // create group - H5::Group location = h5file; - try { - H5::Exception::dontPrint(); - location = h5file.openGroup(path); - } catch (const H5::Exception &e) { - H5::LinkCreatPropList lcpl; - lcpl.setCreateIntermediateGroup(1); - location = h5file.createGroup(path, lcpl); + std::filesystem::path grouppath(path); + std::filesystem::path subpath; + std::string groupfolder = "/"; + for (const auto &part : grouppath) { + if (part.empty()) continue; + subpath /= part; + groupfolder = subpath.generic_string(); + if (!h5file.exists(groupfolder)) h5file.createGroup(groupfolder); } + H5::Group location = h5file.openGroup(groupfolder); + // write data this->to_hdf5(location, overwrite); h5file.close(); return; @@ -116,18 +118,18 @@ namespace cytnx { const bool restore_device) { std::string ext = fname.extension().string(); if (ext == ".h5" || ext == ".hdf5" || ext == ".H5" || ext == ".HDF5" || ext == ".hdf" || - ext == ".HDF") { - // load HDF5 + ext == ".HDF") { // load HDF5 H5::H5File h5file(fname, H5F_ACC_RDONLY); + // open group H5::Group location; try { - H5::Exception::dontPrint(); - location = h5file.openGroup(path); + location = h5file.openGroup(path.empty() ? "/" : path); } catch (const H5::Exception &e) { std::cerr << e.getDetailMsg() << std::endl; cytnx_error_msg(true, "[ERROR] HDF5 path '%s' not found or is not a group in file '%s'.", path.c_str(), fname.c_str()); } + // read data this->from_hdf5(location, restore_device); h5file.close(); } else { // load binary From f975f9ce489e39ec8378e8e3656de06ff0389381 Mon Sep 17 00:00:00 2001 From: Manuel Schneider Date: Fri, 1 May 2026 22:07:25 +0800 Subject: [PATCH 15/40] replaced Symmetry.name() by Symmetry.getname() to be consistent with other classes --- include/Symmetry.hpp | 14 +++++++------- include/UniTensor.hpp | 2 +- pybind/symmetry_py.cpp | 2 +- src/Bond.cpp | 2 +- src/Symmetry.cpp | 12 ++++++------ src/UniTensor.cpp | 20 ++++++++++++++++++++ src/UniTensor_base.cpp | 11 +++++------ 7 files changed, 41 insertions(+), 22 deletions(-) diff --git a/include/Symmetry.hpp b/include/Symmetry.hpp index fd3cba7e4..966a4b0be 100644 --- a/include/Symmetry.hpp +++ b/include/Symmetry.hpp @@ -31,7 +31,7 @@ namespace cytnx { * fPar | -2, fermionParity symmetry * fNum | -3, fermionNumber symmetry * - * @see Symmetry::stype(), Symmetry::name(), Symmetry::stype_str() + * @see Symmetry::stype(), Symmetry::getname(), Symmetry::stype_str() */ enum SymmetryType : int { Void = -99, U = -1, Z = 0, fPar = -2, fNum = -3 }; @@ -95,7 +95,7 @@ namespace cytnx { virtual bool is_fermionic() const { return false; }; virtual void print_info() const; - virtual std::string name() const; + virtual std::string getname() const; virtual std::string stype_str() const; // virtual std::vector& combine_rule(const std::vector &inL, const // std::vector &inR); @@ -124,7 +124,7 @@ namespace cytnx { const bool &is_reverse); void reverse_rule_(cytnx_int64 &out, const cytnx_int64 &in); void print_info() const; - std::string name() const override { return "U1"; } + std::string getname() const override { return "U1"; } std::string stype_str() const override { return "U1"; }; }; ///@endcond @@ -151,7 +151,7 @@ namespace cytnx { const bool &is_reverse); void reverse_rule_(cytnx_int64 &out, const cytnx_int64 &in); void print_info() const; - std::string name() const override { return "Z" + std::to_string(this->n); }; + std::string getname() const override { return "Z" + std::to_string(this->n); }; std::string stype_str() const override { return "Z" + std::to_string(this->n); }; }; ///@endcond @@ -177,7 +177,7 @@ namespace cytnx { fermionParity get_fermion_parity(const cytnx_int64 &in_qnum) const override; bool is_fermionic() const override { return true; }; void print_info() const; - std::string name() const override { return "fermion parity"; } + std::string getname() const override { return "fermion parity"; } std::string stype_str() const override { return "fP"; } }; ///@endcond @@ -203,7 +203,7 @@ namespace cytnx { fermionParity get_fermion_parity(const cytnx_int64 &in_qnum) const override; bool is_fermionic() const override { return true; }; void print_info() const; - std::string name() const override { return "fermion number"; } + std::string getname() const override { return "fermion number"; } std::string stype_str() const override { return "f#"; } }; ///@endcond @@ -418,7 +418,7 @@ namespace cytnx { @brief Type name of the Symmetry in long form @return [std::string] the symmetry type long name. */ - std::string name() const { return this->_impl->name(); } + std::string getname() const { return this->_impl->getname(); } /** @brief Type name of the Symmetry in short form @return [std::string] the symmetry type short name. diff --git a/include/UniTensor.hpp b/include/UniTensor.hpp index 37cbe5d4d..8adf5ca01 100644 --- a/include/UniTensor.hpp +++ b/include/UniTensor.hpp @@ -2695,6 +2695,7 @@ namespace cytnx { ///@cond boost::intrusive_ptr _impl; UniTensor() : _impl(new UniTensor_base()){}; + UniTensor(const std::string name); UniTensor(const UniTensor &rhs) { this->_impl = rhs._impl; } UniTensor &operator=(const UniTensor &rhs) { this->_impl = rhs._impl; @@ -2754,7 +2755,6 @@ namespace cytnx { */ void Init(const Tensor &in_tensor, const bool &is_diag = false, const cytnx_int64 &rowrank = -1, const std::vector &in_labels = {}, const std::string &name = "") { - // std::cout << "[entry!]" << std::endl; boost::intrusive_ptr out(new DenseUniTensor()); out->Init_by_Tensor(in_tensor, is_diag, rowrank, name); this->_impl = out; diff --git a/pybind/symmetry_py.cpp b/pybind/symmetry_py.cpp index b4335d6ec..178cbd5da 100644 --- a/pybind/symmetry_py.cpp +++ b/pybind/symmetry_py.cpp @@ -49,7 +49,7 @@ void symmetry_binding(py::module &m) { .def_static("FermionNumber", &Symmetry::FermionNumber) .def("clone", &Symmetry::clone) .def("stype", &Symmetry::stype) - .def("name", &Symmetry::name) + .def("getname", &Symmetry::getname) .def("stype_str", &Symmetry::stype_str) .def("n", &Symmetry::n) .def("clone", &Symmetry::clone) diff --git a/src/Bond.cpp b/src/Bond.cpp index d8098e178..445748b00 100644 --- a/src/Bond.cpp +++ b/src/Bond.cpp @@ -665,7 +665,7 @@ namespace cytnx { // std::vector c_strings; // std::string symstring; // for (const auto& s : this->_impl->_syms) { - // symstring = s.name(); + // symstring = s.getname(); // c_strings.push_back(symstring.c_str()); // } // dataset.write(c_strings.data(), str_type); diff --git a/src/Symmetry.cpp b/src/Symmetry.cpp index a271ccd9b..3fbfeb632 100644 --- a/src/Symmetry.cpp +++ b/src/Symmetry.cpp @@ -66,7 +66,7 @@ namespace cytnx { cytnx_error_msg(1, "%s", "[ERROR][Internal] should not call Symmerty base!"); } - std::string cytnx::Symmetry_base::name() const { + std::string cytnx::Symmetry_base::getname() const { cytnx_error_msg(1, "%s", "[ERROR][Internal] should not call Symmerty base!"); } @@ -98,7 +98,7 @@ namespace cytnx { void cytnx::U1Symmetry::print_info() const { cout << "--------------------\n"; cout << "[Symmetry]" << endl; - cout << "type : Abelian, U1" << endl; + cout << "type : Abelian, " << this->getname() << endl; cout << "combine rule : Q1 + Q2" << endl; cout << "reverse rule : Q*(-1) " << endl; cout << "--------------------\n"; @@ -141,7 +141,7 @@ namespace cytnx { void cytnx::ZnSymmetry::print_info() const { cout << "--------------------\n"; cout << "[Symmetry]" << endl; - cout << "type : Abelian, Z(" << this->n << ")" << endl; + cout << "type : Abelian, " << this->getname() << endl; cout << "combine rule : (Q1 + Q2)\%" << this->n << endl; cout << "reverse rule : Q*(-1) " << endl; cout << "--------------------\n"; @@ -195,7 +195,7 @@ namespace cytnx { void cytnx::FermionParitySymmetry::print_info() const { cout << "--------------------\n"; cout << "[Symmetry]" << endl; - cout << "type : fermionic, FermionParity" << endl; + cout << "type : Fermionic, " << this->getname() << endl; cout << "combine rule : (Q1 + Q2)\%2" << endl; cout << "reverse rule : Q*(-1) " << endl; cout << "--------------------\n"; @@ -237,7 +237,7 @@ namespace cytnx { void cytnx::FermionNumberSymmetry::print_info() const { cout << "--------------------\n"; cout << "[Symmetry]" << endl; - cout << "type : fermionic, FermionNumber" << endl; + cout << "type : fermionic, " << this->getname() << endl; cout << "combine rule : Q1 + Q2" << endl; cout << "reverse rule : Q*(-1) " << endl; cout << "--------------------\n"; @@ -384,7 +384,7 @@ namespace cytnx { if (location.attrExists(name)) location.removeAttr(name); } - std::string symname = this->name(); + std::string symname = this->getname(); H5::StrType str_type(H5::PredType::C_S1, symname.length() + 1); H5::DataSpace dataspace = H5::DataSpace(H5S_SCALAR); H5::Attribute attr = location.createAttribute(name, str_type, dataspace); diff --git a/src/UniTensor.cpp b/src/UniTensor.cpp index b4fa86b92..12f4fdffc 100644 --- a/src/UniTensor.cpp +++ b/src/UniTensor.cpp @@ -16,6 +16,26 @@ using namespace std; namespace cytnx { + UniTensor::UniTensor(const std::string name) { + if (name == UTenType.getname(UTenType.Block)) { + boost::intrusive_ptr tmp(new BlockUniTensor); + this->_impl = tmp; + } else if (name == UTenType.getname(UTenType.BlockFermionic)) { + boost::intrusive_ptr tmp(new BlockFermionicUniTensor); + this->_impl = tmp; + } else if (name == UTenType.getname(UTenType.Dense)) { + boost::intrusive_ptr tmp(new DenseUniTensor); + this->_impl = tmp; + } else if (name == UTenType.getname(UTenType.Void)) { + boost::intrusive_ptr tmp(new UniTensor_base); + this->_impl = tmp; + } else if (name == UTenType.getname(UTenType.Sparse)) { + cytnx_error_msg(true, "[ERROR] SparseUniTensor is deprecated.\s","\n"); + } else { + cytnx_error_msg(true, "[ERROR] No UniTensor type matches the string '%s'.\n", name.c_str()); + } + } + UniTensor UniTensor::Pow(const double &p) const { return cytnx::linalg::Pow(*this, p); } UniTensor &UniTensor::Pow_(const double &p) { cytnx::linalg::Pow_(*this, p); diff --git a/src/UniTensor_base.cpp b/src/UniTensor_base.cpp index ef9ff19c4..90ab58a94 100644 --- a/src/UniTensor_base.cpp +++ b/src/UniTensor_base.cpp @@ -11,20 +11,19 @@ namespace cytnx { //==================================================== std::string UniTensorType_class::getname(const int &ut_type) const { if (ut_type == this->Void) { - return std::string("Void (un-initialize UniTensor)"); + return std::string("Void (uninitialized UniTensor)"); } else if (ut_type == this->Dense) { - return std::string("Dense"); + return std::string("DenseUniTensor"); } else if (ut_type == this->Sparse) { - return std::string("Sparse"); + return std::string("SparseUniTensor"); } else if (ut_type == this->Block) { - return std::string("Block"); + return std::string("BlockUniTensor"); } else if (ut_type == this->BlockFermionic) { - return std::string("Block Fermionic"); + return std::string("BlockFermionicUniTensor"); } else { cytnx_error_msg(true, "%s\n", "[ERROR] invalid ut_type"); return std::string(""); } - // extend more in here!! } UniTensorType_class UTenType; //=================================================== From 261464f2a5a77ed30e6528eac61c0352fe377157 Mon Sep 17 00:00:00 2001 From: Manuel Schneider Date: Sat, 2 May 2026 12:58:14 +0800 Subject: [PATCH 16/40] enable reuse of space from deleted objects in HDF5 files; requires HDF5 1.10.0 or newer --- CMakeLists.txt | 2 +- src/Bond.cpp | 23 ++++++++++++++++------- src/Symmetry.cpp | 23 ++++++++++++++++------- src/Tensor.cpp | 23 ++++++++++++++++------- src/UniTensor.cpp | 25 +++++++++++++++++-------- src/backend/Storage.cpp | 23 ++++++++++++++++------- src/tn_algo/MPS.cpp | 23 ++++++++++++++++------- 7 files changed, 98 insertions(+), 44 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index cd2b6d25c..bb9f2ef2a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -289,7 +289,7 @@ message(STATUS "Found ARPACK_LIB at: ${ARPACK_LIB}") target_link_libraries(cytnx PRIVATE ${ARPACK_LIB}) #HDF5 -find_package(HDF5 REQUIRED COMPONENTS CXX) +find_package(HDF5 2.0.0 REQUIRED COMPONENTS CXX) target_include_directories(cytnx PRIVATE ${HDF5_INCLUDE_DIRS}) target_link_libraries(cytnx PRIVATE ${HDF5_LIBRARIES}) diff --git a/src/Bond.cpp b/src/Bond.cpp index 445748b00..eb045f3e5 100644 --- a/src/Bond.cpp +++ b/src/Bond.cpp @@ -481,23 +481,32 @@ namespace cytnx { ext == ".HDF") { // save as HDF5 H5::H5File h5file; - bool overwrite = false; + // Enable reuse of space after data is deleted; + // Set the strategy: FSM_AGGR is standard for free-space management + // Parameters: strategy, persist (true), threshold (default 1: track all free-space + // sections) + H5::FileCreatPropList fcpl; + fcpl.setFileSpaceStrategy(H5F_FSPACE_STRATEGY_FSM_AGGR, true, 1); + // Persistent free space requires HDF5 1.10.x format or later + H5::FileAccPropList fapl; + fapl.setLibverBounds(H5F_LIBVER_V200, H5F_LIBVER_LATEST); // open file + bool overwrite = false; if (mode == 'w') { // Write new file - h5file = H5::H5File(fname, H5F_ACC_TRUNC); + h5file = H5::H5File(fname, H5F_ACC_TRUNC, fcpl, fapl); } else if (mode == 'x') { // eXclusive create - h5file = H5::H5File(fname, H5F_ACC_EXCL); + h5file = H5::H5File(fname, H5F_ACC_EXCL, fcpl, fapl); } else if (mode == 'a') { // Append data if (std::filesystem::exists(fname)) - h5file = H5::H5File(fname, H5F_ACC_RDWR); + h5file = H5::H5File(fname, H5F_ACC_RDWR, H5::FileCreatPropList::DEFAULT, fapl); else - h5file = H5::H5File(fname, H5F_ACC_EXCL); + h5file = H5::H5File(fname, H5F_ACC_EXCL, fcpl, fapl); } else if (mode == 'u') { // Update data if (std::filesystem::exists(fname)) { - h5file = H5::H5File(fname, H5F_ACC_RDWR); + h5file = H5::H5File(fname, H5F_ACC_RDWR, H5::FileCreatPropList::DEFAULT, fapl); overwrite = true; } else { - h5file = H5::H5File(fname, H5F_ACC_EXCL); + h5file = H5::H5File(fname, H5F_ACC_EXCL, fcpl, fapl); } } else { cytnx_error_msg(true, "[ERROR] Unknown mode '%c' for writing to HDF5 file.", mode); diff --git a/src/Symmetry.cpp b/src/Symmetry.cpp index 3fbfeb632..631ca0a3a 100644 --- a/src/Symmetry.cpp +++ b/src/Symmetry.cpp @@ -255,23 +255,32 @@ namespace cytnx { ext == ".HDF") { // save as HDF5 H5::H5File h5file; - bool overwrite = false; + // Enable reuse of space after data is deleted; + // Set the strategy: FSM_AGGR is standard for free-space management + // Parameters: strategy, persist (true), threshold (default 1: track all free-space + // sections) + H5::FileCreatPropList fcpl; + fcpl.setFileSpaceStrategy(H5F_FSPACE_STRATEGY_FSM_AGGR, true, 1); + // Persistent free space requires HDF5 1.10.x format or later + H5::FileAccPropList fapl; + fapl.setLibverBounds(H5F_LIBVER_V200, H5F_LIBVER_LATEST); // open file + bool overwrite = false; if (mode == 'w') { // Write new file - h5file = H5::H5File(fname, H5F_ACC_TRUNC); + h5file = H5::H5File(fname, H5F_ACC_TRUNC, fcpl, fapl); } else if (mode == 'x') { // eXclusive create - h5file = H5::H5File(fname, H5F_ACC_EXCL); + h5file = H5::H5File(fname, H5F_ACC_EXCL, fcpl, fapl); } else if (mode == 'a') { // Append data if (std::filesystem::exists(fname)) - h5file = H5::H5File(fname, H5F_ACC_RDWR); + h5file = H5::H5File(fname, H5F_ACC_RDWR, H5::FileCreatPropList::DEFAULT, fapl); else - h5file = H5::H5File(fname, H5F_ACC_EXCL); + h5file = H5::H5File(fname, H5F_ACC_EXCL, fcpl, fapl); } else if (mode == 'u') { // Update data if (std::filesystem::exists(fname)) { - h5file = H5::H5File(fname, H5F_ACC_RDWR); + h5file = H5::H5File(fname, H5F_ACC_RDWR, H5::FileCreatPropList::DEFAULT, fapl); overwrite = true; } else { - h5file = H5::H5File(fname, H5F_ACC_EXCL); + h5file = H5::H5File(fname, H5F_ACC_EXCL, fcpl, fapl); } } else { cytnx_error_msg(true, "[ERROR] Unknown mode '%c' for writing to HDF5 file.", mode); diff --git a/src/Tensor.cpp b/src/Tensor.cpp index c36f9ac82..f757040a3 100644 --- a/src/Tensor.cpp +++ b/src/Tensor.cpp @@ -435,23 +435,32 @@ namespace cytnx { ext == ".HDF") { // save as HDF5 H5::H5File h5file; - bool overwrite = false; + // Enable reuse of space after data is deleted; + // Set the strategy: FSM_AGGR is standard for free-space management + // Parameters: strategy, persist (true), threshold (default 1: track all free-space + // sections) + H5::FileCreatPropList fcpl; + fcpl.setFileSpaceStrategy(H5F_FSPACE_STRATEGY_FSM_AGGR, true, 1); + // Persistent free space requires HDF5 1.10.x format or later + H5::FileAccPropList fapl; + fapl.setLibverBounds(H5F_LIBVER_V200, H5F_LIBVER_LATEST); // open file + bool overwrite = false; if (mode == 'w') { // Write new file - h5file = H5::H5File(fname, H5F_ACC_TRUNC); + h5file = H5::H5File(fname, H5F_ACC_TRUNC, fcpl, fapl); } else if (mode == 'x') { // eXclusive create - h5file = H5::H5File(fname, H5F_ACC_EXCL); + h5file = H5::H5File(fname, H5F_ACC_EXCL, fcpl, fapl); } else if (mode == 'a') { // Append data if (std::filesystem::exists(fname)) - h5file = H5::H5File(fname, H5F_ACC_RDWR); + h5file = H5::H5File(fname, H5F_ACC_RDWR, H5::FileCreatPropList::DEFAULT, fapl); else - h5file = H5::H5File(fname, H5F_ACC_EXCL); + h5file = H5::H5File(fname, H5F_ACC_EXCL, fcpl, fapl); } else if (mode == 'u') { // Update data if (std::filesystem::exists(fname)) { - h5file = H5::H5File(fname, H5F_ACC_RDWR); + h5file = H5::H5File(fname, H5F_ACC_RDWR, H5::FileCreatPropList::DEFAULT, fapl); overwrite = true; } else { - h5file = H5::H5File(fname, H5F_ACC_EXCL); + h5file = H5::H5File(fname, H5F_ACC_EXCL, fcpl, fapl); } } else { cytnx_error_msg(true, "[ERROR] Unknown mode '%c' for writing to HDF5 file.", mode); diff --git a/src/UniTensor.cpp b/src/UniTensor.cpp index 12f4fdffc..3f97290cb 100644 --- a/src/UniTensor.cpp +++ b/src/UniTensor.cpp @@ -30,7 +30,7 @@ namespace cytnx { boost::intrusive_ptr tmp(new UniTensor_base); this->_impl = tmp; } else if (name == UTenType.getname(UTenType.Sparse)) { - cytnx_error_msg(true, "[ERROR] SparseUniTensor is deprecated.\s","\n"); + cytnx_error_msg(true, "[ERROR] SparseUniTensor is deprecated.\s", "\n"); } else { cytnx_error_msg(true, "[ERROR] No UniTensor type matches the string '%s'.\n", name.c_str()); } @@ -73,23 +73,32 @@ namespace cytnx { ext == ".HDF") { // save as HDF5 H5::H5File h5file; - bool overwrite = false; + // Enable reuse of space after data is deleted; + // Set the strategy: FSM_AGGR is standard for free-space management + // Parameters: strategy, persist (true), threshold (default 1: track all free-space + // sections) + H5::FileCreatPropList fcpl; + fcpl.setFileSpaceStrategy(H5F_FSPACE_STRATEGY_FSM_AGGR, true, 1); + // Persistent free space requires HDF5 1.10.x format or later + H5::FileAccPropList fapl; + fapl.setLibverBounds(H5F_LIBVER_V200, H5F_LIBVER_LATEST); // open file + bool overwrite = false; if (mode == 'w') { // Write new file - h5file = H5::H5File(fname, H5F_ACC_TRUNC); + h5file = H5::H5File(fname, H5F_ACC_TRUNC, fcpl, fapl); } else if (mode == 'x') { // eXclusive create - h5file = H5::H5File(fname, H5F_ACC_EXCL); + h5file = H5::H5File(fname, H5F_ACC_EXCL, fcpl, fapl); } else if (mode == 'a') { // Append data if (std::filesystem::exists(fname)) - h5file = H5::H5File(fname, H5F_ACC_RDWR); + h5file = H5::H5File(fname, H5F_ACC_RDWR, H5::FileCreatPropList::DEFAULT, fapl); else - h5file = H5::H5File(fname, H5F_ACC_EXCL); + h5file = H5::H5File(fname, H5F_ACC_EXCL, fcpl, fapl); } else if (mode == 'u') { // Update data if (std::filesystem::exists(fname)) { - h5file = H5::H5File(fname, H5F_ACC_RDWR); + h5file = H5::H5File(fname, H5F_ACC_RDWR, H5::FileCreatPropList::DEFAULT, fapl); overwrite = true; } else { - h5file = H5::H5File(fname, H5F_ACC_EXCL); + h5file = H5::H5File(fname, H5F_ACC_EXCL, fcpl, fapl); } } else { cytnx_error_msg(true, "[ERROR] Unknown mode '%c' for writing to HDF5 file.", mode); diff --git a/src/backend/Storage.cpp b/src/backend/Storage.cpp index e1d4f3c17..e0e06019b 100644 --- a/src/backend/Storage.cpp +++ b/src/backend/Storage.cpp @@ -94,23 +94,32 @@ namespace cytnx { ext == ".HDF") { // save as HDF5 H5::H5File h5file; - bool overwrite = false; + // Enable reuse of space after data is deleted; + // Set the strategy: FSM_AGGR is standard for free-space management + // Parameters: strategy, persist (true), threshold (default 1: track all free-space + // sections) + H5::FileCreatPropList fcpl; + fcpl.setFileSpaceStrategy(H5F_FSPACE_STRATEGY_FSM_AGGR, true, 1); + // Persistent free space requires HDF5 1.10.x format or later + H5::FileAccPropList fapl; + fapl.setLibverBounds(H5F_LIBVER_V200, H5F_LIBVER_LATEST); // open file + bool overwrite = false; if (mode == 'w') { // Write new file - h5file = H5::H5File(fname, H5F_ACC_TRUNC); + h5file = H5::H5File(fname, H5F_ACC_TRUNC, fcpl, fapl); } else if (mode == 'x') { // eXclusive create - h5file = H5::H5File(fname, H5F_ACC_EXCL); + h5file = H5::H5File(fname, H5F_ACC_EXCL, fcpl, fapl); } else if (mode == 'a') { // Append data if (std::filesystem::exists(fname)) - h5file = H5::H5File(fname, H5F_ACC_RDWR); + h5file = H5::H5File(fname, H5F_ACC_RDWR, H5::FileCreatPropList::DEFAULT, fapl); else - h5file = H5::H5File(fname, H5F_ACC_EXCL); + h5file = H5::H5File(fname, H5F_ACC_EXCL, fcpl, fapl); } else if (mode == 'u') { // Update data if (std::filesystem::exists(fname)) { - h5file = H5::H5File(fname, H5F_ACC_RDWR); + h5file = H5::H5File(fname, H5F_ACC_RDWR, H5::FileCreatPropList::DEFAULT, fapl); overwrite = true; } else { - h5file = H5::H5File(fname, H5F_ACC_EXCL); + h5file = H5::H5File(fname, H5F_ACC_EXCL, fcpl, fapl); } } else { cytnx_error_msg(true, "[ERROR] Unknown mode '%c' for writing to HDF5 file.", mode); diff --git a/src/tn_algo/MPS.cpp b/src/tn_algo/MPS.cpp index 0e9b6a876..2ac449620 100644 --- a/src/tn_algo/MPS.cpp +++ b/src/tn_algo/MPS.cpp @@ -29,23 +29,32 @@ namespace cytnx { ext == ".HDF") { // save as HDF5 H5::H5File h5file; - bool overwrite = false; + // Enable reuse of space after data is deleted; + // Set the strategy: FSM_AGGR is standard for free-space management + // Parameters: strategy, persist (true), threshold (default 1: track all free-space + // sections) + H5::FileCreatPropList fcpl; + fcpl.setFileSpaceStrategy(H5F_FSPACE_STRATEGY_FSM_AGGR, true, 1); + // Persistent free space requires HDF5 1.10.x format or later + H5::FileAccPropList fapl; + fapl.setLibverBounds(H5F_LIBVER_V200, H5F_LIBVER_LATEST); // open file + bool overwrite = false; if (mode == 'w') { // Write new file - h5file = H5::H5File(fname, H5F_ACC_TRUNC); + h5file = H5::H5File(fname, H5F_ACC_TRUNC, fcpl, fapl); } else if (mode == 'x') { // eXclusive create - h5file = H5::H5File(fname, H5F_ACC_EXCL); + h5file = H5::H5File(fname, H5F_ACC_EXCL, fcpl, fapl); } else if (mode == 'a') { // Append data if (std::filesystem::exists(fname)) - h5file = H5::H5File(fname, H5F_ACC_RDWR); + h5file = H5::H5File(fname, H5F_ACC_RDWR, H5::FileCreatPropList::DEFAULT, fapl); else - h5file = H5::H5File(fname, H5F_ACC_EXCL); + h5file = H5::H5File(fname, H5F_ACC_EXCL, fcpl, fapl); } else if (mode == 'u') { // Update data if (std::filesystem::exists(fname)) { - h5file = H5::H5File(fname, H5F_ACC_RDWR); + h5file = H5::H5File(fname, H5F_ACC_RDWR, H5::FileCreatPropList::DEFAULT, fapl); overwrite = true; } else { - h5file = H5::H5File(fname, H5F_ACC_EXCL); + h5file = H5::H5File(fname, H5F_ACC_EXCL, fcpl, fapl); } } else { cytnx_error_msg(true, "[ERROR] Unknown mode '%c' for writing to HDF5 file.", mode); From 5f110cc9ccaa8c1079036acf041044eed27ed4e4 Mon Sep 17 00:00:00 2001 From: Manuel Schneider Date: Sat, 2 May 2026 12:59:53 +0800 Subject: [PATCH 17/40] using native HDF5 complex types; requires HDF5 2.0.0 or newer --- include/Type.hpp | 29 +++++++---------------------- 1 file changed, 7 insertions(+), 22 deletions(-) diff --git a/include/Type.hpp b/include/Type.hpp index a627db87c..a2e3adb95 100644 --- a/include/Type.hpp +++ b/include/Type.hpp @@ -424,25 +424,11 @@ namespace cytnx { case Type::Bool: return H5::PredType::NATIVE_HBOOL; - case Type::ComplexDouble: { - static const H5::CompType ct = [] { - H5::CompType tmp(sizeof(cytnx_complex128)); - tmp.insertMember("r", 0, H5::PredType::NATIVE_DOUBLE); - tmp.insertMember("i", sizeof(double), H5::PredType::NATIVE_DOUBLE); - return tmp; - }(); - return ct; - } + case Type::ComplexDouble: + return H5::DataType(H5Tcopy(H5T_NATIVE_DOUBLE_COMPLEX)); - case Type::ComplexFloat: { - static const H5::CompType ct = [] { - H5::CompType tmp(sizeof(cytnx_complex64)); - tmp.insertMember("r", 0, H5::PredType::NATIVE_FLOAT); - tmp.insertMember("i", sizeof(float), H5::PredType::NATIVE_FLOAT); - return tmp; - }(); - return ct; - } + case Type::ComplexFloat: + return H5::DataType(H5Tcopy(H5T_NATIVE_FLOAT_COMPLEX)); case Type::Void: cytnx_error_msg(true, "[ERROR] Void dtype cannot be mapped to HDF5%s", "\n"); @@ -466,10 +452,9 @@ namespace cytnx { if (h5_type == H5::PredType::NATIVE_INT16) return Type::Int16; if (h5_type == H5::PredType::NATIVE_UINT16) return Type::Uint16; if (h5_type == H5::PredType::NATIVE_HBOOL) return Type::Bool; - if (h5_type.getClass() == H5T_COMPOUND) { - H5::CompType ct(h5_type.getId()); - if (ct.getSize() == sizeof(cytnx_complex128)) return Type::ComplexDouble; - if (ct.getSize() == sizeof(cytnx_complex64)) return Type::ComplexFloat; + if (h5_type.getClass() == H5T_COMPLEX) { + if (h5_type.getSize() == sizeof(cytnx_complex128)) return Type::ComplexDouble; + if (h5_type.getSize() == sizeof(cytnx_complex64)) return Type::ComplexFloat; } cytnx_error_msg(true, "[ERROR] HDF5 DataType cannot be mapped to Cytnx dtype.%s", "\n"); } From fc322a24ed265b0372e6e97e88484b18e194613c Mon Sep 17 00:00:00 2001 From: Manuel Schneider Date: Sat, 2 May 2026 17:09:40 +0800 Subject: [PATCH 18/40] added save/load support for DenseUniTensor --- include/UniTensor.hpp | 17 +++- src/BlockFermionicUniTensor.cpp | 10 ++ src/BlockUniTensor.cpp | 9 ++ src/Bond.cpp | 25 +++-- src/DenseUniTensor.cpp | 50 ++++++++++ src/SparseUniTensor.cpp | 8 ++ src/UniTensor.cpp | 171 +++++++++++++++++++++++++++++++- src/UniTensor_base.cpp | 10 ++ 8 files changed, 283 insertions(+), 17 deletions(-) diff --git a/include/UniTensor.hpp b/include/UniTensor.hpp index 8adf5ca01..fa0009988 100644 --- a/include/UniTensor.hpp +++ b/include/UniTensor.hpp @@ -447,6 +447,9 @@ namespace cytnx { virtual const vec2d &get_itoi() const; virtual vec2d &get_itoi(); + virtual void to_hdf5_dispatch(H5::Group &location, const bool overwrite) const; + virtual void from_hdf5_dispatch(H5::Group &location, const bool restore_device = true); + virtual void to_binary_dispatch(std::ostream &f) const; virtual void from_binary_dispatch(std::istream &f, const bool restore_device = true); @@ -1082,6 +1085,9 @@ namespace cytnx { "\n"); } + void to_hdf5_dispatch(H5::Group &location, const bool overwrite) const; + void from_hdf5_dispatch(H5::Group &location, const bool restore_device = true); + void to_binary_dispatch(std::ostream &f) const; void from_binary_dispatch(std::istream &f, const bool restore_device = true); @@ -1755,6 +1761,9 @@ namespace cytnx { cytnx_uint16 &at_for_sparse(const std::vector &locator, const cytnx_uint16 &aux); cytnx_int16 &at_for_sparse(const std::vector &locator, const cytnx_int16 &aux); + void to_hdf5_dispatch(H5::Group &location, const bool overwrite) const; + void from_hdf5_dispatch(H5::Group &location, const bool restore_device = true); + void to_binary_dispatch(std::ostream &f) const; void from_binary_dispatch(std::istream &f, const bool restore_device = true); @@ -2548,6 +2557,9 @@ namespace cytnx { cytnx_uint16 &at_for_sparse(const std::vector &locator, const cytnx_uint16 &aux); cytnx_int16 &at_for_sparse(const std::vector &locator, const cytnx_int16 &aux); + void to_hdf5_dispatch(H5::Group &location, const bool overwrite) const; + void from_hdf5_dispatch(H5::Group &location, const bool restore_device = true); + void to_binary_dispatch(std::ostream &f) const; void from_binary_dispatch(std::istream &f, const bool restore_device = true); @@ -2695,7 +2707,6 @@ namespace cytnx { ///@cond boost::intrusive_ptr _impl; UniTensor() : _impl(new UniTensor_base()){}; - UniTensor(const std::string name); UniTensor(const UniTensor &rhs) { this->_impl = rhs._impl; } UniTensor &operator=(const UniTensor &rhs) { this->_impl = rhs._impl; @@ -2762,6 +2773,10 @@ namespace cytnx { } //@} + /// @cond + void Init(const std::string name); + /// @endcond + //@{ /** @brief Construct a UniTensor. diff --git a/src/BlockFermionicUniTensor.cpp b/src/BlockFermionicUniTensor.cpp index e92791530..414531ecd 100644 --- a/src/BlockFermionicUniTensor.cpp +++ b/src/BlockFermionicUniTensor.cpp @@ -2372,6 +2372,16 @@ namespace cytnx { return this->_blocks[bidx].at(loc_in_T); } + void BlockFermionicUniTensor::to_hdf5_dispatch(H5::Group &location, const bool overwrite) const { + cytnx_error_msg( + true, "[ERROR] Saving BlockFermionicUniTensor to HDF5 is not implemented yet!%s", "\n"); + } + + void BlockFermionicUniTensor::from_hdf5_dispatch(H5::Group &location, const bool restore_device) { + cytnx_error_msg( + true, "[ERROR] Loading BlockFermionicUniTensor from HDF5 is not implemented yet!%s", "\n"); + } + void BlockFermionicUniTensor::to_binary_dispatch(std::ostream &f) const { //[21 Aug 2024] This is a copy from BlockUniTensor; saves signs as well cytnx_uint64 Nblocks = this->_blocks.size(); diff --git a/src/BlockUniTensor.cpp b/src/BlockUniTensor.cpp index 450c7fbe6..37e00ec2a 100644 --- a/src/BlockUniTensor.cpp +++ b/src/BlockUniTensor.cpp @@ -1582,6 +1582,15 @@ namespace cytnx { return this->_blocks[bidx].at(loc_in_T); } + void BlockUniTensor::to_hdf5_dispatch(H5::Group &location, const bool overwrite) const { + cytnx_error_msg(true, "[ERROR] Saving BlockUniTensor to HDF5 is not implemented yet!%s", "\n"); + } + + void BlockUniTensor::from_hdf5_dispatch(H5::Group &location, const bool restore_device) { + cytnx_error_msg(true, "[ERROR] Loading BlockUniTensor from HDF5 is not implemented yet!%s", + "\n"); + } + void BlockUniTensor::to_binary_dispatch(std::ostream &f) const { cytnx_uint64 Nblocks = this->_blocks.size(); f.write((char *)&Nblocks, sizeof(cytnx_uint64)); diff --git a/src/Bond.cpp b/src/Bond.cpp index eb045f3e5..e58c66de3 100644 --- a/src/Bond.cpp +++ b/src/Bond.cpp @@ -612,7 +612,7 @@ namespace cytnx { if (location.nameExists("degeneracies")) location.unlink("degeneracies"); if (location.nameExists("quantum_numbers")) location.unlink("quantum_numbers"); // remove groups and its contents recursively - if (location.nameExists("Symmetries")) location.unlink("Symmetries"); + if (location.nameExists("symmetries")) location.unlink("symmetries"); } H5::DataType datatype; @@ -664,13 +664,13 @@ namespace cytnx { attr = dataset.createAttribute("axis_labels", str_type, attr_space); attr.write(str_type, labels); - // Symmetries + // symmetries if (save_symmetries) { // vecdims[1] = { this->_impl->_syms.size() }; // dataspace = H5::DataSpace(1, vecdims); // str_type = H5::StrType(H5::PredType::C_S1, H5T_VARIABLE); // dataset = location.createDataSet("symmetries", str_type, dataspace); - + // // create cstrings // std::vector c_strings; // std::string symstring; // for (const auto& s : this->_impl->_syms) { @@ -678,7 +678,7 @@ namespace cytnx { // c_strings.push_back(symstring.c_str()); // } // dataset.write(c_strings.data(), str_type); - H5::Group symloc = location.createGroup("Symmetries"); + H5::Group symloc = location.createGroup("symmetries"); for (int sidx = 0; sidx < this->_impl->_syms.size(); sidx++) { this->_impl->_syms[sidx].to_hdf5(symloc, overwrite, "Symmetry" + std::to_string(sidx)); } @@ -760,10 +760,10 @@ namespace cytnx { this->_impl->_qnums = {}; } - // Symmetries + // symmetries if (syms.empty()) { - if (location.exists("Symmetries")) { - H5::Group symloc = location.openGroup("Symmetries"); + if (location.exists("symmetries")) { + H5::Group symloc = location.openGroup("symmetries"); this->_impl->_syms.clear(); hsize_t sidx = 0; while (true) { @@ -776,32 +776,31 @@ namespace cytnx { this->_impl->_syms.push_back(sym); sidx++; } - symloc.close(); if (symmetric) { cytnx_error_msg(sidx != qnumdim, - "[ERROR] %d Symmetries were found, but second dimension of " + "[ERROR] %d symmetries were found, but second dimension of " "'quantum_numbers' is %d. The HDF5 data seems corrupt!\n", sidx, qnumdim); } else { cytnx_error_msg(sidx > 0, "[ERROR] 'degeneracies' and 'quantum_numbers' were not found in HDF5 " - "location, but 'Symmetries' exist. The HDF5 data seems corrupt!%s", + "location, but 'symmetries' exist. The HDF5 data seems corrupt!%s", "\n"); this->_impl->_syms = {}; } } else { cytnx_error_msg(symmetric, "[ERROR] 'degeneracies' and 'quantum_numbers' exist in HDF5 location, but " - "'Symmetries' are missing. The HDF5 data seems corrupt!%s", + "'symmetries' are missing. The HDF5 data seems corrupt!%s", "\n"); } } else { cytnx_error_msg(!symmetric, "[ERROR] 'degeneracies' and 'quantum_numbers' not found in HDF5 location, " - "but Symmetries are passed as arguments.%s", + "but symmetries are passed as arguments.%s", "\n"); cytnx_error_msg(syms.size() != qnumdim, - "[ERROR] %d Symmetries are passed, but second dimension of 'quantum_numbers' " + "[ERROR] %d symmetries are passed, but second dimension of 'quantum_numbers' " "is %d. The HDF5 data seems corrupt!\n", syms.size(), qnumdim); this->_impl->_syms = syms; diff --git a/src/DenseUniTensor.cpp b/src/DenseUniTensor.cpp index 5003600fb..9fe188a70 100644 --- a/src/DenseUniTensor.cpp +++ b/src/DenseUniTensor.cpp @@ -1282,6 +1282,56 @@ namespace cytnx { void DenseUniTensor::normalize_() { this->_block /= linalg::Norm(this->_block); } + void DenseUniTensor::to_hdf5_dispatch(H5::Group &location, const bool overwrite) const { + // is_tag, write as attribute + H5::DataType datatype = Type.get_hdf5_type(this->_is_tag); + H5::Attribute attr = location.createAttribute("directed", datatype, H5::DataSpace(H5S_SCALAR)); + attr.write(H5::PredType::NATIVE_INT, &this->_is_tag); + + this->_block.to_hdf5(location, overwrite, "Tensor"); + } + + void DenseUniTensor::from_hdf5_dispatch(H5::Group &location, const bool restore_device) { + this->_block.from_hdf5(location, "Tensor", restore_device); + // check data consistency + auto shape = this->_block.shape(); + if (this->_bonds.empty()) { + size_t Nelem = std::accumulate(shape.begin(), shape.end(), 1, std::multiplies()); + cytnx_error_msg( + Nelem > 1, + "[ERROR] No bonds exit, but the Tensor has %d > 1 elements. The HDF5 data seems corrupt!\n", + Nelem); + } else if (!this->_is_diag) { + cytnx_error_msg( + this->_bonds.size() != shape.size(), + "[ERROR] %d bonds exit, but the Tensor has rank %d. The HDF5 data seems corrupt!\n", + this->_bonds.size(), shape.size()); + for (cytnx_uint64 i = 0; i < shape.size(); i++) { + cytnx_error_msg(shape[i] != this->_bonds[i].dim(), + "[ERROR] Tensor has dimension %d on index %d, but the corresponding bond " + "has dimension %d. The HDF5 data seems corrupt!\n", + shape[i], i, this->_bonds[i].dim()); + } + } + + // is_tag, read from attribute or reproduce + if (location.attrExists("directed")) { + H5::Attribute attr = location.openAttribute("directed"); + H5::DataType datatype = attr.getDataType(); + cytnx_error_msg( + datatype.getSize() != Type.get_hdf5_type(this->_is_tag).getSize(), + "[ERROR] 'directed' bit-length mismatch. File: %zu bytes, expected: %zu bytes.\n", + datatype.getSize(), Type.get_hdf5_type(this->_is_tag).getSize()); + attr.read(datatype, &this->_is_tag); + } else { + if (this->_bonds.empty()) { + this->_is_tag = false; + } else { + this->_is_tag = (this->_bonds[0].type() != bondType::BD_REG); + } + } + } + void DenseUniTensor::to_binary_dispatch(std::ostream &f) const { this->_block.to_binary(f); } void DenseUniTensor::from_binary_dispatch(std::istream &f, const bool restore_device) { this->_block.from_binary(f, restore_device); diff --git a/src/SparseUniTensor.cpp b/src/SparseUniTensor.cpp index c2a97a042..9721016ab 100644 --- a/src/SparseUniTensor.cpp +++ b/src/SparseUniTensor.cpp @@ -2332,6 +2332,14 @@ namespace cytnx { cytnx_error_msg(true, "[ERROR] truncate for SparseUniTensor is under developing!!%s", "\n"); } + void SparseUniTensor::to_hdf5_dispatch(H5::Group &location, const bool overwrite) const { + cytnx_error_msg(true, "[ERROR] Saving SparseUniTensor to HDF5 is not implemented!%s", "\n"); + } + + void SparseUniTensor::from_hdf5_dispatch(H5::Group &location, const bool restore_device) { + cytnx_error_msg(true, "[ERROR] Loading SparseUniTensor from HDF5 is not implemented!%s", "\n"); + } + void SparseUniTensor::to_binary_dispatch(std::ostream &f) const { // cytnx_error_msg(true,"[ERROR] Save for SparseUniTensor is under developing!!%s","\n"); diff --git a/src/UniTensor.cpp b/src/UniTensor.cpp index 3f97290cb..a557e38c1 100644 --- a/src/UniTensor.cpp +++ b/src/UniTensor.cpp @@ -16,7 +16,7 @@ using namespace std; namespace cytnx { - UniTensor::UniTensor(const std::string name) { + void UniTensor::Init(const std::string name) { if (name == UTenType.getname(UTenType.Block)) { boost::intrusive_ptr tmp(new BlockUniTensor); this->_impl = tmp; @@ -198,10 +198,175 @@ namespace cytnx { } void UniTensor::to_hdf5(H5::Group &location, const bool overwrite) const { - cytnx_error_msg(true, "[ERROR] Saving UniTensor to HDF5 is not implemented yet!%s", "\n"); + if (overwrite) { // delete previous data + // delete all entries that could be written by one of the implementations; + // remove attributes + if (location.attrExists("type")) location.removeAttr("type"); + if (location.attrExists("diagonal")) location.removeAttr("diagonal"); + if (location.attrExists("rowrank")) location.removeAttr("rowrank"); + if (location.attrExists("name")) location.removeAttr("name"); + if (location.attrExists("directed")) location.removeAttr("directed"); + // remove datasets + if (location.nameExists("labels")) location.unlink("labels"); + if (location.nameExists("Tensor")) location.unlink("Tensor"); + // remove groups and its contents recursively + if (location.nameExists("bonds")) location.unlink("bonds"); + } + + H5::DataType datatype; + H5::Attribute attr; + H5::DataSet dataset; + H5::DataSpace dataspace; + H5::StrType str_type; + + // type, write as string attribute + std::string type = UTenType.getname(this->_impl->uten_type_id); + str_type = H5::StrType(H5::PredType::C_S1, type.length() + 1); + dataspace = H5::DataSpace(H5S_SCALAR); + attr = location.createAttribute("type", str_type, dataspace); + attr.write(str_type, type); + + // is_diag, write as attribute only for diagonal tensors + if (this->_impl->_is_diag) { + datatype = Type.get_hdf5_type(this->_impl->_is_diag); + attr = location.createAttribute("diagonal", datatype, H5::DataSpace(H5S_SCALAR)); + attr.write(H5::PredType::NATIVE_INT, &this->_impl->_is_diag); + } + + // rowrank, write as attribute + datatype = Type.get_hdf5_type(this->_impl->_rowrank); + attr = location.createAttribute("rowrank", datatype, H5::DataSpace(H5S_SCALAR)); + attr.write(H5::PredType::NATIVE_INT, &this->_impl->_rowrank); + + // name, write as string attribute + str_type = H5::StrType(H5::PredType::C_S1, this->_impl->_name.length() + 1); + dataspace = H5::DataSpace(H5S_SCALAR); + attr = location.createAttribute("name", str_type, dataspace); + attr.write(str_type, this->_impl->_name); + + // labels; write as string vector + if (!this->_impl->_labels.empty()) { + hsize_t vecdims[1] = {this->_impl->_labels.size()}; + dataspace = H5::DataSpace(1, vecdims); + str_type = H5::StrType(H5::PredType::C_S1, H5T_VARIABLE); + dataset = location.createDataSet("labels", str_type, dataspace); + std::vector c_strings; // H5 needs cstrings + std::string symstring; + for (const auto &label : this->_impl->_labels) { + c_strings.push_back(label.c_str()); + } + dataset.write(c_strings.data(), str_type); + } + + // bonds; write in group + if (!this->_impl->_bonds.empty()) { + H5::Group bonddir = location.createGroup("bonds"); + for (int i = 0; i < this->_impl->_bonds.size(); i++) { + H5::Group bondgroup = bonddir.createGroup("Bond" + std::to_string(i)); + this->_impl->_bonds[i].to_hdf5(bondgroup, overwrite); + } + } + + this->_impl->to_hdf5_dispatch(location, overwrite); } + void UniTensor::from_hdf5(H5::Group &location, const bool restore_device) { - cytnx_error_msg(true, "[ERROR] Loading UniTensor from HDF5 is not implemented yet!%s", "\n"); + H5::DataType datatype; + H5::Attribute attr; + H5::StrType str_type; + size_t size; + + // type, read from attribute + attr = location.openAttribute("type"); + str_type = attr.getStrType(); + size = str_type.getSize() - 1; // remove the null terminator + std::string utenname; + utenname.resize(size); + attr.read(str_type, &utenname[0]); + this->Init(utenname); + + // is_diag, read from attribute + if (location.attrExists("diagonal")) { + H5::Attribute attr = location.openAttribute("diagonal"); + datatype = attr.getDataType(); + cytnx_error_msg( + datatype.getSize() != Type.get_hdf5_type(this->_impl->_is_diag).getSize(), + "[ERROR] 'device' bit-length mismatch. File: %zu bytes, expected: %zu bytes.\n", + datatype.getSize(), Type.get_hdf5_type(this->_impl->_is_diag).getSize()); + attr.read(datatype, &this->_impl->_is_diag); + } else { + this->_impl->_is_diag = false; + } + + // rowrank, read from attribute + attr = location.openAttribute("rowrank"); + datatype = attr.getDataType(); + cytnx_error_msg( + datatype.getSize() != Type.get_hdf5_type(this->_impl->_rowrank).getSize(), + "[ERROR] 'rowrank' bit-length mismatch. File: %zu bytes, expected: %zu bytes.\n", + datatype.getSize(), Type.get_hdf5_type(this->_impl->_rowrank).getSize()); + attr.read(datatype, &this->_impl->_rowrank); + + // name, read from string attribute + if (location.attrExists("name")) { + attr = location.openAttribute("name"); + str_type = attr.getStrType(); + size = str_type.getSize() - 1; // remove the null terminator + this->_impl->_name.resize(size); + attr.read(str_type, &this->_impl->_name[0]); + } else { + this->_impl->_name.clear(); + } + + // labels; read from string vector + this->_impl->_labels.clear(); + if (location.exists("labels")) { + H5::DataSet dataset = location.openDataSet("labels"); + H5::DataSpace dataspace = dataset.getSpace(); + hsize_t dims[1]; + dataspace.getSimpleExtentDims(dims); + // H5T_VARIABLE requires reading into an array of char pointers (char**) + H5::StrType str_type(H5::PredType::C_S1, H5T_VARIABLE); + std::vector c_strings(dims[0]); + dataset.read(c_strings.data(), str_type); + this->_impl->_labels.reserve(dims[0]); + for (size_t i = 0; i < dims[0]; ++i) { + this->_impl->_labels.push_back(std::string(c_strings[i])); + } + // free the space of each char* that was allocated in dataset.read() + dataset.vlenReclaim(c_strings.data(), str_type, dataspace); + } + + // bonds; read from group + this->_impl->_bonds.clear(); + if (location.exists("bonds")) { + H5::Group bonddir = location.openGroup("bonds"); + hsize_t idx = 0; + while (true) { + std::string name = "Bond" + std::to_string(idx); + if (!bonddir.exists(name)) { + break; + } + H5::Group bondgroup = bonddir.openGroup(name); + Bond bond; + bond.from_hdf5(bondgroup); + this->_impl->_bonds.push_back(bond); + idx++; + } + cytnx_error_msg( + idx != this->_impl->_labels.size(), + "[ERROR] %d bonds were found, but %d labels exist. The HDF5 data seems corrupt!\n", idx, + this->_impl->_labels.size()); + } else { + cytnx_error_msg( + !this->_impl->_labels.empty(), + "[ERROR] %d labels exist, but no bonds were found. The HDF5 data seems corrupt!\n", + this->_impl->_labels.size()); + this->_impl->_bonds.clear(); + } + + this->_impl->from_hdf5_dispatch(location, restore_device); + this->_impl->_is_braket_form = this->_impl->_update_braket(); } void UniTensor::to_binary(std::ostream &f) const { diff --git a/src/UniTensor_base.cpp b/src/UniTensor_base.cpp index 90ab58a94..e2529b378 100644 --- a/src/UniTensor_base.cpp +++ b/src/UniTensor_base.cpp @@ -691,6 +691,16 @@ namespace cytnx { true, "[ERROR] fatal internal, cannot call on an un-initialized UniTensor_base%s", "\n"); } + void UniTensor_base::to_hdf5_dispatch(H5::Group &location, const bool overwrite) const { + cytnx_error_msg( + true, "[ERROR] fatal internal, cannot call on an un-initialized UniTensor_base%s", "\n"); + } + + void UniTensor_base::from_hdf5_dispatch(H5::Group &location, const bool restore_device) { + cytnx_error_msg(true, "[ERROR] Loading BlockUniTensor from HDF5 is not implemented yet!%s", + "\n"); + } + void UniTensor_base::to_binary_dispatch(std::ostream &f) const { cytnx_error_msg( true, "[ERROR] fatal internal, cannot call on an un-initialized UniTensor_base%s", "\n"); From 81d4c5358d6d536b010bd2e5cd9085ec7e234281 Mon Sep 17 00:00:00 2001 From: Manuel Schneider Date: Sun, 3 May 2026 04:10:39 +0800 Subject: [PATCH 19/40] added HDF5 support for BlockUniTensor and BlockFermionicUniTensor added const attribute to operator+-*/% for Tensor --- include/Tensor.hpp | 14 ++-- include/UniTensor.hpp | 2 +- src/BlockFermionicUniTensor.cpp | 126 ++++++++++++++++++++++++++------ src/BlockUniTensor.cpp | 119 ++++++++++++++++++++++++------ src/Bond.cpp | 25 ++++--- src/UniTensor.cpp | 12 +-- 6 files changed, 232 insertions(+), 66 deletions(-) diff --git a/include/Tensor.hpp b/include/Tensor.hpp index f3e1e187f..f0ad23863 100644 --- a/include/Tensor.hpp +++ b/include/Tensor.hpp @@ -1356,7 +1356,7 @@ namespace cytnx { * @param[in] rhs the added Tensor or scalar. */ template - Tensor Add(const T &rhs) { + Tensor Add(const T &rhs) const { return *this + rhs; } @@ -1376,7 +1376,7 @@ namespace cytnx { * @param[in] rhs the subtracted Tensor or scalar. */ template - Tensor Sub(const T &rhs) { + Tensor Sub(const T &rhs) const { return *this - rhs; } @@ -1396,7 +1396,7 @@ namespace cytnx { * @param[in] rhs the multiplied Tensor or scalar. */ template - Tensor Mul(const T &rhs) { + Tensor Mul(const T &rhs) const { return *this * rhs; } @@ -1417,7 +1417,7 @@ namespace cytnx { * @attension \p rhs cannot be zero. */ template - Tensor Div(const T &rhs) { + Tensor Div(const T &rhs) const { return *this / rhs; } @@ -1439,7 +1439,7 @@ namespace cytnx { * @param[in] rhs the compared object. */ template - Tensor Cpr(const T &rhs) { + Tensor Cpr(const T &rhs) const { return *this == rhs; } @@ -1482,7 +1482,7 @@ namespace cytnx { // } template - Tensor Mod(const T &rhs) { + Tensor Mod(const T &rhs) const { return *this % rhs; } @@ -1492,7 +1492,7 @@ namespace cytnx { * tensor is \f$A\f$, then the output tensor is \f$-A\f$. * @return The negation of the current tensor. */ - Tensor operator-() { return this->Mul(-1.); } + Tensor operator-() const { return this->Mul(-1.); } /** * @brief The flatten function. diff --git a/include/UniTensor.hpp b/include/UniTensor.hpp index fa0009988..23dcddf63 100644 --- a/include/UniTensor.hpp +++ b/include/UniTensor.hpp @@ -1292,7 +1292,7 @@ namespace cytnx { std::vector inds(indices.begin(), indices.end()); - // find if the indices specify exists! + // find if the specified indices exists! cytnx_int64 b = -1; for (cytnx_uint64 i = 0; i < this->_inner_to_outer_idx.size(); i++) { if (inds == this->_inner_to_outer_idx[i]) { diff --git a/src/BlockFermionicUniTensor.cpp b/src/BlockFermionicUniTensor.cpp index 414531ecd..dbace81dc 100644 --- a/src/BlockFermionicUniTensor.cpp +++ b/src/BlockFermionicUniTensor.cpp @@ -145,7 +145,7 @@ namespace cytnx { } } else { - // checking how many blocks are there, and the size: + // find the number of blocks and their sizes: std::vector Loc(this->_bonds.size(), 0); std::vector tot_qns( this->_bonds[0].Nsym()); // use first bond to determine symmetry size @@ -162,29 +162,22 @@ namespace cytnx { // std::cout << "tot_flx: "; // cytnx::vec_print_simple(std::cout, tot_qns); - // if exists: + // if total flux is zero -> block exists: if (std::all_of(tot_qns.begin(), tot_qns.end(), [](const int &i) { return i == 0; })) { // get size & init block! - if (!no_alloc) { - // cytnx_uint64 blockNelem = 1; - for (cytnx_int32 i = 0; i < Loc.size(); i++) { - size[i] = this->_bonds[i]._impl->_degs[Loc[i]]; - // blockNelem *= size[i]; - } - this->_blocks.push_back(zeros(size, dtype, device)); - // blocklens.push_back(blockNelem); - // blocksizes.push_back(size); - // totblocksize += blockNelem; - } else { - for (cytnx_int32 i = 0; i < Loc.size(); i++) { - size[i] = this->_bonds[i]._impl->_degs[Loc[i]]; - } + for (cytnx_int32 i = 0; i < Loc.size(); i++) { + size[i] = this->_bonds[i]._impl->_degs[Loc[i]]; + } + if (no_alloc) { this->_blocks.push_back(Tensor(size, dtype, device, false)); + } else { + this->_blocks.push_back(zeros(size, dtype, device)); } // push its loc this->_inner_to_outer_idx.push_back(Loc); } + // increment Loc by one or emtpy Loc if last element is reached while (Loc.size() != 0) { if (Loc.back() == this->_bonds[Loc.size() - 1]._impl->_qnums.size() - 1) { Loc.pop_back(); @@ -2112,11 +2105,14 @@ namespace cytnx { } // helper function: + // @param[in] locator: indices to check + // @param[out] bidx: block index corresponding to the locator + // @param[out] loc_in_T: indices in block[bidx] that locator corresponds to void BlockFermionicUniTensor::_fx_locate_elem(cytnx_int64 &bidx, std::vector &loc_in_T, const std::vector &locator) const { //[21 Aug 2024] This is a copy from BlockUniTensor; error message differs - // 1. check if out of range: + // 1. check if out of range cytnx_error_msg(locator.size() != this->_bonds.size(), "[ERROR] len(locator) does not match the rank of tensor.%s", "\n"); @@ -2126,7 +2122,8 @@ namespace cytnx { "[ERROR][BlockFermionicUniTensor][elem_exists] locator @index: %d out of range.\n", i); } - // 2. calculate the location is in which qindices: + // 2. calculate qindices corresponding to the locator + // subtracts the degeneracies from the locator until loc_in_T < degeneracy on that index if (this->is_diag()) { if (locator[0] != locator[1]) bidx = -1; @@ -2373,13 +2370,98 @@ namespace cytnx { } void BlockFermionicUniTensor::to_hdf5_dispatch(H5::Group &location, const bool overwrite) const { - cytnx_error_msg( - true, "[ERROR] Saving BlockFermionicUniTensor to HDF5 is not implemented yet!%s", "\n"); + // blocks; write to group + if (!this->_blocks.empty()) { + H5::Group dir = location.createGroup("blocks"); + for (int i = 0; i < this->_blocks.size(); i++) { + if (this->_signflip[i]) { + Tensor block = -this->_blocks[i]; + block.to_hdf5(dir, overwrite, "Tensor" + std::to_string(i)); + } else { + this->_blocks[i].to_hdf5(dir, overwrite, "Tensor" + std::to_string(i)); + } + } + } + // inner_to_outer_idx; write matrix (blocknum x rank) + if (!this->_inner_to_outer_idx.empty()) { + hsize_t blocknum = this->_inner_to_outer_idx.size(); + hsize_t rank = this->_bonds.size(); + std::vector flat(blocknum * rank); // flatten vector + for (hsize_t i = 0; i < blocknum; ++i) { + std::copy(this->_inner_to_outer_idx[i].begin(), this->_inner_to_outer_idx[i].end(), + flat.begin() + i * blocknum); + } + hsize_t matdims[2] = {blocknum, rank}; + H5::DataSpace dataspace(2, matdims); + H5::DataType datatype = Type.get_hdf5_type(flat[0]); + H5::DataSet dataset = location.createDataSet("block_to_sectors", datatype, dataspace); + dataset.write(flat.data(), datatype); + // label axes + char labels[2][6] = {"block", "bond"}; + H5::StrType str_type(H5::PredType::C_S1, 6); + hsize_t attr_dims[1] = {2}; + H5::DataSpace attr_space(1, attr_dims); + H5::Attribute attr = dataset.createAttribute("axis_labels", str_type, attr_space); + attr.write(str_type, labels); + } } void BlockFermionicUniTensor::from_hdf5_dispatch(H5::Group &location, const bool restore_device) { - cytnx_error_msg( - true, "[ERROR] Loading BlockFermionicUniTensor from HDF5 is not implemented yet!%s", "\n"); + this->_is_tag = true; + // blocks; read from group + this->_blocks.clear(); + if (location.exists("blocks")) { + H5::Group dir = location.openGroup("blocks"); + hsize_t idx = 0; + while (true) { + std::string name = "Tensor" + std::to_string(idx); + if (!dir.exists(name)) { + break; + } + Tensor block; + block.from_hdf5(dir, name, restore_device); + this->_blocks.push_back(block); + idx++; + } + } + // inner_to_outer_idx; read matrix (blocknum x rank) + if (location.exists("block_to_sectors")) { + H5::DataSet dataset = location.openDataSet("block_to_sectors"); + H5::DataSpace dataspace = dataset.getSpace(); + cytnx_error_msg(dataspace.getSimpleExtentNdims() != 2, + "[ERROR] 'block_to_sectors' should be a two-dimensional array. The HDF5 data " + "seems corrupt!%s", + "\n"); + hsize_t dims[2]; + dataspace.getSimpleExtentDims(dims); + hsize_t blocknum = dims[0]; + hsize_t rank = dims[1]; + cytnx_error_msg(blocknum != this->_blocks.size(), + "[ERROR] %d blocks found, but first dimension of 'block_to_sectors' is %d. " + "The HDF5 data seems corrupt!\n", + this->_blocks.size(), blocknum); + cytnx_error_msg(rank != this->_bonds.size(), + "[ERROR] %d bonds found, but second dimension of 'block_to_sectors' is %d. " + "The HDF5 data seems corrupt!\n", + this->_bonds.size(), rank); + // Read HDF5 data into a flattened temporary vector + std::vector flat(blocknum * rank); + H5::DataType datatype = dataset.getDataType(); + cytnx_error_msg( + datatype.getSize() != sizeof(cytnx_uint64), + "[ERROR] 'block_to_sectors' bit-length mismatch. File: %zu bytes, expected: %zu bytes.\n", + datatype.getSize(), sizeof(cytnx_uint64)); + dataset.read(flat.data(), datatype); + // Reconstruct the vector of vectors + this->_inner_to_outer_idx.assign(blocknum, std::vector(rank)); + for (hsize_t i = 0; i < blocknum; ++i) { + std::copy(flat.begin() + i * rank, flat.begin() + (i + 1) * rank, + this->_inner_to_outer_idx[i].begin()); + } + } else { + this->_inner_to_outer_idx.empty(); + } + this->_signflip = std::vector(this->_blocks.size(), false); } void BlockFermionicUniTensor::to_binary_dispatch(std::ostream &f) const { diff --git a/src/BlockUniTensor.cpp b/src/BlockUniTensor.cpp index 37e00ec2a..f526ed794 100644 --- a/src/BlockUniTensor.cpp +++ b/src/BlockUniTensor.cpp @@ -134,7 +134,7 @@ namespace cytnx { } } else { - // checking how many blocks are there, and the size: + // find the number of blocks and their sizes: std::vector Loc(this->_bonds.size(), 0); std::vector tot_qns( this->_bonds[0].Nsym()); // use first bond to determine symmetry size @@ -151,29 +151,22 @@ namespace cytnx { // std::cout << "tot_flx: "; // cytnx::vec_print_simple(std::cout, tot_qns); - // if exists: + // if total flux is zero -> block exists: if (std::all_of(tot_qns.begin(), tot_qns.end(), [](const int &i) { return i == 0; })) { // get size & init block! - if (!no_alloc) { - // cytnx_uint64 blockNelem = 1; - for (cytnx_int32 i = 0; i < Loc.size(); i++) { - size[i] = this->_bonds[i]._impl->_degs[Loc[i]]; - // blockNelem *= size[i]; - } - this->_blocks.push_back(zeros(size, dtype, device)); - // blocklens.push_back(blockNelem); - // blocksizes.push_back(size); - // totblocksize += blockNelem; - } else { - for (cytnx_int32 i = 0; i < Loc.size(); i++) { - size[i] = this->_bonds[i]._impl->_degs[Loc[i]]; - } + for (cytnx_int32 i = 0; i < Loc.size(); i++) { + size[i] = this->_bonds[i]._impl->_degs[Loc[i]]; + } + if (no_alloc) { this->_blocks.push_back(Tensor(size, dtype, device, false)); + } else { + this->_blocks.push_back(zeros(size, dtype, device)); } // push its loc this->_inner_to_outer_idx.push_back(Loc); } + // increment Loc by one or emtpy Loc if last element is reached while (Loc.size() != 0) { if (Loc.back() == this->_bonds[Loc.size() - 1]._impl->_qnums.size() - 1) { Loc.pop_back(); @@ -1351,9 +1344,12 @@ namespace cytnx { } // helper function: + // @param[in] locator: indices to check + // @param[out] bidx: block index corresponding to the locator + // @param[out] loc_in_T: indices in block[bidx] that locator corresponds to void BlockUniTensor::_fx_locate_elem(cytnx_int64 &bidx, std::vector &loc_in_T, const std::vector &locator) const { - // 1. check if out of range: + // 1. check if out of range cytnx_error_msg(locator.size() != this->_bonds.size(), "[ERROR] len(locator) does not match the rank of tensor.%s", "\n"); @@ -1362,7 +1358,8 @@ namespace cytnx { "[ERROR][BlockUniTensor][elem_exists] locator @index: %d out of range.\n", i); } - // 2. calculate the location is in which qindices: + // 2. calculate qindices corresponding to the locator + // subtracts the degeneracies from the locator until loc_in_T < degeneracy on that index if (this->is_diag()) { if (locator[0] != locator[1]) bidx = -1; @@ -1583,12 +1580,92 @@ namespace cytnx { } void BlockUniTensor::to_hdf5_dispatch(H5::Group &location, const bool overwrite) const { - cytnx_error_msg(true, "[ERROR] Saving BlockUniTensor to HDF5 is not implemented yet!%s", "\n"); + // blocks; write to group + if (!this->_blocks.empty()) { + H5::Group dir = location.createGroup("blocks"); + for (int i = 0; i < this->_blocks.size(); i++) { + this->_blocks[i].to_hdf5(dir, overwrite, "Tensor" + std::to_string(i)); + } + } + // inner_to_outer_idx; write matrix (blocknum x rank) + if (!this->_inner_to_outer_idx.empty()) { + hsize_t blocknum = this->_inner_to_outer_idx.size(); + hsize_t rank = this->_bonds.size(); + std::vector flat(blocknum * rank); // flatten vector + for (hsize_t i = 0; i < blocknum; ++i) { + std::copy(this->_inner_to_outer_idx[i].begin(), this->_inner_to_outer_idx[i].end(), + flat.begin() + i * blocknum); + } + hsize_t matdims[2] = {blocknum, rank}; + H5::DataSpace dataspace(2, matdims); + H5::DataType datatype = Type.get_hdf5_type(flat[0]); + H5::DataSet dataset = location.createDataSet("block_to_sectors", datatype, dataspace); + dataset.write(flat.data(), datatype); + // label axes + char labels[2][6] = {"block", "bond"}; + H5::StrType str_type(H5::PredType::C_S1, 6); + hsize_t attr_dims[1] = {2}; + H5::DataSpace attr_space(1, attr_dims); + H5::Attribute attr = dataset.createAttribute("axis_labels", str_type, attr_space); + attr.write(str_type, labels); + } } void BlockUniTensor::from_hdf5_dispatch(H5::Group &location, const bool restore_device) { - cytnx_error_msg(true, "[ERROR] Loading BlockUniTensor from HDF5 is not implemented yet!%s", - "\n"); + this->_is_tag = true; + // blocks; read from group + this->_blocks.clear(); + if (location.exists("blocks")) { + H5::Group dir = location.openGroup("blocks"); + hsize_t idx = 0; + while (true) { + std::string name = "Tensor" + std::to_string(idx); + if (!dir.exists(name)) { + break; + } + Tensor block; + block.from_hdf5(dir, name, restore_device); + this->_blocks.push_back(block); + idx++; + } + } + // inner_to_outer_idx; read matrix (blocknum x rank) + if (location.exists("block_to_sectors")) { + H5::DataSet dataset = location.openDataSet("block_to_sectors"); + H5::DataSpace dataspace = dataset.getSpace(); + cytnx_error_msg(dataspace.getSimpleExtentNdims() != 2, + "[ERROR] 'block_to_sectors' should be a two-dimensional array. The HDF5 data " + "seems corrupt!%s", + "\n"); + hsize_t dims[2]; + dataspace.getSimpleExtentDims(dims); + hsize_t blocknum = dims[0]; + hsize_t rank = dims[1]; + cytnx_error_msg(blocknum != this->_blocks.size(), + "[ERROR] %d blocks found, but first dimension of 'block_to_sectors' is %d. " + "The HDF5 data seems corrupt!\n", + this->_blocks.size(), blocknum); + cytnx_error_msg(rank != this->_bonds.size(), + "[ERROR] %d bonds found, but second dimension of 'block_to_sectors' is %d. " + "The HDF5 data seems corrupt!\n", + this->_bonds.size(), rank); + // Read HDF5 data into a flattened temporary vector + std::vector flat(blocknum * rank); + H5::DataType datatype = dataset.getDataType(); + cytnx_error_msg( + datatype.getSize() != sizeof(cytnx_uint64), + "[ERROR] 'block_to_sectors' bit-length mismatch. File: %zu bytes, expected: %zu bytes.\n", + datatype.getSize(), sizeof(cytnx_uint64)); + dataset.read(flat.data(), datatype); + // Reconstruct the vector of vectors + this->_inner_to_outer_idx.assign(blocknum, std::vector(rank)); + for (hsize_t i = 0; i < blocknum; ++i) { + std::copy(flat.begin() + i * rank, flat.begin() + (i + 1) * rank, + this->_inner_to_outer_idx[i].begin()); + } + } else { + this->_inner_to_outer_idx.empty(); + } } void BlockUniTensor::to_binary_dispatch(std::ostream &f) const { diff --git a/src/Bond.cpp b/src/Bond.cpp index e58c66de3..6a19df8de 100644 --- a/src/Bond.cpp +++ b/src/Bond.cpp @@ -643,6 +643,11 @@ namespace cytnx { dataset = location.createDataSet("degeneracies", Type.get_hdf5_type(this->_impl->_degs[0]), dataspace); dataset.write(this->_impl->_degs.data(), datatype); + // label axis + dataspace = H5::DataSpace(H5S_SCALAR); + str_type = H5::StrType(H5::PredType::C_S1, 7); + attr = dataset.createAttribute("axis_label", str_type, dataspace); + attr.write(str_type, "sector"); // qnums; write matrix (dim x qnumdim) hsize_t qnumdim = this->_impl->_syms.size(); @@ -679,8 +684,8 @@ namespace cytnx { // } // dataset.write(c_strings.data(), str_type); H5::Group symloc = location.createGroup("symmetries"); - for (int sidx = 0; sidx < this->_impl->_syms.size(); sidx++) { - this->_impl->_syms[sidx].to_hdf5(symloc, overwrite, "Symmetry" + std::to_string(sidx)); + for (int i = 0; i < this->_impl->_syms.size(); i++) { + this->_impl->_syms[i].to_hdf5(symloc, overwrite, "Symmetry" + std::to_string(i)); } } } @@ -727,7 +732,7 @@ namespace cytnx { if (location.exists("quantum_numbers")) { cytnx_error_msg(!symmetric, "[ERROR] 'degeneracies' were not found in HDF5 location, but " - "'quantum_numbers' existn. The HDF5 data seems corrupt!%s", + "'quantum_numbers' exist. The HDF5 data seems corrupt!%s", "\n"); dataset = location.openDataSet("quantum_numbers"); dataspace = dataset.getSpace(); @@ -757,7 +762,7 @@ namespace cytnx { this->_impl->_qnums[i].begin()); } } else { - this->_impl->_qnums = {}; + this->_impl->_qnums.empty(); } // symmetries @@ -765,24 +770,24 @@ namespace cytnx { if (location.exists("symmetries")) { H5::Group symloc = location.openGroup("symmetries"); this->_impl->_syms.clear(); - hsize_t sidx = 0; + hsize_t idx = 0; while (true) { - std::string name = "Symmetry" + std::to_string(sidx); + std::string name = "Symmetry" + std::to_string(idx); if (!symloc.attrExists(name) && !symloc.exists(name)) { break; } Symmetry sym; sym.from_hdf5(symloc, name); this->_impl->_syms.push_back(sym); - sidx++; + idx++; } if (symmetric) { - cytnx_error_msg(sidx != qnumdim, + cytnx_error_msg(idx != qnumdim, "[ERROR] %d symmetries were found, but second dimension of " "'quantum_numbers' is %d. The HDF5 data seems corrupt!\n", - sidx, qnumdim); + idx, qnumdim); } else { - cytnx_error_msg(sidx > 0, + cytnx_error_msg(idx > 0, "[ERROR] 'degeneracies' and 'quantum_numbers' were not found in HDF5 " "location, but 'symmetries' exist. The HDF5 data seems corrupt!%s", "\n"); diff --git a/src/UniTensor.cpp b/src/UniTensor.cpp index a557e38c1..37b73876c 100644 --- a/src/UniTensor.cpp +++ b/src/UniTensor.cpp @@ -209,8 +209,10 @@ namespace cytnx { // remove datasets if (location.nameExists("labels")) location.unlink("labels"); if (location.nameExists("Tensor")) location.unlink("Tensor"); + if (location.nameExists("block_to_sectors")) location.unlink("block_to_sectors"); // remove groups and its contents recursively if (location.nameExists("bonds")) location.unlink("bonds"); + if (location.nameExists("blocks")) location.unlink("blocks"); } H5::DataType datatype; @@ -260,9 +262,9 @@ namespace cytnx { // bonds; write in group if (!this->_impl->_bonds.empty()) { - H5::Group bonddir = location.createGroup("bonds"); + H5::Group dir = location.createGroup("bonds"); for (int i = 0; i < this->_impl->_bonds.size(); i++) { - H5::Group bondgroup = bonddir.createGroup("Bond" + std::to_string(i)); + H5::Group bondgroup = dir.createGroup("Bond" + std::to_string(i)); this->_impl->_bonds[i].to_hdf5(bondgroup, overwrite); } } @@ -340,14 +342,14 @@ namespace cytnx { // bonds; read from group this->_impl->_bonds.clear(); if (location.exists("bonds")) { - H5::Group bonddir = location.openGroup("bonds"); + H5::Group dir = location.openGroup("bonds"); hsize_t idx = 0; while (true) { std::string name = "Bond" + std::to_string(idx); - if (!bonddir.exists(name)) { + if (!dir.exists(name)) { break; } - H5::Group bondgroup = bonddir.openGroup(name); + H5::Group bondgroup = dir.openGroup(name); Bond bond; bond.from_hdf5(bondgroup); this->_impl->_bonds.push_back(bond); From 4242b9996acb81df548f8c5fe02deb8546663f71 Mon Sep 17 00:00:00 2001 From: Manuel Schneider Date: Sun, 3 May 2026 04:27:24 +0800 Subject: [PATCH 20/40] removed unused option to save Bonds without Symmetries; cleaned up code --- include/Bond.hpp | 8 ++--- src/Bond.cpp | 86 +++++++++++++++++------------------------------- 2 files changed, 32 insertions(+), 62 deletions(-) diff --git a/include/Bond.hpp b/include/Bond.hpp index af91dea6d..fb2e954d2 100644 --- a/include/Bond.hpp +++ b/include/Bond.hpp @@ -917,23 +917,19 @@ namespace cytnx { * @brief Save Bond to HDF5 file * @param[in] location the HDF5 group where the Bond will be saved. * @param[in] overwrite overwrite previous Bond information in the location. - * @param[in] save_symmetries whether to save the symmetry information in the HDF5 file. * @warning This function is only available in C++. Use Save() for saving to file in C++ or * Python. * @see from_hdf5() */ - void to_hdf5(H5::Group &location, const bool overwrite = false, - const bool save_symmetries = true) const; + void to_hdf5(H5::Group &location, const bool overwrite = false) const; /** * @brief Load Bond from HDF5 file (inline) * @param[in] location the HDF5 group where the Bond will be loaded from. - * @param[in] syms vector containing the Symmetries. Leave emtpy to read the Symmetries from - * HDF5 * @warning This function is only available in C++. Use Load() for loading from file in C++ or * Python. * @see to_hdf5() const */ - void from_hdf5(H5::Group &location, const std::vector &syms = {}); + void from_hdf5(H5::Group &location); /** * @brief Save Bond to binary file diff --git a/src/Bond.cpp b/src/Bond.cpp index 6a19df8de..82e6281e4 100644 --- a/src/Bond.cpp +++ b/src/Bond.cpp @@ -603,7 +603,7 @@ namespace cytnx { this->Load_(std::filesystem::path(fname), path); } - void Bond::to_hdf5(H5::Group &location, const bool overwrite, const bool save_symmetries) const { + void Bond::to_hdf5(H5::Group &location, const bool overwrite) const { if (overwrite) { // delete previous data // remove attributes if (location.attrExists("dimension")) location.removeAttr("dimension"); @@ -670,27 +670,13 @@ namespace cytnx { attr.write(str_type, labels); // symmetries - if (save_symmetries) { - // vecdims[1] = { this->_impl->_syms.size() }; - // dataspace = H5::DataSpace(1, vecdims); - // str_type = H5::StrType(H5::PredType::C_S1, H5T_VARIABLE); - // dataset = location.createDataSet("symmetries", str_type, dataspace); - // // create cstrings - // std::vector c_strings; - // std::string symstring; - // for (const auto& s : this->_impl->_syms) { - // symstring = s.getname(); - // c_strings.push_back(symstring.c_str()); - // } - // dataset.write(c_strings.data(), str_type); - H5::Group symloc = location.createGroup("symmetries"); - for (int i = 0; i < this->_impl->_syms.size(); i++) { - this->_impl->_syms[i].to_hdf5(symloc, overwrite, "Symmetry" + std::to_string(i)); - } + H5::Group symloc = location.createGroup("symmetries"); + for (int i = 0; i < this->_impl->_syms.size(); i++) { + this->_impl->_syms[i].to_hdf5(symloc, overwrite, "Symmetry" + std::to_string(i)); } } } - void Bond::from_hdf5(H5::Group &location, const std::vector &syms) { + void Bond::from_hdf5(H5::Group &location) { H5::DataType datatype; H5::Attribute attr; H5::DataSet dataset; @@ -766,49 +752,37 @@ namespace cytnx { } // symmetries - if (syms.empty()) { - if (location.exists("symmetries")) { - H5::Group symloc = location.openGroup("symmetries"); - this->_impl->_syms.clear(); - hsize_t idx = 0; - while (true) { - std::string name = "Symmetry" + std::to_string(idx); - if (!symloc.attrExists(name) && !symloc.exists(name)) { - break; - } - Symmetry sym; - sym.from_hdf5(symloc, name); - this->_impl->_syms.push_back(sym); - idx++; - } - if (symmetric) { - cytnx_error_msg(idx != qnumdim, - "[ERROR] %d symmetries were found, but second dimension of " - "'quantum_numbers' is %d. The HDF5 data seems corrupt!\n", - idx, qnumdim); - } else { - cytnx_error_msg(idx > 0, - "[ERROR] 'degeneracies' and 'quantum_numbers' were not found in HDF5 " - "location, but 'symmetries' exist. The HDF5 data seems corrupt!%s", - "\n"); - this->_impl->_syms = {}; + if (location.exists("symmetries")) { + H5::Group symloc = location.openGroup("symmetries"); + this->_impl->_syms.clear(); + hsize_t idx = 0; + while (true) { + std::string name = "Symmetry" + std::to_string(idx); + if (!symloc.attrExists(name) && !symloc.exists(name)) { + break; } + Symmetry sym; + sym.from_hdf5(symloc, name); + this->_impl->_syms.push_back(sym); + idx++; + } + if (symmetric) { + cytnx_error_msg(idx != qnumdim, + "[ERROR] %d symmetries were found, but second dimension of " + "'quantum_numbers' is %d. The HDF5 data seems corrupt!\n", + idx, qnumdim); } else { - cytnx_error_msg(symmetric, - "[ERROR] 'degeneracies' and 'quantum_numbers' exist in HDF5 location, but " - "'symmetries' are missing. The HDF5 data seems corrupt!%s", + cytnx_error_msg(idx > 0, + "[ERROR] 'degeneracies' and 'quantum_numbers' were not found in HDF5 " + "location, but 'symmetries' exist. The HDF5 data seems corrupt!%s", "\n"); + this->_impl->_syms = {}; } } else { - cytnx_error_msg(!symmetric, - "[ERROR] 'degeneracies' and 'quantum_numbers' not found in HDF5 location, " - "but symmetries are passed as arguments.%s", + cytnx_error_msg(symmetric, + "[ERROR] 'degeneracies' and 'quantum_numbers' exist in HDF5 location, but " + "'symmetries' are missing. The HDF5 data seems corrupt!%s", "\n"); - cytnx_error_msg(syms.size() != qnumdim, - "[ERROR] %d symmetries are passed, but second dimension of 'quantum_numbers' " - "is %d. The HDF5 data seems corrupt!\n", - syms.size(), qnumdim); - this->_impl->_syms = syms; } // dim; from attribute From 0d9049932d58bd8d4c4f16e5ebfc45c127e26a2f Mon Sep 17 00:00:00 2001 From: Manuel Schneider Date: Sun, 3 May 2026 06:47:02 +0800 Subject: [PATCH 21/40] bug fixes, github action uses hdf5, warnings if data requests GPU without GPU support --- .github/workflows/ci-cmake_tests.yml | 2 +- src/BlockFermionicUniTensor.cpp | 8 ++++++-- src/BlockUniTensor.cpp | 8 ++++++-- src/Bond.cpp | 20 ++++++++++--------- src/DenseUniTensor.cpp | 2 +- src/RegularGncon.cpp | 6 +++--- src/RegularNetwork.cpp | 6 +++--- src/Symmetry.cpp | 11 +++++----- src/Tensor.cpp | 25 ++++++++++++++++------- src/UniTensor.cpp | 25 +++++++++++++---------- src/backend/Storage.cpp | 30 ++++++++++++++++++++-------- src/tn_algo/MPS.cpp | 11 +++++----- 12 files changed, 98 insertions(+), 56 deletions(-) diff --git a/.github/workflows/ci-cmake_tests.yml b/.github/workflows/ci-cmake_tests.yml index 3e3956471..fae21c44c 100644 --- a/.github/workflows/ci-cmake_tests.yml +++ b/.github/workflows/ci-cmake_tests.yml @@ -39,7 +39,7 @@ jobs: - name: Install dependencies shell: bash -l {0} run: | - mamba install _openmp_mutex=*=*_llvm cmake make boost git compilers numpy mkl mkl-include mkl-service pybind11 libblas=*=*mkl arpack beartype + mamba install _openmp_mutex=*=*_llvm cmake make boost git compilers hdf5 numpy mkl mkl-include mkl-service pybind11 libblas=*=*mkl arpack beartype python -m pip install gcovr mamba install gtest mamba install pytest pytest-cov diff --git a/src/BlockFermionicUniTensor.cpp b/src/BlockFermionicUniTensor.cpp index dbace81dc..259dfd001 100644 --- a/src/BlockFermionicUniTensor.cpp +++ b/src/BlockFermionicUniTensor.cpp @@ -2389,7 +2389,7 @@ namespace cytnx { std::vector flat(blocknum * rank); // flatten vector for (hsize_t i = 0; i < blocknum; ++i) { std::copy(this->_inner_to_outer_idx[i].begin(), this->_inner_to_outer_idx[i].end(), - flat.begin() + i * blocknum); + flat.begin() + i * rank); } hsize_t matdims[2] = {blocknum, rank}; H5::DataSpace dataspace(2, matdims); @@ -2459,7 +2459,11 @@ namespace cytnx { this->_inner_to_outer_idx[i].begin()); } } else { - this->_inner_to_outer_idx.empty(); + cytnx_error_msg( + !(this->_blocks.empty()), + "[ERROR] 'block_to_sectors' not found, but %d blocks exist. The HDF5 data seems corrupt!\n", + this->_blocks.size()); + this->_inner_to_outer_idx.clear(); } this->_signflip = std::vector(this->_blocks.size(), false); } diff --git a/src/BlockUniTensor.cpp b/src/BlockUniTensor.cpp index f526ed794..c6b4ef6fc 100644 --- a/src/BlockUniTensor.cpp +++ b/src/BlockUniTensor.cpp @@ -1594,7 +1594,7 @@ namespace cytnx { std::vector flat(blocknum * rank); // flatten vector for (hsize_t i = 0; i < blocknum; ++i) { std::copy(this->_inner_to_outer_idx[i].begin(), this->_inner_to_outer_idx[i].end(), - flat.begin() + i * blocknum); + flat.begin() + i * rank); } hsize_t matdims[2] = {blocknum, rank}; H5::DataSpace dataspace(2, matdims); @@ -1664,7 +1664,11 @@ namespace cytnx { this->_inner_to_outer_idx[i].begin()); } } else { - this->_inner_to_outer_idx.empty(); + cytnx_error_msg( + !(this->_blocks.empty()), + "[ERROR] 'block_to_sectors' not found, but %d blocks exist. The HDF5 data seems corrupt!\n", + this->_blocks.size()); + this->_inner_to_outer_idx.clear(); } } diff --git a/src/Bond.cpp b/src/Bond.cpp index 82e6281e4..09dade78d 100644 --- a/src/Bond.cpp +++ b/src/Bond.cpp @@ -529,7 +529,8 @@ namespace cytnx { } else { // create binary file if (mode == 'x') { cytnx_error_msg(std::filesystem::exists(fname), - "[ERROR] File %s already exists. Use mode 'w' to overwrite.", fname); + "[ERROR] File %s already exists. Use mode 'w' to overwrite.", + fname.string().c_str()); } else { cytnx_error_msg(mode != 'w', "[ERROR] Unknown mode '%c' for writing to binary file.", mode); @@ -542,11 +543,11 @@ namespace cytnx { cytnx_warning_msg(true, "Missing file extension in fname '%s'. I am adding the extension '.cybd'. " "This is deprecated, please provide the file extension in the future.\n", - fname.c_str()); + fname.string().c_str()); if (mode == 'x') { cytnx_error_msg(std::filesystem::exists(fnameext), "[ERROR] File %s already exists. Use mode 'w' to overwrite.", - fnameext.c_str()); + fnameext.string().c_str()); } else { cytnx_error_msg(mode != 'w', "[ERROR] Unknown mode '%c' for writing to binary file.", mode); } @@ -584,7 +585,7 @@ namespace cytnx { } catch (const H5::Exception &e) { std::cerr << e.getDetailMsg() << std::endl; cytnx_error_msg(true, "[ERROR] HDF5 path '%s' not found or is not a group in file '%s'.", - path.c_str(), fname.c_str()); + path.c_str(), fname.string().c_str()); } // read data this->from_hdf5(location); @@ -593,7 +594,7 @@ namespace cytnx { fstream f; f.open(fname, std::ios::in | std::ios::binary); if (!f.is_open()) { - cytnx_error_msg(true, "[ERROR] Cannot open file '%s'.\n", fname.c_str()); + cytnx_error_msg(true, "[ERROR] Cannot open file '%s'.\n", fname.string().c_str()); } this->from_binary(f); f.close(); @@ -625,7 +626,7 @@ namespace cytnx { auto dim = this->_impl->_dim; datatype = Type.get_hdf5_type(dim); attr = location.createAttribute("dimension", datatype, H5::DataSpace(H5S_SCALAR)); - attr.write(H5::PredType::NATIVE_INT, &dim); + attr.write(datatype, &dim); // type, write as string std::string typestr = bondtype_to_string.at(this->_impl->_type); @@ -714,7 +715,7 @@ namespace cytnx { } // qnums; read matrix (dim x qnumdim) - hsize_t qnumdim; + hsize_t qnumdim = 0; if (location.exists("quantum_numbers")) { cytnx_error_msg(!symmetric, "[ERROR] 'degeneracies' were not found in HDF5 location, but " @@ -748,7 +749,7 @@ namespace cytnx { this->_impl->_qnums[i].begin()); } } else { - this->_impl->_qnums.empty(); + this->_impl->_qnums.clear(); } // symmetries @@ -776,13 +777,14 @@ namespace cytnx { "[ERROR] 'degeneracies' and 'quantum_numbers' were not found in HDF5 " "location, but 'symmetries' exist. The HDF5 data seems corrupt!%s", "\n"); - this->_impl->_syms = {}; + this->_impl->_syms.clear(); } } else { cytnx_error_msg(symmetric, "[ERROR] 'degeneracies' and 'quantum_numbers' exist in HDF5 location, but " "'symmetries' are missing. The HDF5 data seems corrupt!%s", "\n"); + this->_impl->_syms.clear(); } // dim; from attribute diff --git a/src/DenseUniTensor.cpp b/src/DenseUniTensor.cpp index 9fe188a70..11c1d0be4 100644 --- a/src/DenseUniTensor.cpp +++ b/src/DenseUniTensor.cpp @@ -1286,7 +1286,7 @@ namespace cytnx { // is_tag, write as attribute H5::DataType datatype = Type.get_hdf5_type(this->_is_tag); H5::Attribute attr = location.createAttribute("directed", datatype, H5::DataSpace(H5S_SCALAR)); - attr.write(H5::PredType::NATIVE_INT, &this->_is_tag); + attr.write(datatype, &this->_is_tag); this->_block.to_hdf5(location, overwrite, "Tensor"); } diff --git a/src/RegularGncon.cpp b/src/RegularGncon.cpp index 557d1692b..e6cbbea2f 100644 --- a/src/RegularGncon.cpp +++ b/src/RegularGncon.cpp @@ -412,9 +412,9 @@ namespace cytnx { // open file std::ifstream infile; - infile.open(fname.c_str()); + infile.open(fname.string().c_str()); if (!(infile.is_open())) { - cytnx_error_msg(true, "[Gncon] Error in opening file \'", fname.c_str(), "\'.\n"); + cytnx_error_msg(true, "[Gncon] Error in opening file \'", fname.string().c_str(), "\'.\n"); } filename = fname; @@ -493,7 +493,7 @@ namespace cytnx { fo.open(std::filesystem::path(fname) += ".net", ios::out | ios::trunc); if (!fo.is_open()) { cytnx_error_msg(true, "[ERROR][RegularGncon][Savefile] Cannot open/create file:%s\n", - fname.c_str()); + fname.string().c_str()); } for (int i = 0; i < this->label_arr.size(); i++) { diff --git a/src/RegularNetwork.cpp b/src/RegularNetwork.cpp index e9ee798a8..59a148462 100644 --- a/src/RegularNetwork.cpp +++ b/src/RegularNetwork.cpp @@ -658,9 +658,9 @@ namespace cytnx { // open file ifstream infile; - infile.open(fname.c_str()); + infile.open(fname.string().c_str()); if (!(infile.is_open())) { - cytnx_error_msg(true, "[Network] Error in opening file \'", fname.c_str(), "\'.\n"); + cytnx_error_msg(true, "[Network] Error in opening file \'", fname.string().c_str(), "\'.\n"); } filename = fname; @@ -747,7 +747,7 @@ namespace cytnx { fo.open(std::filesystem::path(fname) += ".net", ios::out | ios::trunc); if (!fo.is_open()) { cytnx_error_msg(true, "[ERROR][RegularNetwork][Savefile] Cannot open/create file:%s\n", - fname.c_str()); + fname.string().c_str()); } for (int i = 0; i < this->label_arr.size(); i++) { diff --git a/src/Symmetry.cpp b/src/Symmetry.cpp index 631ca0a3a..50b51b09f 100644 --- a/src/Symmetry.cpp +++ b/src/Symmetry.cpp @@ -307,7 +307,8 @@ namespace cytnx { } else { // create binary file if (mode == 'x') { cytnx_error_msg(std::filesystem::exists(fname), - "[ERROR] File %s already exists. Use mode 'w' to overwrite.", fname); + "[ERROR] File %s already exists. Use mode 'w' to overwrite.", + fname.string().c_str()); } else { cytnx_error_msg(mode != 'w', "[ERROR] Unknown mode '%c' for writing to binary file.", mode); @@ -320,11 +321,11 @@ namespace cytnx { cytnx_warning_msg(true, "Missing file extension in fname '%s'. I am adding the extension '.cysym'. " "This is deprecated, please provide the file extension in the future.\n", - fname.c_str()); + fname.string().c_str()); if (mode == 'x') { cytnx_error_msg(std::filesystem::exists(fnameext), "[ERROR] File %s already exists. Use mode 'w' to overwrite.", - fnameext.c_str()); + fnameext.string().c_str()); } else { cytnx_error_msg(mode != 'w', "[ERROR] Unknown mode '%c' for writing to binary file.", mode); } @@ -368,7 +369,7 @@ namespace cytnx { } catch (const H5::Exception &e) { std::cerr << e.getDetailMsg() << std::endl; cytnx_error_msg(true, "[ERROR] HDF5 path '%s' not found or is not a group in file '%s'.", - grouppath.c_str(), fname.c_str()); + grouppath.c_str(), fname.string().c_str()); } // read data this->from_hdf5(location, datasetname); @@ -377,7 +378,7 @@ namespace cytnx { fstream f; f.open(fname, ios::in | ios::binary); if (!f.is_open()) { - cytnx_error_msg(true, "[ERROR] Cannot open file '%s'.\n", fname.c_str()); + cytnx_error_msg(true, "[ERROR] Cannot open file '%s'.\n", fname.string().c_str()); } this->from_binary(f); f.close(); diff --git a/src/Tensor.cpp b/src/Tensor.cpp index f757040a3..bcb49c75a 100644 --- a/src/Tensor.cpp +++ b/src/Tensor.cpp @@ -487,7 +487,8 @@ namespace cytnx { } else { // create binary file if (mode == 'x') { cytnx_error_msg(std::filesystem::exists(fname), - "[ERROR] File %s already exists. Use mode 'w' to overwrite.", fname); + "[ERROR] File %s already exists. Use mode 'w' to overwrite.", + fname.string().c_str()); } else { cytnx_error_msg(mode != 'w', "[ERROR] Unknown mode '%c' for writing to binary file.", mode); @@ -500,11 +501,11 @@ namespace cytnx { cytnx_warning_msg(true, "Missing file extension in fname '%s'. I am adding the extension '.cytn'. " "This is deprecated, please provide the file extension in the future.\n", - fname.c_str()); + fname.string().c_str()); if (mode == 'x') { cytnx_error_msg(std::filesystem::exists(fnameext), "[ERROR] File %s already exists. Use mode 'w' to overwrite.", - fnameext.c_str()); + fnameext.string().c_str()); } else { cytnx_error_msg(mode != 'w', "[ERROR] Unknown mode '%c' for writing to binary file.", mode); } @@ -550,7 +551,7 @@ namespace cytnx { } catch (const H5::Exception &e) { std::cerr << e.getDetailMsg() << std::endl; cytnx_error_msg(true, "[ERROR] HDF5 path '%s' not found or is not a group in file '%s'.", - grouppath.c_str(), fname.c_str()); + grouppath.c_str(), fname.string().c_str()); } // read data this->from_hdf5(location, datasetname, restore_device); @@ -559,7 +560,7 @@ namespace cytnx { fstream f; f.open(fname, ios::in | ios::binary); if (!f.is_open()) { - cytnx_error_msg(true, "[ERROR] Cannot open file '%s'.\n", fname.c_str()); + cytnx_error_msg(true, "[ERROR] Cannot open file '%s'.\n", fname.string().c_str()); } this->from_binary(f, restore_device); f.close(); @@ -582,10 +583,10 @@ namespace cytnx { H5::DataSet dataset = location.createDataSet(name, datatype, dataspace); ten.storage().data_to_hdf5(dataset, datatype); - if (this->device() != Device.cpu) { + int device = this->device(); + if (device != Device.cpu) { H5::Attribute attr = dataset.createAttribute("device", H5::PredType::NATIVE_INT, H5::DataSpace(H5S_SCALAR)); - int device = this->device(); attr.write(H5::PredType::NATIVE_INT, &device); } } @@ -614,6 +615,16 @@ namespace cytnx { "[ERROR] 'device' bit-length mismatch. File: %zu bytes, expected: %zu bytes.\n", datatype.getSize(), sizeof(int)); attr.read(datatype, &device); + #ifndef UNI_GPU + if (device != Device.cpu) { + cytnx_warning_msg(true, + "Cytnx was compiled without CPU support, but Tensor in HDF5 file " + "requests to load to GPU with id %d. Loading Tensor to CPU memory " + "instead. Use restore_device=false to disable this message.", + device); + device = Device.cpu; + } + #endif } this->_impl->_storage.data_from_hdf5(dataset, Nelem, dtype, datatype, device); diff --git a/src/UniTensor.cpp b/src/UniTensor.cpp index 37b73876c..c9cba3f46 100644 --- a/src/UniTensor.cpp +++ b/src/UniTensor.cpp @@ -121,7 +121,8 @@ namespace cytnx { } else { // create binary file if (mode == 'x') { cytnx_error_msg(std::filesystem::exists(fname), - "[ERROR] File %s already exists. Use mode 'w' to overwrite.", fname); + "[ERROR] File %s already exists. Use mode 'w' to overwrite.", + fname.string().c_str()); } else { cytnx_error_msg(mode != 'w', "[ERROR] Unknown mode '%c' for writing to binary file.", mode); @@ -134,11 +135,11 @@ namespace cytnx { cytnx_warning_msg(true, "Missing file extension in fname '%s'. I am adding the extension '.cytnx'. " "This is deprecated, please provide the file extension in the future.\n", - fname.c_str()); + fname.string().c_str()); if (mode == 'x') { cytnx_error_msg(std::filesystem::exists(fnameext), "[ERROR] File %s already exists. Use mode 'w' to overwrite.", - fnameext.c_str()); + fnameext.string().c_str()); } else { cytnx_error_msg(mode != 'w', "[ERROR] Unknown mode '%c' for writing to binary file.", mode); } @@ -178,7 +179,7 @@ namespace cytnx { } catch (const H5::Exception &e) { std::cerr << e.getDetailMsg() << std::endl; cytnx_error_msg(true, "[ERROR] HDF5 path '%s' not found or is not a group in file '%s'.", - path.c_str(), fname.c_str()); + path.c_str(), fname.string().c_str()); } // read data this->from_hdf5(location, restore_device); @@ -187,7 +188,7 @@ namespace cytnx { fstream f; f.open(fname, std::ios::in | std::ios::binary); if (!f.is_open()) { - cytnx_error_msg(true, "[ERROR] Cannot open file '%s'.\n", fname.c_str()); + cytnx_error_msg(true, "[ERROR] Cannot open file '%s'.\n", fname.string().c_str()); } this->from_binary(f, restore_device); f.close(); @@ -232,13 +233,13 @@ namespace cytnx { if (this->_impl->_is_diag) { datatype = Type.get_hdf5_type(this->_impl->_is_diag); attr = location.createAttribute("diagonal", datatype, H5::DataSpace(H5S_SCALAR)); - attr.write(H5::PredType::NATIVE_INT, &this->_impl->_is_diag); + attr.write(datatype, &this->_impl->_is_diag); } // rowrank, write as attribute datatype = Type.get_hdf5_type(this->_impl->_rowrank); attr = location.createAttribute("rowrank", datatype, H5::DataSpace(H5S_SCALAR)); - attr.write(H5::PredType::NATIVE_INT, &this->_impl->_rowrank); + attr.write(datatype, &this->_impl->_rowrank); // name, write as string attribute str_type = H5::StrType(H5::PredType::C_S1, this->_impl->_name.length() + 1); @@ -314,8 +315,12 @@ namespace cytnx { attr = location.openAttribute("name"); str_type = attr.getStrType(); size = str_type.getSize() - 1; // remove the null terminator - this->_impl->_name.resize(size); - attr.read(str_type, &this->_impl->_name[0]); + if (size > 0) { + this->_impl->_name.resize(size); + attr.read(str_type, &this->_impl->_name[0]); + } else { + this->_impl->_name.clear(); + } } else { this->_impl->_name.clear(); } @@ -458,7 +463,7 @@ namespace cytnx { if (len_name != 0) { char *cname = (char *)malloc(sizeof(char) * len_name); f.read(cname, sizeof(char) * len_name); - this->_impl->_name = std::string(cname, len_name); + this->_impl->_name = std::string(cname); free(cname); } diff --git a/src/backend/Storage.cpp b/src/backend/Storage.cpp index e0e06019b..f626fdb4c 100644 --- a/src/backend/Storage.cpp +++ b/src/backend/Storage.cpp @@ -146,7 +146,8 @@ namespace cytnx { } else { // create binary file if (mode == 'x') { cytnx_error_msg(std::filesystem::exists(fname), - "[ERROR] File %s already exists. Use mode 'w' to overwrite.", fname); + "[ERROR] File %s already exists. Use mode 'w' to overwrite.", + fname.string().c_str()); } else { cytnx_error_msg(mode != 'w', "[ERROR] Unknown mode '%c' for writing to binary file.", mode); @@ -159,11 +160,11 @@ namespace cytnx { cytnx_warning_msg(true, "Missing file extension in fname '%s'. I am adding the extension '.cyst'. " "This is deprecated, please provide the file extension in the future.\n", - fname.c_str()); + fname.string().c_str()); if (mode == 'x') { cytnx_error_msg(std::filesystem::exists(fnameext), "[ERROR] File %s already exists. Use mode 'w' to overwrite.", - fnameext.c_str()); + fnameext.string().c_str()); } else { cytnx_error_msg(mode != 'w', "[ERROR] Unknown mode '%c' for writing to binary file.", mode); } @@ -208,7 +209,7 @@ namespace cytnx { } catch (const H5::Exception &e) { std::cerr << e.getDetailMsg() << std::endl; cytnx_error_msg(true, "[ERROR] HDF5 path '%s' not found or is not a group in file '%s'.", - grouppath.c_str(), fname.c_str()); + grouppath.c_str(), fname.string().c_str()); } // read data this->from_hdf5(location, datasetname, restore_device); @@ -217,7 +218,7 @@ namespace cytnx { fstream f; f.open(fname, ios::in | ios::binary); if (!f.is_open()) { - cytnx_error_msg(true, "[ERROR] Cannot open file '%s'.\n", fname.c_str()); + cytnx_error_msg(true, "[ERROR] Cannot open file '%s'.\n", fname.string().c_str()); } this->from_binary(f, restore_device); f.close(); @@ -261,6 +262,16 @@ namespace cytnx { "[ERROR] 'device' bit-length mismatch. File: %zu bytes, expected: %zu bytes.\n", datatype.getSize(), sizeof(int)); attr.read(datatype, &device); +#ifndef UNI_GPU + if (device != Device.cpu) { + cytnx_warning_msg(true, + "Cytnx was compiled without CPU support, but Storage in HDF5 file " + "requests to load to GPU with id %d. Loading Storage to CPU memory " + "instead. Use restore_device=false to disable this message.", + device); + device = Device.cpu; + } +#endif } this->data_from_hdf5(dataset, Nelem, dtype, datatype, device); @@ -301,7 +312,10 @@ namespace cytnx { cudaMemcpyHostToDevice)); free(htmp); #else - cytnx_error_msg(true, "ERROR internal fatal error in Load Storage%s", "\n"); + cytnx_error_msg(true, + "[ERROR] Trying to load Storage from HDF5 file to the GPU with id %d, but " + "cytnx was compiled without GPU support!\n", + device); #endif } } @@ -432,7 +446,7 @@ namespace cytnx { ifstream jf; jf.open(fname, ios::ate | ios::binary); if (!jf.is_open()) { - cytnx_error_msg(true, "[ERROR] Cannot open file '%s'.\n", fname.c_str()); + cytnx_error_msg(true, "[ERROR] Cannot open file '%s'.\n", fname.string().c_str()); } Nbytes = jf.tellg(); jf.close(); @@ -453,7 +467,7 @@ namespace cytnx { f.open(fname, ios::in | ios::binary); if (!f.is_open()) { - cytnx_error_msg(true, "[ERROR] Cannot open file '%s'.\n", fname.c_str()); + cytnx_error_msg(true, "[ERROR] Cannot open file '%s'.\n", fname.string().c_str()); } out.data_from_binary(f, Nelem, dtype, device); f.close(); diff --git a/src/tn_algo/MPS.cpp b/src/tn_algo/MPS.cpp index 2ac449620..916306564 100644 --- a/src/tn_algo/MPS.cpp +++ b/src/tn_algo/MPS.cpp @@ -77,7 +77,8 @@ namespace cytnx { } else { // create binary file if (mode == 'x') { cytnx_error_msg(std::filesystem::exists(fname), - "[ERROR] File %s already exists. Use mode 'w' to overwrite.", fname); + "[ERROR] File %s already exists. Use mode 'w' to overwrite.", + fname.string().c_str()); } else { cytnx_error_msg(mode != 'w', "[ERROR] Unknown mode '%c' for writing to binary file.", mode); @@ -91,11 +92,11 @@ namespace cytnx { true, "Missing file extension in fname '%s'. I am adding the extension '.cymps'. This is " "deprecated, please provide the file extension in the future.\n", - fname.c_str()); + fname.string().c_str()); if (mode == 'x') { cytnx_error_msg(std::filesystem::exists(fnameext), "[ERROR] File %s already exists. Use mode 'w' to overwrite.", - fnameext.c_str()); + fnameext.string().c_str()); } else { cytnx_error_msg(mode != 'w', "[ERROR] Unknown mode '%c' for writing to binary file.", mode); @@ -136,7 +137,7 @@ namespace cytnx { } catch (const H5::Exception &e) { std::cerr << e.getDetailMsg() << std::endl; cytnx_error_msg(true, "[ERROR] HDF5 path '%s' not found or is not a group in file '%s'.", - path.c_str(), fname.c_str()); + path.c_str(), fname.string().c_str()); } // read data this->from_hdf5(location, restore_device); @@ -145,7 +146,7 @@ namespace cytnx { fstream f; f.open(fname, std::ios::in | std::ios::binary); if (!f.is_open()) { - cytnx_error_msg(true, "[ERROR] Cannot open file '%s'.\n", fname.c_str()); + cytnx_error_msg(true, "[ERROR] Cannot open file '%s'.\n", fname.string().c_str()); } this->from_binary(f, restore_device); f.close(); From fe622f0d94f7db55ed2e8bdcd1767e724cb58c07 Mon Sep 17 00:00:00 2001 From: Manuel Schneider Date: Sun, 3 May 2026 06:49:15 +0800 Subject: [PATCH 22/40] added support for older versions of hdf5 without native complex types --- CMakeLists.txt | 2 +- include/Type.hpp | 35 +++++++++++++++++++++++++++++++++++ src/Bond.cpp | 2 +- src/Symmetry.cpp | 2 +- src/Tensor.cpp | 2 +- src/UniTensor.cpp | 2 +- src/backend/Storage.cpp | 2 +- src/tn_algo/MPS.cpp | 2 +- 8 files changed, 42 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index bb9f2ef2a..c9950d6df 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -289,7 +289,7 @@ message(STATUS "Found ARPACK_LIB at: ${ARPACK_LIB}") target_link_libraries(cytnx PRIVATE ${ARPACK_LIB}) #HDF5 -find_package(HDF5 2.0.0 REQUIRED COMPONENTS CXX) +find_package(HDF5 1.10.0 REQUIRED COMPONENTS CXX) target_include_directories(cytnx PRIVATE ${HDF5_INCLUDE_DIRS}) target_link_libraries(cytnx PRIVATE ${HDF5_LIBRARIES}) diff --git a/include/Type.hpp b/include/Type.hpp index a2e3adb95..fb2788424 100644 --- a/include/Type.hpp +++ b/include/Type.hpp @@ -424,11 +424,33 @@ namespace cytnx { case Type::Bool: return H5::PredType::NATIVE_HBOOL; +#if H5_VERSION_GE(2, 0, 0) case Type::ComplexDouble: return H5::DataType(H5Tcopy(H5T_NATIVE_DOUBLE_COMPLEX)); case Type::ComplexFloat: return H5::DataType(H5Tcopy(H5T_NATIVE_FLOAT_COMPLEX)); +#else + case Type::ComplexDouble: { + static const H5::CompType ct = [] { + H5::CompType tmp(sizeof(cytnx_complex128)); + tmp.insertMember("r", 0, H5::PredType::NATIVE_DOUBLE); + tmp.insertMember("i", sizeof(double), H5::PredType::NATIVE_DOUBLE); + return tmp; + }(); + return ct; + } + + case Type::ComplexFloat: { + static const H5::CompType ct = [] { + H5::CompType tmp(sizeof(cytnx_complex64)); + tmp.insertMember("r", 0, H5::PredType::NATIVE_FLOAT); + tmp.insertMember("i", sizeof(float), H5::PredType::NATIVE_FLOAT); + return tmp; + }(); + return ct; + } +#endif case Type::Void: cytnx_error_msg(true, "[ERROR] Void dtype cannot be mapped to HDF5%s", "\n"); @@ -452,10 +474,23 @@ namespace cytnx { if (h5_type == H5::PredType::NATIVE_INT16) return Type::Int16; if (h5_type == H5::PredType::NATIVE_UINT16) return Type::Uint16; if (h5_type == H5::PredType::NATIVE_HBOOL) return Type::Bool; +#if H5_VERSION_GE(2, 0, 0) if (h5_type.getClass() == H5T_COMPLEX) { if (h5_type.getSize() == sizeof(cytnx_complex128)) return Type::ComplexDouble; if (h5_type.getSize() == sizeof(cytnx_complex64)) return Type::ComplexFloat; } +#endif + if (h5_type.getClass() == H5T_COMPOUND) { // supporting older versions of HDF5 + H5::CompType ct(h5_type.getId()); + if (ct.getNmembers() == 2) { + H5::DataType m0 = ct.getMemberDataType(0); + H5::DataType m1 = ct.getMemberDataType(1); + if (m0 == m1 && m0.getClass() == H5T_FLOAT) { + if (ct.getSize() == sizeof(cytnx_complex128)) return Type::ComplexDouble; + if (ct.getSize() == sizeof(cytnx_complex64)) return Type::ComplexFloat; + } + } + } cytnx_error_msg(true, "[ERROR] HDF5 DataType cannot be mapped to Cytnx dtype.%s", "\n"); } diff --git a/src/Bond.cpp b/src/Bond.cpp index 09dade78d..50387e3a9 100644 --- a/src/Bond.cpp +++ b/src/Bond.cpp @@ -489,7 +489,7 @@ namespace cytnx { fcpl.setFileSpaceStrategy(H5F_FSPACE_STRATEGY_FSM_AGGR, true, 1); // Persistent free space requires HDF5 1.10.x format or later H5::FileAccPropList fapl; - fapl.setLibverBounds(H5F_LIBVER_V200, H5F_LIBVER_LATEST); + fapl.setLibverBounds(H5F_LIBVER_V110, H5F_LIBVER_LATEST); // open file bool overwrite = false; if (mode == 'w') { // Write new file diff --git a/src/Symmetry.cpp b/src/Symmetry.cpp index 50b51b09f..8c197e0af 100644 --- a/src/Symmetry.cpp +++ b/src/Symmetry.cpp @@ -263,7 +263,7 @@ namespace cytnx { fcpl.setFileSpaceStrategy(H5F_FSPACE_STRATEGY_FSM_AGGR, true, 1); // Persistent free space requires HDF5 1.10.x format or later H5::FileAccPropList fapl; - fapl.setLibverBounds(H5F_LIBVER_V200, H5F_LIBVER_LATEST); + fapl.setLibverBounds(H5F_LIBVER_V110, H5F_LIBVER_LATEST); // open file bool overwrite = false; if (mode == 'w') { // Write new file diff --git a/src/Tensor.cpp b/src/Tensor.cpp index bcb49c75a..e14e6620b 100644 --- a/src/Tensor.cpp +++ b/src/Tensor.cpp @@ -443,7 +443,7 @@ namespace cytnx { fcpl.setFileSpaceStrategy(H5F_FSPACE_STRATEGY_FSM_AGGR, true, 1); // Persistent free space requires HDF5 1.10.x format or later H5::FileAccPropList fapl; - fapl.setLibverBounds(H5F_LIBVER_V200, H5F_LIBVER_LATEST); + fapl.setLibverBounds(H5F_LIBVER_V110, H5F_LIBVER_LATEST); // open file bool overwrite = false; if (mode == 'w') { // Write new file diff --git a/src/UniTensor.cpp b/src/UniTensor.cpp index c9cba3f46..9c4e78cf6 100644 --- a/src/UniTensor.cpp +++ b/src/UniTensor.cpp @@ -81,7 +81,7 @@ namespace cytnx { fcpl.setFileSpaceStrategy(H5F_FSPACE_STRATEGY_FSM_AGGR, true, 1); // Persistent free space requires HDF5 1.10.x format or later H5::FileAccPropList fapl; - fapl.setLibverBounds(H5F_LIBVER_V200, H5F_LIBVER_LATEST); + fapl.setLibverBounds(H5F_LIBVER_V110, H5F_LIBVER_LATEST); // open file bool overwrite = false; if (mode == 'w') { // Write new file diff --git a/src/backend/Storage.cpp b/src/backend/Storage.cpp index f626fdb4c..92607e760 100644 --- a/src/backend/Storage.cpp +++ b/src/backend/Storage.cpp @@ -102,7 +102,7 @@ namespace cytnx { fcpl.setFileSpaceStrategy(H5F_FSPACE_STRATEGY_FSM_AGGR, true, 1); // Persistent free space requires HDF5 1.10.x format or later H5::FileAccPropList fapl; - fapl.setLibverBounds(H5F_LIBVER_V200, H5F_LIBVER_LATEST); + fapl.setLibverBounds(H5F_LIBVER_V110, H5F_LIBVER_LATEST); // open file bool overwrite = false; if (mode == 'w') { // Write new file diff --git a/src/tn_algo/MPS.cpp b/src/tn_algo/MPS.cpp index 916306564..141d77f73 100644 --- a/src/tn_algo/MPS.cpp +++ b/src/tn_algo/MPS.cpp @@ -37,7 +37,7 @@ namespace cytnx { fcpl.setFileSpaceStrategy(H5F_FSPACE_STRATEGY_FSM_AGGR, true, 1); // Persistent free space requires HDF5 1.10.x format or later H5::FileAccPropList fapl; - fapl.setLibverBounds(H5F_LIBVER_V200, H5F_LIBVER_LATEST); + fapl.setLibverBounds(H5F_LIBVER_V110, H5F_LIBVER_LATEST); // open file bool overwrite = false; if (mode == 'w') { // Write new file From 6a0683a1225ff63f3f36b412aedb95fcd2acf98f Mon Sep 17 00:00:00 2001 From: Manuel Schneider Date: Sun, 3 May 2026 18:36:22 +0800 Subject: [PATCH 23/40] removed variable length array --- include/tn_algo/MPS.hpp | 2 ++ src/Tensor.cpp | 6 +++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/include/tn_algo/MPS.hpp b/include/tn_algo/MPS.hpp index 53145bc6e..405f759b1 100644 --- a/include/tn_algo/MPS.hpp +++ b/include/tn_algo/MPS.hpp @@ -307,6 +307,7 @@ namespace cytnx { * @warning HDF5 file format is strongly recommended for compatibility with other libraries, * readability, and future-proofing. * @see Load() + * @warning HDF5 file format is still under development for MPS. */ void Save(const std::filesystem::path &fname, const std::string &path = "/MPS/", const char mode = 'w') const; @@ -329,6 +330,7 @@ namespace cytnx { * @pre The file must be a MPS object which is saved by Save(). * @note For HDF5 file format, one of the file endings ".h5", ".hdf5", ".H5", ".HDF5", ".hdf" * is expected. For binary format, the common file ending for a MPS is ".cymps". + * @warning HDF5 file format is still under development for MPS. */ static MPS Load(const std::filesystem::path &fname, const std::string &path = "/MPS/", const bool restore_device = true); diff --git a/src/Tensor.cpp b/src/Tensor.cpp index e14e6620b..ef30746b9 100644 --- a/src/Tensor.cpp +++ b/src/Tensor.cpp @@ -597,11 +597,11 @@ namespace cytnx { unsigned int dtype = Type.from_hdf5_type(datatype); H5::DataSpace dataspace = dataset.getSpace(); int rank = dataspace.getSimpleExtentNdims(); - hsize_t dims[rank]; - dataspace.getSimpleExtentDims(dims); + std::vector dims(rank); + dataspace.getSimpleExtentDims(dims.data()); auto Nelem = dataspace.getSimpleExtentNpoints(); - this->_impl->_shape = std::vector(dims, dims + rank); + this->_impl->_shape = std::vector(dims.begin(), dims.end()); this->_impl->_mapper = vec_range(this->_impl->_shape.size()); this->_impl->_invmapper = this->_impl->_mapper; this->_impl->_contiguous = true; // HDF5 data is always stored in contiguous layout From b76d918733b3d91430da7ba6fc9cfa34a951b812 Mon Sep 17 00:00:00 2001 From: Manuel Schneider Date: Mon, 4 May 2026 18:13:37 +0800 Subject: [PATCH 24/40] fixed unit tests --- include/Tensor.hpp | 2 +- tests/DenseUniTensor_test.cpp | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/include/Tensor.hpp b/include/Tensor.hpp index f0ad23863..733df529f 100644 --- a/include/Tensor.hpp +++ b/include/Tensor.hpp @@ -388,7 +388,7 @@ namespace cytnx { * @brief Save Tensor to HDF5 file * @param[in] location the HDF5 group where the Tensor will be saved. * @param[in] overwrite overwrite previous Bond information in the location. - * @param[in] name the name of the dataset in the HDF5 file.d in the + * @param[in] name the name of the dataset in the HDF5 file. * @warning This function is only available in C++. Use Save() for saving to file in C++ or * Python. * @see from_hdf5() diff --git a/tests/DenseUniTensor_test.cpp b/tests/DenseUniTensor_test.cpp index 8e3b9faa9..129f83132 100644 --- a/tests/DenseUniTensor_test.cpp +++ b/tests/DenseUniTensor_test.cpp @@ -343,13 +343,15 @@ TEST_F(DenseUniTensorTest, dtype_str_uninit) { EXPECT_ANY_THROW(ut_uninit.dtype_ /*=====test info===== describe:test uten_type_str for dense tensor. ====================*/ -TEST_F(DenseUniTensorTest, uten_type_str) { EXPECT_EQ(utzero345.uten_type_str(), "Dense"); } +TEST_F(DenseUniTensorTest, uten_type_str) { + EXPECT_EQ(utzero345.uten_type_str(), "DenseUniTensor"); +} /*=====test info===== describe:test uten_type for uninitialized tensor. ====================*/ TEST_F(DenseUniTensorTest, uten_type_str_uninit) { - EXPECT_EQ(ut_uninit.uten_type_str(), "Void (un-initialize UniTensor)"); + EXPECT_EQ(ut_uninit.uten_type_str(), "Void (uninitialized UniTensor)"); } TEST_F(DenseUniTensorTest, device_str) { EXPECT_EQ(Spf.device_str(), "cytnx device: CPU"); } From 12609d77ea76ae0b1cc662c675a9949a1c4b2d0b Mon Sep 17 00:00:00 2001 From: Manuel Schneider Date: Tue, 5 May 2026 17:08:10 +0800 Subject: [PATCH 25/40] added hdf5 to dependencies in github workflow --- .github/workflows/ci-cmake_tests.yml | 23 +++++++++++++++++------ .github/workflows/coverity-scan.yml | 9 +++++++-- conda_build/meta.yaml | 5 +++++ 3 files changed, 29 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci-cmake_tests.yml b/.github/workflows/ci-cmake_tests.yml index fae21c44c..afc5f2940 100644 --- a/.github/workflows/ci-cmake_tests.yml +++ b/.github/workflows/ci-cmake_tests.yml @@ -39,22 +39,33 @@ jobs: - name: Install dependencies shell: bash -l {0} run: | - mamba install _openmp_mutex=*=*_llvm cmake make boost git compilers hdf5 numpy mkl mkl-include mkl-service pybind11 libblas=*=*mkl arpack beartype - python -m pip install gcovr - mamba install gtest - mamba install pytest pytest-cov + mamba config append channels conda-forge + + mamba install \ + _openmp_mutex=*=*_llvm \ + cmake make boost git compilers \ + hdf5 numpy mkl mkl-include mkl-service \ + pybind11 libblas=*=*mkl arpack beartype \ + gtest pytest pytest-cov gcovr - name: CPU info shell: bash -l {0} run: lscpu - name: Configure CMake shell: bash -l {0} - run: cmake -S ${{github.workspace}} -B ${{github.workspace}}/build -DCMAKE_INSTALL_PREFIX=/home/runner/work/Cytnx_lib -DUSE_MKL=on -DUSE_HPTT=on -DHPTT_ENABLE_FINE_TUNE=on -DHPTT_ENABLE_AVX=off -DBUILD_PYTHON=on -DCMAKE_EXPORT_COMPILE_COMMANDS=1 -DRUN_TESTS=on + run: | + cmake -S ${{github.workspace}} -B ${{github.workspace}}/build \ + -DCMAKE_INSTALL_PREFIX=/home/runner/work/Cytnx_lib \ + -DCMAKE_PREFIX_PATH=$CONDA_PREFIX \ + -DHDF5_ROOT=$CONDA_PREFIX \ + -DUSE_MKL=on -DUSE_HPTT=on -DHPTT_ENABLE_FINE_TUNE=on \ + -DHPTT_ENABLE_AVX=off -DBUILD_PYTHON=on \ + -DCMAKE_EXPORT_COMPILE_COMMANDS=1 -DRUN_TESTS=on - name: Build shell: bash -l {0} working-directory: ${{github.workspace}}/build run: | cmake --version - cmake --build . -j `nproc` + cmake --build . -j $(nproc) - name: Run CTest shell: bash -l {0} working-directory: ${{github.workspace}}/build diff --git a/.github/workflows/coverity-scan.yml b/.github/workflows/coverity-scan.yml index 6210532d1..09e374bb4 100644 --- a/.github/workflows/coverity-scan.yml +++ b/.github/workflows/coverity-scan.yml @@ -33,10 +33,15 @@ jobs: - name: Install dependencies shell: bash -l {0} run: | - mamba install cmake make boost compilers blas=*=mkl pybind11 arpack + mamba config append channels conda-forge - cmake -S ${{github.workspace}} -B ${{github.workspace}}/build -DCMAKE_INSTALL_PREFIX=/home/runner/works/Cytnx_lib --preset=mkl-cpu + mamba install cmake make boost compilers hdf5 mkl mkl-include arpack + cmake -S ${{github.workspace}} -B ${{github.workspace}}/build \ + -DCMAKE_INSTALL_PREFIX=/home/runner/works/Cytnx_lib \ + -DCMAKE_PREFIX_PATH=$CONDA_PREFIX \ + -DHDF5_ROOT=$CONDA_PREFIX \ + --preset=mkl-cpu -DBUILD_PYTHON=OFF - name: Download Coverity Build Tool shell: bash -l {0} working-directory: ${{github.workspace}}/build diff --git a/conda_build/meta.yaml b/conda_build/meta.yaml index 71794a6fb..d178cdd03 100644 --- a/conda_build/meta.yaml +++ b/conda_build/meta.yaml @@ -10,6 +10,9 @@ source: build: number: 0 + channels: + - conda-forge + - defaults script_env: # Refer to : https://github.com/ccache/ccache/discussions/821#discussioncomment-521209 - CCACHE_NOHASHDIR=1 @@ -39,6 +42,7 @@ requirements: - boost >=1.82.0 - blas=*=mkl # [x86] - blas=*=openblas # [not x86] + - hdf5 >=1.10.0 - gtest - arpack run: @@ -49,6 +53,7 @@ requirements: - graphviz - blas=*=mkl # [x86] - blas=*=openblas # [not x86] + - hdf5 >=1.10.0 - beartype - arpack From 19ac5b06f8159a3b009f1f4968c87b567a475796 Mon Sep 17 00:00:00 2001 From: Manuel Schneider Date: Tue, 5 May 2026 17:16:02 +0800 Subject: [PATCH 26/40] fixed issue --- src/Bond.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Bond.cpp b/src/Bond.cpp index e95a6cfe6..3307ce60e 100644 --- a/src/Bond.cpp +++ b/src/Bond.cpp @@ -610,6 +610,8 @@ namespace cytnx { for (int j = 0; j < Nsym_in; j++) { this->_impl->_syms[j].from_binary(f); } + } else { + this->_impl->_syms.clear(); } } From 13ad2a01c26566ae6cabb14f4bfaccd3be8a4f7e Mon Sep 17 00:00:00 2001 From: Manuel Schneider Date: Wed, 6 May 2026 14:01:44 +0800 Subject: [PATCH 27/40] fixed pybind issues --- pybind/storage_py.cpp | 2 +- pybind/tensor_py.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pybind/storage_py.cpp b/pybind/storage_py.cpp index 7cfc44bc9..2199c77e9 100644 --- a/pybind/storage_py.cpp +++ b/pybind/storage_py.cpp @@ -282,7 +282,7 @@ void storage_binding(py::module &m) { [](const std::string &fname, const unsigned int &dtype, const cytnx_int64 &count, const int device) { return cytnx::Storage::Fromfile(fname, dtype, count, device); }, py::arg("fname"), py::arg("dtype"), py::arg("count") = (cytnx_int64)(-1), - py::arg("device") = cytnx::Device.cpu) + py::arg("device") = (int)cytnx::Device.cpu) .def( py::pickle( diff --git a/pybind/tensor_py.cpp b/pybind/tensor_py.cpp index a45f93f37..44759f0af 100644 --- a/pybind/tensor_py.cpp +++ b/pybind/tensor_py.cpp @@ -322,9 +322,9 @@ void tensor_binding(py::module &m) { .def_static( "Fromfile", [](const std::string &fname, const unsigned int &dtype, const cytnx::cytnx_int64 &count, - const bool restore_device) { return cytnx::Tensor::Load(fname, restore_device); }, + const int device) { return cytnx::Tensor::Fromfile(fname, dtype, count, device); }, py::arg("fname"), py::arg("dtype"), py::arg("count") = cytnx::cytnx_int64(-1), - py::arg("restore_device") = true) + py::arg("device") = (int)cytnx::Device.cpu) .def_static( "from_storage", From 1f96612eeab3e37029320c2956c933c66e4235ae Mon Sep 17 00:00:00 2001 From: Manuel Schneider Date: Sun, 10 May 2026 17:36:46 +0800 Subject: [PATCH 28/40] added io namespace, implemented Save and Load there --- CMakeLists.txt | 1 + include/Bond.hpp | 11 ++- include/Symmetry.hpp | 6 +- include/Tensor.hpp | 24 +++--- include/UniTensor.hpp | 41 +++++----- include/backend/Storage.hpp | 22 +++--- include/cytnx.hpp | 1 + include/io.hpp | 131 ++++++++++++++++++++++++++++++++ include/tn_algo/MPS.hpp | 31 ++++---- pybind/cytnx.cpp | 2 + pybind/io_py.cpp | 119 +++++++++++++++++++++++++++++ src/BlockFermionicUniTensor.cpp | 14 ++-- src/BlockUniTensor.cpp | 12 +-- src/Bond.cpp | 79 +++++++++++-------- src/CMakeLists.txt | 1 + src/DenseUniTensor.cpp | 6 +- src/SparseUniTensor.cpp | 4 +- src/Symmetry.cpp | 8 +- src/Tensor.cpp | 18 ++--- src/UniTensor.cpp | 99 +++++++++++++----------- src/UniTensor_base.cpp | 4 +- src/backend/Storage.cpp | 18 ++--- src/io.cpp | 100 ++++++++++++++++++++++++ src/tn_algo/MPS.cpp | 20 ++--- src/tn_algo/MPS_base.cpp | 2 +- src/tn_algo/RegularMPS.cpp | 2 +- src/tn_algo/iMPS.cpp | 2 +- 27 files changed, 581 insertions(+), 197 deletions(-) create mode 100644 include/io.hpp create mode 100644 pybind/io_py.cpp create mode 100644 src/io.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index c9950d6df..1202d98f9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -345,6 +345,7 @@ IF(BUILD_PYTHON) endif() pybind11_add_module(pycytnx MODULE pybind/cytnx.cpp + pybind/io_py.cpp pybind/generator_py.cpp pybind/storage_py.cpp pybind/tensor_py.cpp diff --git a/include/Bond.hpp b/include/Bond.hpp index fb2e954d2..f5eb2b023 100644 --- a/include/Bond.hpp +++ b/include/Bond.hpp @@ -915,21 +915,24 @@ namespace cytnx { /** * @brief Save Bond to HDF5 file - * @param[in] location the HDF5 group where the Bond will be saved. + * @param[in] location the HDF5 parent group. + * @param[in] name the subgroup in which the Bond will be saved. * @param[in] overwrite overwrite previous Bond information in the location. * @warning This function is only available in C++. Use Save() for saving to file in C++ or * Python. * @see from_hdf5() */ - void to_hdf5(H5::Group &location, const bool overwrite = false) const; + void to_hdf5(H5::Group &location, const std::string &name = "Bond", + const bool overwrite = false) const; /** * @brief Load Bond from HDF5 file (inline) - * @param[in] location the HDF5 group where the Bond will be loaded from. + * @param[in] location the HDF5 parent group. + * @param[in] name the subgroup from which the Bond will be loaded. * @warning This function is only available in C++. Use Load() for loading from file in C++ or * Python. * @see to_hdf5() const */ - void from_hdf5(H5::Group &location); + void from_hdf5(H5::Group &location, const std::string &name = "Bond"); /** * @brief Save Bond to binary file diff --git a/include/Symmetry.hpp b/include/Symmetry.hpp index 966a4b0be..f733b039b 100644 --- a/include/Symmetry.hpp +++ b/include/Symmetry.hpp @@ -589,14 +589,14 @@ namespace cytnx { /** * @brief Save Symmetry to HDF5 file * @param[in] location the HDF5 group where the Symmetry will be saved. - * @param[in] overwrite overwrite previous Bond information in the location. * @param[in] name the name of the attribute in the HDF5 file. + * @param[in] overwrite overwrite previous Bond information in the location. * @warning This function is only available in C++. Use Save() for saving to file in C++ or * Python. * @see from_hdf5() */ - void to_hdf5(H5::Group &location, const bool overwrite = false, - const std::string &name = "Symmetry") const; + void to_hdf5(H5::Group &location, const std::string &name = "Symmetry", + const bool overwrite = false) const; /** * @brief Load Symmetry from HDF5 file (inline) * @param[in] location the HDF5 group where the Symmetry will be loaded from. diff --git a/include/Tensor.hpp b/include/Tensor.hpp index 733df529f..aff17f815 100644 --- a/include/Tensor.hpp +++ b/include/Tensor.hpp @@ -361,14 +361,13 @@ namespace cytnx { * expected. For binary format, the common file ending for a Tensor is ".cytn". */ static cytnx::Tensor Load(const std::filesystem::path &fname, - const std::string &path = "/Tensor", - const bool restore_device = true); + const std::string &path = "/Tensor", bool restore_device = true); /** * @see Load(const std::filesystem::path &fname, const std::string &path, const bool * restore_device) */ static cytnx::Tensor Load(const char *fname, const std::string &path = "/Tensor", - const bool restore_device = true); + bool restore_device = true); /** * @brief Load Tensor from file and overwrite current instance @@ -376,25 +375,24 @@ namespace cytnx { * @see Load() */ void Load_(const std::filesystem::path &fname, const std::string &path = "/Tensor", - const bool restore_device = true); + bool restore_device = true); /** * @see Load_(const std::filesystem::path &fname, const std::string &path, const bool * restore_device) */ - void Load_(const char *fname, const std::string &path = "/Tensor", - const bool restore_device = true); + void Load_(const char *fname, const std::string &path = "/Tensor", bool restore_device = true); /** * @brief Save Tensor to HDF5 file - * @param[in] location the HDF5 group where the Tensor will be saved. - * @param[in] overwrite overwrite previous Bond information in the location. - * @param[in] name the name of the dataset in the HDF5 file. + * @param[in] location the HDF5 parent group. + * @param[in] name the subgroup in which the Tensor will be saved. + * @param[in] overwrite overwrite previous Tensor information in the location. * @warning This function is only available in C++. Use Save() for saving to file in C++ or * Python. * @see from_hdf5() */ - void to_hdf5(H5::Group &location, const bool overwrite = false, - const std::string &name = "Tensor") const; + void to_hdf5(H5::Group &location, const std::string &name = "Tensor", + const bool overwrite = false) const; /** * @brief Load Tensor from HDF5 file (inline) * @param[in] location the HDF5 group where the Tensor will be loaded from. @@ -407,7 +405,7 @@ namespace cytnx { * @see to_hdf5() */ void from_hdf5(H5::Group &location, const std::string &name = "Tensor", - const bool restore_device = true); + bool restore_device = true); /** * @brief Save Tensor to binary file @@ -427,7 +425,7 @@ namespace cytnx { * file format. Use Load() for loading from file in C++ or Python. * @see to_binary() */ - void from_binary(std::istream &f, const bool restore_device = true); + void from_binary(std::istream &f, bool restore_device = true); /** * @brief Save current Tensor to the binary file diff --git a/include/UniTensor.hpp b/include/UniTensor.hpp index 23dcddf63..847f1dc6e 100644 --- a/include/UniTensor.hpp +++ b/include/UniTensor.hpp @@ -448,10 +448,10 @@ namespace cytnx { virtual vec2d &get_itoi(); virtual void to_hdf5_dispatch(H5::Group &location, const bool overwrite) const; - virtual void from_hdf5_dispatch(H5::Group &location, const bool restore_device = true); + virtual void from_hdf5_dispatch(H5::Group &location, bool restore_device = true); virtual void to_binary_dispatch(std::ostream &f) const; - virtual void from_binary_dispatch(std::istream &f, const bool restore_device = true); + virtual void from_binary_dispatch(std::istream &f, bool restore_device = true); virtual ~UniTensor_base(){}; }; @@ -1086,10 +1086,10 @@ namespace cytnx { } void to_hdf5_dispatch(H5::Group &location, const bool overwrite) const; - void from_hdf5_dispatch(H5::Group &location, const bool restore_device = true); + void from_hdf5_dispatch(H5::Group &location, bool restore_device = true); void to_binary_dispatch(std::ostream &f) const; - void from_binary_dispatch(std::istream &f, const bool restore_device = true); + void from_binary_dispatch(std::istream &f, bool restore_device = true); const std::vector &get_qindices(const cytnx_uint64 &bidx) const { cytnx_error_msg(true, "[ERROR] get_qindices can only be unsed on UniTensor with Symmetry.%s", @@ -1762,10 +1762,10 @@ namespace cytnx { cytnx_int16 &at_for_sparse(const std::vector &locator, const cytnx_int16 &aux); void to_hdf5_dispatch(H5::Group &location, const bool overwrite) const; - void from_hdf5_dispatch(H5::Group &location, const bool restore_device = true); + void from_hdf5_dispatch(H5::Group &location, bool restore_device = true); void to_binary_dispatch(std::ostream &f) const; - void from_binary_dispatch(std::istream &f, const bool restore_device = true); + void from_binary_dispatch(std::istream &f, bool restore_device = true); // this will remove the [q_index]-th qnum at [bond_idx]-th Bond! void truncate_(const std::string &label, const cytnx_uint64 &q_index); @@ -2558,10 +2558,10 @@ namespace cytnx { cytnx_int16 &at_for_sparse(const std::vector &locator, const cytnx_int16 &aux); void to_hdf5_dispatch(H5::Group &location, const bool overwrite) const; - void from_hdf5_dispatch(H5::Group &location, const bool restore_device = true); + void from_hdf5_dispatch(H5::Group &location, bool restore_device = true); void to_binary_dispatch(std::ostream &f) const; - void from_binary_dispatch(std::istream &f, const bool restore_device = true); + void from_binary_dispatch(std::istream &f, bool restore_device = true); // this will remove the [q_index]-th qnum at [bond_idx]-th Bond! void truncate_(const std::string &label, const cytnx_uint64 &q_index); @@ -5516,14 +5516,13 @@ namespace cytnx { * expected. For binary format, the common file ending for a UniTensor is ".cytnx". */ static UniTensor Load(const std::filesystem::path &fname, - const std::string &path = "/UniTensor/", - const bool restore_device = true); + const std::string &path = "/UniTensor/", bool restore_device = true); /** * @see Load(const std::filesystem::path &fname, const std::string &path, const bool * restore_device) */ static UniTensor Load(const char *fname, const std::string &path = "/UniTensor/", - const bool restore_device = true); + bool restore_device = true); /** * @brief Load UniTensor from file and overwrite current instance @@ -5532,26 +5531,29 @@ namespace cytnx { * @see Load() */ void Load_(const std::filesystem::path &fname, const std::string &path = "/UniTensor/", - const bool restore_device = true); + bool restore_device = true); /** * @see Load_(const std::filesystem::path &fname, const std::string &path, const bool * restore_device) */ void Load_(const char *fname, const std::string &path = "/UniTensor/", - const bool restore_device = true); + bool restore_device = true); /** * @brief Save UniTensor to HDF5 file - * @param[in] location the HDF5 group where the UniTensor will be saved. - * @param[in] overwrite overwrite previous Bond information in the location. + * @param[in] location the HDF5 parent group. + * @param[in] name the subgroup in which the UniTensor will be saved. + * @param[in] overwrite overwrite previous UniTensor information in the location. * @warning This function is only available in C++. Use Save() for saving to file in C++ or * Python. * @see from_hdf5() */ - void to_hdf5(H5::Group &location, const bool overwrite = false) const; + void to_hdf5(H5::Group &location, const std::string &name = "UniTensor", + const bool overwrite = false) const; /** * @brief Load UniTensor from HDF5 file (inline) - * @param[in] location the HDF5 group where the UniTensor will be loaded from. + * @param[in] location the HDF5 parent group. + * @param[in] name the subgroup from which the UniTensor will be loaded. * @param[in] restore_device whether to try restoring the device on which the data is stored; if * false, the data will be kept on the CPU. Use .to_() to move it to the target device after * loading. @@ -5559,7 +5561,8 @@ namespace cytnx { * Python. * @see to_hdf5() */ - void from_hdf5(H5::Group &location, const bool restore_device = true); + void from_hdf5(H5::Group &location, const std::string &name = "UniTensor", + bool restore_device = true); /** * @brief Save UniTensor to binary file @@ -5579,7 +5582,7 @@ namespace cytnx { * file format. Use Load() for loading from file in C++ or Python. * @see to_binary() */ - void from_binary(std::istream &f, const bool restore_device = true); + void from_binary(std::istream &f, bool restore_device = true); /** * @brief truncate bond dimension of the UniTensor by the given bond label and dimension. diff --git a/include/backend/Storage.hpp b/include/backend/Storage.hpp index 9ccb1fb50..6ffd5d9be 100644 --- a/include/backend/Storage.hpp +++ b/include/backend/Storage.hpp @@ -568,14 +568,13 @@ namespace cytnx { * expected. For binary format, the common file ending for a Storage is ".cyst". */ static cytnx::Storage Load(const std::filesystem::path &fname, - const std::string &path = "/Storage", - const bool restore_device = true); + const std::string &path = "/Storage", bool restore_device = true); /** * @see Load(const std::filesystem::path &fname, const std::string &path, const bool * restore_device) */ static cytnx::Storage Load(const char *fname, const std::string &path = "/Storage", - const bool restore_device = true); + bool restore_device = true); /** * @brief Load Storage from file and overwrite current instance @@ -584,25 +583,24 @@ namespace cytnx { * @see Load() */ void Load_(const std::filesystem::path &fname, const std::string &path = "/Storage", - const bool restore_device = true); + bool restore_device = true); /** * @see Load_(const std::filesystem::path &fname, const std::string &path, const bool * restore_device) */ - void Load_(const char *fname, const std::string &path = "/Storage", - const bool restore_device = true); + void Load_(const char *fname, const std::string &path = "/Storage", bool restore_device = true); /** * @brief Save Storage to HDF5 file * @param[in] location the HDF5 group where the Storage will be saved. + * @param[in] name the name of the dataset in the HDF5 file. * @param[in] overwrite overwrite previous Bond information in the location. - * @param[in] name the name of the dataset in the HDF5 file.d in the * @warning This function is only available in C++. Use Save() for saving to file in C++ or * Python. * @see from_hdf5() */ - void to_hdf5(H5::Group &location, const bool overwrite = false, - const std::string &name = "Storage") const; + void to_hdf5(H5::Group &location, const std::string &name = "Storage", + const bool overwrite = false) const; /** * @brief Load Storage from HDF5 file (inline) * @param[in] location the HDF5 group where the Storage will be loaded from. @@ -615,7 +613,7 @@ namespace cytnx { * @see to_hdf5() */ void from_hdf5(H5::Group &location, const std::string &name = "Storage", - const bool restore_device = true); + bool restore_device = true); /** * @brief Save only the data of the Storage to HDF5 dataset. @@ -664,7 +662,7 @@ namespace cytnx { * file format. Use Load() for loading from file in C++ or Python. * @see to_binary() */ - void from_binary(std::istream &f, const bool restore_device = true); + void from_binary(std::istream &f, bool restore_device = true); /** * @brief Save only the data of the Storage to binary filestream. @@ -683,7 +681,7 @@ namespace cytnx { * @param[in] device the device on which the Storage will be loaded. * @note This function overwrites the current Storage with a new instance. * @warning This function is only available in C++. In Python, use pickle for the same binary - * file format. Use \link Load(const std::filesystem::path &fname, const bool restore_device) + * file format. Use \link Load(const std::filesystem::path &fname, bool restore_device) * Load() \endlink for loading from file in C++ or Python. * @see data_to_binary(std::ostream &f) const */ diff --git a/include/cytnx.hpp b/include/cytnx.hpp index 619adb704..900d51ee2 100644 --- a/include/cytnx.hpp +++ b/include/cytnx.hpp @@ -38,6 +38,7 @@ #include "LinOp.hpp" #include "utils/is.hpp" #include "utils/print.hpp" +#include "io.hpp" #include "tn_algo/MPS.hpp" #include "tn_algo/MPO.hpp" diff --git a/include/io.hpp b/include/io.hpp new file mode 100644 index 000000000..5f2bd9071 --- /dev/null +++ b/include/io.hpp @@ -0,0 +1,131 @@ +#ifndef CYTNX_IO_H_ +#define CYTNX_IO_H_ + +#include +#include + +#include "H5Cpp.h" + +#include "backend/Storage.hpp" +#include "Bond.hpp" +#include "tn_algo/MPS.hpp" +#include "Symmetry.hpp" +#include "Tensor.hpp" +#include "Type.hpp" +#include "UniTensor.hpp" +#include "cytnx_error.hpp" + +namespace cytnx { + /** + @namespace cytnx::io + @brief IO functions for saving an loading to HDF5 file format. + */ + namespace io { + + ///@cond + /** + * @brief Create a group, given a path that can contain subpathes + * @details Opens the group or creates it newly + * @param[in] file root group + * @param[in] path a path that can contain subpathes + * @returns the opened group or a newly created group + */ + H5::Group create_group(H5::Group &file, const std::string &path); + ///@endcond + + /** + * @brief Input/output file access mode for HDF5 files. + */ + enum IoMode : int { + ACC_TRUNC, /**< Open file for reading and writing; overwrites if file exists; creates new file otherwise */ + ACC_NOREPLACE, /**< Open file for reading and writing; fails if file exists; creates new file otherwise */ + ACC_IN, /**< Open file for reading only; fails if file does not exist */ + ACC_INOUT /**< Open file for reading and writing; opens an existing file; creates new file otherwise */ + }; + + /** + * @brief Open HDF5 file + * @details Use file.close() to close the file after use. + * @param[in] fname file name + * @param[in] mode the write mode:\n + * ACC_TRUNC Open file for reading and writing; overwrites if file exists; creates new file otherwise\n + * ACC_NOREPLACE Open file for reading and writing; fails if file exists; creates new file otherwise\n + * ACC_IN Open file for reading only; fails if file does not exist\n + * ACC_INOUT Open file for reading and writing; opens an existing file; creates new file otherwise + * @returns the file handle + * @note The file ending should be one of ".h5", ".hdf5", ".H5", ".HDF5", ".hdf". + */ + H5::H5File open(const std::filesystem::path &fname, IoMode mode = ACC_TRUNC); + + /** + * @brief Classes that can be saved and loaded to/from HDF5 + */ + using savable_class = std::variant; + + /** + * @brief Save object to HDF5 + * @details Can be used with most cytnx classes as objects. + * @param[in] object the cytnx object to be saved + * @param[in] file HDF5 object that should be opened for writing; can be a file or a group + * @param[in] path path inside the file; a path '/foo/bar/Obj' will write the object to the dataset 'Obj' the group '/foo/bar' in the file. + * @param[in] name the name of the object to save to + * @param[in] overwrite if true, overwrite previous data in the file + * @note The file ending should be one of ".h5", ".hdf5", ".H5", ".HDF5", ".hdf". + * @see Load(), open() + */ + void Save(const savable_class &object, H5::Group &file, const std::string &name, const std::string &path = "", bool overwrite = false); + + /** + * @brief Open HDF5 file, save object, and close file. + * @param[in] object the cytnx object to be saved + * @param[in] fname file name + * @param[in] path path inside the file; a path '/foo/bar/Obj' will write the object to the dataset 'Obj' the group '/foo/bar' in the file. + * @param[in] name the name of the attribute, dataset, or group + * @param[in] mode the write mode:\n + * `w` write; creates a new file. If the given file exists, its contents are destroyed.\n + * `x` exclusive; reates a new file. Fails if the given file exists already.\n + * `a` append. opens for writing without overwriting any existing content. Creates the file if it + * doesn't exist.\n + * `u` update; opens for writing. Existing content will be updated(overwritten). + * Creates the file if it doesn't exist. + * @see Save(const savable_class object, hid_t file, const std::string &name), open() + */ + // void Save(const savable_class object, const std::filesystem::path &fname, const std::string &name, const std::string &path = "", const char mode = 'w') { + // cytnx_error_msg(mode == 'r', "Mode 'r' is read-only and not available for writing files.%s", "\n"); + // bool overwrite = (mode == 'u'); + // if (mode == 'a') + // mode = 'u'; + // hid_t file = open(fname, mode); + // Save(object, file, name, path, overwrite); + // Close(file); + // } + + /** + * @brief Load object from HDF5 file and create new instance + * @details Can be used with most cytnx classes as objects. + * @param[in] object an object of the correct type that will be modified (inline) + * @param[in] file HDF5 object that should be opened for writing; can be a file or a group + * @param[in] name the name of the attribute, dataset, or group + * @param[in] path path inside the file; a path /foo/bar/Obj will read the object from the dataset 'Obj' the group '/foo/bar' in the file. + * @param[in] name the name of the attribute, dataset, or group + * @param[in] restore_device whether to try restoring the device on which the data is stored; if false, the data will be kept on the CPU. Only effects objects that contain Tensor or Storage. + * @pre The file must be an object which was saved by Save(). + * @see Save(), open() + */ + void Load(savable_class &object, H5::Group &file, const std::string &name, const std::string &path = "", bool restore_device = true); + + /** + * @brief Open HDF5 file, load object, and close file. + * @param[in] fname file name + * @see Load(const savable_class object, const std::string &name, const std::filesystem::path &fname, const std::string &path, bool restore_device), open() + */ + // void Load(const savable_class object, const std::filesystem::path &fname, const std::string &name, const std::string &path = "", bool restore_device = true) { + // hid_t file = open(fname, 'r'); + // Load(object, file, name, path, restore_device); + // Close(file); + // } + + } // namespace io +} // namespace cytnx + +#endif // CYTNX_IO_H_ \ No newline at end of file diff --git a/include/tn_algo/MPS.hpp b/include/tn_algo/MPS.hpp index 405f759b1..5cedcc5a5 100644 --- a/include/tn_algo/MPS.hpp +++ b/include/tn_algo/MPS.hpp @@ -78,7 +78,7 @@ namespace cytnx { virtual void S_mvright(); virtual void to_binary_dispatch(std::ostream &f); - virtual void from_binary_dispatch(std::istream &f, const bool restore_device = true); + virtual void from_binary_dispatch(std::istream &f, bool restore_device = true); }; // finite size: @@ -120,7 +120,7 @@ namespace cytnx { } void to_binary_dispatch(std::ostream &f); - void from_binary_dispatch(std::istream &f, const bool restore_device = true); + void from_binary_dispatch(std::istream &f, bool restore_device = true); }; // infinite size: @@ -164,7 +164,7 @@ namespace cytnx { } Scalar norm() const; void to_binary_dispatch(std::ostream &f); - void from_binary_dispatch(std::istream &f, const bool restore_device = true); + void from_binary_dispatch(std::istream &f, bool restore_device = true); }; ///@endcond @@ -333,13 +333,13 @@ namespace cytnx { * @warning HDF5 file format is still under development for MPS. */ static MPS Load(const std::filesystem::path &fname, const std::string &path = "/MPS/", - const bool restore_device = true); + bool restore_device = true); /** * @see Load(const std::filesystem::path &fname, const std::string &path, const bool * restore_device) */ static MPS Load(const char *fname, const std::string &path = "/MPS/", - const bool restore_device = true); + bool restore_device = true); /** * @brief Load MPS from file and overwrite current instance @@ -347,26 +347,28 @@ namespace cytnx { * @see Load() */ void Load_(const std::filesystem::path &fname, const std::string &path = "/MPS/", - const bool restore_device = true); + bool restore_device = true); /** * @see Load_(const std::filesystem::path &fname, const std::string &path, const bool * restore_device) */ - void Load_(const char *fname, const std::string &path = "/MPS/", - const bool restore_device = true); + void Load_(const char *fname, const std::string &path = "/MPS/", bool restore_device = true); /** * @brief Save MPS to HDF5 file - * @param[in] location the HDF5 group where the MPS will be saved. - * @param[in] overwrite overwrite previous Bond information in the location. + * @param[in] location the HDF5 parent group. + * @param[in] name the subgroup in which the MPS will be saved. + * @param[in] overwrite overwrite previous MPS information in the location. * @warning This function is only available in C++. Use Save() for saving to file in C++ or * Python. * @see from_hdf5() */ - void to_hdf5(H5::Group &location, const bool overwrite = false) const; + void to_hdf5(H5::Group &location, const std::string &name = "MPS", + const bool overwrite = false) const; /** * @brief Load MPS from HDF5 file (inline) - * @param[in] location the HDF5 group where the MPS will be loaded from. + * @param[in] location the HDF5 parent group. + * @param[in] name the subgroup from which the MPS will be loaded. * @param[in] restore_device whether to try restoring the device on which the data is stored; * if false, the data will be kept on the CPU. Use .to_() to move it to the target device * after loading. @@ -374,7 +376,8 @@ namespace cytnx { * Python. * @see to_hdf5() */ - void from_hdf5(H5::Group &location, const bool restore_device = true); + void from_hdf5(H5::Group &location, const std::string &name = "MPS", + bool restore_device = true); /** * @brief Save MPS to binary file @@ -394,7 +397,7 @@ namespace cytnx { * file format. Use Load() for loading from file in C++ or Python. * @see to_binary() */ - void from_binary(std::istream &f, const bool restore_device = true); + void from_binary(std::istream &f, bool restore_device = true); }; std::ostream &operator<<(std::ostream &os, const MPS &in); diff --git a/pybind/cytnx.cpp b/pybind/cytnx.cpp index 774f4db4f..b74b01a0c 100644 --- a/pybind/cytnx.cpp +++ b/pybind/cytnx.cpp @@ -30,6 +30,7 @@ void symmetry_binding(py::module &m); #else void generator_binding(py::module &m); +void io_binding(py::module &m); void storage_binding(py::module &m); void tensor_binding(py::module &m); @@ -118,6 +119,7 @@ PYBIND11_MODULE(cytnx, m) { // py::arg("out_labels") = std::vector()); generator_binding(m); + io_binding(m); scalar_binding(m); storage_binding(m); tensor_binding(m); diff --git a/pybind/io_py.cpp b/pybind/io_py.cpp new file mode 100644 index 000000000..e43852fc3 --- /dev/null +++ b/pybind/io_py.cpp @@ -0,0 +1,119 @@ +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "cytnx.hpp" +#include "complex.h" +#include "H5Cpp.h" + +namespace py = pybind11; +using namespace pybind11::literals; +using namespace cytnx; + +#ifdef BACKEND_TORCH +#else + +void io_binding(py::module &m) { + // [Submodule io] + pybind11::module m_io = m.def_submodule("io", "Input/Output related."); + + py::enum_(m_io, "IoMode") + .value("ACC_TRUNC", cytnx::io::IoMode::ACC_TRUNC) + .value("ACC_NOREPLACE", cytnx::io::IoMode::ACC_NOREPLACE) + .value("ACC_IN", cytnx::io::IoMode::ACC_IN) + .value("ACC_INOUT", cytnx::io::IoMode::ACC_INOUT) + .export_values(); + + py::class_(m_io, "Group") + // construction + .def(py::init<>()) + .def(py::init(), py::arg("original")) + // Group specific methods + .def("assign", &H5::Group::operator=) + .def("close", &H5::Group::close) + .def("fromClass", &H5::Group::fromClass) + .def("getId", &H5::Group::getId) + + // inherited + // Group management + .def("createGroup", [](const H5::Group &self, const std::string &name, size_t size_hint) + { return self.createGroup(name, size_hint); }, + py::arg("name"), py::arg("size_hint") = 0) + .def("openGroup", [](const H5::Group &self, const std::string &name) + { return self.openGroup(name); }, + py::arg("name")) + + // Attribute management + .def("attrExists", [](const H5::Group &self, const std::string &name) { return self.attrExists(name); }, py::arg("name")) + .def("removeAttr", [](const H5::Group &self, const std::string &name) { self.removeAttr(name); }, py::arg("name")) + .def("renameAttr", [](const H5::Group &self, const std::string &old_n, const std::string &new_n) + { self.renameAttr(old_n, new_n); }, + py::arg("old_name"), py::arg("new_name")) + + // link/object management + .def("moveLink", [](const H5::Group &self, const std::string &src, const std::string &dst) + { self.moveLink(src, dst); }, + py::arg("src_name"), py::arg("dst_name")) + .def("nameExists", [](const H5::Group &self, const std::string &name) { return self.nameExists(name); }, py::arg("name")) + .def("link", [](const H5::Group &self, const std::string &curr, const std::string &next) + { self.link(curr, next); }, + py::arg("curr_name"), py::arg("new_name")) + .def("unlink", [](const H5::Group &self, const std::string &name) { self.unlink(name); }, py::arg("name")) + + // comments + .def("setComment", [](const H5::Group &self, const std::string &name, const std::string &comment) + { self.setComment(name, comment); }, + py::arg("name"), py::arg("comment")) + .def("getComment", [](const H5::Group &self, const std::string &name) + { return self.getComment(name); }, + py::arg("name")) + .def("removeComment", [](const H5::Group &self, const std::string &name) { self.removeComment(name); }, py::arg("name")) + + // helper + .def("getFileName", [](const H5::Group &self) { return self.getFileName(); }) + ; // end of object line + + py::class_(m_io, "H5File") + // construction + .def(py::init<>()) + .def(py::init(), py::arg("original")) + .def(py::init(), py::arg("name"), py::arg("flags")) + // methods + .def("close", &H5::H5File::close) + .def("fromClass", &H5::H5File::fromClass) + .def("getId", &H5::H5File::getId) + .def("getFileSize", &H5::H5File::getFileSize) + .def("getFreeSpace", &H5::H5File::getFreeSpace) + .def("isAccessible", [](H5::H5File &self, const std::string &name) { return self.isAccessible(name); }, py::arg("name") ) + .def("isHdf5", [](H5::H5File &self, const std::string &name) { return self.isHdf5(name); }, py::arg("name") ) + .def("getObjCount", [](H5::H5File &self) { return self.getObjCount(); }) + ; // end of object line + + m_io.def("open", [](const std::filesystem::path &fname, cytnx::io::IoMode mode) + { return cytnx::io::open(fname, mode); }, + py::arg("fname"), py::arg("mode") = cytnx::io::ACC_TRUNC); + // m_io.def("Close", [](H5::H5File file) { cytnx::io::Close(file); }, py::arg("file")); + + m_io.def("Save", &cytnx::io::Save, + py::arg("object"), + py::arg("file"), + py::arg("name"), + py::arg("path") = "", + py::arg("overwrite") = false); + + m_io.def("Load", &cytnx::io::Save, + py::arg("object"), + py::arg("file"), + py::arg("name"), + py::arg("path") = "", + py::arg("restore_device") = true); + +} // io_binding + +#endif \ No newline at end of file diff --git a/src/BlockFermionicUniTensor.cpp b/src/BlockFermionicUniTensor.cpp index 259dfd001..e6833cb90 100644 --- a/src/BlockFermionicUniTensor.cpp +++ b/src/BlockFermionicUniTensor.cpp @@ -2376,9 +2376,9 @@ namespace cytnx { for (int i = 0; i < this->_blocks.size(); i++) { if (this->_signflip[i]) { Tensor block = -this->_blocks[i]; - block.to_hdf5(dir, overwrite, "Tensor" + std::to_string(i)); + block.to_hdf5(dir, "Tensor" + std::to_string(i), overwrite); } else { - this->_blocks[i].to_hdf5(dir, overwrite, "Tensor" + std::to_string(i)); + this->_blocks[i].to_hdf5(dir, "Tensor" + std::to_string(i), overwrite); } } } @@ -2406,16 +2406,16 @@ namespace cytnx { } } - void BlockFermionicUniTensor::from_hdf5_dispatch(H5::Group &location, const bool restore_device) { + void BlockFermionicUniTensor::from_hdf5_dispatch(H5::Group &location, bool restore_device) { this->_is_tag = true; // blocks; read from group this->_blocks.clear(); - if (location.exists("blocks")) { + if (location.nameExists("blocks")) { H5::Group dir = location.openGroup("blocks"); hsize_t idx = 0; while (true) { std::string name = "Tensor" + std::to_string(idx); - if (!dir.exists(name)) { + if (!dir.nameExists(name)) { break; } Tensor block; @@ -2425,7 +2425,7 @@ namespace cytnx { } } // inner_to_outer_idx; read matrix (blocknum x rank) - if (location.exists("block_to_sectors")) { + if (location.nameExists("block_to_sectors")) { H5::DataSet dataset = location.openDataSet("block_to_sectors"); H5::DataSpace dataspace = dataset.getSpace(); cytnx_error_msg(dataspace.getSimpleExtentNdims() != 2, @@ -2490,7 +2490,7 @@ namespace cytnx { } } - void BlockFermionicUniTensor::from_binary_dispatch(std::istream &f, const bool restore_device) { + void BlockFermionicUniTensor::from_binary_dispatch(std::istream &f, bool restore_device) { //[21 Aug 2024] This is a copy from BlockUniTensor; reads signs as well cytnx_uint64 Nblocks; f.read((char *)&Nblocks, sizeof(cytnx_uint64)); diff --git a/src/BlockUniTensor.cpp b/src/BlockUniTensor.cpp index c6b4ef6fc..6390853c9 100644 --- a/src/BlockUniTensor.cpp +++ b/src/BlockUniTensor.cpp @@ -1584,7 +1584,7 @@ namespace cytnx { if (!this->_blocks.empty()) { H5::Group dir = location.createGroup("blocks"); for (int i = 0; i < this->_blocks.size(); i++) { - this->_blocks[i].to_hdf5(dir, overwrite, "Tensor" + std::to_string(i)); + this->_blocks[i].to_hdf5(dir, "Tensor" + std::to_string(i), overwrite); } } // inner_to_outer_idx; write matrix (blocknum x rank) @@ -1611,16 +1611,16 @@ namespace cytnx { } } - void BlockUniTensor::from_hdf5_dispatch(H5::Group &location, const bool restore_device) { + void BlockUniTensor::from_hdf5_dispatch(H5::Group &location, bool restore_device) { this->_is_tag = true; // blocks; read from group this->_blocks.clear(); - if (location.exists("blocks")) { + if (location.nameExists("blocks")) { H5::Group dir = location.openGroup("blocks"); hsize_t idx = 0; while (true) { std::string name = "Tensor" + std::to_string(idx); - if (!dir.exists(name)) { + if (!dir.nameExists(name)) { break; } Tensor block; @@ -1630,7 +1630,7 @@ namespace cytnx { } } // inner_to_outer_idx; read matrix (blocknum x rank) - if (location.exists("block_to_sectors")) { + if (location.nameExists("block_to_sectors")) { H5::DataSet dataset = location.openDataSet("block_to_sectors"); H5::DataSpace dataspace = dataset.getSpace(); cytnx_error_msg(dataspace.getSimpleExtentNdims() != 2, @@ -1685,7 +1685,7 @@ namespace cytnx { } } - void BlockUniTensor::from_binary_dispatch(std::istream &f, const bool restore_device) { + void BlockUniTensor::from_binary_dispatch(std::istream &f, bool restore_device) { cytnx_uint64 Nblocks; f.read((char *)&Nblocks, sizeof(cytnx_uint64)); diff --git a/src/Bond.cpp b/src/Bond.cpp index 50387e3a9..a4e8d3bc8 100644 --- a/src/Bond.cpp +++ b/src/Bond.cpp @@ -4,6 +4,7 @@ #include #include +#include "io.hpp" #include "H5Cpp.h" #include "utils/utils.hpp" @@ -519,11 +520,11 @@ namespace cytnx { if (part.empty()) continue; subpath /= part; groupfolder = subpath.generic_string(); - if (!h5file.exists(groupfolder)) h5file.createGroup(groupfolder); + if (!h5file.nameExists(groupfolder)) h5file.createGroup(groupfolder); } H5::Group location = h5file.openGroup(groupfolder); // write data - this->to_hdf5(location, overwrite); + this->to_hdf5(location, "", overwrite); h5file.close(); return; } else { // create binary file @@ -588,7 +589,7 @@ namespace cytnx { path.c_str(), fname.string().c_str()); } // read data - this->from_hdf5(location); + this->from_hdf5(location, ""); h5file.close(); } else { // load binary fstream f; @@ -604,16 +605,27 @@ namespace cytnx { this->Load_(std::filesystem::path(fname), path); } - void Bond::to_hdf5(H5::Group &location, const bool overwrite) const { + void Bond::to_hdf5(H5::Group &location, const std::string &name, const bool overwrite) const { + H5::Group rootgroup; + if (name.empty()) + rootgroup = location; + else { + if (location.nameExists(name)) { + rootgroup = location.openGroup(name); + } else { + rootgroup = location.createGroup(name); + } + } + if (overwrite) { // delete previous data // remove attributes - if (location.attrExists("dimension")) location.removeAttr("dimension"); - if (location.attrExists("type")) location.removeAttr("type"); + if (rootgroup.attrExists("dimension")) rootgroup.removeAttr("dimension"); + if (rootgroup.attrExists("type")) rootgroup.removeAttr("type"); // remove datasets - if (location.nameExists("degeneracies")) location.unlink("degeneracies"); - if (location.nameExists("quantum_numbers")) location.unlink("quantum_numbers"); + if (rootgroup.nameExists("degeneracies")) rootgroup.unlink("degeneracies"); + if (rootgroup.nameExists("quantum_numbers")) rootgroup.unlink("quantum_numbers"); // remove groups and its contents recursively - if (location.nameExists("symmetries")) location.unlink("symmetries"); + if (rootgroup.nameExists("symmetries")) rootgroup.unlink("symmetries"); } H5::DataType datatype; @@ -625,14 +637,14 @@ namespace cytnx { // dimension, write as attribute auto dim = this->_impl->_dim; datatype = Type.get_hdf5_type(dim); - attr = location.createAttribute("dimension", datatype, H5::DataSpace(H5S_SCALAR)); + attr = rootgroup.createAttribute("dimension", datatype, H5::DataSpace(H5S_SCALAR)); attr.write(datatype, &dim); // type, write as string std::string typestr = bondtype_to_string.at(this->_impl->_type); str_type = H5::StrType(H5::PredType::C_S1, typestr.length() + 1); dataspace = H5::DataSpace(H5S_SCALAR); - attr = location.createAttribute("type", str_type, dataspace); + attr = rootgroup.createAttribute("type", str_type, dataspace); attr.write(str_type, typestr); // degs; write vector @@ -641,8 +653,8 @@ namespace cytnx { hsize_t vecdims[1] = {sectordim}; dataspace = H5::DataSpace(1, vecdims); datatype = Type.get_hdf5_type(this->_impl->_degs[0]); - dataset = location.createDataSet("degeneracies", Type.get_hdf5_type(this->_impl->_degs[0]), - dataspace); + dataset = rootgroup.createDataSet("degeneracies", Type.get_hdf5_type(this->_impl->_degs[0]), + dataspace); dataset.write(this->_impl->_degs.data(), datatype); // label axis dataspace = H5::DataSpace(H5S_SCALAR); @@ -660,7 +672,7 @@ namespace cytnx { hsize_t matdims[2] = {sectordim, qnumdim}; dataspace = H5::DataSpace(2, matdims); datatype = Type.get_hdf5_type(flat[0]); - dataset = location.createDataSet("quantum_numbers", datatype, dataspace); + dataset = rootgroup.createDataSet("quantum_numbers", datatype, dataspace); dataset.write(flat.data(), datatype); // label axes char labels[2][9] = {"sector", "symmetry"}; @@ -671,13 +683,14 @@ namespace cytnx { attr.write(str_type, labels); // symmetries - H5::Group symloc = location.createGroup("symmetries"); + H5::Group symloc = rootgroup.createGroup("symmetries"); for (int i = 0; i < this->_impl->_syms.size(); i++) { - this->_impl->_syms[i].to_hdf5(symloc, overwrite, "Symmetry" + std::to_string(i)); + this->_impl->_syms[i].to_hdf5(symloc, "Symmetry" + std::to_string(i), overwrite); } } } - void Bond::from_hdf5(H5::Group &location) { + void Bond::from_hdf5(H5::Group &location, const std::string &name) { + H5::Group rootgroup = (name.empty() ? location : location.openGroup(name)); H5::DataType datatype; H5::Attribute attr; H5::DataSet dataset; @@ -685,7 +698,7 @@ namespace cytnx { H5::StrType str_type; // type from string - attr = location.openAttribute("type"); + attr = rootgroup.openAttribute("type"); str_type = attr.getStrType(); size_t size = str_type.getSize() - 1; // not including the null terminator std::string typestr; @@ -696,9 +709,9 @@ namespace cytnx { // degs; read vector bool symmetric = false; hssize_t sectordim; - if (location.exists("degeneracies")) { + if (rootgroup.nameExists("degeneracies")) { symmetric = true; - dataset = location.openDataSet("degeneracies"); + dataset = rootgroup.openDataSet("degeneracies"); dataspace = dataset.getSpace(); sectordim = dataspace.getSimpleExtentNpoints(); this->_impl->_degs.resize(sectordim); @@ -716,12 +729,12 @@ namespace cytnx { // qnums; read matrix (dim x qnumdim) hsize_t qnumdim = 0; - if (location.exists("quantum_numbers")) { + if (rootgroup.nameExists("quantum_numbers")) { cytnx_error_msg(!symmetric, - "[ERROR] 'degeneracies' were not found in HDF5 location, but " + "[ERROR] 'degeneracies' were not found, but " "'quantum_numbers' exist. The HDF5 data seems corrupt!%s", "\n"); - dataset = location.openDataSet("quantum_numbers"); + dataset = rootgroup.openDataSet("quantum_numbers"); dataspace = dataset.getSpace(); cytnx_error_msg(dataspace.getSimpleExtentNdims() != 2, "[ERROR] 'quantum_numbers' should be a two-dimensional array. The HDF5 data " @@ -753,17 +766,17 @@ namespace cytnx { } // symmetries - if (location.exists("symmetries")) { - H5::Group symloc = location.openGroup("symmetries"); + if (rootgroup.nameExists("symmetries")) { + H5::Group symloc = rootgroup.openGroup("symmetries"); this->_impl->_syms.clear(); hsize_t idx = 0; while (true) { - std::string name = "Symmetry" + std::to_string(idx); - if (!symloc.attrExists(name) && !symloc.exists(name)) { + std::string symmname = "Symmetry" + std::to_string(idx); + if (!symloc.attrExists(symmname) && !symloc.nameExists(symmname)) { break; } Symmetry sym; - sym.from_hdf5(symloc, name); + sym.from_hdf5(symloc, symmname); this->_impl->_syms.push_back(sym); idx++; } @@ -774,22 +787,22 @@ namespace cytnx { idx, qnumdim); } else { cytnx_error_msg(idx > 0, - "[ERROR] 'degeneracies' and 'quantum_numbers' were not found in HDF5 " - "location, but 'symmetries' exist. The HDF5 data seems corrupt!%s", + "[ERROR] 'degeneracies' and 'quantum_numbers' were not found, but " + "'symmetries' exist. The HDF5 data seems corrupt!%s", "\n"); this->_impl->_syms.clear(); } } else { cytnx_error_msg(symmetric, - "[ERROR] 'degeneracies' and 'quantum_numbers' exist in HDF5 location, but " + "[ERROR] 'degeneracies' and 'quantum_numbers' exist, but " "'symmetries' are missing. The HDF5 data seems corrupt!%s", "\n"); this->_impl->_syms.clear(); } // dim; from attribute - if (location.attrExists("dimension")) { - attr = location.openAttribute("dimension"); + if (rootgroup.attrExists("dimension")) { + attr = rootgroup.openAttribute("dimension"); cytnx_uint64 dimension; datatype = attr.getDataType(); cytnx_error_msg( diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b6bd41c50..69ed78efb 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -26,6 +26,7 @@ target_sources_local(cytnx Physics.cpp Accessor.cpp LinOp.cpp + io.cpp Type.cpp Tensor.cpp diff --git a/src/DenseUniTensor.cpp b/src/DenseUniTensor.cpp index 11c1d0be4..8b67faaaa 100644 --- a/src/DenseUniTensor.cpp +++ b/src/DenseUniTensor.cpp @@ -1288,10 +1288,10 @@ namespace cytnx { H5::Attribute attr = location.createAttribute("directed", datatype, H5::DataSpace(H5S_SCALAR)); attr.write(datatype, &this->_is_tag); - this->_block.to_hdf5(location, overwrite, "Tensor"); + this->_block.to_hdf5(location, "Tensor", overwrite); } - void DenseUniTensor::from_hdf5_dispatch(H5::Group &location, const bool restore_device) { + void DenseUniTensor::from_hdf5_dispatch(H5::Group &location, bool restore_device) { this->_block.from_hdf5(location, "Tensor", restore_device); // check data consistency auto shape = this->_block.shape(); @@ -1333,7 +1333,7 @@ namespace cytnx { } void DenseUniTensor::to_binary_dispatch(std::ostream &f) const { this->_block.to_binary(f); } - void DenseUniTensor::from_binary_dispatch(std::istream &f, const bool restore_device) { + void DenseUniTensor::from_binary_dispatch(std::istream &f, bool restore_device) { this->_block.from_binary(f, restore_device); } diff --git a/src/SparseUniTensor.cpp b/src/SparseUniTensor.cpp index 9721016ab..3faa65356 100644 --- a/src/SparseUniTensor.cpp +++ b/src/SparseUniTensor.cpp @@ -2336,7 +2336,7 @@ namespace cytnx { cytnx_error_msg(true, "[ERROR] Saving SparseUniTensor to HDF5 is not implemented!%s", "\n"); } - void SparseUniTensor::from_hdf5_dispatch(H5::Group &location, const bool restore_device) { + void SparseUniTensor::from_hdf5_dispatch(H5::Group &location, bool restore_device) { cytnx_error_msg(true, "[ERROR] Loading SparseUniTensor from HDF5 is not implemented!%s", "\n"); } @@ -2351,7 +2351,7 @@ namespace cytnx { } } - void SparseUniTensor::from_binary_dispatch(std::istream &f, const bool restore_device) { + void SparseUniTensor::from_binary_dispatch(std::istream &f, bool restore_device) { // cytnx_error_msg(true,"[ERROR] Save for SparseUniTensor is under developing!!%s","\n"); cytnx_uint64 Nblocks; diff --git a/src/Symmetry.cpp b/src/Symmetry.cpp index 8c197e0af..5e692ddfa 100644 --- a/src/Symmetry.cpp +++ b/src/Symmetry.cpp @@ -297,11 +297,11 @@ namespace cytnx { if (part.empty()) continue; subpath /= part; groupfolder = subpath.generic_string(); - if (!h5file.exists(groupfolder)) h5file.createGroup(groupfolder); + if (!h5file.nameExists(groupfolder)) h5file.createGroup(groupfolder); } H5::Group location = h5file.openGroup(groupfolder); // write data - this->to_hdf5(location, overwrite, datasetname); + this->to_hdf5(location, datasetname, overwrite); h5file.close(); return; } else { // create binary file @@ -388,8 +388,8 @@ namespace cytnx { this->Load_(std::filesystem::path(fname), path); } - void cytnx::Symmetry::to_hdf5(H5::Group &location, const bool overwrite, - const std::string &name) const { + void cytnx::Symmetry::to_hdf5(H5::Group &location, const std::string &name, + const bool overwrite) const { if (overwrite) { // delete previous data if (location.attrExists(name)) location.removeAttr(name); } diff --git a/src/Tensor.cpp b/src/Tensor.cpp index ef30746b9..3ae1af517 100644 --- a/src/Tensor.cpp +++ b/src/Tensor.cpp @@ -477,11 +477,11 @@ namespace cytnx { if (part.empty()) continue; subpath /= part; groupfolder = subpath.generic_string(); - if (!h5file.exists(groupfolder)) h5file.createGroup(groupfolder); + if (!h5file.nameExists(groupfolder)) h5file.createGroup(groupfolder); } H5::Group location = h5file.openGroup(groupfolder); // write data - this->to_hdf5(location, overwrite, datasetname); + this->to_hdf5(location, datasetname, overwrite); h5file.close(); return; } else { // create binary file @@ -523,18 +523,18 @@ namespace cytnx { } cytnx::Tensor cytnx::Tensor::Load(const std::filesystem::path &fname, const std::string &path, - const bool restore_device) { + bool restore_device) { Tensor out; out.Load_(fname, path, restore_device); return out; } cytnx::Tensor cytnx::Tensor::Load(const char *fname, const std::string &path, - const bool restore_device) { + bool restore_device) { return cytnx::Tensor::Load(std::filesystem::path(fname), path, restore_device); } void cytnx::Tensor::Load_(const std::filesystem::path &fname, const std::string &path, - const bool restore_device) { + bool restore_device) { std::string ext = fname.extension().string(); if (ext == ".h5" || ext == ".hdf5" || ext == ".H5" || ext == ".HDF5" || ext == ".hdf" || ext == ".HDF") { // load HDF5 @@ -566,11 +566,11 @@ namespace cytnx { f.close(); } } - void cytnx::Tensor::Load_(const char *fname, const std::string &path, const bool restore_device) { + void cytnx::Tensor::Load_(const char *fname, const std::string &path, bool restore_device) { this->Load_(std::filesystem::path(fname), path, restore_device); } - void Tensor::to_hdf5(H5::Group &location, const bool overwrite, const std::string &name) const { + void Tensor::to_hdf5(H5::Group &location, const std::string &name, const bool overwrite) const { if (overwrite) { // delete previous data if (location.nameExists(name)) location.unlink(name); } @@ -591,7 +591,7 @@ namespace cytnx { } } - void Tensor::from_hdf5(H5::Group &location, const std::string &name, const bool restore_device) { + void Tensor::from_hdf5(H5::Group &location, const std::string &name, bool restore_device) { H5::DataSet dataset = location.openDataSet(name); H5::DataType datatype = dataset.getDataType(); unsigned int dtype = Type.from_hdf5_type(datatype); @@ -646,7 +646,7 @@ namespace cytnx { this->storage().to_binary(f); } - void Tensor::from_binary(std::istream &f, const bool restore_device) { + void Tensor::from_binary(std::istream &f, bool restore_device) { unsigned int tmpIDDs; f.read((char *)&tmpIDDs, sizeof(unsigned int)); cytnx_error_msg(tmpIDDs != 888, "[ERROR] the object is not a cytnx tensor!%s", "\n"); diff --git a/src/UniTensor.cpp b/src/UniTensor.cpp index 9c4e78cf6..08fa7ca59 100644 --- a/src/UniTensor.cpp +++ b/src/UniTensor.cpp @@ -111,11 +111,11 @@ namespace cytnx { if (part.empty()) continue; subpath /= part; groupfolder = subpath.generic_string(); - if (!h5file.exists(groupfolder)) h5file.createGroup(groupfolder); + if (!h5file.nameExists(groupfolder)) h5file.createGroup(groupfolder); } H5::Group location = h5file.openGroup(groupfolder); // write data - this->to_hdf5(location, overwrite); + this->to_hdf5(location, "", overwrite); h5file.close(); return; } else { // create binary file @@ -157,17 +157,17 @@ namespace cytnx { } UniTensor UniTensor::Load(const std::filesystem::path &fname, const std::string &path, - const bool restore_device) { + bool restore_device) { UniTensor out; out.Load_(fname, path, restore_device); return out; } - UniTensor UniTensor::Load(const char *fname, const std::string &path, const bool restore_device) { + UniTensor UniTensor::Load(const char *fname, const std::string &path, bool restore_device) { return UniTensor::Load(std::filesystem::path(fname), path, restore_device); } void UniTensor::Load_(const std::filesystem::path &fname, const std::string &path, - const bool restore_device) { + bool restore_device) { std::string ext = fname.extension().string(); if (ext == ".h5" || ext == ".hdf5" || ext == ".H5" || ext == ".HDF5" || ext == ".hdf" || ext == ".HDF") { // load HDF5 @@ -182,7 +182,7 @@ namespace cytnx { path.c_str(), fname.string().c_str()); } // read data - this->from_hdf5(location, restore_device); + this->from_hdf5(location, "", restore_device); h5file.close(); } else { // load binary fstream f; @@ -194,26 +194,38 @@ namespace cytnx { f.close(); } } - void UniTensor::Load_(const char *fname, const std::string &path, const bool restore_device) { + void UniTensor::Load_(const char *fname, const std::string &path, bool restore_device) { this->Load_(std::filesystem::path(fname), path, restore_device); } - void UniTensor::to_hdf5(H5::Group &location, const bool overwrite) const { + void UniTensor::to_hdf5(H5::Group &location, const std::string &name, + const bool overwrite) const { + H5::Group rootgroup; + if (name.empty()) + rootgroup = location; + else { + if (location.nameExists(name)) { + rootgroup = location.openGroup(name); + } else { + rootgroup = location.createGroup(name); + } + } + if (overwrite) { // delete previous data // delete all entries that could be written by one of the implementations; // remove attributes - if (location.attrExists("type")) location.removeAttr("type"); - if (location.attrExists("diagonal")) location.removeAttr("diagonal"); - if (location.attrExists("rowrank")) location.removeAttr("rowrank"); - if (location.attrExists("name")) location.removeAttr("name"); - if (location.attrExists("directed")) location.removeAttr("directed"); + if (rootgroup.attrExists("type")) rootgroup.removeAttr("type"); + if (rootgroup.attrExists("diagonal")) rootgroup.removeAttr("diagonal"); + if (rootgroup.attrExists("rowrank")) rootgroup.removeAttr("rowrank"); + if (rootgroup.attrExists("name")) rootgroup.removeAttr("name"); + if (rootgroup.attrExists("directed")) rootgroup.removeAttr("directed"); // remove datasets - if (location.nameExists("labels")) location.unlink("labels"); - if (location.nameExists("Tensor")) location.unlink("Tensor"); - if (location.nameExists("block_to_sectors")) location.unlink("block_to_sectors"); + if (rootgroup.nameExists("labels")) rootgroup.unlink("labels"); + if (rootgroup.nameExists("Tensor")) rootgroup.unlink("Tensor"); + if (rootgroup.nameExists("block_to_sectors")) rootgroup.unlink("block_to_sectors"); // remove groups and its contents recursively - if (location.nameExists("bonds")) location.unlink("bonds"); - if (location.nameExists("blocks")) location.unlink("blocks"); + if (rootgroup.nameExists("bonds")) rootgroup.unlink("bonds"); + if (rootgroup.nameExists("blocks")) rootgroup.unlink("blocks"); } H5::DataType datatype; @@ -226,25 +238,25 @@ namespace cytnx { std::string type = UTenType.getname(this->_impl->uten_type_id); str_type = H5::StrType(H5::PredType::C_S1, type.length() + 1); dataspace = H5::DataSpace(H5S_SCALAR); - attr = location.createAttribute("type", str_type, dataspace); + attr = rootgroup.createAttribute("type", str_type, dataspace); attr.write(str_type, type); // is_diag, write as attribute only for diagonal tensors if (this->_impl->_is_diag) { datatype = Type.get_hdf5_type(this->_impl->_is_diag); - attr = location.createAttribute("diagonal", datatype, H5::DataSpace(H5S_SCALAR)); + attr = rootgroup.createAttribute("diagonal", datatype, H5::DataSpace(H5S_SCALAR)); attr.write(datatype, &this->_impl->_is_diag); } // rowrank, write as attribute datatype = Type.get_hdf5_type(this->_impl->_rowrank); - attr = location.createAttribute("rowrank", datatype, H5::DataSpace(H5S_SCALAR)); + attr = rootgroup.createAttribute("rowrank", datatype, H5::DataSpace(H5S_SCALAR)); attr.write(datatype, &this->_impl->_rowrank); // name, write as string attribute str_type = H5::StrType(H5::PredType::C_S1, this->_impl->_name.length() + 1); dataspace = H5::DataSpace(H5S_SCALAR); - attr = location.createAttribute("name", str_type, dataspace); + attr = rootgroup.createAttribute("name", str_type, dataspace); attr.write(str_type, this->_impl->_name); // labels; write as string vector @@ -252,7 +264,7 @@ namespace cytnx { hsize_t vecdims[1] = {this->_impl->_labels.size()}; dataspace = H5::DataSpace(1, vecdims); str_type = H5::StrType(H5::PredType::C_S1, H5T_VARIABLE); - dataset = location.createDataSet("labels", str_type, dataspace); + dataset = rootgroup.createDataSet("labels", str_type, dataspace); std::vector c_strings; // H5 needs cstrings std::string symstring; for (const auto &label : this->_impl->_labels) { @@ -263,24 +275,24 @@ namespace cytnx { // bonds; write in group if (!this->_impl->_bonds.empty()) { - H5::Group dir = location.createGroup("bonds"); + H5::Group dir = rootgroup.createGroup("bonds"); for (int i = 0; i < this->_impl->_bonds.size(); i++) { - H5::Group bondgroup = dir.createGroup("Bond" + std::to_string(i)); - this->_impl->_bonds[i].to_hdf5(bondgroup, overwrite); + this->_impl->_bonds[i].to_hdf5(dir, "Bond" + std::to_string(i), overwrite); } } - this->_impl->to_hdf5_dispatch(location, overwrite); + this->_impl->to_hdf5_dispatch(rootgroup, overwrite); } - void UniTensor::from_hdf5(H5::Group &location, const bool restore_device) { + void UniTensor::from_hdf5(H5::Group &location, const std::string &name, bool restore_device) { + H5::Group rootgroup = (name.empty() ? location : location.openGroup(name)); H5::DataType datatype; H5::Attribute attr; H5::StrType str_type; size_t size; // type, read from attribute - attr = location.openAttribute("type"); + attr = rootgroup.openAttribute("type"); str_type = attr.getStrType(); size = str_type.getSize() - 1; // remove the null terminator std::string utenname; @@ -289,8 +301,8 @@ namespace cytnx { this->Init(utenname); // is_diag, read from attribute - if (location.attrExists("diagonal")) { - H5::Attribute attr = location.openAttribute("diagonal"); + if (rootgroup.attrExists("diagonal")) { + H5::Attribute attr = rootgroup.openAttribute("diagonal"); datatype = attr.getDataType(); cytnx_error_msg( datatype.getSize() != Type.get_hdf5_type(this->_impl->_is_diag).getSize(), @@ -302,7 +314,7 @@ namespace cytnx { } // rowrank, read from attribute - attr = location.openAttribute("rowrank"); + attr = rootgroup.openAttribute("rowrank"); datatype = attr.getDataType(); cytnx_error_msg( datatype.getSize() != Type.get_hdf5_type(this->_impl->_rowrank).getSize(), @@ -311,8 +323,8 @@ namespace cytnx { attr.read(datatype, &this->_impl->_rowrank); // name, read from string attribute - if (location.attrExists("name")) { - attr = location.openAttribute("name"); + if (rootgroup.attrExists("name")) { + attr = rootgroup.openAttribute("name"); str_type = attr.getStrType(); size = str_type.getSize() - 1; // remove the null terminator if (size > 0) { @@ -327,8 +339,8 @@ namespace cytnx { // labels; read from string vector this->_impl->_labels.clear(); - if (location.exists("labels")) { - H5::DataSet dataset = location.openDataSet("labels"); + if (rootgroup.nameExists("labels")) { + H5::DataSet dataset = rootgroup.openDataSet("labels"); H5::DataSpace dataspace = dataset.getSpace(); hsize_t dims[1]; dataspace.getSimpleExtentDims(dims); @@ -346,17 +358,16 @@ namespace cytnx { // bonds; read from group this->_impl->_bonds.clear(); - if (location.exists("bonds")) { - H5::Group dir = location.openGroup("bonds"); + if (rootgroup.nameExists("bonds")) { + H5::Group dir = rootgroup.openGroup("bonds"); hsize_t idx = 0; while (true) { - std::string name = "Bond" + std::to_string(idx); - if (!dir.exists(name)) { + std::string bondname = "Bond" + std::to_string(idx); + if (!dir.nameExists(bondname)) { break; } - H5::Group bondgroup = dir.openGroup(name); Bond bond; - bond.from_hdf5(bondgroup); + bond.from_hdf5(dir, bondname); this->_impl->_bonds.push_back(bond); idx++; } @@ -372,7 +383,7 @@ namespace cytnx { this->_impl->_bonds.clear(); } - this->_impl->from_hdf5_dispatch(location, restore_device); + this->_impl->from_hdf5_dispatch(rootgroup, restore_device); this->_impl->_is_braket_form = this->_impl->_update_braket(); } @@ -426,7 +437,7 @@ namespace cytnx { this->_impl->to_binary_dispatch(f); } - void UniTensor::from_binary(std::istream &f, const bool restore_device) { + void UniTensor::from_binary(std::istream &f, bool restore_device) { unsigned int tmpIDDs; f.read((char *)&tmpIDDs, sizeof(unsigned int)); cytnx_error_msg(tmpIDDs != 555, "[ERROR] the object is not a cytnx UniTensor!%s", "\n"); diff --git a/src/UniTensor_base.cpp b/src/UniTensor_base.cpp index e2529b378..8c2ea753b 100644 --- a/src/UniTensor_base.cpp +++ b/src/UniTensor_base.cpp @@ -696,7 +696,7 @@ namespace cytnx { true, "[ERROR] fatal internal, cannot call on an un-initialized UniTensor_base%s", "\n"); } - void UniTensor_base::from_hdf5_dispatch(H5::Group &location, const bool restore_device) { + void UniTensor_base::from_hdf5_dispatch(H5::Group &location, bool restore_device) { cytnx_error_msg(true, "[ERROR] Loading BlockUniTensor from HDF5 is not implemented yet!%s", "\n"); } @@ -706,7 +706,7 @@ namespace cytnx { true, "[ERROR] fatal internal, cannot call on an un-initialized UniTensor_base%s", "\n"); } - void UniTensor_base::from_binary_dispatch(std::istream &f, const bool restore_device) { + void UniTensor_base::from_binary_dispatch(std::istream &f, bool restore_device) { cytnx_error_msg( true, "[ERROR] fatal internal, cannot call on an un-initialized UniTensor_base%s", "\n"); } diff --git a/src/backend/Storage.cpp b/src/backend/Storage.cpp index 92607e760..b137f3bbd 100644 --- a/src/backend/Storage.cpp +++ b/src/backend/Storage.cpp @@ -136,11 +136,11 @@ namespace cytnx { if (part.empty()) continue; subpath /= part; groupfolder = subpath.generic_string(); - if (!h5file.exists(groupfolder)) h5file.createGroup(groupfolder); + if (!h5file.nameExists(groupfolder)) h5file.createGroup(groupfolder); } H5::Group location = h5file.openGroup(groupfolder); // write data - this->to_hdf5(location, overwrite, datasetname); + this->to_hdf5(location, datasetname, overwrite); h5file.close(); return; } else { // create binary file @@ -182,17 +182,17 @@ namespace cytnx { } Storage Storage::Load(const std::filesystem::path &fname, const std::string &path, - const bool restore_device) { + bool restore_device) { Storage out; out.Load_(fname, path, restore_device); return out; } - Storage Storage::Load(const char *fname, const std::string &path, const bool restore_device) { + Storage Storage::Load(const char *fname, const std::string &path, bool restore_device) { return Storage::Load(std::filesystem::path(fname), path, restore_device); } void Storage::Load_(const std::filesystem::path &fname, const std::string &path, - const bool restore_device) { + bool restore_device) { std::string ext = fname.extension().string(); if (ext == ".h5" || ext == ".hdf5" || ext == ".H5" || ext == ".HDF5" || ext == ".hdf" || ext == ".HDF") { // load HDF5 @@ -224,11 +224,11 @@ namespace cytnx { f.close(); } } - void Storage::Load_(const char *fname, const std::string &path, const bool restore_device) { + void Storage::Load_(const char *fname, const std::string &path, bool restore_device) { this->Load_(std::filesystem::path(fname), path, restore_device); } - void Storage::to_hdf5(H5::Group &location, const bool overwrite, const std::string &name) const { + void Storage::to_hdf5(H5::Group &location, const std::string &name, const bool overwrite) const { if (overwrite) { // delete previous data if (location.nameExists(name)) location.unlink(name); } @@ -246,7 +246,7 @@ namespace cytnx { } } - void Storage::from_hdf5(H5::Group &location, const std::string &name, const bool restore_device) { + void Storage::from_hdf5(H5::Group &location, const std::string &name, bool restore_device) { H5::DataSet dataset = location.openDataSet(name); H5::DataType datatype = dataset.getDataType(); unsigned int dtype = Type.from_hdf5_type(datatype); @@ -333,7 +333,7 @@ namespace cytnx { this->data_to_binary(f); } - void Storage::from_binary(std::istream &f, const bool restore_device) { + void Storage::from_binary(std::istream &f, bool restore_device) { unsigned long long Nelem; unsigned int dtype; int device; diff --git a/src/io.cpp b/src/io.cpp new file mode 100644 index 000000000..fb3c19625 --- /dev/null +++ b/src/io.cpp @@ -0,0 +1,100 @@ +#include "io.hpp" + +#include +#include +#include + +#include "H5Cpp.h" + +namespace cytnx { + namespace io { + + H5::Group create_group(H5::Group &file, const std::string &path) { + std::filesystem::path grouppath(path); + std::filesystem::path subpath; + std::string groupfolder = "/"; + for (const auto &part : grouppath) { + if (part.empty()) continue; + subpath /= part; + groupfolder = subpath.generic_string(); // use generic_string() to avoid incompatibilites between file systems + if (!file.nameExists(groupfolder)) file.createGroup(groupfolder); + } + H5::Group location = file.openGroup(groupfolder); + return location; + } + + H5::H5File open(const std::filesystem::path &fname, IoMode mode) { + H5::H5File h5file; + + // Enable reuse of space after data is deleted; + // Set the strategy: FSM_AGGR is standard for free-space management + // Parameters: strategy, persist (true), threshold (default 1: track all free-space + // sections) + H5::FileCreatPropList fcpl; + fcpl.setFileSpaceStrategy(H5F_FSPACE_STRATEGY_FSM_AGGR, true, 1); + + // Persistent free space requires HDF5 1.10.x format or later + H5::FileAccPropList fapl; + fapl.setLibverBounds(H5F_LIBVER_V110, H5F_LIBVER_LATEST); + + // open file + switch (mode) { + case ACC_TRUNC: + h5file = H5::H5File(fname, H5F_ACC_TRUNC, fcpl, fapl); + break; + case ACC_NOREPLACE: + h5file = H5::H5File(fname, H5F_ACC_EXCL, fcpl, fapl); + break; + case ACC_IN: + h5file = H5::H5File(fname, H5F_ACC_RDONLY, fcpl, fapl); + break; + case ACC_INOUT: + if (std::filesystem::exists(fname)) { + h5file = H5::H5File(fname, H5F_ACC_RDWR, H5::FileCreatPropList::DEFAULT, fapl); + } else { + h5file = H5::H5File(fname, H5F_ACC_EXCL, fcpl, fapl); + } + break; + default: + cytnx_error_msg(true, "[ERROR] Unknown mode '%d' for writing to HDF5 file.", mode); + } + + return h5file; + } + + // void close(H5::H5File &file) { + // file.close(); + // } + + void Save(const savable_class &object, H5::Group &file, const std::string &name, const std::string &path, bool overwrite) { + std::visit([&](const auto &concreteObj) { + H5::Group location = create_group(file, path); + concreteObj.to_hdf5(location, name, overwrite); + }, object); + } + + void Load(savable_class &object, H5::Group &file, const std::string &name, const std::string &path, bool restore_device) { + std::visit([&](auto &concreteObj) { + // open path + H5::Group location; + if (path.empty()) + location = file; + else { + try { + location = file.openGroup(path); + } catch (const H5::Exception &e) { + std::cerr << e.getDetailMsg() << std::endl; + cytnx_error_msg(true, "[ERROR] HDF5 path '%s' not found or is not a group.\n", path.c_str()); + } + } + if constexpr (requires { concreteObj.from_hdf5(location, name, restore_device); }) { + // Class supports restore_device + concreteObj.from_hdf5(location, name, restore_device); + } else { + concreteObj.from_hdf5(location, name); + } + }, object); + } + + } // namespace io +} // namespace cytnx diff --git a/src/tn_algo/MPS.cpp b/src/tn_algo/MPS.cpp index 141d77f73..0ff9c9a96 100644 --- a/src/tn_algo/MPS.cpp +++ b/src/tn_algo/MPS.cpp @@ -67,11 +67,11 @@ namespace cytnx { if (part.empty()) continue; subpath /= part; groupfolder = subpath.generic_string(); - if (!h5file.exists(groupfolder)) h5file.createGroup(groupfolder); + if (!h5file.nameExists(groupfolder)) h5file.createGroup(groupfolder); } H5::Group location = h5file.openGroup(groupfolder); // write data - this->to_hdf5(location, overwrite); + this->to_hdf5(location, "MPS", overwrite); h5file.close(); return; } else { // create binary file @@ -115,17 +115,17 @@ namespace cytnx { } MPS MPS::Load(const std::filesystem::path &fname, const std::string &path, - const bool restore_device) { + bool restore_device) { MPS out; out.Load_(fname, path, restore_device); return out; } - MPS MPS::Load(const char *fname, const std::string &path, const bool restore_device) { + MPS MPS::Load(const char *fname, const std::string &path, bool restore_device) { return MPS::Load(std::filesystem::path(fname), path, restore_device); } void MPS::Load_(const std::filesystem::path &fname, const std::string &path, - const bool restore_device) { + bool restore_device) { std::string ext = fname.extension().string(); if (ext == ".h5" || ext == ".hdf5" || ext == ".H5" || ext == ".HDF5" || ext == ".hdf" || ext == ".HDF") { // load HDF5 @@ -140,7 +140,7 @@ namespace cytnx { path.c_str(), fname.string().c_str()); } // read data - this->from_hdf5(location, restore_device); + this->from_hdf5(location, "", restore_device); h5file.close(); } else { // load binary fstream f; @@ -152,14 +152,14 @@ namespace cytnx { f.close(); } } - void MPS::Load_(const char *fname, const std::string &path, const bool restore_device) { + void MPS::Load_(const char *fname, const std::string &path, bool restore_device) { this->Load_(std::filesystem::path(fname), path, restore_device); } - void MPS::to_hdf5(H5::Group &location, const bool overwrite) const { + void MPS::to_hdf5(H5::Group &location, const std::string &name, const bool overwrite) const { cytnx_error_msg(true, "[ERROR] Saving MPS to HDF5 is not implemented yet!%s", "\n"); } - void MPS::from_hdf5(H5::Group &location, const bool restore_device) { + void MPS::from_hdf5(H5::Group &location, const std::string &name, bool restore_device) { cytnx_error_msg(true, "[ERROR] Loading MPS from HDF5 is not implemented yet!%s", "\n"); } @@ -181,7 +181,7 @@ namespace cytnx { this->_impl->to_binary_dispatch(f); } - void MPS::from_binary(std::istream &f, const bool restore_device) { + void MPS::from_binary(std::istream &f, bool restore_device) { unsigned int tmpIDDs; f.read((char *)&tmpIDDs, sizeof(unsigned int)); cytnx_error_msg(tmpIDDs != 109, "[ERROR] the object is not a cytnx MPS!%s", "\n"); diff --git a/src/tn_algo/MPS_base.cpp b/src/tn_algo/MPS_base.cpp index 32b7a30f5..b305fd966 100644 --- a/src/tn_algo/MPS_base.cpp +++ b/src/tn_algo/MPS_base.cpp @@ -79,7 +79,7 @@ namespace cytnx { true, "[ERROR] MPS_Base should not be called. Please initialize the MPS first.%s", "\n"); } - void MPS_impl::from_binary_dispatch(std::istream &f, const bool restore_device) { + void MPS_impl::from_binary_dispatch(std::istream &f, bool restore_device) { cytnx_error_msg( true, "[ERROR] MPS_Base should not be called. Please initialize the MPS first.%s", "\n"); } diff --git a/src/tn_algo/RegularMPS.cpp b/src/tn_algo/RegularMPS.cpp index 4285f6687..a74db66a7 100644 --- a/src/tn_algo/RegularMPS.cpp +++ b/src/tn_algo/RegularMPS.cpp @@ -275,7 +275,7 @@ namespace cytnx { this->_TNs[i].to_binary(f); } } - void RegularMPS::from_binary_dispatch(std::istream &f, const bool restore_device) { + void RegularMPS::from_binary_dispatch(std::istream &f, bool restore_device) { cytnx_uint64 N; f.read((char *)&N, sizeof(cytnx_uint64)); diff --git a/src/tn_algo/iMPS.cpp b/src/tn_algo/iMPS.cpp index 081a02876..9492dfa04 100644 --- a/src/tn_algo/iMPS.cpp +++ b/src/tn_algo/iMPS.cpp @@ -69,7 +69,7 @@ namespace cytnx { this->_TNs[i].to_binary(f); } } - void iMPS::from_binary_dispatch(std::istream &f, const bool restore_device) { + void iMPS::from_binary_dispatch(std::istream &f, bool restore_device) { cytnx_uint64 N; f.read((char *)&N, sizeof(cytnx_uint64)); From 5be63fe71a27eec8440ec35bec6325a8a97fa3d3 Mon Sep 17 00:00:00 2001 From: Manuel Schneider Date: Fri, 22 May 2026 10:00:27 +0800 Subject: [PATCH 29/40] added save_attribute --- include/Bond.hpp | 10 +- include/Symmetry.hpp | 10 +- include/Tensor.hpp | 33 +--- include/Type.hpp | 24 ++- include/UniTensor.hpp | 26 +-- include/backend/Storage.hpp | 10 +- include/io.hpp | 134 +++++++++++---- include/tn_algo/MPS.hpp | 10 +- pybind/io_py.cpp | 162 +++++++++++------- src/BlockFermionicUniTensor.cpp | 16 +- src/BlockUniTensor.cpp | 16 +- src/Bond.cpp | 24 +-- src/DenseUniTensor.cpp | 14 +- src/SparseUniTensor.cpp | 4 +- src/Symmetry.cpp | 20 +-- src/Tensor.cpp | 27 ++- src/UniTensor.cpp | 79 +++------ src/UniTensor_base.cpp | 4 +- src/backend/Storage.cpp | 20 +-- .../algo_internal_gpu/cuSort_internal.cuh | 2 +- src/io.cpp | 135 +++++++++++---- src/tn_algo/MPS.cpp | 14 +- 22 files changed, 468 insertions(+), 326 deletions(-) diff --git a/include/Bond.hpp b/include/Bond.hpp index f5eb2b023..0e9c87797 100644 --- a/include/Bond.hpp +++ b/include/Bond.hpp @@ -915,24 +915,24 @@ namespace cytnx { /** * @brief Save Bond to HDF5 file - * @param[in] location the HDF5 parent group. + * @param[in] container the HDF5 parent group. * @param[in] name the subgroup in which the Bond will be saved. - * @param[in] overwrite overwrite previous Bond information in the location. + * @param[in] overwrite overwrite previous Bond information in the container. * @warning This function is only available in C++. Use Save() for saving to file in C++ or * Python. * @see from_hdf5() */ - void to_hdf5(H5::Group &location, const std::string &name = "Bond", + void to_hdf5(H5::Group &container, const std::string &name = "Bond", const bool overwrite = false) const; /** * @brief Load Bond from HDF5 file (inline) - * @param[in] location the HDF5 parent group. + * @param[in] container the HDF5 parent group. * @param[in] name the subgroup from which the Bond will be loaded. * @warning This function is only available in C++. Use Load() for loading from file in C++ or * Python. * @see to_hdf5() const */ - void from_hdf5(H5::Group &location, const std::string &name = "Bond"); + void from_hdf5(H5::Group &container, const std::string &name = "Bond"); /** * @brief Save Bond to binary file diff --git a/include/Symmetry.hpp b/include/Symmetry.hpp index f733b039b..8f29a2a39 100644 --- a/include/Symmetry.hpp +++ b/include/Symmetry.hpp @@ -588,24 +588,24 @@ namespace cytnx { /** * @brief Save Symmetry to HDF5 file - * @param[in] location the HDF5 group where the Symmetry will be saved. + * @param[in] container the HDF5 group where the Symmetry will be saved. * @param[in] name the name of the attribute in the HDF5 file. - * @param[in] overwrite overwrite previous Bond information in the location. + * @param[in] overwrite overwrite previous Bond information in the container. * @warning This function is only available in C++. Use Save() for saving to file in C++ or * Python. * @see from_hdf5() */ - void to_hdf5(H5::Group &location, const std::string &name = "Symmetry", + void to_hdf5(H5::Group &container, const std::string &name = "Symmetry", const bool overwrite = false) const; /** * @brief Load Symmetry from HDF5 file (inline) - * @param[in] location the HDF5 group where the Symmetry will be loaded from. + * @param[in] container the HDF5 group where the Symmetry will be loaded from. * @param[in] name the name of the attribute in the HDF5 file. * @warning This function is only available in C++. Use Load() for loading from file in C++ or * Python. * @see to_hdf5() */ - void from_hdf5(H5::Group &location, const std::string &name = "Symmetry"); + void from_hdf5(H5::Group &container, const std::string &name = "Symmetry"); /** * @brief Save Symmetry to binary file diff --git a/include/Tensor.hpp b/include/Tensor.hpp index aff17f815..b93911417 100644 --- a/include/Tensor.hpp +++ b/include/Tensor.hpp @@ -384,18 +384,18 @@ namespace cytnx { /** * @brief Save Tensor to HDF5 file - * @param[in] location the HDF5 parent group. + * @param[in] container the HDF5 parent group. * @param[in] name the subgroup in which the Tensor will be saved. - * @param[in] overwrite overwrite previous Tensor information in the location. + * @param[in] overwrite overwrite previous Tensor information in the container. * @warning This function is only available in C++. Use Save() for saving to file in C++ or * Python. * @see from_hdf5() */ - void to_hdf5(H5::Group &location, const std::string &name = "Tensor", + void to_hdf5(H5::Group &container, const std::string &name = "Tensor", const bool overwrite = false) const; /** * @brief Load Tensor from HDF5 file (inline) - * @param[in] location the HDF5 group where the Tensor will be loaded from. + * @param[in] container the HDF5 group where the Tensor will be loaded from. * @param[in] name the name of the dataset in the HDF5 file. * @param[in] restore_device whether to try restoring the device on which the data is stored; if * false, the data will be kept on the CPU. Use .to_() to move it to the target device after @@ -404,7 +404,7 @@ namespace cytnx { * Python. * @see to_hdf5() */ - void from_hdf5(H5::Group &location, const std::string &name = "Tensor", + void from_hdf5(H5::Group &container, const std::string &name = "Tensor", bool restore_device = true); /** @@ -580,25 +580,8 @@ namespace cytnx { // } //@} - // This mechanism is to remove the 'void' type from Type_list. Taking advantage of it - // appearing first ... - - /// @cond - struct internal { - template - struct exclude_first; - - template - struct exclude_first> { - using type = std::variant; - }; - }; // internal - /// @endcond - // std::variant of pointers to Type_list, without void .... - using pointer_types = - make_variant_from_transform_t::type, - std::add_pointer>; + using pointer_types = make_variant_from_transform_t; // convert this->_impl->_storage._impl->Mem to a typed variant of pointers, excluding void* pointer_types ptr() const; @@ -618,9 +601,7 @@ namespace cytnx { #ifdef UNI_GPU // std::variant of pointers to Type_list_gpu, without void .... - using gpu_pointer_types = - make_variant_from_transform_t::type, - std::add_pointer>; + using gpu_pointer_types = make_variant_from_transform_t; // convert this->_impl->_storage->Mem to a typed variant of pointers, excluding void* gpu_pointer_types gpu_ptr() const; diff --git a/include/Type.hpp b/include/Type.hpp index fb2788424..f52cb065a 100644 --- a/include/Type.hpp +++ b/include/Type.hpp @@ -75,7 +75,6 @@ namespace cytnx { return index_in_tuple_helper(); } } - } // namespace internal // helper metafunction to transform a variant into another variant via a @@ -156,6 +155,29 @@ namespace cytnx { cytnx_uint64, cytnx_int32, cytnx_uint32, cytnx_int16, cytnx_uint16, cytnx_bool>; #endif + namespace internal { + /// @cond + // This mechanism is to remove the 'void' type from Type_list. Taking advantage of it + // appearing first ... + struct truncate_variant { + template + struct exclude_first; + + template + struct exclude_first> { + using type = std::variant; + }; + }; // truncate_variant + /// @endcond + } // namespace internal + + // the list of supported Scalar types. Removes void because it cannot be used in visit + using Scalar_list = internal::truncate_variant::exclude_first::type; + +#ifdef UNI_GPU + using Scalar_list_gpu = internal::truncate_variant::exclude_first::type; +#endif + // The number of supported types constexpr int N_Type = std::variant_size_v; constexpr int N_fType = 5; diff --git a/include/UniTensor.hpp b/include/UniTensor.hpp index 847f1dc6e..fef132f2b 100644 --- a/include/UniTensor.hpp +++ b/include/UniTensor.hpp @@ -447,8 +447,8 @@ namespace cytnx { virtual const vec2d &get_itoi() const; virtual vec2d &get_itoi(); - virtual void to_hdf5_dispatch(H5::Group &location, const bool overwrite) const; - virtual void from_hdf5_dispatch(H5::Group &location, bool restore_device = true); + virtual void to_hdf5_dispatch(H5::Group &container, const bool overwrite) const; + virtual void from_hdf5_dispatch(H5::Group &container, bool restore_device = true); virtual void to_binary_dispatch(std::ostream &f) const; virtual void from_binary_dispatch(std::istream &f, bool restore_device = true); @@ -1085,8 +1085,8 @@ namespace cytnx { "\n"); } - void to_hdf5_dispatch(H5::Group &location, const bool overwrite) const; - void from_hdf5_dispatch(H5::Group &location, bool restore_device = true); + void to_hdf5_dispatch(H5::Group &container, const bool overwrite) const; + void from_hdf5_dispatch(H5::Group &container, bool restore_device = true); void to_binary_dispatch(std::ostream &f) const; void from_binary_dispatch(std::istream &f, bool restore_device = true); @@ -1761,8 +1761,8 @@ namespace cytnx { cytnx_uint16 &at_for_sparse(const std::vector &locator, const cytnx_uint16 &aux); cytnx_int16 &at_for_sparse(const std::vector &locator, const cytnx_int16 &aux); - void to_hdf5_dispatch(H5::Group &location, const bool overwrite) const; - void from_hdf5_dispatch(H5::Group &location, bool restore_device = true); + void to_hdf5_dispatch(H5::Group &container, const bool overwrite) const; + void from_hdf5_dispatch(H5::Group &container, bool restore_device = true); void to_binary_dispatch(std::ostream &f) const; void from_binary_dispatch(std::istream &f, bool restore_device = true); @@ -2557,8 +2557,8 @@ namespace cytnx { cytnx_uint16 &at_for_sparse(const std::vector &locator, const cytnx_uint16 &aux); cytnx_int16 &at_for_sparse(const std::vector &locator, const cytnx_int16 &aux); - void to_hdf5_dispatch(H5::Group &location, const bool overwrite) const; - void from_hdf5_dispatch(H5::Group &location, bool restore_device = true); + void to_hdf5_dispatch(H5::Group &container, const bool overwrite) const; + void from_hdf5_dispatch(H5::Group &container, bool restore_device = true); void to_binary_dispatch(std::ostream &f) const; void from_binary_dispatch(std::istream &f, bool restore_device = true); @@ -5541,18 +5541,18 @@ namespace cytnx { /** * @brief Save UniTensor to HDF5 file - * @param[in] location the HDF5 parent group. + * @param[in] container the HDF5 parent group. * @param[in] name the subgroup in which the UniTensor will be saved. - * @param[in] overwrite overwrite previous UniTensor information in the location. + * @param[in] overwrite overwrite previous UniTensor information in the container. * @warning This function is only available in C++. Use Save() for saving to file in C++ or * Python. * @see from_hdf5() */ - void to_hdf5(H5::Group &location, const std::string &name = "UniTensor", + void to_hdf5(H5::Group &container, const std::string &name = "UniTensor", const bool overwrite = false) const; /** * @brief Load UniTensor from HDF5 file (inline) - * @param[in] location the HDF5 parent group. + * @param[in] container the HDF5 parent group. * @param[in] name the subgroup from which the UniTensor will be loaded. * @param[in] restore_device whether to try restoring the device on which the data is stored; if * false, the data will be kept on the CPU. Use .to_() to move it to the target device after @@ -5561,7 +5561,7 @@ namespace cytnx { * Python. * @see to_hdf5() */ - void from_hdf5(H5::Group &location, const std::string &name = "UniTensor", + void from_hdf5(H5::Group &container, const std::string &name = "UniTensor", bool restore_device = true); /** diff --git a/include/backend/Storage.hpp b/include/backend/Storage.hpp index 6ffd5d9be..719c09a48 100644 --- a/include/backend/Storage.hpp +++ b/include/backend/Storage.hpp @@ -592,18 +592,18 @@ namespace cytnx { /** * @brief Save Storage to HDF5 file - * @param[in] location the HDF5 group where the Storage will be saved. + * @param[in] container the HDF5 group where the Storage will be saved. * @param[in] name the name of the dataset in the HDF5 file. - * @param[in] overwrite overwrite previous Bond information in the location. + * @param[in] overwrite overwrite previous Bond information in the container. * @warning This function is only available in C++. Use Save() for saving to file in C++ or * Python. * @see from_hdf5() */ - void to_hdf5(H5::Group &location, const std::string &name = "Storage", + void to_hdf5(H5::Group &container, const std::string &name = "Storage", const bool overwrite = false) const; /** * @brief Load Storage from HDF5 file (inline) - * @param[in] location the HDF5 group where the Storage will be loaded from. + * @param[in] container the HDF5 group where the Storage will be loaded from. * @param[in] name the name of the dataset in the HDF5 file. * @param[in] restore_device whether to try restoring the device on which the data is stored; if * false, the data will be kept on the CPU. Use .to_() to move it to the target device after @@ -612,7 +612,7 @@ namespace cytnx { * Python. * @see to_hdf5() */ - void from_hdf5(H5::Group &location, const std::string &name = "Storage", + void from_hdf5(H5::Group &container, const std::string &name = "Storage", bool restore_device = true); /** diff --git a/include/io.hpp b/include/io.hpp index 5f2bd9071..4215c8ed0 100644 --- a/include/io.hpp +++ b/include/io.hpp @@ -22,25 +22,60 @@ namespace cytnx { */ namespace io { - ///@cond /** * @brief Create a group, given a path that can contain subpathes * @details Opens the group or creates it newly - * @param[in] file root group + * @param[in] container root group * @param[in] path a path that can contain subpathes * @returns the opened group or a newly created group */ - H5::Group create_group(H5::Group &file, const std::string &path); - ///@endcond + H5::Group create_group(H5::Group &container, const std::string &path); + + /** + * @brief Save data to an attribute + * @param[in] object a scalar of any type that is supported by cytnx + * @param[in] container group; should be opened for writing + * @param[in] name the name of the attribute + * @param[in] overwrite if true, overwrite previous data in the container + */ + void save_attribute(const Scalar_list &object, H5::Group &container, const std::string &name, + bool overwrite = false); + /** + * @brief Save string to an attribute + * @see save_attribute(const Scalar_list &object, H5::Group &container, const std::string &name) + */ + void save_attribute(const std::string &object, H5::Group &container, const std::string &name, + bool overwrite = false); + + /** + * @brief Save data to a dataset + * @param[in] object a vector of supported types + * @param[in] container group; should be opened for writing + * @param[in] name the name of the dataset + */ + void save_dataset(const std::vector &object, H5::Group &container, + const std::string &name); + + /** + * @brief Remove an attribute if it exists + * @param[in] container group or file from which the attribute will be removed + * @param[in] name the name of the attribute + * @param[in] overwrite if true, the attribute will be removed. If false, an error is thrown if + * the attribute exists + */ + void remove_attribute(H5::Group &container, const std::string &name, bool overwrite = true); /** * @brief Input/output file access mode for HDF5 files. */ enum IoMode : int { - ACC_TRUNC, /**< Open file for reading and writing; overwrites if file exists; creates new file otherwise */ - ACC_NOREPLACE, /**< Open file for reading and writing; fails if file exists; creates new file otherwise */ - ACC_IN, /**< Open file for reading only; fails if file does not exist */ - ACC_INOUT /**< Open file for reading and writing; opens an existing file; creates new file otherwise */ + ACC_TRUNC, /**< Open file for reading and writing; overwrites if file exists; creates new file + otherwise */ + ACC_NOREPLACE, /**< Open file for reading and writing; fails if file exists; creates new file + otherwise */ + ACC_IN, /**< Open file for reading only; fails if file does not exist */ + ACC_INOUT /**< Open file for reading and writing; opens an existing file; creates new file + otherwise */ }; /** @@ -48,10 +83,11 @@ namespace cytnx { * @details Use file.close() to close the file after use. * @param[in] fname file name * @param[in] mode the write mode:\n - * ACC_TRUNC Open file for reading and writing; overwrites if file exists; creates new file otherwise\n - * ACC_NOREPLACE Open file for reading and writing; fails if file exists; creates new file otherwise\n - * ACC_IN Open file for reading only; fails if file does not exist\n - * ACC_INOUT Open file for reading and writing; opens an existing file; creates new file otherwise + * ACC_TRUNC Open file for reading and writing; overwrites if file exists; creates new file + * otherwise\n ACC_NOREPLACE Open file for reading and writing; fails if file exists; creates + * new file otherwise\n ACC_IN Open file for reading only; fails if file does not exist\n + * ACC_INOUT Open file for reading and writing; opens an existing file; creates new file + * otherwise * @returns the file handle * @note The file ending should be one of ".h5", ".hdf5", ".H5", ".HDF5", ".hdf". */ @@ -66,66 +102,92 @@ namespace cytnx { * @brief Save object to HDF5 * @details Can be used with most cytnx classes as objects. * @param[in] object the cytnx object to be saved - * @param[in] file HDF5 object that should be opened for writing; can be a file or a group - * @param[in] path path inside the file; a path '/foo/bar/Obj' will write the object to the dataset 'Obj' the group '/foo/bar' in the file. + * @param[in] container HDF5 object that should be opened for writing; can be a file or a group + * @param[in] path path inside the file; a path '/foo/bar/Obj' will write the object to the + * dataset 'Obj' the group '/foo/bar' in the file. * @param[in] name the name of the object to save to - * @param[in] overwrite if true, overwrite previous data in the file + * @param[in] overwrite if true, overwrite previous data in the container * @note The file ending should be one of ".h5", ".hdf5", ".H5", ".HDF5", ".hdf". * @see Load(), open() */ - void Save(const savable_class &object, H5::Group &file, const std::string &name, const std::string &path = "", bool overwrite = false); - + void Save(const savable_class &object, H5::Group &container, const std::string &name, + const std::string &path = "", bool overwrite = false); + /** * @brief Open HDF5 file, save object, and close file. * @param[in] object the cytnx object to be saved * @param[in] fname file name - * @param[in] path path inside the file; a path '/foo/bar/Obj' will write the object to the dataset 'Obj' the group '/foo/bar' in the file. + * @param[in] path path inside the file; a path '/foo/bar/Obj' will write the object to the + * dataset 'Obj' the group '/foo/bar' in the file. * @param[in] name the name of the attribute, dataset, or group * @param[in] mode the write mode:\n * `w` write; creates a new file. If the given file exists, its contents are destroyed.\n - * `x` exclusive; reates a new file. Fails if the given file exists already.\n - * `a` append. opens for writing without overwriting any existing content. Creates the file if it - * doesn't exist.\n - * `u` update; opens for writing. Existing content will be updated(overwritten). - * Creates the file if it doesn't exist. - * @see Save(const savable_class object, hid_t file, const std::string &name), open() + * `x` exclusive; creates a new file. Fails if the given file exists already.\n + * `a` append. opens for writing without overwriting any existing content. Creates the file if + * it doesn't exist.\n `u` update; opens for writing. Existing content will be + * updated(overwritten). Creates the file if it doesn't exist. + * @see Save(const savable_class &object, H5::Group &file, const std::string &name, const + * std::string &path, bool overwrite), open() */ - // void Save(const savable_class object, const std::filesystem::path &fname, const std::string &name, const std::string &path = "", const char mode = 'w') { - // cytnx_error_msg(mode == 'r', "Mode 'r' is read-only and not available for writing files.%s", "\n"); - // bool overwrite = (mode == 'u'); - // if (mode == 'a') + + /// @cond + // void Save(const savable_class object, const std::filesystem::path &fname, const std::string + // &name, const std::string &path = "", const char mode = 'w') { + // cytnx_error_msg(mode == 'r', "Mode 'r' is read-only and not available for writing + // files.%s", "\n"); bool overwrite = (mode == 'u'); if (mode == 'a') // mode = 'u'; // hid_t file = open(fname, mode); // Save(object, file, name, path, overwrite); // Close(file); // } + /// @endcond /** - * @brief Load object from HDF5 file and create new instance + * @brief Load object from HDF5 file * @details Can be used with most cytnx classes as objects. * @param[in] object an object of the correct type that will be modified (inline) - * @param[in] file HDF5 object that should be opened for writing; can be a file or a group + * @param[in] container HDF5 object that should be opened for writing; can be a file or a group * @param[in] name the name of the attribute, dataset, or group - * @param[in] path path inside the file; a path /foo/bar/Obj will read the object from the dataset 'Obj' the group '/foo/bar' in the file. + * @param[in] path path inside the file; a path /foo/bar/Obj will read the object from the + * dataset 'Obj' the group '/foo/bar' in the file. * @param[in] name the name of the attribute, dataset, or group - * @param[in] restore_device whether to try restoring the device on which the data is stored; if false, the data will be kept on the CPU. Only effects objects that contain Tensor or Storage. * @pre The file must be an object which was saved by Save(). * @see Save(), open() */ - void Load(savable_class &object, H5::Group &file, const std::string &name, const std::string &path = "", bool restore_device = true); + void Load(savable_class &object, H5::Group &container, const std::string &name, + const std::string &path = ""); + + /** + * @brief savable_class objects that can be loaded to different devices + */ + using loadable_to_device = std::variant; + + /** + * @brief Load object from HDF5 file and restore the device the data was saved to + * @param[in] restore_device whether to try restoring the device on which the data is stored; if + * false, the data will be kept on the CPU. Only effects objects that contain Tensor or Storage. + * @see Load(savable_class &object, H5::Group &container, const std::string &name, const + * std::string &path) + */ + void Load(loadable_to_device &object, H5::Group &container, const std::string &name, + const std::string &path, bool restore_device); + /// @cond /** * @brief Open HDF5 file, load object, and close file. * @param[in] fname file name - * @see Load(const savable_class object, const std::string &name, const std::filesystem::path &fname, const std::string &path, bool restore_device), open() + * @see Load(const savable_class object, const std::string &name, const std::filesystem::path + * &fname, const std::string &path, bool restore_device), open() */ - // void Load(const savable_class object, const std::filesystem::path &fname, const std::string &name, const std::string &path = "", bool restore_device = true) { + // void Load(const savable_class object, const std::filesystem::path &fname, const std::string + // &name, const std::string &path = "", bool restore_device = true) { // hid_t file = open(fname, 'r'); // Load(object, file, name, path, restore_device); // Close(file); // } + /// @endcond } // namespace io } // namespace cytnx -#endif // CYTNX_IO_H_ \ No newline at end of file +#endif // CYTNX_IO_H_ diff --git a/include/tn_algo/MPS.hpp b/include/tn_algo/MPS.hpp index 5cedcc5a5..e7a7973a8 100644 --- a/include/tn_algo/MPS.hpp +++ b/include/tn_algo/MPS.hpp @@ -356,18 +356,18 @@ namespace cytnx { /** * @brief Save MPS to HDF5 file - * @param[in] location the HDF5 parent group. + * @param[in] container the HDF5 parent group. * @param[in] name the subgroup in which the MPS will be saved. - * @param[in] overwrite overwrite previous MPS information in the location. + * @param[in] overwrite overwrite previous MPS information in the container. * @warning This function is only available in C++. Use Save() for saving to file in C++ or * Python. * @see from_hdf5() */ - void to_hdf5(H5::Group &location, const std::string &name = "MPS", + void to_hdf5(H5::Group &container, const std::string &name = "MPS", const bool overwrite = false) const; /** * @brief Load MPS from HDF5 file (inline) - * @param[in] location the HDF5 parent group. + * @param[in] container the HDF5 parent group. * @param[in] name the subgroup from which the MPS will be loaded. * @param[in] restore_device whether to try restoring the device on which the data is stored; * if false, the data will be kept on the CPU. Use .to_() to move it to the target device @@ -376,7 +376,7 @@ namespace cytnx { * Python. * @see to_hdf5() */ - void from_hdf5(H5::Group &location, const std::string &name = "MPS", + void from_hdf5(H5::Group &container, const std::string &name = "MPS", bool restore_device = true); /** diff --git a/pybind/io_py.cpp b/pybind/io_py.cpp index e43852fc3..98df27be5 100644 --- a/pybind/io_py.cpp +++ b/pybind/io_py.cpp @@ -24,12 +24,12 @@ void io_binding(py::module &m) { pybind11::module m_io = m.def_submodule("io", "Input/Output related."); py::enum_(m_io, "IoMode") - .value("ACC_TRUNC", cytnx::io::IoMode::ACC_TRUNC) + .value("ACC_TRUNC", cytnx::io::IoMode::ACC_TRUNC) .value("ACC_NOREPLACE", cytnx::io::IoMode::ACC_NOREPLACE) - .value("ACC_IN", cytnx::io::IoMode::ACC_IN) - .value("ACC_INOUT", cytnx::io::IoMode::ACC_INOUT) + .value("ACC_IN", cytnx::io::IoMode::ACC_IN) + .value("ACC_INOUT", cytnx::io::IoMode::ACC_INOUT) .export_values(); - + py::class_(m_io, "Group") // construction .def(py::init<>()) @@ -39,45 +39,75 @@ void io_binding(py::module &m) { .def("close", &H5::Group::close) .def("fromClass", &H5::Group::fromClass) .def("getId", &H5::Group::getId) - + // inherited // Group management - .def("createGroup", [](const H5::Group &self, const std::string &name, size_t size_hint) - { return self.createGroup(name, size_hint); }, - py::arg("name"), py::arg("size_hint") = 0) - .def("openGroup", [](const H5::Group &self, const std::string &name) - { return self.openGroup(name); }, - py::arg("name")) + .def( + "createGroup", + [](const H5::Group &self, const std::string &name, size_t size_hint) { + return self.createGroup(name, size_hint); + }, + py::arg("name"), py::arg("size_hint") = 0) + .def( + "openGroup", + [](const H5::Group &self, const std::string &name) { return self.openGroup(name); }, + py::arg("name")) // Attribute management - .def("attrExists", [](const H5::Group &self, const std::string &name) { return self.attrExists(name); }, py::arg("name")) - .def("removeAttr", [](const H5::Group &self, const std::string &name) { self.removeAttr(name); }, py::arg("name")) - .def("renameAttr", [](const H5::Group &self, const std::string &old_n, const std::string &new_n) - { self.renameAttr(old_n, new_n); }, - py::arg("old_name"), py::arg("new_name")) - + .def( + "attrExists", + [](const H5::Group &self, const std::string &name) { return self.attrExists(name); }, + py::arg("name")) + .def( + "removeAttr", [](const H5::Group &self, const std::string &name) { self.removeAttr(name); }, + py::arg("name")) + .def( + "renameAttr", + [](const H5::Group &self, const std::string &old_n, const std::string &new_n) { + self.renameAttr(old_n, new_n); + }, + py::arg("old_name"), py::arg("new_name")) + // link/object management - .def("moveLink", [](const H5::Group &self, const std::string &src, const std::string &dst) - { self.moveLink(src, dst); }, - py::arg("src_name"), py::arg("dst_name")) - .def("nameExists", [](const H5::Group &self, const std::string &name) { return self.nameExists(name); }, py::arg("name")) - .def("link", [](const H5::Group &self, const std::string &curr, const std::string &next) - { self.link(curr, next); }, - py::arg("curr_name"), py::arg("new_name")) - .def("unlink", [](const H5::Group &self, const std::string &name) { self.unlink(name); }, py::arg("name")) - + .def( + "moveLink", + [](const H5::Group &self, const std::string &src, const std::string &dst) { + self.moveLink(src, dst); + }, + py::arg("src_name"), py::arg("dst_name")) + .def( + "nameExists", + [](const H5::Group &self, const std::string &name) { return self.nameExists(name); }, + py::arg("name")) + .def( + "link", + [](const H5::Group &self, const std::string &curr, const std::string &next) { + self.link(curr, next); + }, + py::arg("curr_name"), py::arg("new_name")) + .def( + "unlink", [](const H5::Group &self, const std::string &name) { self.unlink(name); }, + py::arg("name")) + // comments - .def("setComment", [](const H5::Group &self, const std::string &name, const std::string &comment) - { self.setComment(name, comment); }, - py::arg("name"), py::arg("comment")) - .def("getComment", [](const H5::Group &self, const std::string &name) - { return self.getComment(name); }, - py::arg("name")) - .def("removeComment", [](const H5::Group &self, const std::string &name) { self.removeComment(name); }, py::arg("name")) + .def( + "setComment", + [](const H5::Group &self, const std::string &name, const std::string &comment) { + self.setComment(name, comment); + }, + py::arg("name"), py::arg("comment")) + .def( + "getComment", + [](const H5::Group &self, const std::string &name) { return self.getComment(name); }, + py::arg("name")) + .def( + "removeComment", + [](const H5::Group &self, const std::string &name) { self.removeComment(name); }, + py::arg("name")) // helper - .def("getFileName", [](const H5::Group &self) { return self.getFileName(); }) - ; // end of object line + .def("getFileName", + [](const H5::Group &self) { return self.getFileName(); }); // end of object line py::class_(m_io, "H5File") // construction @@ -90,30 +120,46 @@ void io_binding(py::module &m) { .def("getId", &H5::H5File::getId) .def("getFileSize", &H5::H5File::getFileSize) .def("getFreeSpace", &H5::H5File::getFreeSpace) - .def("isAccessible", [](H5::H5File &self, const std::string &name) { return self.isAccessible(name); }, py::arg("name") ) - .def("isHdf5", [](H5::H5File &self, const std::string &name) { return self.isHdf5(name); }, py::arg("name") ) - .def("getObjCount", [](H5::H5File &self) { return self.getObjCount(); }) - ; // end of object line - - m_io.def("open", [](const std::filesystem::path &fname, cytnx::io::IoMode mode) - { return cytnx::io::open(fname, mode); }, - py::arg("fname"), py::arg("mode") = cytnx::io::ACC_TRUNC); + .def( + "isAccessible", + [](H5::H5File &self, const std::string &name) { return self.isAccessible(name); }, + py::arg("name")) + .def( + "isHdf5", [](H5::H5File &self, const std::string &name) { return self.isHdf5(name); }, + py::arg("name")) + .def("getObjCount", [](H5::H5File &self) { return self.getObjCount(); }); // end of object line + + m_io.def( + "create_group", + [](H5::Group &container, const std::string &path) { + return cytnx::io::create_group(container, path); + }, + py::arg("container"), py::arg("path")); + + m_io.def( + "open", + [](const std::filesystem::path &fname, cytnx::io::IoMode mode) { + return cytnx::io::open(fname, mode); + }, + py::arg("fname"), py::arg("mode") = cytnx::io::ACC_TRUNC); // m_io.def("Close", [](H5::H5File file) { cytnx::io::Close(file); }, py::arg("file")); - - m_io.def("Save", &cytnx::io::Save, - py::arg("object"), - py::arg("file"), - py::arg("name"), - py::arg("path") = "", - py::arg("overwrite") = false); - - m_io.def("Load", &cytnx::io::Save, - py::arg("object"), - py::arg("file"), - py::arg("name"), - py::arg("path") = "", - py::arg("restore_device") = true); + + m_io.def("Save", &cytnx::io::Save, py::arg("object"), py::arg("container"), py::arg("name"), + py::arg("path") = "", py::arg("overwrite") = false); + + m_io.def( + "Load", + [](cytnx::io::savable_class &object, H5::Group &container, const std::string &name, + const std::string &path) { cytnx::io::Load(object, container, name, path); }, + py::arg("object"), py::arg("container"), py::arg("name"), py::arg("path") = ""); + m_io.def( + "Load", + [](cytnx::io::loadable_to_device &object, H5::Group &container, const std::string &name, + const std::string &path, + bool restore_device) { cytnx::io::Load(object, container, name, path, restore_device); }, + py::arg("object"), py::arg("container"), py::arg("name"), py::arg("path"), + py::arg("restore_device")); } // io_binding -#endif \ No newline at end of file +#endif diff --git a/src/BlockFermionicUniTensor.cpp b/src/BlockFermionicUniTensor.cpp index e6833cb90..47bb1681e 100644 --- a/src/BlockFermionicUniTensor.cpp +++ b/src/BlockFermionicUniTensor.cpp @@ -2369,10 +2369,10 @@ namespace cytnx { return this->_blocks[bidx].at(loc_in_T); } - void BlockFermionicUniTensor::to_hdf5_dispatch(H5::Group &location, const bool overwrite) const { + void BlockFermionicUniTensor::to_hdf5_dispatch(H5::Group &container, const bool overwrite) const { // blocks; write to group if (!this->_blocks.empty()) { - H5::Group dir = location.createGroup("blocks"); + H5::Group dir = container.createGroup("blocks"); for (int i = 0; i < this->_blocks.size(); i++) { if (this->_signflip[i]) { Tensor block = -this->_blocks[i]; @@ -2394,7 +2394,7 @@ namespace cytnx { hsize_t matdims[2] = {blocknum, rank}; H5::DataSpace dataspace(2, matdims); H5::DataType datatype = Type.get_hdf5_type(flat[0]); - H5::DataSet dataset = location.createDataSet("block_to_sectors", datatype, dataspace); + H5::DataSet dataset = container.createDataSet("block_to_sectors", datatype, dataspace); dataset.write(flat.data(), datatype); // label axes char labels[2][6] = {"block", "bond"}; @@ -2406,12 +2406,12 @@ namespace cytnx { } } - void BlockFermionicUniTensor::from_hdf5_dispatch(H5::Group &location, bool restore_device) { + void BlockFermionicUniTensor::from_hdf5_dispatch(H5::Group &container, bool restore_device) { this->_is_tag = true; // blocks; read from group this->_blocks.clear(); - if (location.nameExists("blocks")) { - H5::Group dir = location.openGroup("blocks"); + if (container.nameExists("blocks")) { + H5::Group dir = container.openGroup("blocks"); hsize_t idx = 0; while (true) { std::string name = "Tensor" + std::to_string(idx); @@ -2425,8 +2425,8 @@ namespace cytnx { } } // inner_to_outer_idx; read matrix (blocknum x rank) - if (location.nameExists("block_to_sectors")) { - H5::DataSet dataset = location.openDataSet("block_to_sectors"); + if (container.nameExists("block_to_sectors")) { + H5::DataSet dataset = container.openDataSet("block_to_sectors"); H5::DataSpace dataspace = dataset.getSpace(); cytnx_error_msg(dataspace.getSimpleExtentNdims() != 2, "[ERROR] 'block_to_sectors' should be a two-dimensional array. The HDF5 data " diff --git a/src/BlockUniTensor.cpp b/src/BlockUniTensor.cpp index 6390853c9..fb8669f0a 100644 --- a/src/BlockUniTensor.cpp +++ b/src/BlockUniTensor.cpp @@ -1579,10 +1579,10 @@ namespace cytnx { return this->_blocks[bidx].at(loc_in_T); } - void BlockUniTensor::to_hdf5_dispatch(H5::Group &location, const bool overwrite) const { + void BlockUniTensor::to_hdf5_dispatch(H5::Group &container, const bool overwrite) const { // blocks; write to group if (!this->_blocks.empty()) { - H5::Group dir = location.createGroup("blocks"); + H5::Group dir = container.createGroup("blocks"); for (int i = 0; i < this->_blocks.size(); i++) { this->_blocks[i].to_hdf5(dir, "Tensor" + std::to_string(i), overwrite); } @@ -1599,7 +1599,7 @@ namespace cytnx { hsize_t matdims[2] = {blocknum, rank}; H5::DataSpace dataspace(2, matdims); H5::DataType datatype = Type.get_hdf5_type(flat[0]); - H5::DataSet dataset = location.createDataSet("block_to_sectors", datatype, dataspace); + H5::DataSet dataset = container.createDataSet("block_to_sectors", datatype, dataspace); dataset.write(flat.data(), datatype); // label axes char labels[2][6] = {"block", "bond"}; @@ -1611,12 +1611,12 @@ namespace cytnx { } } - void BlockUniTensor::from_hdf5_dispatch(H5::Group &location, bool restore_device) { + void BlockUniTensor::from_hdf5_dispatch(H5::Group &container, bool restore_device) { this->_is_tag = true; // blocks; read from group this->_blocks.clear(); - if (location.nameExists("blocks")) { - H5::Group dir = location.openGroup("blocks"); + if (container.nameExists("blocks")) { + H5::Group dir = container.openGroup("blocks"); hsize_t idx = 0; while (true) { std::string name = "Tensor" + std::to_string(idx); @@ -1630,8 +1630,8 @@ namespace cytnx { } } // inner_to_outer_idx; read matrix (blocknum x rank) - if (location.nameExists("block_to_sectors")) { - H5::DataSet dataset = location.openDataSet("block_to_sectors"); + if (container.nameExists("block_to_sectors")) { + H5::DataSet dataset = container.openDataSet("block_to_sectors"); H5::DataSpace dataspace = dataset.getSpace(); cytnx_error_msg(dataspace.getSimpleExtentNdims() != 2, "[ERROR] 'block_to_sectors' should be a two-dimensional array. The HDF5 data " diff --git a/src/Bond.cpp b/src/Bond.cpp index a4e8d3bc8..1c21560f9 100644 --- a/src/Bond.cpp +++ b/src/Bond.cpp @@ -522,9 +522,9 @@ namespace cytnx { groupfolder = subpath.generic_string(); if (!h5file.nameExists(groupfolder)) h5file.createGroup(groupfolder); } - H5::Group location = h5file.openGroup(groupfolder); + H5::Group group = h5file.openGroup(groupfolder); // write data - this->to_hdf5(location, "", overwrite); + this->to_hdf5(group, "", overwrite); h5file.close(); return; } else { // create binary file @@ -580,16 +580,16 @@ namespace cytnx { ext == ".HDF") { // load HDF5 H5::H5File h5file(fname, H5F_ACC_RDONLY); // open group - H5::Group location; + H5::Group group; try { - location = h5file.openGroup(path.empty() ? "/" : path); + group = h5file.openGroup(path.empty() ? "/" : path); } catch (const H5::Exception &e) { std::cerr << e.getDetailMsg() << std::endl; cytnx_error_msg(true, "[ERROR] HDF5 path '%s' not found or is not a group in file '%s'.", path.c_str(), fname.string().c_str()); } // read data - this->from_hdf5(location, ""); + this->from_hdf5(group, ""); h5file.close(); } else { // load binary fstream f; @@ -605,15 +605,15 @@ namespace cytnx { this->Load_(std::filesystem::path(fname), path); } - void Bond::to_hdf5(H5::Group &location, const std::string &name, const bool overwrite) const { + void Bond::to_hdf5(H5::Group &container, const std::string &name, const bool overwrite) const { H5::Group rootgroup; if (name.empty()) - rootgroup = location; + rootgroup = container; else { - if (location.nameExists(name)) { - rootgroup = location.openGroup(name); + if (container.nameExists(name)) { + rootgroup = container.openGroup(name); } else { - rootgroup = location.createGroup(name); + rootgroup = container.createGroup(name); } } @@ -689,8 +689,8 @@ namespace cytnx { } } } - void Bond::from_hdf5(H5::Group &location, const std::string &name) { - H5::Group rootgroup = (name.empty() ? location : location.openGroup(name)); + void Bond::from_hdf5(H5::Group &container, const std::string &name) { + H5::Group rootgroup = (name.empty() ? container : container.openGroup(name)); H5::DataType datatype; H5::Attribute attr; H5::DataSet dataset; diff --git a/src/DenseUniTensor.cpp b/src/DenseUniTensor.cpp index 8b67faaaa..50636d3ef 100644 --- a/src/DenseUniTensor.cpp +++ b/src/DenseUniTensor.cpp @@ -1282,17 +1282,17 @@ namespace cytnx { void DenseUniTensor::normalize_() { this->_block /= linalg::Norm(this->_block); } - void DenseUniTensor::to_hdf5_dispatch(H5::Group &location, const bool overwrite) const { + void DenseUniTensor::to_hdf5_dispatch(H5::Group &container, const bool overwrite) const { // is_tag, write as attribute H5::DataType datatype = Type.get_hdf5_type(this->_is_tag); - H5::Attribute attr = location.createAttribute("directed", datatype, H5::DataSpace(H5S_SCALAR)); + H5::Attribute attr = container.createAttribute("directed", datatype, H5::DataSpace(H5S_SCALAR)); attr.write(datatype, &this->_is_tag); - this->_block.to_hdf5(location, "Tensor", overwrite); + this->_block.to_hdf5(container, "Tensor", overwrite); } - void DenseUniTensor::from_hdf5_dispatch(H5::Group &location, bool restore_device) { - this->_block.from_hdf5(location, "Tensor", restore_device); + void DenseUniTensor::from_hdf5_dispatch(H5::Group &container, bool restore_device) { + this->_block.from_hdf5(container, "Tensor", restore_device); // check data consistency auto shape = this->_block.shape(); if (this->_bonds.empty()) { @@ -1315,8 +1315,8 @@ namespace cytnx { } // is_tag, read from attribute or reproduce - if (location.attrExists("directed")) { - H5::Attribute attr = location.openAttribute("directed"); + if (container.attrExists("directed")) { + H5::Attribute attr = container.openAttribute("directed"); H5::DataType datatype = attr.getDataType(); cytnx_error_msg( datatype.getSize() != Type.get_hdf5_type(this->_is_tag).getSize(), diff --git a/src/SparseUniTensor.cpp b/src/SparseUniTensor.cpp index 3faa65356..5bfd480af 100644 --- a/src/SparseUniTensor.cpp +++ b/src/SparseUniTensor.cpp @@ -2332,11 +2332,11 @@ namespace cytnx { cytnx_error_msg(true, "[ERROR] truncate for SparseUniTensor is under developing!!%s", "\n"); } - void SparseUniTensor::to_hdf5_dispatch(H5::Group &location, const bool overwrite) const { + void SparseUniTensor::to_hdf5_dispatch(H5::Group &container, const bool overwrite) const { cytnx_error_msg(true, "[ERROR] Saving SparseUniTensor to HDF5 is not implemented!%s", "\n"); } - void SparseUniTensor::from_hdf5_dispatch(H5::Group &location, bool restore_device) { + void SparseUniTensor::from_hdf5_dispatch(H5::Group &container, bool restore_device) { cytnx_error_msg(true, "[ERROR] Loading SparseUniTensor from HDF5 is not implemented!%s", "\n"); } diff --git a/src/Symmetry.cpp b/src/Symmetry.cpp index 5e692ddfa..029f3fecc 100644 --- a/src/Symmetry.cpp +++ b/src/Symmetry.cpp @@ -299,9 +299,9 @@ namespace cytnx { groupfolder = subpath.generic_string(); if (!h5file.nameExists(groupfolder)) h5file.createGroup(groupfolder); } - H5::Group location = h5file.openGroup(groupfolder); + H5::Group group = h5file.openGroup(groupfolder); // write data - this->to_hdf5(location, datasetname, overwrite); + this->to_hdf5(group, datasetname, overwrite); h5file.close(); return; } else { // create binary file @@ -363,16 +363,16 @@ namespace cytnx { std::string datasetname = p.filename().string(); if (datasetname.empty()) datasetname = "Symmetry"; // open group - H5::Group location; + H5::Group group; try { - location = h5file.openGroup(grouppath.empty() ? "/" : grouppath); + group = h5file.openGroup(grouppath.empty() ? "/" : grouppath); } catch (const H5::Exception &e) { std::cerr << e.getDetailMsg() << std::endl; cytnx_error_msg(true, "[ERROR] HDF5 path '%s' not found or is not a group in file '%s'.", grouppath.c_str(), fname.string().c_str()); } // read data - this->from_hdf5(location, datasetname); + this->from_hdf5(group, datasetname); h5file.close(); } else { // load binary fstream f; @@ -388,20 +388,20 @@ namespace cytnx { this->Load_(std::filesystem::path(fname), path); } - void cytnx::Symmetry::to_hdf5(H5::Group &location, const std::string &name, + void cytnx::Symmetry::to_hdf5(H5::Group &container, const std::string &name, const bool overwrite) const { if (overwrite) { // delete previous data - if (location.attrExists(name)) location.removeAttr(name); + if (container.attrExists(name)) container.removeAttr(name); } std::string symname = this->getname(); H5::StrType str_type(H5::PredType::C_S1, symname.length() + 1); H5::DataSpace dataspace = H5::DataSpace(H5S_SCALAR); - H5::Attribute attr = location.createAttribute(name, str_type, dataspace); + H5::Attribute attr = container.createAttribute(name, str_type, dataspace); attr.write(str_type, symname); } - void cytnx::Symmetry::from_hdf5(H5::Group &location, const std::string &name) { - H5::Attribute attr = location.openAttribute(name); + void cytnx::Symmetry::from_hdf5(H5::Group &container, const std::string &name) { + H5::Attribute attr = container.openAttribute(name); H5::StrType str_type = attr.getStrType(); size_t size = str_type.getSize() - 1; // remove the null terminator std::string symname; diff --git a/src/Tensor.cpp b/src/Tensor.cpp index 3ae1af517..f3b7b08db 100644 --- a/src/Tensor.cpp +++ b/src/Tensor.cpp @@ -67,9 +67,7 @@ namespace cytnx { ( [&]() { if (dtype == Is) { - using TargetType = - std::variant_alternative_t::type>; + using TargetType = std::variant_alternative_t; result = static_cast(p); } }(), @@ -94,8 +92,7 @@ namespace cytnx { ( [&]() { if (dtype == Is) { - using TargetType = std::variant_alternative_t< - Is, typename Tensor::internal::exclude_first::type>; + using TargetType = std::variant_alternative_t; result = static_cast(p); } }(), @@ -479,9 +476,9 @@ namespace cytnx { groupfolder = subpath.generic_string(); if (!h5file.nameExists(groupfolder)) h5file.createGroup(groupfolder); } - H5::Group location = h5file.openGroup(groupfolder); + H5::Group group = h5file.openGroup(groupfolder); // write data - this->to_hdf5(location, datasetname, overwrite); + this->to_hdf5(group, datasetname, overwrite); h5file.close(); return; } else { // create binary file @@ -545,16 +542,16 @@ namespace cytnx { std::string datasetname = p.filename().string(); if (datasetname.empty()) datasetname = "Tensor"; // open group - H5::Group location; + H5::Group group; try { - location = h5file.openGroup(grouppath.empty() ? "/" : grouppath); + group = h5file.openGroup(grouppath.empty() ? "/" : grouppath); } catch (const H5::Exception &e) { std::cerr << e.getDetailMsg() << std::endl; cytnx_error_msg(true, "[ERROR] HDF5 path '%s' not found or is not a group in file '%s'.", grouppath.c_str(), fname.string().c_str()); } // read data - this->from_hdf5(location, datasetname, restore_device); + this->from_hdf5(group, datasetname, restore_device); h5file.close(); } else { // load binary fstream f; @@ -570,9 +567,9 @@ namespace cytnx { this->Load_(std::filesystem::path(fname), path, restore_device); } - void Tensor::to_hdf5(H5::Group &location, const std::string &name, const bool overwrite) const { + void Tensor::to_hdf5(H5::Group &container, const std::string &name, const bool overwrite) const { if (overwrite) { // delete previous data - if (location.nameExists(name)) location.unlink(name); + if (container.nameExists(name)) container.unlink(name); } Tensor ten = this->contiguous(); @@ -580,7 +577,7 @@ namespace cytnx { H5::DataSpace dataspace(dims.size(), dims.data()); H5::DataType datatype = Type.dtype_to_hdf5_type(this->dtype()); - H5::DataSet dataset = location.createDataSet(name, datatype, dataspace); + H5::DataSet dataset = container.createDataSet(name, datatype, dataspace); ten.storage().data_to_hdf5(dataset, datatype); int device = this->device(); @@ -591,8 +588,8 @@ namespace cytnx { } } - void Tensor::from_hdf5(H5::Group &location, const std::string &name, bool restore_device) { - H5::DataSet dataset = location.openDataSet(name); + void Tensor::from_hdf5(H5::Group &container, const std::string &name, bool restore_device) { + H5::DataSet dataset = container.openDataSet(name); H5::DataType datatype = dataset.getDataType(); unsigned int dtype = Type.from_hdf5_type(datatype); H5::DataSpace dataspace = dataset.getSpace(); diff --git a/src/UniTensor.cpp b/src/UniTensor.cpp index 08fa7ca59..c25ad50e2 100644 --- a/src/UniTensor.cpp +++ b/src/UniTensor.cpp @@ -5,6 +5,7 @@ #include "H5Cpp.h" +#include "io.hpp" #include "linalg.hpp" #include "random.hpp" #include "utils/utils.hpp" @@ -113,9 +114,9 @@ namespace cytnx { groupfolder = subpath.generic_string(); if (!h5file.nameExists(groupfolder)) h5file.createGroup(groupfolder); } - H5::Group location = h5file.openGroup(groupfolder); + H5::Group group = h5file.openGroup(groupfolder); // write data - this->to_hdf5(location, "", overwrite); + this->to_hdf5(group, "", overwrite); h5file.close(); return; } else { // create binary file @@ -173,16 +174,16 @@ namespace cytnx { ext == ".HDF") { // load HDF5 H5::H5File h5file(fname, H5F_ACC_RDONLY); // open group - H5::Group location; + H5::Group group; try { - location = h5file.openGroup(path.empty() ? "/" : path); + group = h5file.openGroup(path.empty() ? "/" : path); } catch (const H5::Exception &e) { std::cerr << e.getDetailMsg() << std::endl; cytnx_error_msg(true, "[ERROR] HDF5 path '%s' not found or is not a group in file '%s'.", path.c_str(), fname.string().c_str()); } // read data - this->from_hdf5(location, "", restore_device); + this->from_hdf5(group, "", restore_device); h5file.close(); } else { // load binary fstream f; @@ -198,26 +199,22 @@ namespace cytnx { this->Load_(std::filesystem::path(fname), path, restore_device); } - void UniTensor::to_hdf5(H5::Group &location, const std::string &name, + void UniTensor::to_hdf5(H5::Group &container, const std::string &name, const bool overwrite) const { H5::Group rootgroup; if (name.empty()) - rootgroup = location; + rootgroup = container; else { - if (location.nameExists(name)) { - rootgroup = location.openGroup(name); + if (container.nameExists(name)) { + rootgroup = container.openGroup(name); } else { - rootgroup = location.createGroup(name); + rootgroup = container.createGroup(name); } } if (overwrite) { // delete previous data // delete all entries that could be written by one of the implementations; // remove attributes - if (rootgroup.attrExists("type")) rootgroup.removeAttr("type"); - if (rootgroup.attrExists("diagonal")) rootgroup.removeAttr("diagonal"); - if (rootgroup.attrExists("rowrank")) rootgroup.removeAttr("rowrank"); - if (rootgroup.attrExists("name")) rootgroup.removeAttr("name"); if (rootgroup.attrExists("directed")) rootgroup.removeAttr("directed"); // remove datasets if (rootgroup.nameExists("labels")) rootgroup.unlink("labels"); @@ -228,52 +225,22 @@ namespace cytnx { if (rootgroup.nameExists("blocks")) rootgroup.unlink("blocks"); } - H5::DataType datatype; - H5::Attribute attr; - H5::DataSet dataset; - H5::DataSpace dataspace; - H5::StrType str_type; - - // type, write as string attribute - std::string type = UTenType.getname(this->_impl->uten_type_id); - str_type = H5::StrType(H5::PredType::C_S1, type.length() + 1); - dataspace = H5::DataSpace(H5S_SCALAR); - attr = rootgroup.createAttribute("type", str_type, dataspace); - attr.write(str_type, type); - - // is_diag, write as attribute only for diagonal tensors + // write attributes + io::save_attribute(UTenType.getname(this->_impl->uten_type_id), rootgroup, "type", overwrite); if (this->_impl->_is_diag) { - datatype = Type.get_hdf5_type(this->_impl->_is_diag); - attr = rootgroup.createAttribute("diagonal", datatype, H5::DataSpace(H5S_SCALAR)); - attr.write(datatype, &this->_impl->_is_diag); + io::save_attribute(this->_impl->_is_diag, rootgroup, "diagonal", overwrite); + } else { + io::remove_attribute(rootgroup, "diagonal", overwrite); } + io::save_attribute(this->_impl->_rowrank, rootgroup, "rowrank", overwrite); + io::save_attribute(this->_impl->_name, rootgroup, "name", overwrite); - // rowrank, write as attribute - datatype = Type.get_hdf5_type(this->_impl->_rowrank); - attr = rootgroup.createAttribute("rowrank", datatype, H5::DataSpace(H5S_SCALAR)); - attr.write(datatype, &this->_impl->_rowrank); - - // name, write as string attribute - str_type = H5::StrType(H5::PredType::C_S1, this->_impl->_name.length() + 1); - dataspace = H5::DataSpace(H5S_SCALAR); - attr = rootgroup.createAttribute("name", str_type, dataspace); - attr.write(str_type, this->_impl->_name); - - // labels; write as string vector + // write datasets if (!this->_impl->_labels.empty()) { - hsize_t vecdims[1] = {this->_impl->_labels.size()}; - dataspace = H5::DataSpace(1, vecdims); - str_type = H5::StrType(H5::PredType::C_S1, H5T_VARIABLE); - dataset = rootgroup.createDataSet("labels", str_type, dataspace); - std::vector c_strings; // H5 needs cstrings - std::string symstring; - for (const auto &label : this->_impl->_labels) { - c_strings.push_back(label.c_str()); - } - dataset.write(c_strings.data(), str_type); + io::save_dataset(this->_impl->_labels, rootgroup, "labels"); } - // bonds; write in group + // write groups if (!this->_impl->_bonds.empty()) { H5::Group dir = rootgroup.createGroup("bonds"); for (int i = 0; i < this->_impl->_bonds.size(); i++) { @@ -284,8 +251,8 @@ namespace cytnx { this->_impl->to_hdf5_dispatch(rootgroup, overwrite); } - void UniTensor::from_hdf5(H5::Group &location, const std::string &name, bool restore_device) { - H5::Group rootgroup = (name.empty() ? location : location.openGroup(name)); + void UniTensor::from_hdf5(H5::Group &container, const std::string &name, bool restore_device) { + H5::Group rootgroup = (name.empty() ? container : container.openGroup(name)); H5::DataType datatype; H5::Attribute attr; H5::StrType str_type; diff --git a/src/UniTensor_base.cpp b/src/UniTensor_base.cpp index 8c2ea753b..d7655aaf5 100644 --- a/src/UniTensor_base.cpp +++ b/src/UniTensor_base.cpp @@ -691,12 +691,12 @@ namespace cytnx { true, "[ERROR] fatal internal, cannot call on an un-initialized UniTensor_base%s", "\n"); } - void UniTensor_base::to_hdf5_dispatch(H5::Group &location, const bool overwrite) const { + void UniTensor_base::to_hdf5_dispatch(H5::Group &container, const bool overwrite) const { cytnx_error_msg( true, "[ERROR] fatal internal, cannot call on an un-initialized UniTensor_base%s", "\n"); } - void UniTensor_base::from_hdf5_dispatch(H5::Group &location, bool restore_device) { + void UniTensor_base::from_hdf5_dispatch(H5::Group &container, bool restore_device) { cytnx_error_msg(true, "[ERROR] Loading BlockUniTensor from HDF5 is not implemented yet!%s", "\n"); } diff --git a/src/backend/Storage.cpp b/src/backend/Storage.cpp index b137f3bbd..06fa4b1c9 100644 --- a/src/backend/Storage.cpp +++ b/src/backend/Storage.cpp @@ -138,9 +138,9 @@ namespace cytnx { groupfolder = subpath.generic_string(); if (!h5file.nameExists(groupfolder)) h5file.createGroup(groupfolder); } - H5::Group location = h5file.openGroup(groupfolder); + H5::Group group = h5file.openGroup(groupfolder); // write data - this->to_hdf5(location, datasetname, overwrite); + this->to_hdf5(group, datasetname, overwrite); h5file.close(); return; } else { // create binary file @@ -203,16 +203,16 @@ namespace cytnx { std::string datasetname = p.filename().string(); if (datasetname.empty()) datasetname = "Storage"; // open group - H5::Group location; + H5::Group group; try { - location = h5file.openGroup(grouppath.empty() ? "/" : grouppath); + group = h5file.openGroup(grouppath.empty() ? "/" : grouppath); } catch (const H5::Exception &e) { std::cerr << e.getDetailMsg() << std::endl; cytnx_error_msg(true, "[ERROR] HDF5 path '%s' not found or is not a group in file '%s'.", grouppath.c_str(), fname.string().c_str()); } // read data - this->from_hdf5(location, datasetname, restore_device); + this->from_hdf5(group, datasetname, restore_device); h5file.close(); } else { // load binary fstream f; @@ -228,15 +228,15 @@ namespace cytnx { this->Load_(std::filesystem::path(fname), path, restore_device); } - void Storage::to_hdf5(H5::Group &location, const std::string &name, const bool overwrite) const { + void Storage::to_hdf5(H5::Group &container, const std::string &name, const bool overwrite) const { if (overwrite) { // delete previous data - if (location.nameExists(name)) location.unlink(name); + if (container.nameExists(name)) container.unlink(name); } hsize_t Nelem = this->size(); H5::DataSpace dataspace(1, &Nelem); H5::DataType datatype = Type.dtype_to_hdf5_type(this->dtype()); - H5::DataSet dataset = location.createDataSet(name, datatype, dataspace); + H5::DataSet dataset = container.createDataSet(name, datatype, dataspace); this->data_to_hdf5(dataset, datatype); if (this->device() != Device.cpu) { H5::Attribute attr = @@ -246,8 +246,8 @@ namespace cytnx { } } - void Storage::from_hdf5(H5::Group &location, const std::string &name, bool restore_device) { - H5::DataSet dataset = location.openDataSet(name); + void Storage::from_hdf5(H5::Group &container, const std::string &name, bool restore_device) { + H5::DataSet dataset = container.openDataSet(name); H5::DataType datatype = dataset.getDataType(); unsigned int dtype = Type.from_hdf5_type(datatype); H5::DataSpace dataspace = dataset.getSpace(); diff --git a/src/backend/algo_internal_gpu/cuSort_internal.cuh b/src/backend/algo_internal_gpu/cuSort_internal.cuh index 266c1b2ca..afa6fd01b 100644 --- a/src/backend/algo_internal_gpu/cuSort_internal.cuh +++ b/src/backend/algo_internal_gpu/cuSort_internal.cuh @@ -11,7 +11,7 @@ namespace cytnx { namespace algo_internal { - // handle flaot2, double2 + // handle float2, double2 template concept Vec2Like = requires(T t) { t.x; diff --git a/src/io.cpp b/src/io.cpp index fb3c19625..e93c4e6e0 100644 --- a/src/io.cpp +++ b/src/io.cpp @@ -9,18 +9,85 @@ namespace cytnx { namespace io { - H5::Group create_group(H5::Group &file, const std::string &path) { + H5::Group create_group(H5::Group &container, const std::string &path) { std::filesystem::path grouppath(path); std::filesystem::path subpath; std::string groupfolder = "/"; for (const auto &part : grouppath) { if (part.empty()) continue; subpath /= part; - groupfolder = subpath.generic_string(); // use generic_string() to avoid incompatibilites between file systems - if (!file.nameExists(groupfolder)) file.createGroup(groupfolder); + groupfolder = subpath.generic_string(); // use generic_string() to avoid incompatibilities + // between file systems + if (!container.nameExists(groupfolder)) container.createGroup(groupfolder); + } + H5::Group group = container.openGroup(groupfolder); + return group; + } + + void save_attribute(const Scalar_list &object, H5::Group &container, const std::string &name, + bool overwrite) { + std::visit( + [&](auto &scalar) { + H5::DataType datatype = Type.get_hdf5_type(scalar); + if (container.attrExists(name)) { + cytnx_error_msg( + !overwrite, + "Attribute '%s' already exists. Use argument overwrite = true to overwrite.\n", name); + H5::Attribute oldattr = container.openAttribute(name); + if (oldattr.getSpace().getSimpleExtentType() == H5S_SCALAR && + oldattr.getDataType() == datatype) { + oldattr.write(datatype, &scalar); + return; + } // else: remove and create again + container.removeAttr(name); + } + H5::Attribute attr = container.createAttribute(name, datatype, H5::DataSpace(H5S_SCALAR)); + attr.write(datatype, &scalar); + }, + object); + } + + void save_attribute(const std::string &object, H5::Group &container, const std::string &name, + bool overwrite) { + H5::StrType str_type = + H5::StrType(H5::PredType::C_S1, object.length() + 1); // include NULL terminator + if (container.attrExists(name)) { + cytnx_error_msg( + !overwrite, + "Attribute '%s' already exists. Use argument overwrite = true to overwrite.\n", name); + H5::Attribute oldattr = container.openAttribute(name); + if (oldattr.getSpace().getSimpleExtentType() == H5S_SCALAR && + oldattr.getStrType() == str_type) { + oldattr.write(str_type, object); + return; + } // else: remove and create again + container.removeAttr(name); + } + H5::Attribute attr = container.createAttribute(name, str_type, H5::DataSpace(H5S_SCALAR)); + attr.write(str_type, object); + } + + void save_dataset(const std::vector &object, H5::Group &container, + const std::string &name) { + hsize_t vecdims[1] = {object.size()}; + H5::DataSpace dataspace = H5::DataSpace(1, vecdims); + H5::StrType str_type = H5::StrType(H5::PredType::C_S1, H5T_VARIABLE); + H5::DataSet dataset = container.createDataSet(name, str_type, dataspace); + if (!object.empty()) { + std::vector c_strings; // H5 needs cstrings + for (const auto &elem : object) { + c_strings.push_back(elem.c_str()); + } + dataset.write(c_strings.data(), str_type); + } + } + + void remove_attribute(H5::Group &container, const std::string &name, bool overwrite) { + if (container.attrExists(name)) { + cytnx_error_msg( + !overwrite, "Attribute '%s' exists. Use argument overwrite = true to remove it.\n", name); + container.removeAttr(name); } - H5::Group location = file.openGroup(groupfolder); - return location; } H5::H5File open(const std::filesystem::path &fname, IoMode mode) { @@ -46,7 +113,7 @@ namespace cytnx { h5file = H5::H5File(fname, H5F_ACC_EXCL, fcpl, fapl); break; case ACC_IN: - h5file = H5::H5File(fname, H5F_ACC_RDONLY, fcpl, fapl); + h5file = H5::H5File(fname, H5F_ACC_RDONLY, fcpl, fapl); break; case ACC_INOUT: if (std::filesystem::exists(fname)) { @@ -62,38 +129,38 @@ namespace cytnx { return h5file; } - // void close(H5::H5File &file) { - // file.close(); + // void close(H5::H5File &container) { + // container.close(); // } - void Save(const savable_class &object, H5::Group &file, const std::string &name, const std::string &path, bool overwrite) { - std::visit([&](const auto &concreteObj) { - H5::Group location = create_group(file, path); - concreteObj.to_hdf5(location, name, overwrite); - }, object); + void Save(const savable_class &object, H5::Group &container, const std::string &name, + const std::string &path, bool overwrite) { + std::visit( + [&](const auto &concreteObj) { + H5::Group group = create_group(container, path); + concreteObj.to_hdf5(group, name, overwrite); + }, + object); } - void Load(savable_class &object, H5::Group &file, const std::string &name, const std::string &path, bool restore_device) { - std::visit([&](auto &concreteObj) { - // open path - H5::Group location; - if (path.empty()) - location = file; - else { - try { - location = file.openGroup(path); - } catch (const H5::Exception &e) { - std::cerr << e.getDetailMsg() << std::endl; - cytnx_error_msg(true, "[ERROR] HDF5 path '%s' not found or is not a group.\n", path.c_str()); - } - } - if constexpr (requires { concreteObj.from_hdf5(location, name, restore_device); }) { - // Class supports restore_device - concreteObj.from_hdf5(location, name, restore_device); - } else { - concreteObj.from_hdf5(location, name); - } - }, object); + void Load(savable_class &object, H5::Group &container, const std::string &name, + const std::string &path) { + std::visit( + [&](auto &concreteObj) { + H5::Group group = (path.empty() ? container : container.openGroup(path)); + concreteObj.from_hdf5(group, name); + }, + object); + } + + void Load(loadable_to_device &object, H5::Group &container, const std::string &name, + const std::string &path, bool restore_device) { + std::visit( + [&](auto &concreteObj) { + H5::Group group = (path.empty() ? container : container.openGroup(path)); + concreteObj.from_hdf5(group, name, restore_device); + }, + object); } } // namespace io diff --git a/src/tn_algo/MPS.cpp b/src/tn_algo/MPS.cpp index 0ff9c9a96..98e1d16fa 100644 --- a/src/tn_algo/MPS.cpp +++ b/src/tn_algo/MPS.cpp @@ -69,9 +69,9 @@ namespace cytnx { groupfolder = subpath.generic_string(); if (!h5file.nameExists(groupfolder)) h5file.createGroup(groupfolder); } - H5::Group location = h5file.openGroup(groupfolder); + H5::Group group = h5file.openGroup(groupfolder); // write data - this->to_hdf5(location, "MPS", overwrite); + this->to_hdf5(group, "MPS", overwrite); h5file.close(); return; } else { // create binary file @@ -131,16 +131,16 @@ namespace cytnx { ext == ".HDF") { // load HDF5 H5::H5File h5file(fname, H5F_ACC_RDONLY); // open group - H5::Group location; + H5::Group group; try { - location = h5file.openGroup(path.empty() ? "/" : path); + group = h5file.openGroup(path.empty() ? "/" : path); } catch (const H5::Exception &e) { std::cerr << e.getDetailMsg() << std::endl; cytnx_error_msg(true, "[ERROR] HDF5 path '%s' not found or is not a group in file '%s'.", path.c_str(), fname.string().c_str()); } // read data - this->from_hdf5(location, "", restore_device); + this->from_hdf5(group, "", restore_device); h5file.close(); } else { // load binary fstream f; @@ -156,10 +156,10 @@ namespace cytnx { this->Load_(std::filesystem::path(fname), path, restore_device); } - void MPS::to_hdf5(H5::Group &location, const std::string &name, const bool overwrite) const { + void MPS::to_hdf5(H5::Group &container, const std::string &name, const bool overwrite) const { cytnx_error_msg(true, "[ERROR] Saving MPS to HDF5 is not implemented yet!%s", "\n"); } - void MPS::from_hdf5(H5::Group &location, const std::string &name, bool restore_device) { + void MPS::from_hdf5(H5::Group &container, const std::string &name, bool restore_device) { cytnx_error_msg(true, "[ERROR] Loading MPS from HDF5 is not implemented yet!%s", "\n"); } From 6952b18ef8cbf9cdca8db804aee20ac3417cc284 Mon Sep 17 00:00:00 2001 From: Manuel Schneider Date: Fri, 22 May 2026 20:21:02 +0800 Subject: [PATCH 30/40] moved more hdf5 saving functions into io namespace to avoid boilerplate --- include/Type.hpp | 32 +++++ include/io.hpp | 82 +++++++++++-- pybind/io_py.cpp | 46 ++++---- src/BlockFermionicUniTensor.cpp | 51 ++++---- src/BlockUniTensor.cpp | 45 ++++--- src/Bond.cpp | 100 +++++----------- src/DenseUniTensor.cpp | 10 +- src/Symmetry.cpp | 12 +- src/Tensor.cpp | 19 ++- src/UniTensor.cpp | 37 ++---- src/backend/Storage.cpp | 19 ++- src/io.cpp | 203 ++++++++++++++++++++++++++++---- 12 files changed, 417 insertions(+), 239 deletions(-) diff --git a/include/Type.hpp b/include/Type.hpp index f52cb065a..7826382f1 100644 --- a/include/Type.hpp +++ b/include/Type.hpp @@ -178,6 +178,38 @@ namespace cytnx { using Scalar_list_gpu = internal::truncate_variant::exclude_first::type; #endif + namespace internal { + /// @cond + // This is create a variant containing vectors of all supported types + template + struct vector_variant; + + template + struct vector_variant> { + using type = std::variant...>; + }; + /// @endcond + } // namespace internal + + // the list of vectors of all supported Scalar types. + using Vector_list = typename internal::vector_variant::type; + + namespace internal { + /// @cond + // This is create a variant containing vectors of vectors of all supported types + template + struct matrix_variant; + + template + struct matrix_variant> { + using type = std::variant>...>; + }; + /// @endcond + } // namespace internal + + // the list of vectors of vectors of all supported Scalar types. + using Matrix_list = typename internal::matrix_variant::type; + // The number of supported types constexpr int N_Type = std::variant_size_v; constexpr int N_fType = 5; diff --git a/include/io.hpp b/include/io.hpp index 4215c8ed0..aafca852d 100644 --- a/include/io.hpp +++ b/include/io.hpp @@ -22,48 +22,104 @@ namespace cytnx { */ namespace io { + namespace internal { + ///@cond + /** + * @brief Check if a dataset of correct type and size exists, otherwise create a new one + * @param[in] datatype datatype to be written + * @param[in] dimensions size of the tensor to be written + * @param[in] container group to create dataset in + * @param[in] name the name of the dataset + * @param[in] overwrite if true, previous data will be overwritten or deleted. + * @returns an opened dataset, compatible for writing + */ + H5::DataSet create_dataset(H5::DataType &datatype, const std::vector &dimensions, + H5::Group &container, const std::string &name, bool overwrite); + ///@endcond + } // namespace internal + /** * @brief Create a group, given a path that can contain subpathes * @details Opens the group or creates it newly * @param[in] container root group * @param[in] path a path that can contain subpathes + * @param[in] recursive if the path can contain subpathes, such that groups in group are opened + * or created * @returns the opened group or a newly created group */ - H5::Group create_group(H5::Group &container, const std::string &path); + H5::Group create_group(H5::Group &container, const std::string &path, bool recursive = true); + + /** + * @brief Remove a group if dataset if it exists + * @param[in] container group or file from which the attribute will be removed + * @param[in] name the name of the dataset or group + * @param[in] overwrite if true, the data will be removed. If false, an error is thrown if the + * data exists + * @returns true if the name existed, false otherwise + */ + bool unlink(H5::Group &container, const std::string &name, bool overwrite = true); /** * @brief Save data to an attribute * @param[in] object a scalar of any type that is supported by cytnx - * @param[in] container group; should be opened for writing + * @param[in] container file, group or dataset; should be opened for writing * @param[in] name the name of the attribute * @param[in] overwrite if true, overwrite previous data in the container */ - void save_attribute(const Scalar_list &object, H5::Group &container, const std::string &name, + void save_attribute(const Scalar_list &object, H5::H5Object &container, const std::string &name, bool overwrite = false); /** * @brief Save string to an attribute * @see save_attribute(const Scalar_list &object, H5::Group &container, const std::string &name) */ - void save_attribute(const std::string &object, H5::Group &container, const std::string &name, + void save_attribute(const std::string &object, H5::H5Object &container, const std::string &name, bool overwrite = false); - /** - * @brief Save data to a dataset - * @param[in] object a vector of supported types - * @param[in] container group; should be opened for writing - * @param[in] name the name of the dataset + * @brief Save vector of strings to an attribute + * @see save_attribute(const Scalar_list &object, H5::Group &container, const std::string &name) */ - void save_dataset(const std::vector &object, H5::Group &container, - const std::string &name); + void save_attribute(const std::vector &object, H5::H5Object &container, + const std::string &name, bool overwrite = false); /** * @brief Remove an attribute if it exists - * @param[in] container group or file from which the attribute will be removed + * @param[in] container file, group or dataset from which the attribute will be removed * @param[in] name the name of the attribute * @param[in] overwrite if true, the attribute will be removed. If false, an error is thrown if * the attribute exists + * @returns true if the attribute existed, false otherwise + */ + bool remove_attribute(H5::H5Object &container, const std::string &name, bool overwrite = true); + + /** + * @brief Save data to a dataset + * @param[in] object a vector of supported types + * @param[in] container file, group or dataset; should be opened for writing + * @param[in] name the name of the dataset + * @param[in] overwrite if true, overwrite previous data in the container + * @returns the dataset that was written to + */ + H5::DataSet save_dataset(const Vector_list &object, H5::Group &container, + const std::string &name, bool overwrite = false); + /** + * @brief Save string to a dataset + * @see save_dataset(const Vector_list &object, H5::Group &container, const std::string &name, + * bool overwrite) + */ + H5::DataSet save_dataset(const std::vector &object, H5::Group &container, + const std::string &name, bool overwrite = false); + /** + * @brief Save a matrix of a supported type + * @param[in] object a vector of a vector based on a supported type; if the outer vector has \em + * rownum elements, and all inner vectors have \em colnum elements, then the result will be + * written as a \em rownum \em x \em colnum matrix. + * @see save_dataset(const Vector_list &object, H5::Group &container, const std::string &name, + * bool overwrite) + * @note All inner vectors need to have the same length, such that the data structure + * corresponds to a rectangular matrix. */ - void remove_attribute(H5::Group &container, const std::string &name, bool overwrite = true); + H5::DataSet save_dataset(const Matrix_list &object, H5::Group &container, + const std::string &name, bool overwrite = false); /** * @brief Input/output file access mode for HDF5 files. diff --git a/pybind/io_py.cpp b/pybind/io_py.cpp index 98df27be5..0c0fbcf86 100644 --- a/pybind/io_py.cpp +++ b/pybind/io_py.cpp @@ -30,7 +30,25 @@ void io_binding(py::module &m) { .value("ACC_INOUT", cytnx::io::IoMode::ACC_INOUT) .export_values(); - py::class_(m_io, "Group") + // H5Object is an abstract class for all H5 objects that can contain attributes + py::class_>(m_io, "H5Object") + // Attribute management + .def( + "attrExists", + [](const H5::H5Object &self, const std::string &name) { return self.attrExists(name); }, + py::arg("name")) + .def( + "removeAttr", + [](const H5::H5Object &self, const std::string &name) { self.removeAttr(name); }, + py::arg("name")) + .def( + "renameAttr", + [](const H5::H5Object &self, const std::string &old_n, const std::string &new_n) { + self.renameAttr(old_n, new_n); + }, + py::arg("old_name"), py::arg("new_name")); // end of object line + + py::class_(m_io, "Group") // construction .def(py::init<>()) .def(py::init(), py::arg("original")) @@ -53,21 +71,6 @@ void io_binding(py::module &m) { [](const H5::Group &self, const std::string &name) { return self.openGroup(name); }, py::arg("name")) - // Attribute management - .def( - "attrExists", - [](const H5::Group &self, const std::string &name) { return self.attrExists(name); }, - py::arg("name")) - .def( - "removeAttr", [](const H5::Group &self, const std::string &name) { self.removeAttr(name); }, - py::arg("name")) - .def( - "renameAttr", - [](const H5::Group &self, const std::string &old_n, const std::string &new_n) { - self.renameAttr(old_n, new_n); - }, - py::arg("old_name"), py::arg("new_name")) - // link/object management .def( "moveLink", @@ -129,12 +132,13 @@ void io_binding(py::module &m) { py::arg("name")) .def("getObjCount", [](H5::H5File &self) { return self.getObjCount(); }); // end of object line + // implementations from cytnx::io m_io.def( "create_group", - [](H5::Group &container, const std::string &path) { - return cytnx::io::create_group(container, path); + [](H5::Group &container, const std::string &path, bool recursive) { + return cytnx::io::create_group(container, path, recursive); }, - py::arg("container"), py::arg("path")); + py::arg("container"), py::arg("path"), py::arg("recursive") = true); m_io.def( "open", @@ -148,12 +152,12 @@ void io_binding(py::module &m) { py::arg("path") = "", py::arg("overwrite") = false); m_io.def( - "Load", + "c_Load", [](cytnx::io::savable_class &object, H5::Group &container, const std::string &name, const std::string &path) { cytnx::io::Load(object, container, name, path); }, py::arg("object"), py::arg("container"), py::arg("name"), py::arg("path") = ""); m_io.def( - "Load", + "c_Load", [](cytnx::io::loadable_to_device &object, H5::Group &container, const std::string &name, const std::string &path, bool restore_device) { cytnx::io::Load(object, container, name, path, restore_device); }, diff --git a/src/BlockFermionicUniTensor.cpp b/src/BlockFermionicUniTensor.cpp index 47bb1681e..cc4460285 100644 --- a/src/BlockFermionicUniTensor.cpp +++ b/src/BlockFermionicUniTensor.cpp @@ -10,6 +10,8 @@ #include #include #include +#include "io.hpp" + using namespace std; #ifdef BACKEND_TORCH @@ -2370,39 +2372,36 @@ namespace cytnx { } void BlockFermionicUniTensor::to_hdf5_dispatch(H5::Group &container, const bool overwrite) const { + // delete all entries that could be written by one of the UniTensor implementations; + io::remove_attribute(container, "directed", overwrite); + io::unlink(container, "Tensor", overwrite); + // blocks; write to group - if (!this->_blocks.empty()) { - H5::Group dir = container.createGroup("blocks"); - for (int i = 0; i < this->_blocks.size(); i++) { - if (this->_signflip[i]) { - Tensor block = -this->_blocks[i]; - block.to_hdf5(dir, "Tensor" + std::to_string(i), overwrite); + if (this->_blocks.empty()) { + io::unlink(container, "blocks", overwrite); + } else { + H5::Group dir = io::create_group(container, "blocks", false); + hsize_t blknum = 0; + for (; blknum < this->_blocks.size(); blknum++) { + if (this->_signflip[blknum]) { + Tensor block = -this->_blocks[blknum]; + block.to_hdf5(dir, "Tensor" + std::to_string(blknum), overwrite); } else { - this->_blocks[i].to_hdf5(dir, "Tensor" + std::to_string(i), overwrite); + this->_blocks[blknum].to_hdf5(dir, "Tensor" + std::to_string(blknum), overwrite); } } + // delete further blocks if they exist + while (io::remove_attribute(dir, "Tensor" + std::to_string(blknum), overwrite)) blknum++; } // inner_to_outer_idx; write matrix (blocknum x rank) - if (!this->_inner_to_outer_idx.empty()) { - hsize_t blocknum = this->_inner_to_outer_idx.size(); - hsize_t rank = this->_bonds.size(); - std::vector flat(blocknum * rank); // flatten vector - for (hsize_t i = 0; i < blocknum; ++i) { - std::copy(this->_inner_to_outer_idx[i].begin(), this->_inner_to_outer_idx[i].end(), - flat.begin() + i * rank); - } - hsize_t matdims[2] = {blocknum, rank}; - H5::DataSpace dataspace(2, matdims); - H5::DataType datatype = Type.get_hdf5_type(flat[0]); - H5::DataSet dataset = container.createDataSet("block_to_sectors", datatype, dataspace); - dataset.write(flat.data(), datatype); + if (this->_inner_to_outer_idx.empty()) { + io::unlink(container, "block_to_sectors", overwrite); + } else { + H5::DataSet dataset = + io::save_dataset(this->_inner_to_outer_idx, container, "block_to_sectors", overwrite); // label axes - char labels[2][6] = {"block", "bond"}; - H5::StrType str_type(H5::PredType::C_S1, 6); - hsize_t attr_dims[1] = {2}; - H5::DataSpace attr_space(1, attr_dims); - H5::Attribute attr = dataset.createAttribute("axis_labels", str_type, attr_space); - attr.write(str_type, labels); + io::save_attribute(std::vector{"block", "bond"}, dataset, "axis_labels", + overwrite); } } diff --git a/src/BlockUniTensor.cpp b/src/BlockUniTensor.cpp index fb8669f0a..58369e91d 100644 --- a/src/BlockUniTensor.cpp +++ b/src/BlockUniTensor.cpp @@ -10,6 +10,8 @@ #include #include #include +#include "io.hpp" + using namespace std; #ifdef BACKEND_TORCH @@ -1580,34 +1582,31 @@ namespace cytnx { } void BlockUniTensor::to_hdf5_dispatch(H5::Group &container, const bool overwrite) const { + // delete all entries that could be written by one of the UniTensor implementations; + io::remove_attribute(container, "directed", overwrite); + io::unlink(container, "Tensor", overwrite); + // blocks; write to group - if (!this->_blocks.empty()) { - H5::Group dir = container.createGroup("blocks"); - for (int i = 0; i < this->_blocks.size(); i++) { - this->_blocks[i].to_hdf5(dir, "Tensor" + std::to_string(i), overwrite); + if (this->_blocks.empty()) { + io::unlink(container, "blocks", overwrite); + } else { + H5::Group dir = io::create_group(container, "blocks", false); + hsize_t blknum = 0; + for (; blknum < this->_blocks.size(); blknum++) { + this->_blocks[blknum].to_hdf5(dir, "Tensor" + std::to_string(blknum), overwrite); } + // delete further blocks if they exist + while (io::remove_attribute(dir, "Tensor" + std::to_string(blknum), overwrite)) blknum++; } // inner_to_outer_idx; write matrix (blocknum x rank) - if (!this->_inner_to_outer_idx.empty()) { - hsize_t blocknum = this->_inner_to_outer_idx.size(); - hsize_t rank = this->_bonds.size(); - std::vector flat(blocknum * rank); // flatten vector - for (hsize_t i = 0; i < blocknum; ++i) { - std::copy(this->_inner_to_outer_idx[i].begin(), this->_inner_to_outer_idx[i].end(), - flat.begin() + i * rank); - } - hsize_t matdims[2] = {blocknum, rank}; - H5::DataSpace dataspace(2, matdims); - H5::DataType datatype = Type.get_hdf5_type(flat[0]); - H5::DataSet dataset = container.createDataSet("block_to_sectors", datatype, dataspace); - dataset.write(flat.data(), datatype); + if (this->_inner_to_outer_idx.empty()) { + io::unlink(container, "block_to_sectors", overwrite); + } else { + H5::DataSet dataset = + io::save_dataset(this->_inner_to_outer_idx, container, "block_to_sectors", overwrite); // label axes - char labels[2][6] = {"block", "bond"}; - H5::StrType str_type(H5::PredType::C_S1, 6); - hsize_t attr_dims[1] = {2}; - H5::DataSpace attr_space(1, attr_dims); - H5::Attribute attr = dataset.createAttribute("axis_labels", str_type, attr_space); - attr.write(str_type, labels); + io::save_attribute(std::vector{"block", "bond"}, dataset, "axis_labels", + overwrite); } } diff --git a/src/Bond.cpp b/src/Bond.cpp index 1c21560f9..69194df5b 100644 --- a/src/Bond.cpp +++ b/src/Bond.cpp @@ -610,84 +610,40 @@ namespace cytnx { if (name.empty()) rootgroup = container; else { - if (container.nameExists(name)) { - rootgroup = container.openGroup(name); - } else { - rootgroup = container.createGroup(name); - } + rootgroup = io::create_group(container, name, false); } - if (overwrite) { // delete previous data - // remove attributes - if (rootgroup.attrExists("dimension")) rootgroup.removeAttr("dimension"); - if (rootgroup.attrExists("type")) rootgroup.removeAttr("type"); - // remove datasets - if (rootgroup.nameExists("degeneracies")) rootgroup.unlink("degeneracies"); - if (rootgroup.nameExists("quantum_numbers")) rootgroup.unlink("quantum_numbers"); - // remove groups and its contents recursively - if (rootgroup.nameExists("symmetries")) rootgroup.unlink("symmetries"); - } - - H5::DataType datatype; - H5::Attribute attr; - H5::DataSet dataset; - H5::DataSpace dataspace; - H5::StrType str_type; + // write attributes + io::save_attribute(this->_impl->_dim, rootgroup, "dimension", overwrite); + io::save_attribute(bondtype_to_string.at(this->_impl->_type), rootgroup, "type", overwrite); - // dimension, write as attribute - auto dim = this->_impl->_dim; - datatype = Type.get_hdf5_type(dim); - attr = rootgroup.createAttribute("dimension", datatype, H5::DataSpace(H5S_SCALAR)); - attr.write(datatype, &dim); + hsize_t sectordim = this->_impl->_qnums.size(); + if (sectordim < 1) { // no symmetries + io::unlink(rootgroup, "degeneracies", overwrite); + io::unlink(rootgroup, "quantum_numbers", overwrite); + io::unlink(rootgroup, "symmetries", overwrite); + return; + } + // degs; write vector + H5::DataSet dataset = + io::save_dataset(this->_impl->_degs, rootgroup, "degeneracies", overwrite); + // label axis + io::save_attribute("sector", dataset, "axis_label", overwrite); - // type, write as string - std::string typestr = bondtype_to_string.at(this->_impl->_type); - str_type = H5::StrType(H5::PredType::C_S1, typestr.length() + 1); - dataspace = H5::DataSpace(H5S_SCALAR); - attr = rootgroup.createAttribute("type", str_type, dataspace); - attr.write(str_type, typestr); + // qnums; write matrix (dim x qnumdim) + dataset = io::save_dataset(this->_impl->_qnums, rootgroup, "quantum_numbers", overwrite); + // label axes + io::save_attribute(std::vector{"sector", "symmetry"}, dataset, "axis_labels", + overwrite); - // degs; write vector - hsize_t sectordim = this->_impl->_qnums.size(); - if (sectordim > 0) { // only with symmetries - hsize_t vecdims[1] = {sectordim}; - dataspace = H5::DataSpace(1, vecdims); - datatype = Type.get_hdf5_type(this->_impl->_degs[0]); - dataset = rootgroup.createDataSet("degeneracies", Type.get_hdf5_type(this->_impl->_degs[0]), - dataspace); - dataset.write(this->_impl->_degs.data(), datatype); - // label axis - dataspace = H5::DataSpace(H5S_SCALAR); - str_type = H5::StrType(H5::PredType::C_S1, 7); - attr = dataset.createAttribute("axis_label", str_type, dataspace); - attr.write(str_type, "sector"); - - // qnums; write matrix (dim x qnumdim) - hsize_t qnumdim = this->_impl->_syms.size(); - std::vector flat(sectordim * qnumdim); // flatten vector - for (hsize_t i = 0; i < sectordim; ++i) { - std::copy(this->_impl->_qnums[i].begin(), this->_impl->_qnums[i].end(), - flat.begin() + i * qnumdim); - } - hsize_t matdims[2] = {sectordim, qnumdim}; - dataspace = H5::DataSpace(2, matdims); - datatype = Type.get_hdf5_type(flat[0]); - dataset = rootgroup.createDataSet("quantum_numbers", datatype, dataspace); - dataset.write(flat.data(), datatype); - // label axes - char labels[2][9] = {"sector", "symmetry"}; - str_type = H5::StrType(H5::PredType::C_S1, 9); - hsize_t attr_dims[1] = {2}; - H5::DataSpace attr_space(1, attr_dims); - attr = dataset.createAttribute("axis_labels", str_type, attr_space); - attr.write(str_type, labels); - - // symmetries - H5::Group symloc = rootgroup.createGroup("symmetries"); - for (int i = 0; i < this->_impl->_syms.size(); i++) { - this->_impl->_syms[i].to_hdf5(symloc, "Symmetry" + std::to_string(i), overwrite); - } + // symmetries + H5::Group symloc = io::create_group(rootgroup, "symmetries", false); + hsize_t symnum = 0; + for (; symnum < this->_impl->_syms.size(); symnum++) { + this->_impl->_syms[symnum].to_hdf5(symloc, "Symmetry" + std::to_string(symnum), overwrite); } + // delete further symmetries if they exist + while (io::remove_attribute(symloc, "Symmetry" + std::to_string(symnum), overwrite)) symnum++; } void Bond::from_hdf5(H5::Group &container, const std::string &name) { H5::Group rootgroup = (name.empty() ? container : container.openGroup(name)); diff --git a/src/DenseUniTensor.cpp b/src/DenseUniTensor.cpp index 50636d3ef..29e7e9d2f 100644 --- a/src/DenseUniTensor.cpp +++ b/src/DenseUniTensor.cpp @@ -2,10 +2,12 @@ #include "utils/utils.hpp" #include "Generator.hpp" +#include "io.hpp" #include "linalg.hpp" #include #include #include + typedef cytnx::Accessor ac; using namespace std; @@ -1283,10 +1285,12 @@ namespace cytnx { void DenseUniTensor::normalize_() { this->_block /= linalg::Norm(this->_block); } void DenseUniTensor::to_hdf5_dispatch(H5::Group &container, const bool overwrite) const { + // delete all entries that could be written by one of the UniTensor implementations; + io::unlink(container, "block_to_sectors", overwrite); + io::unlink(container, "blocks", overwrite); + // is_tag, write as attribute - H5::DataType datatype = Type.get_hdf5_type(this->_is_tag); - H5::Attribute attr = container.createAttribute("directed", datatype, H5::DataSpace(H5S_SCALAR)); - attr.write(datatype, &this->_is_tag); + io::save_attribute(this->_is_tag, container, "directed", overwrite); this->_block.to_hdf5(container, "Tensor", overwrite); } diff --git a/src/Symmetry.cpp b/src/Symmetry.cpp index 029f3fecc..7e11c89bd 100644 --- a/src/Symmetry.cpp +++ b/src/Symmetry.cpp @@ -8,6 +8,8 @@ #include "H5Cpp.h" +#include "io.hpp" + using namespace std; namespace cytnx { @@ -390,15 +392,7 @@ namespace cytnx { void cytnx::Symmetry::to_hdf5(H5::Group &container, const std::string &name, const bool overwrite) const { - if (overwrite) { // delete previous data - if (container.attrExists(name)) container.removeAttr(name); - } - - std::string symname = this->getname(); - H5::StrType str_type(H5::PredType::C_S1, symname.length() + 1); - H5::DataSpace dataspace = H5::DataSpace(H5S_SCALAR); - H5::Attribute attr = container.createAttribute(name, str_type, dataspace); - attr.write(str_type, symname); + io::save_attribute(this->getname(), container, name, overwrite); } void cytnx::Symmetry::from_hdf5(H5::Group &container, const std::string &name) { H5::Attribute attr = container.openAttribute(name); diff --git a/src/Tensor.cpp b/src/Tensor.cpp index f3b7b08db..3590daa10 100644 --- a/src/Tensor.cpp +++ b/src/Tensor.cpp @@ -4,6 +4,7 @@ #include #include "H5Cpp.h" +#include "io.hpp" #include "Type.hpp" #include "linalg.hpp" #include "utils/is.hpp" @@ -568,23 +569,17 @@ namespace cytnx { } void Tensor::to_hdf5(H5::Group &container, const std::string &name, const bool overwrite) const { - if (overwrite) { // delete previous data - if (container.nameExists(name)) container.unlink(name); - } - Tensor ten = this->contiguous(); - std::vector dims(this->shape().begin(), this->shape().end()); - H5::DataSpace dataspace(dims.size(), dims.data()); H5::DataType datatype = Type.dtype_to_hdf5_type(this->dtype()); - H5::DataSet dataset = container.createDataSet(name, datatype, dataspace); + H5::DataSet dataset = + io::internal::create_dataset(datatype, this->shape(), container, name, overwrite); ten.storage().data_to_hdf5(dataset, datatype); - int device = this->device(); - if (device != Device.cpu) { - H5::Attribute attr = - dataset.createAttribute("device", H5::PredType::NATIVE_INT, H5::DataSpace(H5S_SCALAR)); - attr.write(H5::PredType::NATIVE_INT, &device); + if (this->device() != Device.cpu) { + io::save_attribute(this->device(), dataset, "device", overwrite); + } else { + io::remove_attribute(dataset, "device", overwrite); } } diff --git a/src/UniTensor.cpp b/src/UniTensor.cpp index c25ad50e2..2e55230ae 100644 --- a/src/UniTensor.cpp +++ b/src/UniTensor.cpp @@ -205,24 +205,7 @@ namespace cytnx { if (name.empty()) rootgroup = container; else { - if (container.nameExists(name)) { - rootgroup = container.openGroup(name); - } else { - rootgroup = container.createGroup(name); - } - } - - if (overwrite) { // delete previous data - // delete all entries that could be written by one of the implementations; - // remove attributes - if (rootgroup.attrExists("directed")) rootgroup.removeAttr("directed"); - // remove datasets - if (rootgroup.nameExists("labels")) rootgroup.unlink("labels"); - if (rootgroup.nameExists("Tensor")) rootgroup.unlink("Tensor"); - if (rootgroup.nameExists("block_to_sectors")) rootgroup.unlink("block_to_sectors"); - // remove groups and its contents recursively - if (rootgroup.nameExists("bonds")) rootgroup.unlink("bonds"); - if (rootgroup.nameExists("blocks")) rootgroup.unlink("blocks"); + rootgroup = io::create_group(container, name, false); } // write attributes @@ -235,16 +218,20 @@ namespace cytnx { io::save_attribute(this->_impl->_rowrank, rootgroup, "rowrank", overwrite); io::save_attribute(this->_impl->_name, rootgroup, "name", overwrite); - // write datasets - if (!this->_impl->_labels.empty()) { - io::save_dataset(this->_impl->_labels, rootgroup, "labels"); + // write labels + if (this->_impl->_labels.empty()) { + io::unlink(rootgroup, "labels", overwrite); + } else { + io::save_dataset(this->_impl->_labels, rootgroup, "labels", overwrite); } - // write groups - if (!this->_impl->_bonds.empty()) { - H5::Group dir = rootgroup.createGroup("bonds"); + // write bonds + if (this->_impl->_bonds.empty()) { + io::unlink(rootgroup, "bonds", overwrite); + } else { + H5::Group bondloc = io::create_group(rootgroup, "bonds", false); for (int i = 0; i < this->_impl->_bonds.size(); i++) { - this->_impl->_bonds[i].to_hdf5(dir, "Bond" + std::to_string(i), overwrite); + this->_impl->_bonds[i].to_hdf5(bondloc, "Bond" + std::to_string(i), overwrite); } } diff --git a/src/backend/Storage.cpp b/src/backend/Storage.cpp index 06fa4b1c9..a3ad78174 100644 --- a/src/backend/Storage.cpp +++ b/src/backend/Storage.cpp @@ -5,6 +5,8 @@ #include "H5Cpp.h" +#include "io.hpp" + using namespace std; namespace cytnx { @@ -229,20 +231,15 @@ namespace cytnx { } void Storage::to_hdf5(H5::Group &container, const std::string &name, const bool overwrite) const { - if (overwrite) { // delete previous data - if (container.nameExists(name)) container.unlink(name); - } - - hsize_t Nelem = this->size(); - H5::DataSpace dataspace(1, &Nelem); H5::DataType datatype = Type.dtype_to_hdf5_type(this->dtype()); - H5::DataSet dataset = container.createDataSet(name, datatype, dataspace); + H5::DataSet dataset = + io::internal::create_dataset(datatype, {this->size()}, container, name, overwrite); this->data_to_hdf5(dataset, datatype); + if (this->device() != Device.cpu) { - H5::Attribute attr = - dataset.createAttribute("device", H5::PredType::NATIVE_INT, H5::DataSpace(H5S_SCALAR)); - int device = this->device(); - attr.write(H5::PredType::NATIVE_INT, &device); + io::save_attribute(this->device(), dataset, "device", overwrite); + } else { + io::remove_attribute(dataset, "device", overwrite); } } diff --git a/src/io.cpp b/src/io.cpp index e93c4e6e0..70b2d089f 100644 --- a/src/io.cpp +++ b/src/io.cpp @@ -9,25 +9,75 @@ namespace cytnx { namespace io { - H5::Group create_group(H5::Group &container, const std::string &path) { - std::filesystem::path grouppath(path); - std::filesystem::path subpath; - std::string groupfolder = "/"; - for (const auto &part : grouppath) { - if (part.empty()) continue; - subpath /= part; - groupfolder = subpath.generic_string(); // use generic_string() to avoid incompatibilities - // between file systems - if (!container.nameExists(groupfolder)) container.createGroup(groupfolder); + namespace internal { + H5::DataSet create_dataset(H5::DataType &datatype, const std::vector &dimensions, + H5::Group &container, const std::string &name, bool overwrite) { + H5::DataSet dataset; + // check if old dataset can be overwritten + bool create_new = true; + if (container.nameExists(name)) { // we can overwrite the data if type and size match + cytnx_error_msg( + !overwrite, + "Dataset (or group) '%s' already exists. Use argument overwrite = true to overwrite.\n", + name); + if (container.childObjType(name) == H5O_TYPE_DATASET) { + dataset = container.openDataSet(name); + H5::DataSpace olddataspace = dataset.getSpace(); + auto rank = olddataspace.getSimpleExtentNdims(); + if (olddataspace.getSimpleExtentType() == H5S_SIMPLE && + dataset.getDataType() == datatype && rank == dimensions.size()) { + create_new = false; + std::vector olddims(rank); + olddataspace.getSimpleExtentDims(olddims.data()); + for (int i = 0; i < rank; i++) { + if (olddims[i] != dimensions[i]) { + create_new = true; + break; + } + } + } + if (!create_new) return dataset; + // otherwise, create a new dataset one now + dataset.close(); + } + container.unlink(name); + } + // create a new dataset + H5::DataSpace dataspace(dimensions.size(), dimensions.data()); + dataset = container.createDataSet(name, datatype, dataspace); + return dataset; + } + } // namespace internal + + H5::Group create_group(H5::Group &container, const std::string &path, bool recursive) { + if (recursive) { + std::filesystem::path grouppath(path); + std::filesystem::path subpath; + std::string groupfolder = "/"; + for (const auto &part : grouppath) { + if (part.empty()) continue; + subpath /= part; + groupfolder = subpath.generic_string(); // use generic_string() to avoid + // incompatibilities between file systems + if (!container.nameExists(groupfolder)) container.createGroup(groupfolder); + } + H5::Group group = container.openGroup(groupfolder); + return group; + } else { // simple version without recursion + H5::Group group; + if (container.nameExists(path)) { + group = container.openGroup(path); + } else { + group = container.createGroup(path); + } + return group; } - H5::Group group = container.openGroup(groupfolder); - return group; } - void save_attribute(const Scalar_list &object, H5::Group &container, const std::string &name, + void save_attribute(const Scalar_list &object, H5::H5Object &container, const std::string &name, bool overwrite) { std::visit( - [&](auto &scalar) { + [&](const auto &scalar) { H5::DataType datatype = Type.get_hdf5_type(scalar); if (container.attrExists(name)) { cytnx_error_msg( @@ -47,7 +97,7 @@ namespace cytnx { object); } - void save_attribute(const std::string &object, H5::Group &container, const std::string &name, + void save_attribute(const std::string &object, H5::H5Object &container, const std::string &name, bool overwrite) { H5::StrType str_type = H5::StrType(H5::PredType::C_S1, object.length() + 1); // include NULL terminator @@ -67,27 +117,132 @@ namespace cytnx { attr.write(str_type, object); } - void save_dataset(const std::vector &object, H5::Group &container, - const std::string &name) { + void save_attribute(const std::vector &object, H5::H5Object &container, + const std::string &name, bool overwrite) { + hsize_t vecdims[1] = {object.size()}; + H5::DataSpace dataspace = H5::DataSpace(1, vecdims); + H5::StrType str_type = H5::StrType(H5::PredType::C_S1, H5T_VARIABLE); + if (container.attrExists(name)) { // simply delete old dataset + cytnx_error_msg( + !overwrite, + "Attribute '%s' already exists. Use argument overwrite = true to overwrite.\n", name); + container.removeAttr(name); + } + // create a new dataset + H5::Attribute attr = container.createAttribute(name, str_type, dataspace); + std::vector c_strings; // H5 needs cstrings + c_strings.reserve(object.size()); + for (const auto &elem : object) { + c_strings.push_back(elem.c_str()); + } + attr.write(str_type, c_strings.data()); + } + + H5::DataSet save_dataset(const Vector_list &object, H5::Group &container, + const std::string &name, bool overwrite) { + H5::DataSet dataset; + std::visit( + [&](const auto &vec) { + cytnx_error_msg(vec.empty(), "Cannot write an empty vector to a dataset!\s", "\n"); + + H5::DataType datatype = Type.get_hdf5_type(vec[0]); + dataset = internal::create_dataset(datatype, {vec.size()}, container, name, overwrite); + + using Value_type = typename std::decay_t::value_type; + if constexpr (std::is_same_v) { + // H5cpp writes bool as unsigned char + std::vector flat_bools(vec.begin(), vec.end()); + dataset.write(flat_bools.data(), datatype); + } else { + dataset.write(vec.data(), datatype); + } + }, + object); + return dataset; + } + + H5::DataSet save_dataset(const std::vector &object, H5::Group &container, + const std::string &name, bool overwrite) { hsize_t vecdims[1] = {object.size()}; H5::DataSpace dataspace = H5::DataSpace(1, vecdims); H5::StrType str_type = H5::StrType(H5::PredType::C_S1, H5T_VARIABLE); + if (container.nameExists(name)) { // simply delete old dataset + cytnx_error_msg( + !overwrite, + "Dataset (or group) '%s' already exists. Use argument overwrite = true to overwrite.\n", + name); + container.unlink(name); + } + // create a new dataset H5::DataSet dataset = container.createDataSet(name, str_type, dataspace); - if (!object.empty()) { - std::vector c_strings; // H5 needs cstrings - for (const auto &elem : object) { - c_strings.push_back(elem.c_str()); - } - dataset.write(c_strings.data(), str_type); + std::vector c_strings; // H5 needs cstrings + c_strings.reserve(object.size()); + for (const auto &elem : object) { + c_strings.push_back(elem.c_str()); } + dataset.write(c_strings.data(), str_type); + return dataset; } - void remove_attribute(H5::Group &container, const std::string &name, bool overwrite) { + bool remove_attribute(H5::H5Object &container, const std::string &name, bool overwrite) { if (container.attrExists(name)) { cytnx_error_msg( !overwrite, "Attribute '%s' exists. Use argument overwrite = true to remove it.\n", name); container.removeAttr(name); + return true; + } + return false; + } + + H5::DataSet save_dataset(const Matrix_list &object, H5::Group &container, + const std::string &name, bool overwrite) { + H5::DataSet dataset; + std::visit( + [&](const auto &vec) { + cytnx_error_msg(vec.empty() || vec[0].empty(), + "Cannot write an empty vector to a dataset!\s", "\n"); + hsize_t rownum = vec.size(); + hsize_t colnum = vec[0].size(); + for (hsize_t i = 1; i < vec.size(); ++i) { + cytnx_error_msg(vec[i].size() != colnum, + "[ERROR] object[%d] has different size than object[0]. A rectanglular " + "matrix is expected as input!\n", + i); + } + + H5::DataType datatype = Type.get_hdf5_type(vec[0][0]); + dataset = + internal::create_dataset(datatype, {rownum, colnum}, container, name, overwrite); + + using Inner_vec_type = typename std::decay_t::value_type; + using Scalar_type = typename Inner_vec_type::value_type; + if constexpr (std::is_same_v) { + std::vector flat(rownum * colnum); // flatten vector + for (hsize_t i = 0; i < vec.size(); ++i) { + std::copy(vec[i].begin(), vec[i].end(), flat.begin() + i * colnum); + } + dataset.write(flat.data(), datatype); + } else { + std::vector flat(rownum * colnum); // flatten vector + for (hsize_t i = 0; i < vec.size(); ++i) { + std::copy(vec[i].begin(), vec[i].end(), flat.begin() + i * colnum); + } + dataset.write(flat.data(), datatype); + } + }, + object); + return dataset; + } + + bool unlink(H5::Group &container, const std::string &name, bool overwrite) { + if (container.nameExists(name)) { + cytnx_error_msg( + !overwrite, "Dataset or group '%s' exists. Use argument overwrite = true to remove it.\n", + name); + container.unlink(name); + return true; } + return false; } H5::H5File open(const std::filesystem::path &fname, IoMode mode) { From fce5e49cf25d0bb1d45c9bf8713afe127a3caddc Mon Sep 17 00:00:00 2001 From: Manuel Schneider Date: Fri, 22 May 2026 21:26:15 +0800 Subject: [PATCH 31/40] fixed Load for UniTensor, Storage, MPS --- pybind/io_py.cpp | 42 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/pybind/io_py.cpp b/pybind/io_py.cpp index 0c0fbcf86..ed4d55180 100644 --- a/pybind/io_py.cpp +++ b/pybind/io_py.cpp @@ -19,6 +19,25 @@ using namespace cytnx; #ifdef BACKEND_TORCH #else +// Helper for the three polymorphic wrapper structures +template +void InPlaceLoad(T &object, H5::Group &container, const std::string &name, + const std::string &path) { + cytnx::io::savable_class temp_savable = T(); + cytnx::io::Load(temp_savable, container, name, path); + T &loaded_tensor = std::get(temp_savable); + object = std::move(loaded_tensor); +} + +template +void InPlaceLoadDevice(T &object, H5::Group &container, const std::string &name, + const std::string &path, bool restore_device) { + cytnx::io::loadable_to_device temp_savable = T(); + cytnx::io::Load(temp_savable, container, name, path, restore_device); + T &loaded_tensor = std::get(temp_savable); + object = std::move(loaded_tensor); +} + void io_binding(py::module &m) { // [Submodule io] pybind11::module m_io = m.def_submodule("io", "Input/Output related."); @@ -151,13 +170,32 @@ void io_binding(py::module &m) { m_io.def("Save", &cytnx::io::Save, py::arg("object"), py::arg("container"), py::arg("name"), py::arg("path") = "", py::arg("overwrite") = false); + // ---- Load function ---- + // Wrapper types that break references without an in-place move + m_io.def("Load", &InPlaceLoad, py::arg("object"), py::arg("container"), + py::arg("name"), py::arg("path") = ""); + m_io.def("Load", &InPlaceLoad, py::arg("object"), py::arg("container"), + py::arg("name"), py::arg("path") = ""); + m_io.def("Load", &InPlaceLoad, py::arg("object"), py::arg("container"), + py::arg("name"), py::arg("path") = ""); + // All other types (must come after the previous more specific ones) m_io.def( - "c_Load", + "Load", [](cytnx::io::savable_class &object, H5::Group &container, const std::string &name, const std::string &path) { cytnx::io::Load(object, container, name, path); }, py::arg("object"), py::arg("container"), py::arg("name"), py::arg("path") = ""); + + // ---- Load function for objects that can be restored on a given device ---- + // Wrapper types that break references without an in-place move + m_io.def("Load", &InPlaceLoadDevice, py::arg("object"), py::arg("container"), + py::arg("name"), py::arg("path") = "", py::arg("restore_device")); + m_io.def("Load", &InPlaceLoadDevice, py::arg("object"), py::arg("container"), + py::arg("name"), py::arg("path") = "", py::arg("restore_device")); + m_io.def("Load", &InPlaceLoadDevice, py::arg("object"), py::arg("container"), + py::arg("name"), py::arg("path") = "", py::arg("restore_device")); + // All other types (must come after the previous more specific ones) m_io.def( - "c_Load", + "Load", [](cytnx::io::loadable_to_device &object, H5::Group &container, const std::string &name, const std::string &path, bool restore_device) { cytnx::io::Load(object, container, name, path, restore_device); }, From 88353a08bb3365d189fedf3cd47c60265777b2be Mon Sep 17 00:00:00 2001 From: Manuel Schneider Date: Sat, 23 May 2026 03:28:12 +0800 Subject: [PATCH 32/40] implemented load functions for different datatypes in namespace io this simplifies and unifies from_hdf5 load functions significantly --- include/io.hpp | 89 ++++++++++++++++++++++++++++++++- src/BlockFermionicUniTensor.cpp | 34 +++---------- src/BlockUniTensor.cpp | 34 +++---------- src/Bond.cpp | 73 ++++++--------------------- src/Symmetry.cpp | 6 +-- src/Tensor.cpp | 8 +-- src/UniTensor.cpp | 55 +++----------------- src/backend/Storage.cpp | 8 +-- src/io.cpp | 67 ++++++++++++++++++------- 9 files changed, 173 insertions(+), 201 deletions(-) diff --git a/include/io.hpp b/include/io.hpp index aafca852d..6e73116e1 100644 --- a/include/io.hpp +++ b/include/io.hpp @@ -60,7 +60,7 @@ namespace cytnx { bool unlink(H5::Group &container, const std::string &name, bool overwrite = true); /** - * @brief Save data to an attribute + * @brief Save scalar to an attribute * @param[in] object a scalar of any type that is supported by cytnx * @param[in] container file, group or dataset; should be opened for writing * @param[in] name the name of the attribute @@ -81,6 +81,28 @@ namespace cytnx { void save_attribute(const std::vector &object, H5::H5Object &container, const std::string &name, bool overwrite = false); + /** + * @brief Load scalar from an attribute + * @param[out] object a scalar of any type that is supported by cytnx; needs to be compatible + * with the data format in the file. + * @param[in] container file, group or dataset + * @param[in] name the name of the attribute + */ + template + void load_attribute(T &object, H5::H5Object &container, const std::string &name) { + H5::Attribute attr = container.openAttribute(name); + H5::DataType datatype = attr.getDataType(); + H5::DataType scalartype = Type.get_hdf5_type(object); + cytnx_error_msg(datatype.getClass() != scalartype.getClass(), + "[ERROR] Attribute '%s' data type class mismatch.\n", name.c_str()); + attr.read(scalartype, &object); + } + /** + * @brief Load string from an attribute + * @see load_attribute(T &object, H5::H5Object &container, const std::string &name) + */ + void load_attribute(std::string &object, H5::H5Object &container, const std::string &name); + /** * @brief Remove an attribute if it exists * @param[in] container file, group or dataset from which the attribute will be removed @@ -121,6 +143,69 @@ namespace cytnx { H5::DataSet save_dataset(const Matrix_list &object, H5::Group &container, const std::string &name, bool overwrite = false); + /** + * @brief Load data from a dataset + * @param[out] object vector of supported types + * @param[in] container file, group or dataset + * @param[in] name the name of the attribute + */ + template + void load_dataset(std::vector &object, H5::H5Object &container, const std::string &name) { + H5::DataSet dataset = container.openDataSet(name); + H5::DataSpace dataspace = dataset.getSpace(); + object.resize(dataspace.getSimpleExtentNpoints()); + H5::DataType datatype = dataset.getDataType(); + T scalar; + H5::DataType scalartype = Type.get_hdf5_type(scalar); + cytnx_error_msg(datatype.getClass() != scalartype.getClass(), + "[ERROR] Dataset '%s' type class mismatch.\n", name.c_str()); + dataset.read(object.data(), scalartype); + } + + /** + * @brief Load vector of strings from a dataset + * @param[out] object vector of strings + * @see void load_dataset(std::vector &object, H5::H5Object &container, const std::string + * &name) + */ + void load_dataset(std::vector &object, H5::H5Object &container, + const std::string &name); + /** + * @brief Load a matrix of a supported type + * @param[out] object a vector of a vector based on a supported type + * @see save_dataset(const Matrix_list &object, H5::Group &container, const std::string &name, + * bool overwrite) + * @see void load_dataset(std::vector &object, H5::H5Object &container, const std::string + * &name) + */ + template + void load_dataset(std::vector> &object, H5::H5Object &container, + const std::string &name) { + H5::DataSet dataset = container.openDataSet(name); + H5::DataSpace dataspace = dataset.getSpace(); + cytnx_error_msg( + dataspace.getSimpleExtentNdims() != 2, + "[ERROR] Dataset '%s' should be a two-dimensional array. The HDF5 data seems corrupt!\n", + name.c_str()); + hsize_t dims[2]; + dataspace.getSimpleExtentDims(dims); + hsize_t rownum = dims[0]; + hsize_t colnum = dims[1]; + H5::DataType datatype = dataset.getDataType(); + T scalar; + H5::DataType scalartype = Type.get_hdf5_type(scalar); + cytnx_error_msg(datatype.getClass() != scalartype.getClass(), + "[ERROR] Dataset '%s' type class mismatch.\n", name.c_str()); + // Read HDF5 data into a flattened temporary vector + std::vector flat(rownum * colnum); + dataset.read(flat.data(), scalartype); + // Reconstruct the vector of vectors + object.assign(rownum, std::vector(colnum)); + for (hsize_t i = 0; i < rownum; ++i) { + std::copy(flat.begin() + i * colnum, flat.begin() + (i + 1) * colnum, object[i].begin()); + } + } + /** * @brief Input/output file access mode for HDF5 files. */ @@ -201,7 +286,7 @@ namespace cytnx { /** * @brief Load object from HDF5 file * @details Can be used with most cytnx classes as objects. - * @param[in] object an object of the correct type that will be modified (inline) + * @param[out] object an object of the correct type that will be modified (inline) * @param[in] container HDF5 object that should be opened for writing; can be a file or a group * @param[in] name the name of the attribute, dataset, or group * @param[in] path path inside the file; a path /foo/bar/Obj will read the object from the diff --git a/src/BlockFermionicUniTensor.cpp b/src/BlockFermionicUniTensor.cpp index cc4460285..154d193aa 100644 --- a/src/BlockFermionicUniTensor.cpp +++ b/src/BlockFermionicUniTensor.cpp @@ -2425,38 +2425,16 @@ namespace cytnx { } // inner_to_outer_idx; read matrix (blocknum x rank) if (container.nameExists("block_to_sectors")) { - H5::DataSet dataset = container.openDataSet("block_to_sectors"); - H5::DataSpace dataspace = dataset.getSpace(); - cytnx_error_msg(dataspace.getSimpleExtentNdims() != 2, - "[ERROR] 'block_to_sectors' should be a two-dimensional array. The HDF5 data " - "seems corrupt!%s", - "\n"); - hsize_t dims[2]; - dataspace.getSimpleExtentDims(dims); - hsize_t blocknum = dims[0]; - hsize_t rank = dims[1]; - cytnx_error_msg(blocknum != this->_blocks.size(), + io::load_dataset(this->_inner_to_outer_idx, container, "block_to_sectors"); + cytnx_error_msg(this->_inner_to_outer_idx.size() != this->_blocks.size(), "[ERROR] %d blocks found, but first dimension of 'block_to_sectors' is %d. " "The HDF5 data seems corrupt!\n", - this->_blocks.size(), blocknum); - cytnx_error_msg(rank != this->_bonds.size(), + this->_blocks.size(), this->_inner_to_outer_idx.size()); + cytnx_error_msg(!(this->_inner_to_outer_idx.empty()) && + this->_inner_to_outer_idx[0].size() != this->_bonds.size(), "[ERROR] %d bonds found, but second dimension of 'block_to_sectors' is %d. " "The HDF5 data seems corrupt!\n", - this->_bonds.size(), rank); - // Read HDF5 data into a flattened temporary vector - std::vector flat(blocknum * rank); - H5::DataType datatype = dataset.getDataType(); - cytnx_error_msg( - datatype.getSize() != sizeof(cytnx_uint64), - "[ERROR] 'block_to_sectors' bit-length mismatch. File: %zu bytes, expected: %zu bytes.\n", - datatype.getSize(), sizeof(cytnx_uint64)); - dataset.read(flat.data(), datatype); - // Reconstruct the vector of vectors - this->_inner_to_outer_idx.assign(blocknum, std::vector(rank)); - for (hsize_t i = 0; i < blocknum; ++i) { - std::copy(flat.begin() + i * rank, flat.begin() + (i + 1) * rank, - this->_inner_to_outer_idx[i].begin()); - } + this->_bonds.size(), this->_inner_to_outer_idx[0].size()); } else { cytnx_error_msg( !(this->_blocks.empty()), diff --git a/src/BlockUniTensor.cpp b/src/BlockUniTensor.cpp index 58369e91d..813ed0be8 100644 --- a/src/BlockUniTensor.cpp +++ b/src/BlockUniTensor.cpp @@ -1630,38 +1630,16 @@ namespace cytnx { } // inner_to_outer_idx; read matrix (blocknum x rank) if (container.nameExists("block_to_sectors")) { - H5::DataSet dataset = container.openDataSet("block_to_sectors"); - H5::DataSpace dataspace = dataset.getSpace(); - cytnx_error_msg(dataspace.getSimpleExtentNdims() != 2, - "[ERROR] 'block_to_sectors' should be a two-dimensional array. The HDF5 data " - "seems corrupt!%s", - "\n"); - hsize_t dims[2]; - dataspace.getSimpleExtentDims(dims); - hsize_t blocknum = dims[0]; - hsize_t rank = dims[1]; - cytnx_error_msg(blocknum != this->_blocks.size(), + io::load_dataset(this->_inner_to_outer_idx, container, "block_to_sectors"); + cytnx_error_msg(this->_inner_to_outer_idx.size() != this->_blocks.size(), "[ERROR] %d blocks found, but first dimension of 'block_to_sectors' is %d. " "The HDF5 data seems corrupt!\n", - this->_blocks.size(), blocknum); - cytnx_error_msg(rank != this->_bonds.size(), + this->_blocks.size(), this->_inner_to_outer_idx.size()); + cytnx_error_msg(!(this->_inner_to_outer_idx.empty()) && + this->_inner_to_outer_idx[0].size() != this->_bonds.size(), "[ERROR] %d bonds found, but second dimension of 'block_to_sectors' is %d. " "The HDF5 data seems corrupt!\n", - this->_bonds.size(), rank); - // Read HDF5 data into a flattened temporary vector - std::vector flat(blocknum * rank); - H5::DataType datatype = dataset.getDataType(); - cytnx_error_msg( - datatype.getSize() != sizeof(cytnx_uint64), - "[ERROR] 'block_to_sectors' bit-length mismatch. File: %zu bytes, expected: %zu bytes.\n", - datatype.getSize(), sizeof(cytnx_uint64)); - dataset.read(flat.data(), datatype); - // Reconstruct the vector of vectors - this->_inner_to_outer_idx.assign(blocknum, std::vector(rank)); - for (hsize_t i = 0; i < blocknum; ++i) { - std::copy(flat.begin() + i * rank, flat.begin() + (i + 1) * rank, - this->_inner_to_outer_idx[i].begin()); - } + this->_bonds.size(), this->_inner_to_outer_idx[0].size()); } else { cytnx_error_msg( !(this->_blocks.empty()), diff --git a/src/Bond.cpp b/src/Bond.cpp index 69194df5b..2f59ecaf7 100644 --- a/src/Bond.cpp +++ b/src/Bond.cpp @@ -645,38 +645,20 @@ namespace cytnx { // delete further symmetries if they exist while (io::remove_attribute(symloc, "Symmetry" + std::to_string(symnum), overwrite)) symnum++; } + void Bond::from_hdf5(H5::Group &container, const std::string &name) { H5::Group rootgroup = (name.empty() ? container : container.openGroup(name)); - H5::DataType datatype; - H5::Attribute attr; - H5::DataSet dataset; - H5::DataSpace dataspace; - H5::StrType str_type; // type from string - attr = rootgroup.openAttribute("type"); - str_type = attr.getStrType(); - size_t size = str_type.getSize() - 1; // not including the null terminator std::string typestr; - typestr.resize(size); - attr.read(str_type, &typestr[0]); + io::load_attribute(typestr, rootgroup, "type"); this->_impl->_type = string_to_bondtype.at(typestr); // degs; read vector bool symmetric = false; - hssize_t sectordim; if (rootgroup.nameExists("degeneracies")) { + io::load_dataset(this->_impl->_degs, rootgroup, "degeneracies"); symmetric = true; - dataset = rootgroup.openDataSet("degeneracies"); - dataspace = dataset.getSpace(); - sectordim = dataspace.getSimpleExtentNpoints(); - this->_impl->_degs.resize(sectordim); - datatype = dataset.getDataType(); - cytnx_error_msg( - datatype.getSize() != sizeof(std::size_t), - "[ERROR] 'degeneracies' bit-length mismatch. File: %zu bytes, expected: %zu bytes.\n", - datatype.getSize(), sizeof(std::size_t)); - dataset.read(this->_impl->_degs.data(), datatype); this->_impl->_dim = std::accumulate(this->_impl->_degs.begin(), this->_impl->_degs.end(), cytnx_uint64(0)); } else { @@ -690,33 +672,12 @@ namespace cytnx { "[ERROR] 'degeneracies' were not found, but " "'quantum_numbers' exist. The HDF5 data seems corrupt!%s", "\n"); - dataset = rootgroup.openDataSet("quantum_numbers"); - dataspace = dataset.getSpace(); - cytnx_error_msg(dataspace.getSimpleExtentNdims() != 2, - "[ERROR] 'quantum_numbers' should be a two-dimensional array. The HDF5 data " - "seems corrupt!%s", - "\n"); - hsize_t dims[2]; - dataspace.getSimpleExtentDims(dims); - cytnx_error_msg(dims[0] != sectordim, - "[ERROR] Length of 'degeneracies' = %d, but first dimension of " - "'quantum_numbers' is %d. The HDF5 data seems corrupt!\n", - sectordim, dims[0]); - qnumdim = dims[1]; - // Read HDF5 data into a flattened temporary vector - std::vector flat(sectordim * qnumdim); - datatype = dataset.getDataType(); - cytnx_error_msg( - datatype.getSize() != sizeof(cytnx_int64), - "[ERROR] 'quantum_numbers' bit-length mismatch. File: %zu bytes, expected: %zu bytes.\n", - datatype.getSize(), sizeof(cytnx_int64)); - dataset.read(flat.data(), datatype); - // Reconstruct the vector of vectors - this->_impl->_qnums.assign(sectordim, std::vector(qnumdim)); - for (hsize_t i = 0; i < sectordim; ++i) { - std::copy(flat.begin() + i * qnumdim, flat.begin() + (i + 1) * qnumdim, - this->_impl->_qnums[i].begin()); - } + io::load_dataset(this->_impl->_qnums, rootgroup, "quantum_numbers"); + cytnx_error_msg(this->_impl->_qnums.size() != this->_impl->_degs.size(), + "[ERROR] Length of 'degeneracies' = %zu, but first dimension of " + "'quantum_numbers' is %zu. The HDF5 data seems corrupt!\n", + this->_impl->_degs.size(), this->_impl->_qnums.size()); + if (!(this->_impl->_qnums.empty())) qnumdim = this->_impl->_qnums[0].size(); } else { this->_impl->_qnums.clear(); } @@ -738,8 +699,8 @@ namespace cytnx { } if (symmetric) { cytnx_error_msg(idx != qnumdim, - "[ERROR] %d symmetries were found, but second dimension of " - "'quantum_numbers' is %d. The HDF5 data seems corrupt!\n", + "[ERROR] %zu symmetries were found, but second dimension of " + "'quantum_numbers' is %zu. The HDF5 data seems corrupt!\n", idx, qnumdim); } else { cytnx_error_msg(idx > 0, @@ -758,18 +719,12 @@ namespace cytnx { // dim; from attribute if (rootgroup.attrExists("dimension")) { - attr = rootgroup.openAttribute("dimension"); cytnx_uint64 dimension; - datatype = attr.getDataType(); - cytnx_error_msg( - datatype.getSize() != sizeof(cytnx_uint64), - "[ERROR] 'dimension' bit-length mismatch. File: %zu bytes, expected: %zu bytes.\n", - datatype.getSize(), sizeof(cytnx_uint64)); - attr.read(datatype, &dimension); + io::load_attribute(dimension, rootgroup, "dimension"); if (symmetric) { cytnx_error_msg(dimension != this->_impl->_dim, - "[ERROR] 'dimension' read from HDF5 file is %d, but the sum of all " - "degeneracies is %d. The HDF5 data seems corrupt!\n", + "[ERROR] 'dimension' read from HDF5 file is %llu, but the sum of all " + "degeneracies is %llu. The HDF5 data seems corrupt!\n", dimension, this->_impl->_dim); } else { this->_impl->_dim = dimension; diff --git a/src/Symmetry.cpp b/src/Symmetry.cpp index 7e11c89bd..447411d20 100644 --- a/src/Symmetry.cpp +++ b/src/Symmetry.cpp @@ -395,12 +395,8 @@ namespace cytnx { io::save_attribute(this->getname(), container, name, overwrite); } void cytnx::Symmetry::from_hdf5(H5::Group &container, const std::string &name) { - H5::Attribute attr = container.openAttribute(name); - H5::StrType str_type = attr.getStrType(); - size_t size = str_type.getSize() - 1; // remove the null terminator std::string symname; - symname.resize(size); - attr.read(str_type, &symname[0]); + io::load_attribute(symname, container, name); this->Init(symname); } diff --git a/src/Tensor.cpp b/src/Tensor.cpp index 3590daa10..d85f676f2 100644 --- a/src/Tensor.cpp +++ b/src/Tensor.cpp @@ -600,13 +600,7 @@ namespace cytnx { int device = Device.cpu; if (restore_device && dataset.attrExists("device")) { - H5::Attribute attr = dataset.openAttribute("device"); - datatype = dataset.getDataType(); - cytnx_error_msg( - datatype.getSize() != sizeof(int), - "[ERROR] 'device' bit-length mismatch. File: %zu bytes, expected: %zu bytes.\n", - datatype.getSize(), sizeof(int)); - attr.read(datatype, &device); + io::load_attribute(device, dataset, "device"); #ifndef UNI_GPU if (device != Device.cpu) { cytnx_warning_msg(true, diff --git a/src/UniTensor.cpp b/src/UniTensor.cpp index 2e55230ae..2d096e82d 100644 --- a/src/UniTensor.cpp +++ b/src/UniTensor.cpp @@ -240,74 +240,34 @@ namespace cytnx { void UniTensor::from_hdf5(H5::Group &container, const std::string &name, bool restore_device) { H5::Group rootgroup = (name.empty() ? container : container.openGroup(name)); - H5::DataType datatype; - H5::Attribute attr; - H5::StrType str_type; - size_t size; // type, read from attribute - attr = rootgroup.openAttribute("type"); - str_type = attr.getStrType(); - size = str_type.getSize() - 1; // remove the null terminator std::string utenname; - utenname.resize(size); - attr.read(str_type, &utenname[0]); + io::load_attribute(utenname, rootgroup, "type"); this->Init(utenname); // is_diag, read from attribute if (rootgroup.attrExists("diagonal")) { - H5::Attribute attr = rootgroup.openAttribute("diagonal"); - datatype = attr.getDataType(); - cytnx_error_msg( - datatype.getSize() != Type.get_hdf5_type(this->_impl->_is_diag).getSize(), - "[ERROR] 'device' bit-length mismatch. File: %zu bytes, expected: %zu bytes.\n", - datatype.getSize(), Type.get_hdf5_type(this->_impl->_is_diag).getSize()); - attr.read(datatype, &this->_impl->_is_diag); + io::load_attribute(this->_impl->_is_diag, rootgroup, "diagonal"); } else { this->_impl->_is_diag = false; } // rowrank, read from attribute - attr = rootgroup.openAttribute("rowrank"); - datatype = attr.getDataType(); - cytnx_error_msg( - datatype.getSize() != Type.get_hdf5_type(this->_impl->_rowrank).getSize(), - "[ERROR] 'rowrank' bit-length mismatch. File: %zu bytes, expected: %zu bytes.\n", - datatype.getSize(), Type.get_hdf5_type(this->_impl->_rowrank).getSize()); - attr.read(datatype, &this->_impl->_rowrank); + io::load_attribute(this->_impl->_rowrank, rootgroup, "rowrank"); // name, read from string attribute if (rootgroup.attrExists("name")) { - attr = rootgroup.openAttribute("name"); - str_type = attr.getStrType(); - size = str_type.getSize() - 1; // remove the null terminator - if (size > 0) { - this->_impl->_name.resize(size); - attr.read(str_type, &this->_impl->_name[0]); - } else { - this->_impl->_name.clear(); - } + io::load_attribute(this->_impl->_name, rootgroup, "name"); } else { this->_impl->_name.clear(); } // labels; read from string vector - this->_impl->_labels.clear(); if (rootgroup.nameExists("labels")) { - H5::DataSet dataset = rootgroup.openDataSet("labels"); - H5::DataSpace dataspace = dataset.getSpace(); - hsize_t dims[1]; - dataspace.getSimpleExtentDims(dims); - // H5T_VARIABLE requires reading into an array of char pointers (char**) - H5::StrType str_type(H5::PredType::C_S1, H5T_VARIABLE); - std::vector c_strings(dims[0]); - dataset.read(c_strings.data(), str_type); - this->_impl->_labels.reserve(dims[0]); - for (size_t i = 0; i < dims[0]; ++i) { - this->_impl->_labels.push_back(std::string(c_strings[i])); - } - // free the space of each char* that was allocated in dataset.read() - dataset.vlenReclaim(c_strings.data(), str_type, dataspace); + io::load_dataset(this->_impl->_labels, rootgroup, "labels"); + } else { + this->_impl->_labels.clear(); } // bonds; read from group @@ -337,6 +297,7 @@ namespace cytnx { this->_impl->_bonds.clear(); } + // dispatch this->_impl->from_hdf5_dispatch(rootgroup, restore_device); this->_impl->_is_braket_form = this->_impl->_update_braket(); } diff --git a/src/backend/Storage.cpp b/src/backend/Storage.cpp index a3ad78174..3a66146b4 100644 --- a/src/backend/Storage.cpp +++ b/src/backend/Storage.cpp @@ -252,13 +252,7 @@ namespace cytnx { int device = Device.cpu; if (restore_device && dataset.attrExists("device")) { - H5::Attribute attr = dataset.openAttribute("device"); - datatype = dataset.getDataType(); - cytnx_error_msg( - datatype.getSize() != sizeof(int), - "[ERROR] 'device' bit-length mismatch. File: %zu bytes, expected: %zu bytes.\n", - datatype.getSize(), sizeof(int)); - attr.read(datatype, &device); + io::load_attribute(device, dataset, "device"); #ifndef UNI_GPU if (device != Device.cpu) { cytnx_warning_msg(true, diff --git a/src/io.cpp b/src/io.cpp index 70b2d089f..3441cc73b 100644 --- a/src/io.cpp +++ b/src/io.cpp @@ -74,6 +74,17 @@ namespace cytnx { } } + bool unlink(H5::Group &container, const std::string &name, bool overwrite) { + if (container.nameExists(name)) { + cytnx_error_msg( + !overwrite, "Dataset or group '%s' exists. Use argument overwrite = true to remove it.\n", + name); + container.unlink(name); + return true; + } + return false; + } + void save_attribute(const Scalar_list &object, H5::H5Object &container, const std::string &name, bool overwrite) { std::visit( @@ -138,6 +149,29 @@ namespace cytnx { attr.write(str_type, c_strings.data()); } + void load_attribute(std::string &object, H5::H5Object &container, const std::string &name) { + H5::Attribute attr = container.openAttribute(name); + H5::StrType str_type = attr.getStrType(); + size_t size = str_type.getSize(); + object.resize(size); + attr.read(str_type, object.data()); + + // remove the null terminator + if (!object.empty() && object.back() == '\0') { + object.pop_back(); + } + } + + bool remove_attribute(H5::H5Object &container, const std::string &name, bool overwrite) { + if (container.attrExists(name)) { + cytnx_error_msg( + !overwrite, "Attribute '%s' exists. Use argument overwrite = true to remove it.\n", name); + container.removeAttr(name); + return true; + } + return false; + } + H5::DataSet save_dataset(const Vector_list &object, H5::Group &container, const std::string &name, bool overwrite) { H5::DataSet dataset; @@ -184,16 +218,6 @@ namespace cytnx { return dataset; } - bool remove_attribute(H5::H5Object &container, const std::string &name, bool overwrite) { - if (container.attrExists(name)) { - cytnx_error_msg( - !overwrite, "Attribute '%s' exists. Use argument overwrite = true to remove it.\n", name); - container.removeAttr(name); - return true; - } - return false; - } - H5::DataSet save_dataset(const Matrix_list &object, H5::Group &container, const std::string &name, bool overwrite) { H5::DataSet dataset; @@ -234,15 +258,22 @@ namespace cytnx { return dataset; } - bool unlink(H5::Group &container, const std::string &name, bool overwrite) { - if (container.nameExists(name)) { - cytnx_error_msg( - !overwrite, "Dataset or group '%s' exists. Use argument overwrite = true to remove it.\n", - name); - container.unlink(name); - return true; + void load_dataset(std::vector &object, H5::H5Object &container, + const std::string &name) { + H5::DataSet dataset = container.openDataSet(name); + H5::DataSpace dataspace = dataset.getSpace(); + hsize_t dims[1]; + dataspace.getSimpleExtentDims(dims); + // H5T_VARIABLE requires reading into an array of char pointers (char**) + H5::StrType str_type(H5::PredType::C_S1, H5T_VARIABLE); + std::vector c_strings(dims[0]); + dataset.read(c_strings.data(), str_type); + object.reserve(dims[0]); + for (size_t i = 0; i < dims[0]; ++i) { + object.push_back(std::string(c_strings[i])); } - return false; + // free the space of each char* that was allocated in dataset.read() + dataset.vlenReclaim(c_strings.data(), str_type, dataspace); } H5::H5File open(const std::filesystem::path &fname, IoMode mode) { From a606ef5a6e4d25a4d3076b3f047817284616a5c1 Mon Sep 17 00:00:00 2001 From: Manuel Schneider Date: Sat, 23 May 2026 03:41:52 +0800 Subject: [PATCH 33/40] fixed error messages --- src/BlockFermionicUniTensor.cpp | 12 +++++----- src/BlockUniTensor.cpp | 12 +++++----- src/UniTensor.cpp | 4 ++-- src/backend/Storage.cpp | 5 ++++- src/io.cpp | 39 +++++++++++++++++++-------------- 5 files changed, 41 insertions(+), 31 deletions(-) diff --git a/src/BlockFermionicUniTensor.cpp b/src/BlockFermionicUniTensor.cpp index 154d193aa..9f9b244ab 100644 --- a/src/BlockFermionicUniTensor.cpp +++ b/src/BlockFermionicUniTensor.cpp @@ -2427,19 +2427,19 @@ namespace cytnx { if (container.nameExists("block_to_sectors")) { io::load_dataset(this->_inner_to_outer_idx, container, "block_to_sectors"); cytnx_error_msg(this->_inner_to_outer_idx.size() != this->_blocks.size(), - "[ERROR] %d blocks found, but first dimension of 'block_to_sectors' is %d. " + "[ERROR] %zu blocks found, but first dimension of 'block_to_sectors' is %zu. " "The HDF5 data seems corrupt!\n", this->_blocks.size(), this->_inner_to_outer_idx.size()); cytnx_error_msg(!(this->_inner_to_outer_idx.empty()) && this->_inner_to_outer_idx[0].size() != this->_bonds.size(), - "[ERROR] %d bonds found, but second dimension of 'block_to_sectors' is %d. " + "[ERROR] %zu bonds found, but second dimension of 'block_to_sectors' is %zu. " "The HDF5 data seems corrupt!\n", this->_bonds.size(), this->_inner_to_outer_idx[0].size()); } else { - cytnx_error_msg( - !(this->_blocks.empty()), - "[ERROR] 'block_to_sectors' not found, but %d blocks exist. The HDF5 data seems corrupt!\n", - this->_blocks.size()); + cytnx_error_msg(!(this->_blocks.empty()), + "[ERROR] 'block_to_sectors' not found, but %zu blocks exist. The HDF5 data " + "seems corrupt!\n", + this->_blocks.size()); this->_inner_to_outer_idx.clear(); } this->_signflip = std::vector(this->_blocks.size(), false); diff --git a/src/BlockUniTensor.cpp b/src/BlockUniTensor.cpp index 813ed0be8..7104464f0 100644 --- a/src/BlockUniTensor.cpp +++ b/src/BlockUniTensor.cpp @@ -1632,19 +1632,19 @@ namespace cytnx { if (container.nameExists("block_to_sectors")) { io::load_dataset(this->_inner_to_outer_idx, container, "block_to_sectors"); cytnx_error_msg(this->_inner_to_outer_idx.size() != this->_blocks.size(), - "[ERROR] %d blocks found, but first dimension of 'block_to_sectors' is %d. " + "[ERROR] %zu blocks found, but first dimension of 'block_to_sectors' is %zu. " "The HDF5 data seems corrupt!\n", this->_blocks.size(), this->_inner_to_outer_idx.size()); cytnx_error_msg(!(this->_inner_to_outer_idx.empty()) && this->_inner_to_outer_idx[0].size() != this->_bonds.size(), - "[ERROR] %d bonds found, but second dimension of 'block_to_sectors' is %d. " + "[ERROR] %zu bonds found, but second dimension of 'block_to_sectors' is %zu. " "The HDF5 data seems corrupt!\n", this->_bonds.size(), this->_inner_to_outer_idx[0].size()); } else { - cytnx_error_msg( - !(this->_blocks.empty()), - "[ERROR] 'block_to_sectors' not found, but %d blocks exist. The HDF5 data seems corrupt!\n", - this->_blocks.size()); + cytnx_error_msg(!(this->_blocks.empty()), + "[ERROR] 'block_to_sectors' not found, but %zu blocks exist. The HDF5 data " + "seems corrupt!\n", + this->_blocks.size()); this->_inner_to_outer_idx.clear(); } } diff --git a/src/UniTensor.cpp b/src/UniTensor.cpp index 2d096e82d..f1a83c64d 100644 --- a/src/UniTensor.cpp +++ b/src/UniTensor.cpp @@ -287,12 +287,12 @@ namespace cytnx { } cytnx_error_msg( idx != this->_impl->_labels.size(), - "[ERROR] %d bonds were found, but %d labels exist. The HDF5 data seems corrupt!\n", idx, + "[ERROR] %zu bonds were found, but %zu labels exist. The HDF5 data seems corrupt!\n", idx, this->_impl->_labels.size()); } else { cytnx_error_msg( !this->_impl->_labels.empty(), - "[ERROR] %d labels exist, but no bonds were found. The HDF5 data seems corrupt!\n", + "[ERROR] %zu labels exist, but no bonds were found. The HDF5 data seems corrupt!\n", this->_impl->_labels.size()); this->_impl->_bonds.clear(); } diff --git a/src/backend/Storage.cpp b/src/backend/Storage.cpp index 3a66146b4..027e55651 100644 --- a/src/backend/Storage.cpp +++ b/src/backend/Storage.cpp @@ -281,7 +281,10 @@ namespace cytnx { dataset.write(htmp, hdf5type); free(htmp); #else - cytnx_error_msg(true, "ERROR internal fatal error in Save Storage%s", "\n"); + cytnx_error_msg(true, + "[ERROR][Storage] Trying to access data from GPU without GPU support. " + "Internal fatal error!%s", + "\n"); #endif } } diff --git a/src/io.cpp b/src/io.cpp index 3441cc73b..37856be7d 100644 --- a/src/io.cpp +++ b/src/io.cpp @@ -16,10 +16,10 @@ namespace cytnx { // check if old dataset can be overwritten bool create_new = true; if (container.nameExists(name)) { // we can overwrite the data if type and size match - cytnx_error_msg( - !overwrite, - "Dataset (or group) '%s' already exists. Use argument overwrite = true to overwrite.\n", - name); + cytnx_error_msg(!overwrite, + "[ERROR] Dataset (or group) '%s' already exists. Use argument overwrite " + "= true to overwrite.\n", + name.c_str()); if (container.childObjType(name) == H5O_TYPE_DATASET) { dataset = container.openDataSet(name); H5::DataSpace olddataspace = dataset.getSpace(); @@ -77,8 +77,9 @@ namespace cytnx { bool unlink(H5::Group &container, const std::string &name, bool overwrite) { if (container.nameExists(name)) { cytnx_error_msg( - !overwrite, "Dataset or group '%s' exists. Use argument overwrite = true to remove it.\n", - name); + !overwrite, + "[ERROR] Dataset or group '%s' exists. Use argument overwrite = true to remove it.\n", + name.c_str()); container.unlink(name); return true; } @@ -91,9 +92,10 @@ namespace cytnx { [&](const auto &scalar) { H5::DataType datatype = Type.get_hdf5_type(scalar); if (container.attrExists(name)) { - cytnx_error_msg( - !overwrite, - "Attribute '%s' already exists. Use argument overwrite = true to overwrite.\n", name); + cytnx_error_msg(!overwrite, + "[ERROR] Attribute '%s' already exists. Use argument overwrite = true " + "to overwrite.\n", + name.c_str()); H5::Attribute oldattr = container.openAttribute(name); if (oldattr.getSpace().getSimpleExtentType() == H5S_SCALAR && oldattr.getDataType() == datatype) { @@ -115,7 +117,8 @@ namespace cytnx { if (container.attrExists(name)) { cytnx_error_msg( !overwrite, - "Attribute '%s' already exists. Use argument overwrite = true to overwrite.\n", name); + "[ERROR] Attribute '%s' already exists. Use argument overwrite = true to overwrite.\n", + name.c_str()); H5::Attribute oldattr = container.openAttribute(name); if (oldattr.getSpace().getSimpleExtentType() == H5S_SCALAR && oldattr.getStrType() == str_type) { @@ -136,7 +139,8 @@ namespace cytnx { if (container.attrExists(name)) { // simply delete old dataset cytnx_error_msg( !overwrite, - "Attribute '%s' already exists. Use argument overwrite = true to overwrite.\n", name); + "[ERROR] Attribute '%s' already exists. Use argument overwrite = true to overwrite.\n", + name.c_str()); container.removeAttr(name); } // create a new dataset @@ -165,7 +169,9 @@ namespace cytnx { bool remove_attribute(H5::H5Object &container, const std::string &name, bool overwrite) { if (container.attrExists(name)) { cytnx_error_msg( - !overwrite, "Attribute '%s' exists. Use argument overwrite = true to remove it.\n", name); + !overwrite, + "[ERROR] Attribute '%s' exists. Use argument overwrite = true to remove it.\n", + name.c_str()); container.removeAttr(name); return true; } @@ -177,7 +183,8 @@ namespace cytnx { H5::DataSet dataset; std::visit( [&](const auto &vec) { - cytnx_error_msg(vec.empty(), "Cannot write an empty vector to a dataset!\s", "\n"); + cytnx_error_msg(vec.empty(), "[ERROR] Cannot write an empty vector to dataset '%s'!\n", + name.c_str()); H5::DataType datatype = Type.get_hdf5_type(vec[0]); dataset = internal::create_dataset(datatype, {vec.size()}, container, name, overwrite); @@ -204,7 +211,7 @@ namespace cytnx { cytnx_error_msg( !overwrite, "Dataset (or group) '%s' already exists. Use argument overwrite = true to overwrite.\n", - name); + name.c_str()); container.unlink(name); } // create a new dataset @@ -224,12 +231,12 @@ namespace cytnx { std::visit( [&](const auto &vec) { cytnx_error_msg(vec.empty() || vec[0].empty(), - "Cannot write an empty vector to a dataset!\s", "\n"); + "[ERROR] Cannot write an empty vector to dataset '%s'!\n", name.c_str()); hsize_t rownum = vec.size(); hsize_t colnum = vec[0].size(); for (hsize_t i = 1; i < vec.size(); ++i) { cytnx_error_msg(vec[i].size() != colnum, - "[ERROR] object[%d] has different size than object[0]. A rectanglular " + "[ERROR] object[%zu] has different size than object[0]. A rectanglular " "matrix is expected as input!\n", i); } From b0016634a045832f7ca74649107e07a0cceefb2c Mon Sep 17 00:00:00 2001 From: Manuel Schneider Date: Mon, 25 May 2026 17:47:38 +0800 Subject: [PATCH 34/40] added unit tests --- src/io.cpp | 16 ++- tests/CMakeLists.txt | 5 + tests/io_test/Bond_test.cpp | 92 +++++++++++++++ tests/io_test/Storage_test.cpp | 142 ++++++++++++++++++++++++ tests/io_test/Symmetry_test.cpp | 77 +++++++++++++ tests/io_test/Tensor_test.cpp | 143 ++++++++++++++++++++++++ tests/io_test/UniTensor_test.cpp | 185 +++++++++++++++++++++++++++++++ tests/io_test/io_test_tools.h | 146 ++++++++++++++++++++++++ 8 files changed, 802 insertions(+), 4 deletions(-) create mode 100644 tests/io_test/Bond_test.cpp create mode 100644 tests/io_test/Storage_test.cpp create mode 100644 tests/io_test/Symmetry_test.cpp create mode 100644 tests/io_test/Tensor_test.cpp create mode 100644 tests/io_test/UniTensor_test.cpp create mode 100644 tests/io_test/io_test_tools.h diff --git a/src/io.cpp b/src/io.cpp index 37856be7d..1969964d9 100644 --- a/src/io.cpp +++ b/src/io.cpp @@ -340,8 +340,12 @@ namespace cytnx { const std::string &path) { std::visit( [&](auto &concreteObj) { - H5::Group group = (path.empty() ? container : container.openGroup(path)); - concreteObj.from_hdf5(group, name); + if (path.empty()) { + concreteObj.from_hdf5(container, name); + } else { + H5::Group group = container.openGroup(path); + concreteObj.from_hdf5(group, name); + } }, object); } @@ -350,8 +354,12 @@ namespace cytnx { const std::string &path, bool restore_device) { std::visit( [&](auto &concreteObj) { - H5::Group group = (path.empty() ? container : container.openGroup(path)); - concreteObj.from_hdf5(group, name, restore_device); + if (path.empty()) { + concreteObj.from_hdf5(container, name, restore_device); + } else { + H5::Group group = container.openGroup(path); + concreteObj.from_hdf5(group, name, restore_device); + } }, object); } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index afbdfd68d..b421a82dd 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -50,6 +50,11 @@ add_executable( algo_test/Vsplit_test.cpp algo_test/Vstack_test.cpp random_test/uniform_test.cpp + io_test/Storage_test.cpp + io_test/Tensor_test.cpp + io_test/Bond_test.cpp + io_test/Symmetry_test.cpp + io_test/UniTensor_test.cpp ) if(USE_CUDA) diff --git a/tests/io_test/Bond_test.cpp b/tests/io_test/Bond_test.cpp new file mode 100644 index 000000000..6096a810b --- /dev/null +++ b/tests/io_test/Bond_test.cpp @@ -0,0 +1,92 @@ +#include "io_test_tools.h" + +using namespace cytnx; +using namespace cytnx::IOTest; + +// A plain (untagged, no-symmetry) bond. +TEST(IOBondTest, RoundTripRegular) { + Bond b = Bond(7); + Bond loaded = RoundTrip(b); + EXPECT_TRUE(loaded == b); +} + +// Directional (tagged) bonds without symmetry. +TEST(IOBondTest, RoundTripDirectional) { + for (auto bt : {BD_IN, BD_OUT}) { + Bond b = Bond(5, bt); + Bond loaded = RoundTrip(b); + EXPECT_TRUE(loaded == b); + } +} + +// A single-U1-symmetry bond. +TEST(IOBondTest, RoundTripU1) { + Bond b = Bond(BD_IN, {Qs(0) >> 1, Qs(1) >> 2, Qs(-1) >> 3}); + Bond loaded = RoundTrip(b); + EXPECT_TRUE(loaded == b); +} + +// A bond carrying multiple symmetries (U1 x Z2). +TEST(IOBondTest, RoundTripMultiSymmetry) { + Bond b = Bond(BD_KET, {{0, 2}, {1, 5}, {1, 6}, {0, 1}}, {4, 7, 2, 3}, + {Symmetry::Zn(2), Symmetry::U1()}); + Bond loaded = RoundTrip(b); + EXPECT_TRUE(loaded == b); +} + +// Bonds carrying a fermionic-parity symmetry. +TEST(IOBondTest, RoundTripFermionic) { + Bond b = Bond(BD_IN, {Qs(0) >> 2, Qs(1) >> 3}, {Symmetry::FermionParity()}); + Bond loaded = RoundTrip(b); + EXPECT_TRUE(loaded == b); +} + +// Saving the same name twice must fail without overwrite and succeed with it. +TEST(IOBondTest, Overwrite) { + TempH5File tmp; + Bond a = Bond(BD_IN, {Qs(0) >> 1, Qs(1) >> 2}); + Bond b = Bond(BD_OUT, {Qs(0) >> 2, Qs(2) >> 3, Qs(-1) >> 1}); + { + H5::H5File file = io::open(tmp.str(), io::ACC_TRUNC); + io::Save(a, file, "obj"); + EXPECT_THROW(io::Save(b, file, "obj", "", false), std::logic_error); + EXPECT_NO_THROW(io::Save(b, file, "obj", "", true)); + file.close(); + } + Bond loaded = LoadFromFile(tmp.str(), "obj"); + EXPECT_TRUE(loaded == b); +} + +// Store under a nested group path and read it back. +TEST(IOBondTest, NestedPath) { + TempH5File tmp; + Bond b = Bond(BD_IN, {Qs(0) >> 2, Qs(1) >> 2}); + { + H5::H5File file = io::open(tmp.str(), io::ACC_TRUNC); + io::Save(b, file, "obj", "/bonds/a"); + file.close(); + } + Bond loaded = LoadFromFile(tmp.str(), "obj", "/bonds/a"); + EXPECT_TRUE(loaded == b); +} + +// Multiple bonds coexist in one file under different names. +TEST(IOBondTest, MultipleObjectsInOneFile) { + TempH5File tmp; + Bond b1 = Bond(4); + Bond b2 = Bond(BD_IN, {Qs(0) >> 1, Qs(1) >> 1}); + { + H5::H5File file = io::open(tmp.str(), io::ACC_TRUNC); + io::Save(b1, file, "first"); + io::Save(b2, file, "second"); + file.close(); + } + EXPECT_TRUE(LoadFromFile(tmp.str(), "first") == b1); + EXPECT_TRUE(LoadFromFile(tmp.str(), "second") == b2); +} + +// Load committed reference files (regression coverage for the on-disk format). +TEST(IOBondTest, LoadReference) { + EXPECT_TRUE(LoadFromFile(ref_data_dir() + "bond.h5") == ref::bond()); + EXPECT_TRUE(LoadFromFile(ref_data_dir() + "bond_fermionic.h5") == ref::bond_fermionic()); +} diff --git a/tests/io_test/Storage_test.cpp b/tests/io_test/Storage_test.cpp new file mode 100644 index 000000000..5165c928d --- /dev/null +++ b/tests/io_test/Storage_test.cpp @@ -0,0 +1,142 @@ +#include "io_test_tools.h" + +#include "test_tools.h" + +using namespace cytnx; +using namespace cytnx::IOTest; + +namespace { + + // Build a deterministic Storage of the requested dtype. + Storage MakeStorage(unsigned int dtype, cytnx_uint64 n = 12) { + std::vector v(n); + for (cytnx_uint64 i = 0; i < n; ++i) v[i] = static_cast(i % 5); + return Storage(v).astype(dtype); + } + +} // namespace + +// Round-trip a Storage of every supported dtype through the io module. +TEST(IOStorageTest, RoundTripAllDtypes) { + for (auto dtype : TestTools::dtype_list) { + Storage s = MakeStorage(dtype); + Storage loaded = RoundTrip(s); + EXPECT_EQ(loaded.dtype(), s.dtype()) << "dtype: " << Type.getname(dtype); + EXPECT_TRUE(loaded == s) << "dtype: " << Type.getname(dtype); + } +} + +// Saving the same name twice must fail without overwrite and succeed with it. +TEST(IOStorageTest, Overwrite) { + TempH5File tmp; + Storage a = MakeStorage(Type.Double); + Storage b = MakeStorage(Type.Double, 20); // different size -> dataset gets recreated + { + H5::H5File file = io::open(tmp.str(), io::ACC_TRUNC); + io::Save(a, file, "obj"); + EXPECT_THROW(io::Save(b, file, "obj", "", false), std::logic_error); + EXPECT_NO_THROW(io::Save(b, file, "obj", "", true)); + file.close(); + } + Storage loaded = LoadFromFile(tmp.str(), "obj"); + EXPECT_TRUE(loaded == b); +} + +// Overwrite with identical type and shape reuses the existing dataset. +TEST(IOStorageTest, OverwriteSameShape) { + TempH5File tmp; + Storage a = MakeStorage(Type.Double); + Storage b = MakeStorage(Type.Double); + b.fill(7.0); + { + H5::H5File file = io::open(tmp.str(), io::ACC_TRUNC); + io::Save(a, file, "obj"); + EXPECT_NO_THROW(io::Save(b, file, "obj", "", true)); + file.close(); + } + Storage loaded = LoadFromFile(tmp.str(), "obj"); + EXPECT_TRUE(loaded == b); +} + +// Overwrite must replace data stored with a different dtype (dataset recreated). +TEST(IOStorageTest, OverwriteDifferentDtype) { + TempH5File tmp; + Storage a = MakeStorage(Type.Double); + Storage b = MakeStorage(Type.Int64, 7); // different dtype and size + { + H5::H5File file = io::open(tmp.str(), io::ACC_TRUNC); + io::Save(a, file, "obj"); + EXPECT_THROW(io::Save(b, file, "obj", "", false), std::logic_error); + EXPECT_NO_THROW(io::Save(b, file, "obj", "", true)); + file.close(); + } + Storage loaded = LoadFromFile(tmp.str(), "obj"); + EXPECT_EQ(loaded.dtype(), Type.Int64); + EXPECT_TRUE(loaded == b); +} + +// Re-saving the identical object with overwrite=true keeps the data intact. +TEST(IOStorageTest, OverwriteIdentical) { + TempH5File tmp; + Storage a = MakeStorage(Type.Double); + { + H5::H5File file = io::open(tmp.str(), io::ACC_TRUNC); + io::Save(a, file, "obj"); + EXPECT_NO_THROW(io::Save(a, file, "obj", "", true)); + file.close(); + } + EXPECT_TRUE(LoadFromFile(tmp.str(), "obj") == a); +} + +// Objects can be stored under a nested group path and read back from it. +TEST(IOStorageTest, NestedPath) { + TempH5File tmp; + Storage s = MakeStorage(Type.Int64); + { + H5::H5File file = io::open(tmp.str(), io::ACC_TRUNC); + io::Save(s, file, "obj", "/group/sub"); + file.close(); + } + Storage loaded = LoadFromFile(tmp.str(), "obj", "/group/sub"); + EXPECT_TRUE(loaded == s); +} + +// Several independent objects can live in the same file under different names. +TEST(IOStorageTest, MultipleObjectsInOneFile) { + TempH5File tmp; + Storage s1 = MakeStorage(Type.Double); + Storage s2 = MakeStorage(Type.ComplexDouble, 8); + { + H5::H5File file = io::open(tmp.str(), io::ACC_TRUNC); + io::Save(s1, file, "first"); + io::Save(s2, file, "second"); + file.close(); + } + Storage l1 = LoadFromFile(tmp.str(), "first"); + Storage l2 = LoadFromFile(tmp.str(), "second"); + EXPECT_TRUE(l1 == s1); + EXPECT_TRUE(l2 == s2); +} + +// The device-restoring Load overload keeps CPU data on the CPU either way. +TEST(IOStorageTest, LoadDeviceRestore) { + TempH5File tmp; + Storage s = MakeStorage(Type.Float); + SaveToFile(s, tmp.str()); + + Storage keep = LoadFromFileDevice(tmp.str(), true); + EXPECT_EQ(keep.device(), Device.cpu); + EXPECT_TRUE(keep == s); + + Storage cpu = LoadFromFileDevice(tmp.str(), false); + EXPECT_EQ(cpu.device(), Device.cpu); + EXPECT_TRUE(cpu == s); +} + +// Load committed reference file (regression coverage for the on-disk format). +TEST(IOStorageTest, LoadReference) { + Storage loaded = LoadFromFile(ref_data_dir() + "storage.h5"); + Storage expected = ref::storage(); + EXPECT_EQ(loaded.dtype(), expected.dtype()); + EXPECT_TRUE(loaded == expected); +} diff --git a/tests/io_test/Symmetry_test.cpp b/tests/io_test/Symmetry_test.cpp new file mode 100644 index 000000000..50f9735f4 --- /dev/null +++ b/tests/io_test/Symmetry_test.cpp @@ -0,0 +1,77 @@ +#include "io_test_tools.h" + +using namespace cytnx; +using namespace cytnx::IOTest; + +// Round-trip the U1 and Zn symmetry generators. +TEST(IOSymmetryTest, RoundTripU1) { + Symmetry s = Symmetry::U1(); + Symmetry loaded = RoundTrip(s); + EXPECT_TRUE(loaded == s); +} + +TEST(IOSymmetryTest, RoundTripZn) { + for (int n : {2, 3, 5}) { + Symmetry s = Symmetry::Zn(n); + Symmetry loaded = RoundTrip(s); + EXPECT_TRUE(loaded == s) << "Zn n=" << n; + } +} + +// Round-trip the fermionic symmetry generators. +TEST(IOSymmetryTest, RoundTripFermionic) { + for (Symmetry s : {Symmetry::FermionParity(), Symmetry::FermionNumber()}) { + Symmetry loaded = RoundTrip(s); + EXPECT_TRUE(loaded == s); + } +} + +// Saving the same name twice must fail without overwrite and succeed with it. +TEST(IOSymmetryTest, Overwrite) { + TempH5File tmp; + Symmetry a = Symmetry::U1(); + Symmetry b = Symmetry::Zn(2); + { + H5::H5File file = io::open(tmp.str(), io::ACC_TRUNC); + io::Save(a, file, "obj"); + EXPECT_THROW(io::Save(b, file, "obj", "", false), std::logic_error); + EXPECT_NO_THROW(io::Save(b, file, "obj", "", true)); + file.close(); + } + Symmetry loaded = LoadFromFile(tmp.str(), "obj"); + EXPECT_TRUE(loaded == b); +} + +// Store under a nested group path and read it back. +TEST(IOSymmetryTest, NestedPath) { + TempH5File tmp; + Symmetry s = Symmetry::Zn(4); + { + H5::H5File file = io::open(tmp.str(), io::ACC_TRUNC); + io::Save(s, file, "obj", "/sym/inner"); + file.close(); + } + Symmetry loaded = LoadFromFile(tmp.str(), "obj", "/sym/inner"); + EXPECT_TRUE(loaded == s); +} + +// Multiple symmetries coexist in one file under different names. +TEST(IOSymmetryTest, MultipleObjectsInOneFile) { + TempH5File tmp; + Symmetry s1 = Symmetry::U1(); + Symmetry s2 = Symmetry::Zn(3); + { + H5::H5File file = io::open(tmp.str(), io::ACC_TRUNC); + io::Save(s1, file, "first"); + io::Save(s2, file, "second"); + file.close(); + } + EXPECT_TRUE(LoadFromFile(tmp.str(), "first") == s1); + EXPECT_TRUE(LoadFromFile(tmp.str(), "second") == s2); +} + +// Load committed reference files (regression coverage for the on-disk format). +TEST(IOSymmetryTest, LoadReference) { + EXPECT_TRUE(LoadFromFile(ref_data_dir() + "symmetry.h5") == ref::symmetry()); + EXPECT_TRUE(LoadFromFile(ref_data_dir() + "symmetry_fpar.h5") == ref::symmetry_fpar()); +} diff --git a/tests/io_test/Tensor_test.cpp b/tests/io_test/Tensor_test.cpp new file mode 100644 index 000000000..da7c4a9f0 --- /dev/null +++ b/tests/io_test/Tensor_test.cpp @@ -0,0 +1,143 @@ +#include "io_test_tools.h" + +#include "test_tools.h" + +using namespace cytnx; +using namespace cytnx::IOTest; +using namespace cytnx::TestTools; + +// Round-trip a Tensor of every supported dtype. +TEST(IOTensorTest, RoundTripAllDtypes) { + for (auto dtype : dtype_list) { + Tensor t = Tensor({3, 4, 2}, dtype); + InitTensorUniform(t, /*seed=*/dtype); + Tensor loaded = RoundTrip(t); + EXPECT_TRUE(AreEqTensor(loaded, t)) << "dtype: " << Type.getname(dtype); + } +} + +// Round-trip tensors of several ranks/shapes. +TEST(IOTensorTest, RoundTripShapes) { + std::vector> shapes = {{6}, {3, 4}, {2, 3, 4}, {2, 2, 2, 2}}; + for (const auto& shape : shapes) { + Tensor t = Tensor(shape, Type.Double); + InitTensorUniform(t, 7); + Tensor loaded = RoundTrip(t); + EXPECT_TRUE(AreEqTensor(loaded, t)); + } +} + +// A non-contiguous (permuted) tensor must round-trip to its logical (contiguous) values. +TEST(IOTensorTest, RoundTripNonContiguous) { + Tensor t = Tensor({3, 4, 5}, Type.ComplexDouble); + InitTensorUniform(t, 11); + t.permute_({2, 0, 1}); + ASSERT_FALSE(t.is_contiguous()); + Tensor loaded = RoundTrip(t); + EXPECT_TRUE(loaded.is_contiguous()); + EXPECT_TRUE(AreEqTensor(loaded, t.contiguous())); +} + +// Saving the same name twice must fail without overwrite and succeed with it. +TEST(IOTensorTest, Overwrite) { + TempH5File tmp; + Tensor a = Tensor({3, 4}, Type.Double); + InitTensorUniform(a, 1); + Tensor b = Tensor({2, 5}, Type.Double); // different shape -> dataset recreated + InitTensorUniform(b, 2); + { + H5::H5File file = io::open(tmp.str(), io::ACC_TRUNC); + io::Save(a, file, "obj"); + EXPECT_THROW(io::Save(b, file, "obj", "", false), std::logic_error); + EXPECT_NO_THROW(io::Save(b, file, "obj", "", true)); + file.close(); + } + Tensor loaded = LoadFromFile(tmp.str(), "obj"); + EXPECT_TRUE(AreEqTensor(loaded, b)); +} + +// Overwrite with identical shape/dtype but different values replaces the data. +TEST(IOTensorTest, OverwriteSameShape) { + TempH5File tmp; + Tensor a = Tensor({3, 4}, Type.Double); + Tensor b = Tensor({3, 4}, Type.Double); + InitTensorUniform(a, 1); + InitTensorUniform(b, 99); + { + H5::H5File file = io::open(tmp.str(), io::ACC_TRUNC); + io::Save(a, file, "obj"); + EXPECT_THROW(io::Save(b, file, "obj", "", false), std::logic_error); + EXPECT_NO_THROW(io::Save(b, file, "obj", "", true)); + file.close(); + } + EXPECT_TRUE(AreEqTensor(LoadFromFile(tmp.str(), "obj"), b)); +} + +// Overwrite must replace data stored with a different dtype (dataset recreated). +TEST(IOTensorTest, OverwriteDifferentDtype) { + TempH5File tmp; + Tensor a = Tensor({3, 4}, Type.Double); + Tensor b = Tensor({3, 4}, Type.ComplexDouble); + InitTensorUniform(a, 1); + InitTensorUniform(b, 2); + { + H5::H5File file = io::open(tmp.str(), io::ACC_TRUNC); + io::Save(a, file, "obj"); + EXPECT_NO_THROW(io::Save(b, file, "obj", "", true)); + file.close(); + } + EXPECT_TRUE(AreEqTensor(LoadFromFile(tmp.str(), "obj"), b)); +} + +// Store under a nested group path and read it back. +TEST(IOTensorTest, NestedPath) { + TempH5File tmp; + Tensor t = Tensor({4, 4}, Type.Float); + InitTensorUniform(t, 3); + { + H5::H5File file = io::open(tmp.str(), io::ACC_TRUNC); + io::Save(t, file, "obj", "/deeply/nested/group"); + file.close(); + } + Tensor loaded = LoadFromFile(tmp.str(), "obj", "/deeply/nested/group"); + EXPECT_TRUE(AreEqTensor(loaded, t)); +} + +// Multiple tensors coexist in one file under different names. +TEST(IOTensorTest, MultipleObjectsInOneFile) { + TempH5File tmp; + Tensor t1 = Tensor({3, 3}, Type.Double); + Tensor t2 = Tensor({2, 4}, Type.Int32); + InitTensorUniform(t1, 4); + InitTensorUniform(t2, 5); + { + H5::H5File file = io::open(tmp.str(), io::ACC_TRUNC); + io::Save(t1, file, "alpha"); + io::Save(t2, file, "beta"); + file.close(); + } + EXPECT_TRUE(AreEqTensor(LoadFromFile(tmp.str(), "alpha"), t1)); + EXPECT_TRUE(AreEqTensor(LoadFromFile(tmp.str(), "beta"), t2)); +} + +// The device-restoring Load overload keeps CPU data on the CPU either way. +TEST(IOTensorTest, LoadDeviceRestore) { + TempH5File tmp; + Tensor t = Tensor({3, 4}, Type.Double); + InitTensorUniform(t, 6); + SaveToFile(t, tmp.str()); + + Tensor keep = LoadFromFileDevice(tmp.str(), true); + EXPECT_EQ(keep.device(), Device.cpu); + EXPECT_TRUE(AreEqTensor(keep, t)); + + Tensor cpu = LoadFromFileDevice(tmp.str(), false); + EXPECT_EQ(cpu.device(), Device.cpu); + EXPECT_TRUE(AreEqTensor(cpu, t)); +} + +// Load committed reference file (regression coverage for the on-disk format). +TEST(IOTensorTest, LoadReference) { + Tensor loaded = LoadFromFile(ref_data_dir() + "tensor.h5"); + EXPECT_TRUE(AreEqTensor(loaded, ref::tensor())); +} diff --git a/tests/io_test/UniTensor_test.cpp b/tests/io_test/UniTensor_test.cpp new file mode 100644 index 000000000..7c2343ae4 --- /dev/null +++ b/tests/io_test/UniTensor_test.cpp @@ -0,0 +1,185 @@ +#include "io_test_tools.h" + +#include "test_tools.h" + +using namespace cytnx; +using namespace cytnx::IOTest; +using namespace cytnx::TestTools; + +namespace { + + // Round-trip a UniTensor and check both metadata and element values survive. + void ExpectRoundTrip(const UniTensor& ut) { + UniTensor loaded = RoundTrip(ut); + EXPECT_TRUE(AreEqUniTensorMeta(loaded, ut)) << "metadata mismatch after round-trip"; + EXPECT_TRUE(AreEqUniTensor(loaded, ut)) << "element mismatch after round-trip"; + } + +} // namespace + +// Untagged dense UniTensor across all supported dtypes. +TEST(IOUniTensorTest, RoundTripDenseUntaggedAllDtypes) { + for (auto dtype : dtype_list) { + UniTensor ut = + UniTensor({Bond(3), Bond(4), Bond(2)}, {"a", "b", "c"}, 1, dtype).set_name("dense"); + InitUniTensorUniform(ut, /*seed=*/dtype); + ExpectRoundTrip(ut); + } +} + +// Tagged (directional, no-symmetry) dense UniTensor. +TEST(IOUniTensorTest, RoundTripDenseTagged) { + UniTensor ut = + UniTensor({Bond(3, BD_KET), Bond(3, BD_BRA)}, {"i", "j"}, 1, Type.ComplexDouble).set_name("tagged"); + ASSERT_TRUE(ut.is_tag()); + InitUniTensorUniform(ut, 21); + ExpectRoundTrip(ut); +} + +// Diagonal UniTensor (is_diag = true). +TEST(IOUniTensorTest, RoundTripDiagonal) { + UniTensor ut = + UniTensor({Bond(5), Bond(5)}, {"i", "j"}, 1, Type.Double, Device.cpu, /*is_diag=*/true) + .set_name("diag"); + ASSERT_TRUE(ut.is_diag()); + InitUniTensorUniform(ut, 22); + ExpectRoundTrip(ut); +} + +// Symmetric (block) UniTensor with a single U1 symmetry, across numeric dtypes. +TEST(IOUniTensorTest, RoundTripSymmetricU1) { + // Bool is excluded: block construction (Hstack) does not support it. + for (auto dtype : {Type.Double, Type.ComplexDouble, Type.Float, Type.Int64}) { + Bond bk = Bond(BD_KET, {Qs(0) >> 2, Qs(1) >> 3, Qs(-1) >> 1}); + Bond bb = Bond(BD_BRA, {Qs(0) >> 2, Qs(1) >> 3, Qs(-1) >> 1}); + UniTensor ut = UniTensor({bk, bb}, {"a", "b"}, -1, dtype).set_name("sym_u1"); + ASSERT_EQ(ut.uten_type(), UTenType.Block); + InitUniTensorUniform(ut, 23); + ExpectRoundTrip(ut); + } +} + +// Symmetric UniTensor with multiple symmetries (U1 x Z2). +TEST(IOUniTensorTest, RoundTripSymmetricMulti) { + Bond bk = Bond(BD_KET, {{0, 0}, {1, 1}, {0, 1}}, {2, 3, 1}, {Symmetry::U1(), Symmetry::Zn(2)}); + Bond bb = Bond(BD_BRA, {{0, 0}, {1, 1}, {0, 1}}, {2, 3, 1}, {Symmetry::U1(), Symmetry::Zn(2)}); + UniTensor ut = UniTensor({bk, bb}, {"a", "b"}, -1, Type.ComplexDouble).set_name("sym_u1z2"); + ASSERT_EQ(ut.uten_type(), UTenType.Block); + InitUniTensorUniform(ut, 24); + ExpectRoundTrip(ut); +} + +// Fermionic symmetric (BlockFermionic) UniTensor. +TEST(IOUniTensorTest, RoundTripFermionic) { + Bond bk = Bond(BD_IN, {Qs(0) >> 2, Qs(1) >> 3}, {Symmetry::FermionParity()}); + Bond bb = Bond(BD_OUT, {Qs(0) >> 2, Qs(1) >> 3}, {Symmetry::FermionParity()}); + UniTensor ut = UniTensor({bk, bb}, {"a", "b"}, -1, Type.ComplexDouble).set_name("ferm"); + ASSERT_EQ(ut.uten_type(), UTenType.BlockFermionic); + InitUniTensorUniform(ut, 25); + ExpectRoundTrip(ut); +} + +// Saving the same name twice must fail without overwrite and succeed with it. +TEST(IOUniTensorTest, Overwrite) { + TempH5File tmp; + UniTensor a = UniTensor({Bond(3), Bond(2)}, {"a", "b"}, 1, Type.Double).set_name("A"); + UniTensor b = UniTensor({Bond(4), Bond(5), Bond(2)}, {"x", "y", "z"}, 2, Type.Double).set_name("B"); + InitUniTensorUniform(a, 1); + InitUniTensorUniform(b, 2); + { + H5::H5File file = io::open(tmp.str(), io::ACC_TRUNC); + io::Save(a, file, "obj"); + EXPECT_THROW(io::Save(b, file, "obj", "", false), std::logic_error); + EXPECT_NO_THROW(io::Save(b, file, "obj", "", true)); + file.close(); + } + UniTensor loaded = LoadFromFile(tmp.str(), "obj"); + EXPECT_TRUE(AreEqUniTensorMeta(loaded, b)); + EXPECT_TRUE(AreEqUniTensor(loaded, b)); +} + +// Overwrite with identical structure but different values replaces the data. +TEST(IOUniTensorTest, OverwriteSameStructure) { + TempH5File tmp; + UniTensor a = UniTensor({Bond(3), Bond(2)}, {"a", "b"}, 1, Type.Double).set_name("A"); + UniTensor b = UniTensor({Bond(3), Bond(2)}, {"a", "b"}, 1, Type.Double).set_name("A"); + InitUniTensorUniform(a, 1); + InitUniTensorUniform(b, 77); + { + H5::H5File file = io::open(tmp.str(), io::ACC_TRUNC); + io::Save(a, file, "obj"); + EXPECT_THROW(io::Save(b, file, "obj", "", false), std::logic_error); + EXPECT_NO_THROW(io::Save(b, file, "obj", "", true)); + file.close(); + } + UniTensor loaded = LoadFromFile(tmp.str(), "obj"); + EXPECT_TRUE(AreEqUniTensorMeta(loaded, b)); + EXPECT_TRUE(AreEqUniTensor(loaded, b)); +} + +// Store under a nested group path and read it back. +TEST(IOUniTensorTest, NestedPath) { + TempH5File tmp; + UniTensor ut = UniTensor({Bond(3), Bond(3)}, {"a", "b"}, 1, Type.Double).set_name("nested"); + InitUniTensorUniform(ut, 31); + { + H5::H5File file = io::open(tmp.str(), io::ACC_TRUNC); + io::Save(ut, file, "obj", "/group/sub"); + file.close(); + } + UniTensor loaded = LoadFromFile(tmp.str(), "obj", "/group/sub"); + EXPECT_TRUE(AreEqUniTensorMeta(loaded, ut)); + EXPECT_TRUE(AreEqUniTensor(loaded, ut)); +} + +// Multiple UniTensors coexist in one file under different names. +TEST(IOUniTensorTest, MultipleObjectsInOneFile) { + TempH5File tmp; + UniTensor u1 = UniTensor({Bond(2), Bond(3)}, {"a", "b"}, 1, Type.Double).set_name("u1"); + UniTensor u2 = UniTensor({Bond(4), Bond(2)}, {"c", "d"}, 1, Type.ComplexDouble).set_name("u2"); + InitUniTensorUniform(u1, 41); + InitUniTensorUniform(u2, 42); + { + H5::H5File file = io::open(tmp.str(), io::ACC_TRUNC); + io::Save(u1, file, "first"); + io::Save(u2, file, "second"); + file.close(); + } + EXPECT_TRUE(AreEqUniTensor(LoadFromFile(tmp.str(), "first"), u1)); + EXPECT_TRUE(AreEqUniTensor(LoadFromFile(tmp.str(), "second"), u2)); +} + +// The device-restoring Load overload keeps CPU data on the CPU either way. +TEST(IOUniTensorTest, LoadDeviceRestore) { + TempH5File tmp; + UniTensor ut = UniTensor({Bond(3), Bond(4)}, {"a", "b"}, 1, Type.Double).set_name("dev"); + InitUniTensorUniform(ut, 51); + SaveToFile(ut, tmp.str()); + + UniTensor keep = LoadFromFileDevice(tmp.str(), true); + EXPECT_EQ(keep.device(), Device.cpu); + EXPECT_TRUE(AreEqUniTensor(keep, ut)); + + UniTensor cpu = LoadFromFileDevice(tmp.str(), false); + EXPECT_EQ(cpu.device(), Device.cpu); + EXPECT_TRUE(AreEqUniTensor(cpu, ut)); +} + +// Load committed reference files (regression coverage for the on-disk format). +TEST(IOUniTensorTest, LoadReference) { + struct Ref { + std::string file; + UniTensor expected; + }; + std::vector refs = { + {"unitensor_dense.h5", ref::unitensor_dense()}, + {"unitensor_diag.h5", ref::unitensor_diag()}, + {"unitensor_sym.h5", ref::unitensor_sym()}, + {"unitensor_fermionic.h5", ref::unitensor_fermionic()}, + }; + for (const auto& r : refs) { + UniTensor loaded = LoadFromFile(ref_data_dir() + r.file); + EXPECT_TRUE(AreEqUniTensorMeta(loaded, r.expected)) << r.file; + EXPECT_TRUE(AreEqUniTensor(loaded, r.expected)) << r.file; + } +} diff --git a/tests/io_test/io_test_tools.h b/tests/io_test/io_test_tools.h new file mode 100644 index 000000000..756346145 --- /dev/null +++ b/tests/io_test/io_test_tools.h @@ -0,0 +1,146 @@ +#ifndef CYTNX_TESTS_IO_TEST_TOOLS_H_ +#define CYTNX_TESTS_IO_TEST_TOOLS_H_ + +#include +#include +#include +#include + +#include + +#include "cytnx.hpp" +#include "io.hpp" +#include "test_tools.h" + +// Shared helpers for the cytnx::io HDF5 Save/Load tests. Saving and loading go +// through io::Save / io::Load only; io::open provides the file handle, which is +// passed directly as the container (an H5File is a valid H5::Group). +namespace cytnx { + namespace IOTest { + + // Directory holding the committed reference data produced by the generator + // in developer_tools/io_data_generator.cpp. + inline std::string ref_data_dir() { return std::string(CYTNX_TEST_DATA_DIR) + "/io/"; } + + // Unique temp file path ending in ".h5", removed again on destruction. + class TempH5File { + public: + TempH5File() : path_(std::string(std::tmpnam(nullptr)) + ".h5") {} + ~TempH5File() { + std::error_code ec; + std::filesystem::remove(path_, ec); + } + const std::string& str() const { return path_; } + + private: + std::string path_; + }; + + // Save a single object into a freshly truncated file. + template + void SaveToFile(const T& object, const std::string& fname, const std::string& name = "obj", + const std::string& path = "") { + H5::H5File file = io::open(fname, io::ACC_TRUNC); + io::Save(object, file, name, path); + file.close(); + } + + // Load an object of type T from a file. + template + T LoadFromFile(const std::string& fname, const std::string& name = "obj", + const std::string& path = "") { + io::savable_class holder = T(); + H5::H5File file = io::open(fname, io::ACC_IN); + io::Load(holder, file, name, path); + file.close(); + return std::get(holder); + } + + // Load an object of type T, choosing whether to restore the stored device. + // Only valid for the loadable_to_device alternatives (Storage, Tensor, UniTensor, MPS). + template + T LoadFromFileDevice(const std::string& fname, bool restore_device, + const std::string& name = "obj", const std::string& path = "") { + io::loadable_to_device holder = T(); + H5::H5File file = io::open(fname, io::ACC_IN); + io::Load(holder, file, name, path, restore_device); + file.close(); + return std::get(holder); + } + + // Save then load through a temporary file and return the loaded copy. + template + T RoundTrip(const T& object) { + TempH5File tmp; + SaveToFile(object, tmp.str()); + return LoadFromFile(tmp.str()); + } + + // Deterministic reference objects shared by the data generator + // (developer_tools side) and the load-from-reference tests, so both build + // exactly the same object. Each reference object is stored in its own file + // under the name "obj" in ref_data_dir(). + namespace ref { + + inline Storage storage() { + std::vector v(12); + for (size_t i = 0; i < v.size(); ++i) v[i] = static_cast(i % 5); + return Storage(v).astype(Type.ComplexDouble); + } + + inline Tensor tensor() { + Tensor t = Tensor({3, 4, 2}, Type.Double); + TestTools::InitTensorUniform(t, 1234); + return t; + } + + inline Bond bond() { + return Bond(BD_KET, {{0, 2}, {1, 5}, {1, 6}, {0, 1}}, {4, 7, 2, 3}, + {Symmetry::Zn(2), Symmetry::U1()}); + } + + inline Symmetry symmetry() { return Symmetry::Zn(3); } + + inline Symmetry symmetry_fpar() { return Symmetry::FermionParity(); } + + inline Bond bond_fermionic() { + return Bond(BD_IN, {Qs(0) >> 2, Qs(1) >> 3}, {Symmetry::FermionParity()}); + } + + inline UniTensor unitensor_dense() { + UniTensor ut = + UniTensor({Bond(3), Bond(4), Bond(2)}, {"a", "b", "c"}, 1, Type.Double).set_name("ref_dense"); + TestTools::InitUniTensorUniform(ut, 5678); + return ut; + } + + inline UniTensor unitensor_diag() { + UniTensor ut = + UniTensor({Bond(5), Bond(5)}, {"i", "j"}, 1, Type.Double, Device.cpu, /*is_diag=*/true) + .set_name("ref_diag"); + TestTools::InitUniTensorUniform(ut, 8765); + return ut; + } + + inline UniTensor unitensor_sym() { + Bond bk = Bond(BD_KET, {Qs(0) >> 2, Qs(1) >> 3, Qs(-1) >> 1}); + Bond bb = Bond(BD_BRA, {Qs(0) >> 2, Qs(1) >> 3, Qs(-1) >> 1}); + UniTensor ut = UniTensor({bk, bb}, {"a", "b"}, -1, Type.ComplexDouble).set_name("ref_sym"); + TestTools::InitUniTensorUniform(ut, 4321); + return ut; + } + + inline UniTensor unitensor_fermionic() { + Bond bk = Bond(BD_IN, {Qs(0) >> 2, Qs(1) >> 3}, {Symmetry::FermionParity()}); + Bond bb = Bond(BD_OUT, {Qs(0) >> 2, Qs(1) >> 3}, {Symmetry::FermionParity()}); + UniTensor ut = UniTensor({bk, bb}, {"a", "b"}, -1, Type.ComplexDouble).set_name("ref_ferm"); + TestTools::InitUniTensorUniform(ut, 1357); + return ut; + } + + } // namespace ref + + } // namespace IOTest +} // namespace cytnx + +#endif // CYTNX_TESTS_IO_TEST_TOOLS_H_ From cbc720120116ff65a6e1c3baec63e49afea336c3 Mon Sep 17 00:00:00 2001 From: Manuel Schneider Date: Sun, 31 May 2026 03:39:28 +0800 Subject: [PATCH 35/40] bug fixed in Storage.Fromfile: Nelem was used before being initialized --- src/backend/Storage.cpp | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/src/backend/Storage.cpp b/src/backend/Storage.cpp index 74adb5944..c780dca57 100644 --- a/src/backend/Storage.cpp +++ b/src/backend/Storage.cpp @@ -168,37 +168,34 @@ namespace cytnx { cytnx_error_msg(dtype == Type.Void, "[ERROR] Cannot have Void dtype.%s", "\n"); cytnx_error_msg(count == 0, "[ERROR] count cannot be zero!%s", "\n"); - Storage out; - cytnx_uint64 Nbytes; - cytnx_uint64 Nelem; - - // check size: + // check size ifstream jf; jf.open(fname, ios::ate | ios::binary); if (!jf.is_open()) { cytnx_error_msg(true, "[ERROR] Cannot open file '%s'.\n", fname.c_str()); } - Nbytes = jf.tellg(); + cytnx_uint64 Nbytes = jf.tellg(); jf.close(); - fstream f; - // check if type match? + // check if type matches cytnx_error_msg(Nbytes % Type.typeSize(dtype), "[ERROR] the total size of file is not an interval of assigned dtype.%s", "\n"); - // check count smaller than Nelem: - if (count < 0) - Nelem = Nbytes / Type.typeSize(dtype); - else { - cytnx_error_msg(count > Nelem, "[ERROR] count exceed the total # of elements %d in file.\n", - Nelem); + // check if count <= Nelem + cytnx_uint64 Nelem = Nbytes / Type.typeSize(dtype); // total elements in the file + if (count >= 0) { + cytnx_error_msg(static_cast(count) > Nelem, + "[ERROR] count exceed the total number of elements %d in the file `%s`\n", + Nelem, fname.c_str()); Nelem = count; } + fstream f; f.open(fname, ios::in | ios::binary); if (!f.is_open()) { cytnx_error_msg(true, "[ERROR] Cannot open file '%s'.\n", fname.c_str()); } + Storage out; out.data_from_binary(f, Nelem, dtype, device); f.close(); return out; From b9a36200cf1e9feb905362882f3e824f59d10a76 Mon Sep 17 00:00:00 2001 From: Manuel Schneider Date: Sun, 31 May 2026 03:39:44 +0800 Subject: [PATCH 36/40] Load_: fixes issues where previous data was not erased by Load_ adds tests --- src/Bond.cpp | 4 ++++ src/UniTensor.cpp | 2 ++ tests/Bond_test.cpp | 26 ++++++++++++++++++++++++++ tests/Bond_test.h | 1 + tests/DenseUniTensor_test.cpp | 23 +++++++++++++++++++++++ 5 files changed, 56 insertions(+) diff --git a/src/Bond.cpp b/src/Bond.cpp index 0bd406e7d..09203fd5e 100644 --- a/src/Bond.cpp +++ b/src/Bond.cpp @@ -559,6 +559,10 @@ namespace cytnx { f.read((char *)&tmpIDDs, sizeof(unsigned int)); cytnx_error_msg(tmpIDDs != 666, "[ERROR] the object is not a cytnx Bond!%s", "\n"); + this->_impl->_qnums.clear(); + this->_impl->_degs.clear(); + this->_impl->_syms.clear(); + int ver; f.read((char *)&ver, sizeof(int)); diff --git a/src/UniTensor.cpp b/src/UniTensor.cpp index 7d860428f..cfdc06d21 100644 --- a/src/UniTensor.cpp +++ b/src/UniTensor.cpp @@ -126,6 +126,8 @@ namespace cytnx { f.read(cname, sizeof(char) * len_name); this->_impl->_name = std::string(cname, len_name); free(cname); + } else { + this->_impl->_name.clear(); } cytnx_uint64 rank; diff --git a/tests/Bond_test.cpp b/tests/Bond_test.cpp index bb686d83f..85f266415 100644 --- a/tests/Bond_test.cpp +++ b/tests/Bond_test.cpp @@ -209,6 +209,32 @@ TEST(Bond, Clear_type) { EXPECT_THROW(bd_sym.set_type(BD_REG), std::logic_error); } +// In-place Load_ must not inherit stale qnums/degs/syms from a previously Bond +TEST(Bond, Load_ResetsStaleMetadata) { + const std::string fpath = std::string(std::tmpnam(nullptr)) + ".cytnx"; + + // 1) save a plain non-symmetric Bond (no qnums/degs/syms) + Bond bd_plain(5); + bd_plain.Save(fpath); + + // 2) start with a symmetric Bond that has non-empty _qnums, _degs, _syms + Bond bd_sym = Bond(BD_KET, {{0}, {1}, {2}}, {3, 3, 3}); + EXPECT_FALSE(bd_sym.qnums().empty()); + EXPECT_FALSE(bd_sym.getDegeneracies().empty()); + EXPECT_NE(bd_sym.Nsym(), 0); + + // 3) Load_ the plain payload into the symmetric Bond; the result must equal the saved Bond. + bd_sym.Load_(fpath); + EXPECT_EQ(bd_sym, bd_plain) << "Load_ should fully reconstruct the saved Bond"; + EXPECT_EQ(bd_sym.dim(), 5); + EXPECT_EQ(bd_sym.type(), BD_REG); + EXPECT_EQ(bd_sym.Nsym(), 0); + EXPECT_TRUE(bd_sym.qnums().empty()) << "stale qnums leaked into a non-symmetric Bond"; + EXPECT_TRUE(bd_sym.getDegeneracies().empty()) << "stale degs leaked into a non-symmetric Bond"; + + std::filesystem::remove(fpath); +} + // TEST(Bond, ConstructorTypeQnums){ // // Bond(bondType tp, const std::vector& qnums); // diff --git a/tests/Bond_test.h b/tests/Bond_test.h index 8625be037..d843d3877 100644 --- a/tests/Bond_test.h +++ b/tests/Bond_test.h @@ -2,6 +2,7 @@ // #ifndef __CYTNX_TEST_BOND_H__ // #define __CYTNX_TEST_BOND_H__ #include +#include #include #include #include diff --git a/tests/DenseUniTensor_test.cpp b/tests/DenseUniTensor_test.cpp index 348d00ed8..02862b3cc 100644 --- a/tests/DenseUniTensor_test.cpp +++ b/tests/DenseUniTensor_test.cpp @@ -4894,6 +4894,29 @@ describe:test Save uninitialized UniTensor ====================*/ TEST_F(DenseUniTensorTest, Save_uninit) { EXPECT_ANY_THROW(ut_uninit.Save(temp_file_path)); } +/*=====test info===== +describe:In-place Load_ must not inherit a stale name from a previous UniTensor. +====================*/ +TEST_F(DenseUniTensorTest, Load_ResetsStaleName) { + auto row_rank = 1u; + std::vector bonds = {Bond(3), Bond(2)}; + + // 1) save a UniTensor with an empty name + UniTensor ut_anon = UniTensor(bonds, {"a", "b"}, row_rank); + ASSERT_TRUE(ut_anon.name().empty()); + ut_anon.Save(temp_file_path); + + // 2) build a UniTensor with a non-empty name, then Load_ the anonymous payload into it. + UniTensor ut_named = UniTensor(bonds, {"a", "b"}, row_rank); + ut_named.set_name("stale_name"); + ASSERT_EQ(ut_named.name(), "stale_name"); + ut_named.Load_(temp_file_path); + EXPECT_TRUE(ut_named.name().empty()) + << "stale UniTensor name leaked when loading a payload with empty name"; + EXPECT_TRUE(AreEqUniTensor(ut_named, ut_anon)) + << "Load_ should fully reconstruct the saved UniTensor"; +} + /*=====test info===== describe:test truncate by label ====================*/ From 080e2d3182cf73e463ffd27a45e752eb25ffad98 Mon Sep 17 00:00:00 2001 From: Manuel Schneider Date: Sun, 31 May 2026 04:15:58 +0800 Subject: [PATCH 37/40] expose restore_device in pybind for MPS --- pybind/tnalgo_py.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/pybind/tnalgo_py.cpp b/pybind/tnalgo_py.cpp index 5896f9686..acf82ec13 100644 --- a/pybind/tnalgo_py.cpp +++ b/pybind/tnalgo_py.cpp @@ -55,12 +55,17 @@ void tnalgo_binding(py::module &m) { "Save", [](tn_algo::MPS &self, const std::string &fname) { self.Save(fname); }, py::arg("fname")) .def_static( - "Load", [](const std::string &fname) { return cytnx::tn_algo::MPS::Load(fname); }, - py::arg("fname")) + "Load", + [](const std::string &fname, const bool restore_device) { + return cytnx::tn_algo::MPS::Load(fname, restore_device); + }, + py::arg("fname"), py::arg("restore_device") = true) .def( "Load_", - [](cytnx::tn_algo::MPS &self, const std::string &fname) { return self.Load_(fname); }, - py::arg("fname")) + [](cytnx::tn_algo::MPS &self, const std::string &fname, const bool restore_device) { + return self.Load_(fname, restore_device); + }, + py::arg("fname"), py::arg("restore_device") = true) .def(py::pickle( [](const tn_algo::MPS &self) { // __getstate__ From edd46ade1d2fe216b4e0c8e6783fd0db85e6b846 Mon Sep 17 00:00:00 2001 From: Manuel Schneider Date: Sun, 31 May 2026 11:30:42 +0800 Subject: [PATCH 38/40] fixed merging and ci issues --- .github/workflows/ci-cmake_tests.yml | 3 +-- tests/io_test/Bond_test.cpp | 4 ++-- tests/io_test/UniTensor_test.cpp | 7 ++++--- tests/io_test/io_test_tools.h | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci-cmake_tests.yml b/.github/workflows/ci-cmake_tests.yml index bddf1e116..3187dc6e5 100644 --- a/.github/workflows/ci-cmake_tests.yml +++ b/.github/workflows/ci-cmake_tests.yml @@ -106,8 +106,7 @@ jobs: pip install --editable '.[dev]' --verbose \ --config-settings=build-dir=build \ --config-settings=cmake.define.CMAKE_BUILD_TYPE=Debug \ - --config-settings=cmake.define.RUN_TESTS=ON \ - --config-settings=cmake.define.HDF5_ROOT=$CONDA_PREFIX + --config-settings=cmake.define.RUN_TESTS=ON - name: Sanity-check the install run: python -c "import cytnx; print('cytnx', cytnx.__version__)" diff --git a/tests/io_test/Bond_test.cpp b/tests/io_test/Bond_test.cpp index 6096a810b..0b857f447 100644 --- a/tests/io_test/Bond_test.cpp +++ b/tests/io_test/Bond_test.cpp @@ -28,8 +28,8 @@ TEST(IOBondTest, RoundTripU1) { // A bond carrying multiple symmetries (U1 x Z2). TEST(IOBondTest, RoundTripMultiSymmetry) { - Bond b = Bond(BD_KET, {{0, 2}, {1, 5}, {1, 6}, {0, 1}}, {4, 7, 2, 3}, - {Symmetry::Zn(2), Symmetry::U1()}); + Bond b = + Bond(BD_KET, {{0, 2}, {1, 5}, {1, 6}, {0, 1}}, {4, 7, 2, 3}, {Symmetry::Zn(2), Symmetry::U1()}); Bond loaded = RoundTrip(b); EXPECT_TRUE(loaded == b); } diff --git a/tests/io_test/UniTensor_test.cpp b/tests/io_test/UniTensor_test.cpp index 7c2343ae4..ef182a1f4 100644 --- a/tests/io_test/UniTensor_test.cpp +++ b/tests/io_test/UniTensor_test.cpp @@ -29,8 +29,8 @@ TEST(IOUniTensorTest, RoundTripDenseUntaggedAllDtypes) { // Tagged (directional, no-symmetry) dense UniTensor. TEST(IOUniTensorTest, RoundTripDenseTagged) { - UniTensor ut = - UniTensor({Bond(3, BD_KET), Bond(3, BD_BRA)}, {"i", "j"}, 1, Type.ComplexDouble).set_name("tagged"); + UniTensor ut = UniTensor({Bond(3, BD_KET), Bond(3, BD_BRA)}, {"i", "j"}, 1, Type.ComplexDouble) + .set_name("tagged"); ASSERT_TRUE(ut.is_tag()); InitUniTensorUniform(ut, 21); ExpectRoundTrip(ut); @@ -83,7 +83,8 @@ TEST(IOUniTensorTest, RoundTripFermionic) { TEST(IOUniTensorTest, Overwrite) { TempH5File tmp; UniTensor a = UniTensor({Bond(3), Bond(2)}, {"a", "b"}, 1, Type.Double).set_name("A"); - UniTensor b = UniTensor({Bond(4), Bond(5), Bond(2)}, {"x", "y", "z"}, 2, Type.Double).set_name("B"); + UniTensor b = + UniTensor({Bond(4), Bond(5), Bond(2)}, {"x", "y", "z"}, 2, Type.Double).set_name("B"); InitUniTensorUniform(a, 1); InitUniTensorUniform(b, 2); { diff --git a/tests/io_test/io_test_tools.h b/tests/io_test/io_test_tools.h index 756346145..32e34806c 100644 --- a/tests/io_test/io_test_tools.h +++ b/tests/io_test/io_test_tools.h @@ -108,8 +108,8 @@ namespace cytnx { } inline UniTensor unitensor_dense() { - UniTensor ut = - UniTensor({Bond(3), Bond(4), Bond(2)}, {"a", "b", "c"}, 1, Type.Double).set_name("ref_dense"); + UniTensor ut = UniTensor({Bond(3), Bond(4), Bond(2)}, {"a", "b", "c"}, 1, Type.Double) + .set_name("ref_dense"); TestTools::InitUniTensorUniform(ut, 5678); return ut; } From 822f311f3e1f7e477f7abb7fe1da452a37410dd5 Mon Sep 17 00:00:00 2001 From: Manuel Schneider Date: Sun, 31 May 2026 11:47:05 +0800 Subject: [PATCH 39/40] added mising files --- cytnx/io_conti.py | 9 ++++ tests/test_data_base/io/bond.h5 | Bin 0 -> 6240 bytes tests/test_data_base/io/bond_fermionic.h5 | Bin 0 -> 6176 bytes tests/test_data_base/io/storage.h5 | Bin 0 -> 2240 bytes tests/test_data_base/io/symmetry.h5 | Bin 0 -> 351 bytes tests/test_data_base/io/symmetry_fpar.h5 | Bin 0 -> 351 bytes tests/test_data_base/io/tensor.h5 | Bin 0 -> 2240 bytes tests/test_data_base/io/unitensor_dense.h5 | Bin 0 -> 6519 bytes tests/test_data_base/io/unitensor_diag.h5 | Bin 0 -> 6216 bytes .../test_data_base/io/unitensor_fermionic.h5 | Bin 0 -> 10776 bytes tests/test_data_base/io/unitensor_sym.h5 | Bin 0 -> 11067 bytes tests/utils/io_data_generator.cpp | 41 ++++++++++++++++++ 12 files changed, 50 insertions(+) create mode 100644 cytnx/io_conti.py create mode 100644 tests/test_data_base/io/bond.h5 create mode 100644 tests/test_data_base/io/bond_fermionic.h5 create mode 100644 tests/test_data_base/io/storage.h5 create mode 100644 tests/test_data_base/io/symmetry.h5 create mode 100644 tests/test_data_base/io/symmetry_fpar.h5 create mode 100644 tests/test_data_base/io/tensor.h5 create mode 100644 tests/test_data_base/io/unitensor_dense.h5 create mode 100644 tests/test_data_base/io/unitensor_diag.h5 create mode 100644 tests/test_data_base/io/unitensor_fermionic.h5 create mode 100644 tests/test_data_base/io/unitensor_sym.h5 create mode 100644 tests/utils/io_data_generator.cpp diff --git a/cytnx/io_conti.py b/cytnx/io_conti.py new file mode 100644 index 000000000..81ac007a2 --- /dev/null +++ b/cytnx/io_conti.py @@ -0,0 +1,9 @@ +from typing import Any +from cytnx import * + +def Load(obj: Any, container, name: str, path: str = ""): + io.c_Load(obj, container, name, path) + +# inject into the submodule +obj = io +setattr(obj,"Load",Load) diff --git a/tests/test_data_base/io/bond.h5 b/tests/test_data_base/io/bond.h5 new file mode 100644 index 0000000000000000000000000000000000000000..ef287281b8177e10af48f4cf9fe86a4a0db19bb1 GIT binary patch literal 6240 zcmeH|O=uHQ5XWb;CWduuYfMcn3h@IxDAI@^g+eP$(}-f!;H4mGlCF}{O>8#7D95^yo?OD(K9JAjKl`Uf6xJ?`!_=H}hWZ=Q7y= zYs506QIImq##~YvZ~1$yb^B{Rm$?#3xgYi{^geeI%MdrfFl31#F=UOWVb${EFO_?b zx+j9QwL>v--S5{50jiDc2CU3_d7&uUvm$+~wY&2ES+JzLL&zs-DhFfiM+u@HcZX^k z=N|hwfC(1ZfF1Ky*Qt5cdJTxN3fd%u&=%(QK^h*$J{;cc+;m_s_L4Umb?zd*044@j zOkdog-Z+wtQH*Vx05#49kclC}He*=Y~^r8l_6r z@xDvuK&HEz-W2Ll+#XUDn6y@y~nXXDSKrm^!e`7>S@|e z@Z}O2rtMgsS{-xbIfr>V`D%@(1&T;-5OFmP(X*}PY$2Bc;|u2)R~$akMH{*VZJ3Y) z*Zs!QkMIzwm!2GGWDA99ko$Oj<$V-&DlcX}URRcW|NjK!`k~vGirfKw@>F9_yCfjz z*LosK_Q(VoH#3#L;M;WRV3Bq?N_<2*;#by@e*rZM;Ps$by4D0V0Zl*?&;&FAO+XXS R1T+CnKoigeG=YDOz)!KiY!Lte literal 0 HcmV?d00001 diff --git a/tests/test_data_base/io/bond_fermionic.h5 b/tests/test_data_base/io/bond_fermionic.h5 new file mode 100644 index 0000000000000000000000000000000000000000..5c56d1e3962d95828ae4b6b5de81161aab77ade5 GIT binary patch literal 6176 zcmeH{&ubGw6vy9Y%^KI$#+aHI5F-_G5KDU!p`g$-Miis2*Ah4FD5>2|-AyoikfNSE zdh<`vlX$2HJ!lW!ya}GX2p+^gz>9*uw==JXuoXl^5A(uie$V^P=e?Qx&3a>xU9rtv z4%lW?rCOGiH>zDazFBKE>z6ZC|NXv|(eF;_HWUpo4BjHo72YGt%AW6k$tnlF-bmLr z%oND=xL-R1sMgvGSeU)eN}IJeSo)uq{=;{V(k0!^BcHM<9h_8tq#zpccgU_eHR2ot zFu?`~aA3LXiy-Ru0wBgRXs02AbwwDQLo_{$V>rDZuL; z*=?a7MUY69Ve+E!b^;m0%8?y~oL6_0fg#@WL0 zs&Ym0sLJhR-7I$et{r}``GlaRYqlyRY2xO<3;F}U9Sr=A2%}5tijC_3;p5&C$;s%u`@?Z*<>*m-;RK7XX+q(3g!Bpt_& zwW-9;y literal 0 HcmV?d00001 diff --git a/tests/test_data_base/io/storage.h5 b/tests/test_data_base/io/storage.h5 new file mode 100644 index 0000000000000000000000000000000000000000..138b415119e5e479d9cca0aed88809a102fdf247 GIT binary patch literal 2240 zcmeD5aB<`1lHy|K;9!6O11RGFROk$pAF^t~1b+{gASQ*}JAzrrxKX^8L4=Wkkr8Hu z0E_`MqnZgK@}EZF~5=I5&~I`ZZ8)T1JJc>d<;N}IX@{Y9_Ds8nA0hxb7x#z z0d|ria!`TTze4fa+i z3<5B-U}pTMF-SXLcMrSUDwu>ojzPDdi-`f~T2Ube7EC!F83qPspacg z3<5B-U}pTMF-SXLcMrSUDwu>ojzPDdi-`f~T2Ube7EC!F4F(2gpacg~5=I5&~I`ZZ8)T1JJc>d<;N}IX@{Y9_Ds8nA0hxb7x#z z0d|ria!`TTz<@GgU|?cqWMqa~!UCnCp~QewT!ev9P{E!75;G1ACfp1r|AC5Gfxcnp z;sE-TnGq_548+t_$WOZ0;3@?8UiCY1a3I*IKM&Yz`jIr z=d8QU4m0w$I%~4$IP95PZFT5tgTp-g^px#gZ4NDEJb%w!Z9On)$83$w*P9M}kv*C) zdsDN+G1-6qUxHc>h&Waio&MW?;LW1B`>G224m2=@ZFLK^I$$ty*8E2K(gRl9>o!`y zYI68{JC*;3bmxIPZT|gG>GiYcFWL_P06L=!SO5S3 literal 0 HcmV?d00001 diff --git a/tests/test_data_base/io/unitensor_dense.h5 b/tests/test_data_base/io/unitensor_dense.h5 new file mode 100644 index 0000000000000000000000000000000000000000..754825c2c189a613ff2fea2ec4380547a62f1d54 GIT binary patch literal 6519 zcmeD5aB<`1lHy|K;9!7()$9;fxg>->1C{u_>HxF9vxm2MErSRn10y3$y#R~>)9^t8 zBJ!U`pz{ffKbp~U^JPu^JzRpA6mstfW+CGWCLxe((A59uVqyS#fQ^p#78pbAfjCH1h=B#uu{@d#49pCy3>+ZIlFEWq24SEs zejsK7Vwcps;?&T*%n%@*U&H`W%)!6|Qe2c@UX+-Z%^(1bUPuf(fc3z9%cFv1a$aIC z#AGh0$wjGY@hKqFfqca!32k8SC?bb0hz(2%B0!fgGBQHF1avC|FpDrSN&>9{r4SH? z*vi4c3JiQ^CJtc4Ff&4(#b5x{!>|Fy7&`O;Prq(Z&cL43K(Q?Vj0+Gjgis6&lTu<` zu%{0E2^MG;BLk~beqM@!Bh*YbgqY!dOfjQcR;Z;iFw3#i7A0{>AYZTvg6v?+NlZ%3 zDb@p}FdzVVLYfW2c7{+4vs)GFqjb=ra&@-O58Y74&u;G%AU39ant=3n(doV!aAl z9l(+WZmgo?wc6`)FO6pV(zXb6mkz-S1JhQMeDjE2By z2#kinXb6mkz-S1JhQMeD3}^@(@ME>-S)t*uBrDKt8-5?Hmpc5s`7O?@N@am{Q|$+4k#Feo9?<{c|gpW zcaK_opM!`@^!3@lN*yLlS-)29X1T+x_j5W`zBW5>+ZQhkdfj_Kr)cjcl{wu9TJHE~ z%v_d!;6v1lh?KyT18%_{E)0z3&@hCJQov|f&@wXwFjO$u!zK;touD%zrcebi`pf^o kdbi+UR|eRO2P0@)9t3tmDB!?|3Y2vRI*i6p@^t1*0FaG2hX4Qo literal 0 HcmV?d00001 diff --git a/tests/test_data_base/io/unitensor_diag.h5 b/tests/test_data_base/io/unitensor_diag.h5 new file mode 100644 index 0000000000000000000000000000000000000000..fffb264f8c2f0a589a3661b17e0e1924fbf68834 GIT binary patch literal 6216 zcmeHI&ubGw6n>jEaT{Yze~4N{B3_gVCbVd84Q-M}t5lm7#DXPlwrygvDM`hs_*;=G z3KkFI(Sj6DRsR4BUOn_AMS2m$Yw#k9hxp#kycp|J1r=JD2fJ@}cIM6ZzWLtFbUYH< z>}~Pth5>9r*@$l!jW_bUb>MojFCOW41#-&`ncC;twoA~gfu?Z?KQ}l=+eXn-`A>G> zLU5-uwvwxnmR`B8*9C~yQxE9${77b)vBwzt-}>C6w{y;r-uaQANnI?^Cf5;wwl>0s zU%~qJ0qEccgMLn(pERKfXKlg9g-^t^isqn|9zdTjfNu{Ct^*$pPNkFMc`KO(+7XeW zVNcRTOhXJShKQ%D41izv=BDG&GhPa(z|Q* zL2Xn=GY;1@ji!*X*PB7xf!GLQElf9Vk?t~Z`*cmBf&fh%(@$IcpBUDg;&19w;6N(IMdOwW(LklQU62=YRI?)R6+UQ-sH23o_D zO^%q^;(**sN@7+9@0Kx_>+hU$&f$hL6RTE2D~a37;?AMu(|OC5O|5P@Ki@8=!XZWo zaDTK1E=QY&*El*Pn|O7}Bb3FlqRtEE=#-iIsetTFx!pQ;_;(e_bg`$3YzTB#kAn71 z&|bmw%C1PSsK|i49rm(x=DD$c{L=>=Wn0TgFV@Ql)=P0#p373-;!yo4nW?}NcPtT) zfOb-5$=3#ZPF{!OP=Zdrcdp!$yN8FfZR~yJvnOJSL=^ajtVW)bGhj3{`I*E1YfrEP zqH?MSK7Z4{p66GVX>KzqY)1{%kP(Wx6zfuh&}maP!r%iI-QyOQ#nv-v2Ze F{tn-+rCI<0 literal 0 HcmV?d00001 diff --git a/tests/test_data_base/io/unitensor_fermionic.h5 b/tests/test_data_base/io/unitensor_fermionic.h5 new file mode 100644 index 0000000000000000000000000000000000000000..a35a1921b36a319acd875c42085202305f67b833 GIT binary patch literal 10776 zcmeHN4{TFK7{AwUWsCwFjIoVBmO%mvG2I_Y6bzRE-H4NsZG=170j{?PRvL&CL|C{yY3OWwEKkoToznBnPMoKS@6inr%8%J6b0)i zVFq7Vd9QfVDN~zl5bygdcekHE)vb-Oua3zrDwtJlu(-cXt_r^bhGC#hxB8ul2Eq(5 z4IxYzz0N8-nqxz2`YYX=zuq2MrGrTfvSOp?AV>Bi6dEy%#Pw9xoK1+4#FGRl^f%P1 zWH@^(ja>$I<<)r0t8J>!?ecnDwV{hCB<26sD1)D4-Q2l*SaIq_dtN zQs5GuCXjgM1fwB=ImoC;t%!w{^Ryc?caMBw;SZHCLL!G8cs-4|0#cR`g#4M>urqR$ z^d7jZErrDAc|8@@IkHXDjb~Y3L2y8_3K7fy_p<#~u)>rKeVA$-PPImxDt!q?3>qVv zLnBUJe5pFJ4Qrsf!6d3bD3uVA4kZf)h)45fiFq;vVWNoG3YVM3F^g#)GT|{BkJXtT zJ%3H;bHH`%Sn=90%sz>!X%3Z2{5ZpOzc&KNVY5*<$kx7rya+%xlN9zB2C_yX%{7tcB?wH5h%E3E_cz}2=@}zD z7VlIY+Oi!$jT{)J3;=cLvO0&yU+1=a>fBD%r%jVX#54j*6SW4nTlM=G;8X%}RZCtH zlc$!Y3!Qtkcwv+%IOi&Q7dpFv05~Z89`gA!iwd~|Ww`Uatj7d#!atU}JJ%r|Gxvxj zK_=3&1{p_0+TAr4aA`9$!fzxL$YdRgIbG@q=m_Wt z=m_Wt=m_Wt=m_Wt=m_Wt=m_Wt=m_Ax9{2Hx2=V}tX}?~n%gDTl`F=tDal))b@E;9U z&X>Vf9rOSKn$dxlT8Gc&ZxBo}>LaCh?Jqw@R%tEpjXNb>P%Nw8U?Sg}iAkhD1qHl9#oRU?^40%QgrlCOM19^j9c6;S?huJ#bVBP7N& z`VmX9hd{-uju{;_A=4-8KO%!ShE5+FcyQ%}Ak98fwsE_mG1T__)xrnzi)rTf*S^>g z2!{5gKh{|I##(CK@mn1Itm+UQj>6zwG>+pkDD$CYT=ke3^>CG!bsinc{*Y`Ej>oY&j zoLaMi(z?=J3+k8Br!Jgvth(GtcdXww{bg!ss7=g z*3ruG=W;h(DyL{4O0*LtKG&s=fR2EUfR2EU!2cP6TdnhOeGpCZk3bke;=G>TwC1&r z6UbR4fKj%${oS&`whXsupa$&P@t*Sqx3gtS9`#|Kt)yf&!EYbC)?o9bRb=0ZEyu+2 z43}59h(amtW*<@9ygWN4muLC10!nXOtHwLU4=}sF!TFmT!m2CmgGZjSEi$9Pr-YF9 M3qi=2m!3TE4^bzx5C8xG literal 0 HcmV?d00001 diff --git a/tests/test_data_base/io/unitensor_sym.h5 b/tests/test_data_base/io/unitensor_sym.h5 new file mode 100644 index 0000000000000000000000000000000000000000..b8c9b746f7f8e395c81aa0b1dc3768483fbf4cf1 GIT binary patch literal 11067 zcmeHNYiv|S6rQ_lmt|RK3x(1`aiOGuAlYI~sSkQXA1n&A(pCw?EW70bOS`+UcPrae zppQVPqC7)tf8vmkMQz`NJyPCGpD<>cUwwL0CT6= zo_U-*bLN{fb7#J}&91`Y!M1dp*=i*ijz7s`&rjy^lqmRW>E^*D1+JG<*N_y6ND}HN zVFVxe_$i*#A;RyXLQMBp<`$%%=~Bm--5EJ0uEO~yhyP;oir8PoG!(4qqTgXN5!wOE z5JH7H=w0E)?i6Eh`YZh{=lAKGbTo-V<}&jJo)PVEgN6?!PjoldK8X-BNg!4z4A-ty z$T0d;BK?`@Z)SC{vZ^BBD^~()FhsbrmDoW!6kHwh1gc099bP`dGJ6JlYabU=bHL;0 zHG?M998#9L)mlG|eXG=*Vx1Wqi!H>dBcxDmNs{0R2q?`dL`tI#fq%e{w`nB_6neAC zN{7WP38RREma}jKBae^VvFOL;Ffa5S4aRy19SI28ToB?bpI@i%8$G)#txF;aGlPL8 z&U{g)C7PFgd8BB-i4EPqwC+579eQC&hBhqK9c+lcfVoVp%UV#K+R=S7z-$6zXU}75 zZC~$i>CIoo1f-SmV73#9n%6l`n4j9XpE?Ax2ymQvIsr@@#OJ9e;#1uWMmAfa(_o;g zREFs+C98R(58?l=wz+5H`>Q1t#^1g~S*8S(kf+k8s27E+y|?Y|iMXJSr`@Bg0D_4N zOt&DiDcuAX=W~ZcwfdQQgnS7 zfU55pmUI9$aaD~c5U%mN12ulH5>m5;OIwCRsg16*ekB~DfR_p&vToR2eHy8TG;Xtg zd+$>#AT-pSy~1O(*(es^ov~?%&nhUH!-f(j1`RA8BfTs)y5?iKN95!13%H5sW3BKg zf|D9&g;+G$m_QY%k9sIkUo0hT96{epmqw7-9@giDcO(>u;Dnc_5ex(j1PlZW1PlZW z1PlZW1PlZW1PlZW1PlZW1Q-Iq2|&PeJf81)Zj=u|dR?Co?2**eXG}OTZKDTyN)?oG zW~A21m^82j?5dzV&1f`4U(VQhbGyDtO~6EcB7vub}Z?aGSFrwLLWf$x+(kd&nkRxSBIkd&Xb&N~(toALeU8kgT6R{tf}2;3F$ zQVu~*F>dNXxW)tCbqDpm`r%Hr`1EI2g0poL_tc}GAKdr=J+aWcBZ&;)7hcX;;$q^S z-0Vs^1tHv%JFRpg&m|*->?R5$9qGWi!uqfdZ$gEZxEo<)qS_>uh--C9pf^v&Wi*~N zk0#P0y0!w#n(W9I^rcL429cZ&I`v~&@_GliM&^Q$7K}W4^5zuV=H9UBT-k*A-O_!) zMsx5=wjdObFgBKqiK=E&N+Kj5@5|S16nFWVLILIEKhL$}Gcgh! z90##$^_a%}!i4RCc~^#S(Ck0umTh@yt-M~Eydd*JllF-+F>&#Ndbx3H@_ye3jdIJO zD`C^bdU;>vrP_tBtd||D@-CeCFetlzJ^jMoSF7de1!>=fo15k9Q*Wis4y=|V+O}l} zPF2ZQSLXd@UB5vl&mMUDqS$|wh!@JP`bOG7z(Bx2z(Bx2 z;Bi2px6K~D_u|REY=le4aztR$%)%qQ*;{+&%Z0sRgL@|23v^5OaP(g+u)wWf98#sf z$5}YC;_($uvA}3xnsF%)a-1Dl9I+Ial@$^EPPB8Ax<&Mg+_+kr#rLmIvyqMxQV5dB xrbN}f``5c-|2jh`psb$OK8iJpAFE3EDamb&nBXHz2(4TY@}fh;ia&j>;a{Q8!|DJ4 literal 0 HcmV?d00001 diff --git a/tests/utils/io_data_generator.cpp b/tests/utils/io_data_generator.cpp new file mode 100644 index 000000000..0107f75f0 --- /dev/null +++ b/tests/utils/io_data_generator.cpp @@ -0,0 +1,41 @@ +// Generator for the committed HDF5 reference data used by the io load tests. +// +// This file does not contain real tests; it only (re)generates the reference +// files in "tests/test_data_base/io" using io::Save, so that the io load tests +// can verify that previously-saved files remain readable (format-stability / +// regression coverage). +// +// To (re)generate the reference data, set NEED_GEN_IO_DATA to 1, rebuild, and +// run the test binary once (e.g. `./test_main --gtest_filter='IODataGen.*'`). +// Then set NEED_GEN_IO_DATA back to 0 and commit the generated files. +#define NEED_GEN_IO_DATA 0 +#if NEED_GEN_IO_DATA + + #include + + #include "cytnx.hpp" + #include "io.hpp" + #include "io_test/io_test_tools.h" + +using namespace cytnx; +using namespace cytnx::IOTest; + +namespace { + void ensure_dir() { std::filesystem::create_directories(ref_data_dir()); } +} // namespace + +TEST(IODataGen, GenerateAll) { + ensure_dir(); + SaveToFile(ref::storage(), ref_data_dir() + "storage.h5"); + SaveToFile(ref::tensor(), ref_data_dir() + "tensor.h5"); + SaveToFile(ref::bond(), ref_data_dir() + "bond.h5"); + SaveToFile(ref::bond_fermionic(), ref_data_dir() + "bond_fermionic.h5"); + SaveToFile(ref::symmetry(), ref_data_dir() + "symmetry.h5"); + SaveToFile(ref::symmetry_fpar(), ref_data_dir() + "symmetry_fpar.h5"); + SaveToFile(ref::unitensor_dense(), ref_data_dir() + "unitensor_dense.h5"); + SaveToFile(ref::unitensor_diag(), ref_data_dir() + "unitensor_diag.h5"); + SaveToFile(ref::unitensor_sym(), ref_data_dir() + "unitensor_sym.h5"); + SaveToFile(ref::unitensor_fermionic(), ref_data_dir() + "unitensor_fermionic.h5"); +} + +#endif // NEED_GEN_IO_DATA From 7be8d9c085fce4d33aa5d5def0a08c3fbebcbeb7 Mon Sep 17 00:00:00 2001 From: Manuel Schneider Date: Sun, 31 May 2026 11:54:01 +0800 Subject: [PATCH 40/40] added hdf5 to the build wheels --- tools/cibuildwheel_before_all.sh | 4 ++-- tools/cibuildwheel_before_all_macos.sh | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/cibuildwheel_before_all.sh b/tools/cibuildwheel_before_all.sh index 38b75db5c..e59efbb13 100644 --- a/tools/cibuildwheel_before_all.sh +++ b/tools/cibuildwheel_before_all.sh @@ -2,11 +2,11 @@ set -xe # Install required packages for manylinux_2_28+ (AlmaLinux/RHEL). if command -v dnf >/dev/null 2>&1; then - dnf install -y boost-devel openblas-devel arpack-devel ccache + dnf install -y boost-devel openblas-devel arpack-devel hdf5-devel ccache # musllinux_1_2 images are Alpine-based, so use apk there. elif command -v apk >/dev/null 2>&1; then apk update - apk add boost-dev openblas-dev arpack-dev ccache + apk add boost-dev openblas-dev arpack-dev hdf5-dev ccache else echo "Unsupported package manager: expected dnf or apk" >&2 exit 1 diff --git a/tools/cibuildwheel_before_all_macos.sh b/tools/cibuildwheel_before_all_macos.sh index 7372c9268..24dab1099 100755 --- a/tools/cibuildwheel_before_all_macos.sh +++ b/tools/cibuildwheel_before_all_macos.sh @@ -6,7 +6,7 @@ set -euo pipefail # encode a minimum supported macOS version (minos). We inspect that metadata # and persist both a report and an effective deployment target for cibuildwheel. -probe_packages=(arpack boost openblas) +probe_packages=(arpack boost hdf5 openblas) install_only_packages=(ccache libomp) install_packages=("${probe_packages[@]}" "${install_only_packages[@]}")