Coding Style#

When contributing to the Einsums code base, we want to make sure our code fits to certain guidelines. The main purpose of these guidelines is to get contributors to think more deeply about their code, and to provide a single style to make our code more consistent to read. All files should begin with the following statement.

----------------------------------------------------------------------------------------------
  Copyright (c) The Einsums Developers. All rights reserved.
  Licensed under the MIT License. See LICENSE.txt in the project root for license information.
----------------------------------------------------------------------------------------------

C++ Style#

To make code formatting easier, we have provided two files, .clang-tidy and .clang-format. These files can be used with the clang-tidy package to format C++ code, as well as to hint at possible errors in your code. One thing that may come up when using these is a strange warning about certain C++20 features. If you are getting this error, you may need to add a new file called .clangd which contains the following.

CompileFlags:
#C++20
Add: [-std=c++20]

Coding Conventions#

Names#

Names should follow these conventions.

  • Types, including classes, structs, enums, and typedefs should be in PascalCase. This means that every word begins with a capital letter, and words are concatenated without any extra symbols. Some examples.

    • Tensor

    • GPUView

    • DeviceTensorView

    • Tensor::ValueType

  • Concepts, requirements, and compile-time boolean statements underlying those concepts and requirements should also be in PascalCase. Also, requirements and boolean statements should begin with Is and end in V, standing for “value”. Some examples.

    • TensorConcept is a concept.

    • IsTensorV is a requirement or compile-time boolean statement.

  • Namespaces should be in snake_case, where every word is lower case, and each word is separated by underscores. Some examples.

    • einsums::tensor_algebra

    • einsums::tensor_props

  • Functions and methods should be in snake_case as well. Some examples.

    • einsum(...)

    • A.full_view_of_underlying()

  • Private and protected properties in a class should end in a single underscore. However, some code will have them end in a single underscore instead. Some examples.

    • this->dims_

    • this->_dims

  • Variables and parameters should usually be in snake_case. However, to be consistent with mathematical notation, tensor variables and parameters are usually capitalized. This includes when the name of a tensor is referenced in a variable name. Some examples.

    • std::string const &name: name does not refer to a tensor, so it is in snake case.

    • TensorType const &A: A is a tensor, so it is capitalized.

    • std::tuple<AIndices...> const &A_indices: A is a tensor, but the variable is not. This means that the “A” is capitalized, but the rest of the variable is in snake case.

  • Macros and enum members should be in ALL_CAPS.

Order of Things#

  • cv keywords should never come before the pointer or reference type they modify. As an example,

    • std::string const &name, not const std::string &name.

    • However, const size_t size is allowed.

  • When writing a class, the preferred order of blocks is public, then protected, then private.

  • Place simple return types in front of function names. Place complicated return types after function names. Using the auto keyword can sometimes mess with Doxygen, so this is the compromise we have to make. Some examples.

    • size_t size()

    • Tensor<double, 2> &create_tensor() or auto create_tensor() -> Tensor<double, 2> & are both alright. Use your best jugement here.

    • auto common_initialization(TensorType<T, OtherRank> const &other, Args &&...args) -> std::enable_if_t<std::is_base_of_v<::einsums::tensor_base::Tensor<T, OtherRank>, TensorType<T, OtherRank>>> This is a complicated return type. Use the auto keyword for readability.

Miscellaneous#

  • Indents are four spaces. Do not use tabs.

  • Lines should be at most 140 columns long.

Some constructions need to have serious thought before they are used. Before any code with these constructions is accepted, their use will need to be justified.

  • goto statements.

  • do { } while(false); blocks outside of macros. They are fine within macros, since their use is considered idiomatic to C/C++ for making a macro require a semicolon after the closing parenthesis.

  • Inline assembly will be outright banned. One of the goals of Einsums is portability. This goes against this goal.

  • Anything considered to be undefined behavior. Different compilers and systems may have different behavior, so it is best to not use this. Some examples of undefined behavior includes the following.

    • Anything that uses the binary representation of floating point numbers. IEEE 754 states that this is only an exchange format. Modifying the underlying binary representation is considered to be undefined behavior.

    • Assuming the size of variables. For instance, the presence and size of long double is highly system dependent.

Python Style#

The approach to Python style is to generally follow the standard Python style guidelines. Some things to keep in mind.

  • Try to use type annotations when writing Python code. Some examples.

    • def set_name(name): Bad.

    • def set_name(name: str): Good.

    • def iterate_elements(param): Fine.

  • Prefer PascalCase for type names.

  • Prefer snake_case for functions, methods, and variables.

  • However, the same considerations for tensor variables apply as in C++. Tensor varaibles are in UPPER_CASE, and any reference to a tensor variable in a non-tensor variable should match the case of the tensor.

    • A: Tensor variable.

    • A_indices: References a tensor variable, but is not a tensor variable.