Coder Social home page Coder Social logo

cotila's Introduction

Build Status License GitHub release

Overview

Cotila (compile-time linear algebra) is a header-only library that provides a set of linear algebra functions in C++ intended for use during compile time. All functions available in Cotila are constexpr, meaning they can be used at compile-time to generate constants and lookup tables in a type-safe, readable, and maintainable manner.

Installation

Cotila is a header-only library. Simply point your compiler to include the include/ directory.

If you are using CMake, you can also import the cotila::cotila library.

Code must be compiled with at least C++17 support to use Cotila.

Documentation

The current documentaiton is available online.

The documentation can also be built with CMake and Doxygen:

cmake -D BUILD_DOCS=ON -B build .
cmake --build build --target doc

Quickstart Guide

To use Cotila, all you need to do is #include <cotila/cotila.h>. This header will include all of the headers provided by Cotila.

The Cotila interface is designed around operations commonly found in BLAS or MATLAB and should be simple and predictable.

Types

Cotila provides support for three types: scalars, vectors, and matrices.

Scalars are represented by fundamental types, such as float or double, as well as std::complex. Cotila provides a variety of operations that manipulate scalar types. In some cases, such as square roots, a standard library implementation already exists but it is not constexpr. A simple example below:

constexpr double s = cotila::sqrt(4.);
static_assert(s == 2.); // this evaluates and passes at compile time

Vectors are represented by the cotila::vector class. The vector class is a container for scalar types. Additionally, vector is an aggregate class containing a single array and is constructed via aggregate initialization. If you are confused, some notes on aggregate initialization can be found in the next section. A simple vector example:

constexpr cotila::vector<double, 3> v1 {{1., -2., 3.}}; // very explicit declaration
constexpr cotila::vector v2 {1., 2., 3.}; // deduces the type, omits the extra braces via uniform initialization
static_assert(v2 == cotila::abs(v1));

Matrices are represented by the cotila::matrix class. Like the vector class, matrix is an aggregate class containing a single 2-dimensional array. A matrix is initialized like a normal 2-dimensional array in C++ (i.e. row-major order). A simple matrix example:

/*  m1 contains:        m2 contains:
 *  1 2 3               1 4
 *  4 5 6               2 5
 *                      3 6
 */
constexpr cotila::matrix<double, 2, 3> m1 {{{1., 2., 3.}, {4., 5., 6.}}}; // very explicit declaration
constexpr cotila::matrix m2 {{{1., 4.}, {2., 5.}, {3., 6.}}}; // deduces the type, but the extra braces are required
static_assert(m2 == cotila::transpose(m1));

Complex values are not handled any differently, other than initialization:

/*  m1 contains:        m2 contains:
 *  1 + 0i   2 + 1i     1 + 0i   3 + 1i
 *  3 - 1i   4 + 2i     2 - 1i   4 - 2i
 *
 */
constexpr cotila::matrix<std::complex<double>, 2, 2> m1 {{{{1., 0.}, {2., 1.}}, {{3., -1.}, {4., 2.}}}};
constexpr cotila::matrix m2 {{{{1., 0.}, {3., 1.}}, {{2., -1.}, {4., -2.}}}}; // complex types can be deduced, too!
static_assert(m2 = cotila::hermitian(m1));

Aggregate Initialization

Aggregate objects can be initialized similarly to C structs by simply providing an initializer list with the values to initialize each member. In C++, arrays can be initialized like so:

double arr[3] = {1., 2., 3.};

or

double arr[3] {1., 2., 3.};

The cotila::vector class contains a single array:

template<typename T, std::size_t N>
struct vector {
    T arr[N];
};

To initialize a vector, you must initialize the array member, which results in an extra set of braces:

vector<double, 3> v = {{1., 2., 3.}};

or

vector<double, 3> v {{1., 2., 3.}};

Uniform initialization

Aggregate objects with a single member can be initialized with uniform initialization, which allows you to omit the extra braces:

vector<double, 3> v {1., 2., 3.};

You may omit the extra braces on cotila::matrix, however this can get confusing. You may not use nested initializer lists for uniform initialization, so the elements must be listed in row-major order:

matrix<double, 2, 2> m {1., 2., 3., 4.}; // first row contains [1, 2], second row contains [3, 4]

Class template argument deduction

Cotila's vectors and matrices support class template argument deduction. This allows you to omit the template arguments entirely:

vector v1 {1., 2., 3.};   // uniform initialization
vector v2 {{1., 2., 3.}}; // normal aggregate initialization

Matrices do not support template argument deduction for uniform initialization, since it is impossible to deduce the shape of the matrix. In this case, an extra set of braces is always required:

matrix m {{{1., 2.}, {3., 4.}}};

cotila's People

Contributors

calebzulawski avatar drubinstein avatar leha-bot avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

cotila's Issues

cotila::sum produces 0.0 on floating point vectors

To reproduce, I have this function for computing a vector of gaussian filter weights:

constexpr const auto EULER_NUMBER = 2.71828182845904523536;

template <std::size_t N> constexpr cotila::vector<float, N> generateWeights() {
    constexpr const float sigma = (((float(N) / 2.0f) - 1.0f) / 3.0f) + (1.0f / 3.0f);
    constexpr const auto offset = N / 2;
    constexpr const auto c1 = 1.0 / (sigma * cotila::sqrt(2.0 * M_PI));
    constexpr const auto c2 = 2.0 * cotila::exponentiate(sigma, 2.0);
    constexpr const auto weights = cotila::generate<N>([&](int i) -> float {
        return c1 * cotila::exponentiate(EULER_NUMBER, -cotila::exponentiate(float(i) - float(offset), 2.0f) / c2);
    });
    constexpr const float total_weigths = cotila::sum(weights);
    return weights * total_weigths;
}

The weights here are as follows:

weights	const cotila::vector<float, 31>	
array	float [31]	
[0]	float	0.00141423533
[1]	float	0.00384429004
[2]	float	0.00384429004
[3]	float	0.010449864
[4]	float	0.010449864
[5]	float	0.0284056757
[6]	float	0.0284056757
[7]	float	0.0284056757
[8]	float	0.0772146284
[9]	float	0.0772146284
[10]	float	0.0772146284
[11]	float	0.0772146284
[12]	float	0.0772146284
[13]	float	0.0772146284
[14]	float	0.0772146284
[15]	float	0.0772146284
[16]	float	0.0772146284
[17]	float	0.0772146284
[18]	float	0.0772146284
[19]	float	0.0772146284
[20]	float	0.0772146284
[21]	float	0.0772146284
[22]	float	0.0772146284
[23]	float	0.0284056757
[24]	float	0.0284056757
[25]	float	0.0284056757
[26]	float	0.010449864
[27]	float	0.010449864
[28]	float	0.00384429004
[29]	float	0.00384429004
[30]	float	0.00141423533

The total_weights are reported as:
total_weigths const float 0

I suspect this is because of the sum accumulator starts at 0 as an integer even with a floating point, and because these float values are low it never reaches past 1. See here

Remove #defines from assertion logic?

The assertion logic currently uses #defines heavily. Alternatively, we could've made template structs and stick the static_asserts in the structs. You can then use these as default template arguments (similar to enable_if) or as using declarations inside functions or classes.

Evaluate, discuss and implement or invalidate.

Add matmul

  • matrix multiply
  • matrix multiply - hermitian left side
  • matrix multiply - hermitian right side

CMake build?

Could you make a CMake build? I also could make a PR for you ๐Ÿ˜Š
Thanks in advance!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.