From b037b8dd2f954fd661e113778289109e6777834f Mon Sep 17 00:00:00 2001 From: Arpita Date: Sun, 20 Sep 2026 08:38:34 +0530 Subject: [PATCH] Update README.md Co-Authored-By: Karan <320527448+neu-triino@users.noreply.github.com> --- README.md | 187 ++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 152 insertions(+), 35 deletions(-) diff --git a/README.md b/README.md index 136d046..0f34783 100644 --- a/README.md +++ b/README.md @@ -1,43 +1,71 @@ # Tensor-calculator-cpp -A small N-dimensional tensor library and interactive calculator written in modern C++ (C++17) +![C++17](https://img.shields.io/badge/C%2B%2B-17-blue.svg) +![header-only](https://img.shields.io/badge/header--only-yes-brightgreen.svg) +![license](https://img.shields.io/badge/license-MIT-yellow.svg) + +A small, header-only N-dimensional tensor library written in modern C++ (C++17), plus an interactive command-line calculator built on top of it. No dependencies, just drop `Tensor.hpp` into your project. + +```cpp +Tensor<> a({2, 2}, {1, 2, 3, 4}); +Tensor<> b({2, 2}, {5, 6, 7, 8}); + +std::cout << a.matmul(b) << std::endl; +// [[19.0000, 22.0000], +// [43.0000, 50.0000]] +``` ## Features -- N-dimensional `Tensor` class backed by a flat `std::vector` -- Element-wise `+`, `-`, `*`, `/`, unary `-`, and `+=`/`-=`/`*=`/`/=` (with scalar broadcasting in either direction) -- `==` / `!=` for comparing tensors +- **Templated:** `Tensor` works with `int`, `float`, `double`, `long long` and other number types. `T` defaults to `double`, so `Tensor<>` is `Tensor`. Shorthands `Tensord`, `Tensorf` and `Tensori` are included. +- N-dimensional, backed by a flat `std::vector` in row-major order +- Element-wise `+`, `-`, `*`, `/`, unary `-`, and `+=` `-=` `*=` `/=` +- Scalar broadcasting: a single-element tensor (shape `{1}`) works against any other tensor, on either side - Matrix multiplication and transpose (2D) -- Reshape, sum, mean, and dot product -- Nested-bracket pretty printing, including `operator<<` for direct streaming +- `reshape`, `sum`, `mean`, `dot`, and `cast()` to convert between element types +- `==` / `!=` for comparing tensors +- Nested-bracket pretty printing, with `operator<<` for direct streaming +- Clear exceptions for bad shapes, bad indices and integer division by zero - Interactive CLI: create named tensors and combine them by name +- Unit tests included (no test framework needed) -## Project structure +## Getting started + +Requires a C++17 compiler (g++, clang++, etc.). Both files are in the same folder, so `#include "Tensor.hpp"` works with no include-path flags. +**With g++ / clang++:** + +```bash +g++ -std=c++17 -O2 -Wall -o tensor-calculator main.cpp +./tensor-calculator ``` -tensor-calculator-cpp/ -├── README.md -├── Tensor.hpp # Core tensor class (header-only, path: ./Tensor.hpp) -└── main.cpp # Interactive CLI calculator + +(on Windows the executable is `tensor-calculator.exe`) + +**With CMake:** + +```bash +cmake -S . -B build +cmake --build build +./build/tensor-calculator ``` -Requires a C++17 compiler (g++, clang++, etc.). Both files live in the same -directory, so `#include "Tensor.hpp"` resolves with no include-path flags -needed. If you drop `Tensor.hpp` into a different directory, either compile -with `-I/path/to/that/directory` or update the `#include` to point at it. +**Run the tests:** ```bash -g++ -std=c++17 -O2 -Wall -o tensor-calculator main.cpp +ctest --test-dir build --output-on-failure ``` -This produces an executable named `tensor_calculator` (add `.exe` on Windows). -Run it with: +or without CMake: ```bash -./tensor-calculator +g++ -std=c++17 -O2 -Wall -o test_tensor tests/test_tensor.cpp +./test_tensor ``` -Running the program launches a menu-driven calculator: +## The calculator + +Running the program starts a menu-driven calculator: ``` ===== Tensor Calculator ===== @@ -57,8 +85,7 @@ Running the program launches a menu-driven calculator: 0. Exit ``` -Tensors are created by name, then referenced by that name in later operations — -so you can build up a small workspace of tensors and chain operations on them. +Tensors are created by name and then referenced by that name in later operations, so you can build up a small workspace and chain operations. Any operation that produces a tensor asks if you want to save it; just press Enter to skip. The calculator works with `double` values. ### Example session @@ -87,31 +114,121 @@ Name of B: B Result: [[19.0000, 22.0000], [43.0000, 50.0000]] +Save result as (blank to skip): C +Saved as C. ``` ## Using `Tensor.hpp` as a library -`Tensor.hpp` is header-only — just include it from wherever it lives -relative to your source file: +It's header-only, so copy `Tensor.hpp` next to your code and include it: ```cpp -Tensor<> a({2, 2}, {1, 2, 3, 4}); -Tensor<> b({2, 2}, {5, 6, 7, 8}); +#include "Tensor.hpp" -std::cout << a.matmul(b) << std::endl; -// [[19.0000, 22.0000], -// [43.0000, 50.0000]] +Tensor<> a({2, 2}, {1, 2, 3, 4}); // double (the default) +Tensor x({2, 3}, {1, 2, 3, 4, 5, 6}); + +std::cout << a + a << std::endl; +std::cout << a.transpose() << std::endl; +std::cout << x.matmul(x.transpose()) << std::endl; // int math, prints without decimals +``` + +``` +[[2.0000, 4.0000], + [6.0000, 8.0000]] +[[1.0000, 3.0000], + [2.0000, 4.0000]] +[[14, 32], + [32, 77]] ``` -Compile any program that includes it the normal way: +### Different types, broadcasting, casting -```bash -g++ -std=c++17 -O2 -Wall -o my_program my_program.cpp +```cpp +Tensor x({2, 3}, {1, 2, 3, 4, 5, 6}); + +// a {1} tensor broadcasts against anything, on either side +Tensor ten({1}, {10}); +std::cout << x + ten << std::endl; +// [[11, 12, 13], +// [14, 15, 16]] + +// convert to another element type +Tensor ratio = x.cast() / Tensor({1}, {4}); +std::cout << ratio << std::endl; +// [[0.2500, 0.5000, 0.7500], +// [1.0000, 1.2500, 1.5000]] ``` -No extra flags are needed as long as `Tensor.hpp` is in the same directory -or on your include path (see [Project structure](#project-structure)). +### Errors + +Bad input throws instead of silently giving a wrong answer: + +```cpp +Tensor zeros({2, 3}, 0); +x / zeros; // std::domain_error: integer division by zero + // (float/double follow normal IEEE rules and give inf/nan) + +Tensor s({1}, {1}); +s += x; // std::invalid_argument: += can't change s's shape +s = s + x; // this is fine, it just makes a new tensor +``` + +### API overview + +| Member | Description | +|---|---| +| `Tensor(shape, fill = 0)` | tensor of the given shape filled with one value | +| `Tensor(shape, values)` | tensor from a flat list of values (row-major), throws if the count is wrong | +| `shape`, `data` | public members: the shape vector and the flat data | +| `size()`, `rank()`, `strides()` | number of elements, number of dimensions, row-major strides | +| `at({i, j, ...})` | element access with bounds checking | +| `+ - * /`, unary `-` | element-wise, returns a new tensor | +| `+= -= *= /=` | element-wise in place (shape never changes) | +| `scale(k)` | multiply every element by `k` | +| `matmul(other)` | 2D x 2D matrix multiplication | +| `transpose()` | 2D transpose | +| `reshape(new_shape)` | same data, new shape (element count must match) | +| `cast()` | copy converted to `Tensor` | +| `sum()`, `mean()` | `sum()` returns `T`, `mean()` always returns `double` | +| `dot(other)` | dot product of two 1D tensors | +| `to_string()`, `print()`, `<<` | pretty printing | +| `==`, `!=` | exact comparison of shape and data | + +### Things worth knowing + +- **Scalars are shape `{1}`.** Broadcasting only handles that one case, not general NumPy-style broadcasting. +- **A single value in braces is a fill value.** `Tensor({3}, {7})` gives `[7, 7, 7]`, not an error. Two or more values in braces are read as data, so `Tensor({3}, {7, 8, 9})` does what you'd expect. +- **`==` is exact.** Comparing floating-point results can fail because of rounding, so it's best used with integers or exactly representable values. +- **`reshape` and `cast` make copies.** There are no views yet. +- **`T` must be a number type** (checked at compile time). `bool` isn't supported. +- Integer overflow isn't checked. + +## Project structure + +``` +tensor-calculator-cpp/ +├── README.md +├── LICENSE +├── CMakeLists.txt +├── Tensor.hpp # the tensor class (header-only) +├── main.cpp # interactive CLI calculator +├── tests/ +│ └── test_tensor.cpp # unit tests +└── .github/workflows/ + └── ci.yml # builds + runs tests on Linux and macOS +``` + +## Ideas for later + +Pull requests and issues are welcome. Some things that would be nice to have: + +- General broadcasting (NumPy-style) +- Views instead of copies for `reshape` +- Matrix multiply for higher-rank tensors +- Reductions along an axis (`sum(axis)`, `mean(axis)`) +- `zeros`, `ones` and `eye` helpers ## License -This project is licensed under the MIT License — see the [LICENSE](LICENSE) +This project is licensed under the MIT License. \ No newline at end of file