Skip to content

Latest commit

 

History

18 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Uncertainties Library

This project provides a C++ implementation of a udouble class for handling values with uncertainties, similar to Python's uncertainties package. The udouble class supports automatic error propagation with correlation tracking, so expressions like x - x correctly yield 0 ± 0.

Features

  • Correlation tracking: Variables are tracked through expressions so that x - x = 0 ± 0 and x + x = 2x ± 2σ (not σ√2).
  • Create objects with nominal values and standard deviations.
  • Implicit conversion from double (zero uncertainty).
  • Arithmetic operations (+, -, *, /) with automatic error propagation.
  • Division by zero returns 0 ± inf instead of throwing (see Division by Zero).
  • Unary operators (+, -).
  • Compound assignment operators (+=, -=, *=, /=).
  • Comparison operators (==, !=, <, >, <=, >=) based on nominal values.
  • Mixed-type operations between udouble and double.
  • Exponentiation with pow().
  • Mathematical functions:
    • Trigonometric: sin(), cos(), tan()
    • Inverse trigonometric: asin(), acos(), atan(), atan2()
    • Hyperbolic: sinh(), cosh(), tanh()
    • Inverse hyperbolic: asinh(), acosh(), atanh()
    • Exponential/logarithmic: exp(), log(), log10(), sqrt()
    • Other: abs(), hypot()
  • Multiple output formats: default, scientific notation, compact notation.
  • Eigen matrix library integration (optional).
  • Includes unit tests and examples.

Installation

Prerequisites

  1. C++17 or later
  2. CMake 3.10 or later
  3. Optional: Google Test (GTest) for unit testing.

Build Instructions

  1. Clone the repository:

    git clone <repository_url>
    cd uncertainties-cpp
  2. Create a build directory and configure the project:

    mkdir build && cd build
    cmake ..
  3. Build the project:

    cmake --build .
  4. Optionally, install the library:

    cmake --build . --target install

Running Tests

  1. Ensure tests are enabled in the build configuration:

    cmake -DUNCERTAINTIES_BUILD_TESTS=ON ..
  2. Build and run tests:

    cmake --build .
    ctest --verbose

Alternatively, run tests automatically during the build process by running:

cmake --build . --target run_tests

Examples

Example: Basic Usage

Here’s an example of how to use the udouble class:

#include <iostream>
#include "uncertainties/udouble.hpp"

int main() {
    uncertainties::udouble a(1.0, 0.1); // 1.0 ± 0.1
    uncertainties::udouble b(2.0, 0.2); // 2.0 ± 0.2

    uncertainties::udouble c = a + b; // 3.0 ± sqrt(0.1^2 + 0.2^2)

    std::cout << "c = " << c << std::endl; // Output: 3.0 ± 0.223606

    return 0;
}

Example: Correlation Tracking

The library tracks correlations between variables automatically. When the same variable appears multiple times in an expression, the uncertainties are correctly correlated:

#include <iostream>
#include "uncertainties/udouble.hpp"
#include "uncertainties/umath.hpp"

int main() {
    uncertainties::udouble x(10.0, 0.5);
    uncertainties::udouble y(20.0, 1.0);

    // Correlated: x - x = 0 with zero uncertainty
    uncertainties::udouble zero = x - x;
    std::cout << "x - x = " << zero << std::endl;  // 0 ± 0

    // Correlated: x + x = 2x with doubled uncertainty
    uncertainties::udouble doubled = x + x;
    std::cout << "x + x = " << doubled << std::endl;  // 20 ± 1

    // Mixed: (x + y) - x = y (x cancels out)
    uncertainties::udouble result = (x + y) - x;
    std::cout << "(x + y) - x = " << result << std::endl;  // 20 ± 1

    // Math functions preserve correlations
    uncertainties::udouble s = uncertainties::sin(x);
    std::cout << "sin(x) - sin(x) = " << (s - s) << std::endl;  // 0 ± 0

    // Trig identity: sin²(x) + cos²(x) = 1 with zero uncertainty
    uncertainties::udouble x2(0.5, 0.1);
    uncertainties::udouble identity =
        uncertainties::sin(x2) * uncertainties::sin(x2) +
        uncertainties::cos(x2) * uncertainties::cos(x2);
    std::cout << "sin²+cos² = " << identity << std::endl;  // 1 ± 0

    // Create an independent copy (different variable, same value/stddev)
    uncertainties::udouble x_copy = x.independent_copy();
    std::cout << "x - x_copy = " << (x - x_copy) << std::endl;  // 0 ± 0.707

    return 0;
}

Example: Mathematical Functions

The library provides mathematical functions with automatic error propagation:

#include <iostream>
#include "uncertainties/udouble.hpp"
#include "uncertainties/umath.hpp"

int main() {
    uncertainties::udouble x(1.0, 0.1);

    // Trigonometric functions
    uncertainties::udouble s = uncertainties::sin(x);
    uncertainties::udouble c = uncertainties::cos(x);
    uncertainties::udouble t = uncertainties::tan(x);

    // Inverse trigonometric functions
    uncertainties::udouble as = uncertainties::asin(uncertainties::udouble(0.5, 0.01));
    uncertainties::udouble at = uncertainties::atan(x);

    // Hyperbolic functions
    uncertainties::udouble sh = uncertainties::sinh(x);
    uncertainties::udouble ch = uncertainties::cosh(x);

    // Exponential and logarithmic functions
    uncertainties::udouble e = uncertainties::exp(x);
    uncertainties::udouble l = uncertainties::log(x);
    uncertainties::udouble sq = uncertainties::sqrt(uncertainties::udouble(4.0, 0.1));

    // Exponentiation
    uncertainties::udouble p = uncertainties::pow(x, uncertainties::udouble(2.0, 0.0));

    std::cout << "sin(x) = " << s << std::endl;
    std::cout << "sqrt(4) = " << sq << std::endl;

    return 0;
}

Example: Operators

The library supports various operators for convenient calculations:

#include <iostream>
#include "uncertainties/udouble.hpp"

int main() {
    uncertainties::udouble a(10.0, 0.5);
    uncertainties::udouble b(3.0, 0.2);

    // Unary operators
    uncertainties::udouble neg = -a;  // -10.0 ± 0.5

    // Compound assignment
    uncertainties::udouble c = a;
    c += b;  // 13.0 ± sqrt(0.5² + 0.2²)
    c *= 2.0;  // Scale by constant

    // Comparison (based on nominal values)
    if (a > b) {
        std::cout << "a is greater than b" << std::endl;
    }

    std::cout << "c = " << c << std::endl;

    return 0;
}

Division by Zero

Dividing by zero does not throw. The quotient is unconstrained, so it is reported as 0 ± inf — a nominal value of zero carrying an infinite uncertainty:

#include <iostream>
#include "uncertainties/udouble.hpp"

int main() {
    uncertainties::udouble a(1.0, 0.1);
    uncertainties::udouble zero(0.0, 0.1);

    std::cout << a / zero << std::endl;  // 0 ± inf
    std::cout << a / 0.0  << std::endl;  // 0 ± inf
    std::cout << 1.0 / zero << std::endl;  // 0 ± inf

    // The infinite uncertainty keeps propagating:
    uncertainties::udouble b = (a / zero) + uncertainties::udouble(5.0, 0.2);
    std::cout << b << std::endl;  // 5 ± inf

    return 0;
}

This applies to every division form — udouble / udouble, udouble / double, double / udouble, and /= — and triggers whenever the divisor's nominal value is zero, regardless of its uncertainty.

Each division by zero allocates a fresh uncertainty source, so two of them are treated as independent undefined quantities: (a / zero) - (a / zero) remains 0 ± inf rather than cancelling to 0 ± 0.

Note: This deliberately differs from the Python uncertainties package, which raises ZeroDivisionError on division by zero.

Example: Implicit Conversion

Plain double values are automatically converted to udouble with zero uncertainty:

#include <iostream>
#include "uncertainties/udouble.hpp"

int main() {
    uncertainties::udouble a(5.0, 0.1);

    // Implicit conversion from double
    uncertainties::udouble b = 3.0;  // 3.0 ± 0.0

    // Works in arithmetic too
    uncertainties::udouble c = a + 2.0;  // Adds constant with no additional uncertainty

    // Can pass doubles to functions expecting udouble
    auto square = [](const uncertainties::udouble& x) { return x * x; };
    uncertainties::udouble d = square(4.0);  // 16.0 ± 0.0

    return 0;
}

Example: Output Formatting

The library provides multiple ways to format output:

#include <iostream>
#include "uncertainties/udouble.hpp"

int main() {
    uncertainties::udouble x(1.23456, 0.00789);

    // Default stream output
    std::cout << x << std::endl;  // 1.23456 ± 0.00789

    // Custom precision
    std::cout << x.to_string(3) << std::endl;  // 1.23 ± 0.00789

    // Scientific notation
    std::cout << x.to_scientific(2) << std::endl;  // 1.23e+00 ± 7.89e-03

    // Compact notation (uncertainty in parentheses)
    std::cout << x.to_compact() << std::endl;  // 1.235(79)

    return 0;
}

Example: Eigen Integration

The library integrates with the Eigen matrix library for uncertainty propagation through matrix operations:

#include <Eigen/Dense>
#include "uncertainties/eigen_support.hpp"

int main() {
    // Type aliases for convenience
    using Matrix2u = Eigen::Matrix<uncertainties::udouble, 2, 2>;
    using Vector2u = Eigen::Vector<uncertainties::udouble, 2>;

    // Create a matrix with uncertainties
    Matrix2u A;
    A << uncertainties::udouble(1.0, 0.1), uncertainties::udouble(2.0, 0.2),
         uncertainties::udouble(3.0, 0.3), uncertainties::udouble(4.0, 0.4);

    // Create a vector with uncertainties
    Vector2u b;
    b << uncertainties::udouble(5.0, 0.5), uncertainties::udouble(6.0, 0.6);

    // Matrix-vector multiplication propagates uncertainties
    Vector2u result = A * b;

    // Other operations: transpose, dot product, cross product, determinant, etc.
    Matrix2u At = A.transpose();
    uncertainties::udouble det = A.determinant();

    return 0;
}

To enable Eigen support, ensure Eigen3 is installed and detected by CMake. The eigen_support.hpp header provides the necessary NumTraits specialization for Eigen to work with udouble.

Build and Run Example

  1. Enable examples in the build configuration:

    cmake -DUNCERTAINTIES_BUILD_EXAMPLES=ON ..
  2. Build and run the example:

    cmake --build .
    ./example_basic

Documentation

API documentation can be generated using Doxygen:

  1. Install Doxygen (if not already installed):

    # Ubuntu/Debian
    sudo apt-get install doxygen
    
    # macOS
    brew install doxygen
  2. Generate documentation:

    # From the project root directory
    doxygen Doxyfile

    Or using CMake:

    cmake -DUNCERTAINTIES_BUILD_DOCS=ON ..
    cmake --build . --target docs
  3. Open docs/html/index.html in your browser.

Contributing

Contributions are welcome! Please follow these steps:

  1. Fork the repository.
  2. Create a new branch.
  3. Commit your changes.
  4. Open a pull request.

License

This project is licensed under the BSD 3-Clause License. See the LICENSE file for details.


Happy coding!

About

C++17 library for uncertainty propagation with correlation tracking.

Topics

Resources

Stars

3 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages