Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,16 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

<!-- ---------------------
v1.5.0
--------------------- -->
## v1.5.0 - 27-11-2024

### Added

- Created methods for serialising and deserialising `DTensor` objects


<!-- ---------------------
v1.4.0
--------------------- -->
Expand Down
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,27 @@ The `DTensor` `B` will be overwritten with the solution.
> overwrite only part of the given `B`, as `B` is a
> (4,1,1)-tensor and the solution is a (3,1,1)-tensor.

### 1.8. Saving and loading tensors

Tensor data can be stored in simple text files which have the following structure

```text
number_of_rows
number_of_columns
number_of_matrices
data (one entry per line)
```

To save a tensor in a file, simply call `DTensor::saveToFile(filename)`.

To load a tensor from a file, the static function `DTensor<T>::parseFromTextFile(filename)` can be used. For example:

```c++
auto z = DTensor<double>::parseFromTextFile("path/to/my.dtensor")
```

If necessary, you can provide a second argument to `parseFromTextFile` to specify the order in which the data are stored (the `StorageMode`).

## 2. Cholesky factorisation and system solution

> [!WARNING]
Expand Down
95 changes: 95 additions & 0 deletions include/tensor.cuh
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include <memory>
#include <optional>
#include <cassert>
#include <fstream>

#ifndef TENSOR_CUH
#define TENSOR_CUH
Expand Down Expand Up @@ -250,6 +251,19 @@ public:
*/
static DTensor<T> createRandomTensor(size_t numRows, size_t numCols, size_t numMats, T low, T hi);

/**
* Parse data from text file and create an instance of DTensor
*
* This static function reads data from a text file, creates a DTensor and uploads the data to the device.
*
* @param path_to_file path to file as string
* @param mode storage mode (default: StorageMode::defaultMajor)
* @return instance of DTensor
*
* @throws std::invalid_argument if the file is not found
*/
static DTensor<T> parseFromTextFile(std::string path_to_file, StorageMode mode = StorageMode::defaultMajor);

/**
* Constructs a DTensor object.
*/
Expand Down Expand Up @@ -487,6 +501,13 @@ public:
*/
void reshape(size_t newNumRows, size_t newNumCols, size_t newNumMats = 1);

/**
* Saves the current instance of DTensor to a (text) file
*
* @param pathToFile
*/
void saveToFile(std::string pathToFile);

/* ------------- OPERATORS ------------- */

DTensor &operator=(const DTensor &other);
Expand Down Expand Up @@ -564,6 +585,80 @@ DTensor<T> DTensor<T>::createRandomTensor(size_t numRows, size_t numCols, size_t
throw std::invalid_argument("[createRandomTensor] unsupported type T");
}


template<typename T>
struct data_t {
size_t numRows;
size_t numCols;
size_t numMats;
std::vector<T> data;
};

template<typename T>
data_t<T> vectorFromFile(std::string path_to_file) {
data_t<T> dataStruct;
std::ifstream file;
file.open(path_to_file, std::ios::in);
if (!file.is_open()) { throw std::invalid_argument("the file you provided does not exist"); };

std::string line;
getline(file, line); dataStruct.numRows = atoi(line.c_str());
getline(file, line); dataStruct.numCols = atoi(line.c_str());
getline(file, line); dataStruct.numMats = atoi(line.c_str());

size_t numElements = dataStruct.numRows * dataStruct.numCols * dataStruct.numMats;
std::vector<T> vecDataFromFile(numElements);

size_t i = 0;
while (getline(file, line)) {
if constexpr (std::is_same_v<T, int>) {
vecDataFromFile[i] = atoi(line.c_str());
} else if constexpr (std::is_same_v<T, double>) {
vecDataFromFile[i] = std::stod(line.c_str());
} else if constexpr (std::is_same_v<T, float>) {
vecDataFromFile[i] = std::stof(line.c_str());
} else if constexpr (std::is_same_v<T, long double>) {
vecDataFromFile[i] = std::stold(line.c_str());
} else if constexpr (std::is_same_v<T, long>) {
vecDataFromFile[i] = std::stol(line.c_str());
} else if constexpr (std::is_same_v<T, long long>) {
vecDataFromFile[i] = std::stoll(line.c_str());
} else if constexpr (std::is_same_v<T, unsigned long>) {
vecDataFromFile[i] = std::stoul(line.c_str());
} else if constexpr (std::is_same_v<T, unsigned long long>) {
vecDataFromFile[i] = std::stoull(line.c_str());
} else if constexpr (std::is_same_v<T, size_t>) {
sscanf(line.c_str(), "%zu", &vecDataFromFile[i]);
}
// todo

if (++i == numElements) break;
}
dataStruct.data = vecDataFromFile;
file.close();
return dataStruct;
}

template<typename T>
DTensor<T> DTensor<T>::parseFromTextFile(std::string path_to_file,
StorageMode mode) {
auto parsedData = vectorFromFile<T>(path_to_file);
DTensor<T> tensorFromData(parsedData.data, parsedData.numRows, parsedData.numCols, parsedData.numMats);
return tensorFromData;
}

template<typename T>
void DTensor<T>::saveToFile(std::string pathToFile) {
std::ofstream file(pathToFile);
file << numRows() << std::endl << numCols() << std::endl << numMats() << std::endl;
std::vector<T> myData(numEl()); download(myData);
if constexpr (std::is_floating_point<T>::value) {
int prec = std::numeric_limits<T>::max_digits10 - 1;
file << std::setprecision(prec);
}
for(const T& el : myData) file << el << std::endl;
}

template<typename T>
void DTensor<T>::reshape(size_t newNumRows, size_t newNumCols, size_t newNumMats) {
if (m_numRows == newNumRows && m_numCols == newNumCols && m_numMats == newNumMats) return;
Expand Down
29 changes: 8 additions & 21 deletions main.cu
Original file line number Diff line number Diff line change
@@ -1,27 +1,14 @@
#include <random>
#include <algorithm>
#include <iterator>
#include <vector>
#include <iostream>
#include <cublas_v2.h>
#include "include/tensor.cuh"
#include <memory>
#include <fstream>
#include <iostream>
#include <string>
#include <vector>

#define real_t double

int main() {

std::vector<real_t> aData = {10.0, 2.0, 3.0,
2.0, 20.0, -1.0,
3.0, -1.0, 30.0};
DTensor<real_t> A(3, 3, 2);
DTensor<real_t> A0(A, 2, 0, 0);
DTensor<real_t> A1(A, 2, 1, 1);
A0.upload(aData);
A1.upload(aData);
CholeskyBatchFactoriser<real_t> chol(A);
chol.factorise();
std::cout << chol.info()(0);

auto z = DTensor<size_t>::parseFromTextFile("../test/data/my.dtensor",
StorageMode::rowMajor);
std::cout << z;
z.saveToFile("hohoho.dtensor");
return 0;
}
25 changes: 25 additions & 0 deletions test/testTensor.cu
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include <gtest/gtest.h>
#include "../include/tensor.cuh"
#include <filesystem>

#define PRECISION_LOW 1e-4
#define PRECISION_HIGH 1e-10
Expand Down Expand Up @@ -115,6 +116,30 @@ TEST_F(TensorTest, randomTensorCreation) {
randomTensorCreation<int>();
}

/* ---------------------------------------
* Save to file and parse
* --------------------------------------- */

TEMPLATE_WITH_TYPE_T
void parseTensorFromFile() {
size_t nR = 20, nC = 40, nM = 60;
auto r = DTensor<T>::createRandomTensor(nR, nC, nM, -1, 1);
std::string fName = "myTest.dtensor";
r.saveToFile(fName);
auto a = DTensor<T>::parseFromTextFile(fName);
EXPECT_EQ(nR, a.numRows());
EXPECT_EQ(nC, a.numCols());
EXPECT_EQ(nM, a.numMats());
auto diff = a - r;
T err = diff.maxAbs();
EXPECT_LT(err, 2*std::numeric_limits<T>::epsilon());
}

TEST_F(TensorTest, parseTensorFromFile) {
parseTensorFromFile<float>();
parseTensorFromFile<double>();
}

/* ---------------------------------------
* Move constructor
* --------------------------------------- */
Expand Down