NumCpp: An Open Source C++ Library for Numerical Computation

NumCpp is a template-based, header-only C++ library designed to provide C++ developers with an experience similar to the Python NumPy library. If you need to perform numerical computations or matrix operations in your C++ projects, NumCpp can help you achieve results efficiently with its familiar API and high performance of C++.

🔍 Introduction to NumCpp and Core Features

The main data structure of NumCpp is the NdArray, which is essentially a two-dimensional array class, while one-dimensional arrays are implemented as 1xN two-dimensional arrays. Additionally, the library provides a DataCube class as a convenient container for storing multiple two-dimensional NdArray objects, although its functionality is mainly limited to being a simple container.

NumCpp is designed to provide an interface similar to NumPy, allowing developers familiar with NumPy or Matlab to quickly get started. Here are some of its core features:

  • High Performance: Optimizes the computation process at compile time using C++ expression templates, enhancing execution efficiency.
  • Ease of Use: The API design follows the conventions of C++ STL and provides iterator support, making loops and operations more intuitive.
  • Flexibility: Supports mixed data type operations, allowing different data types to be used in the same expression.
  • Rich Functionality: Offers a comprehensive set of features ranging from basic array creation and manipulation to advanced mathematical functions (such as linear algebra, Fourier transforms, etc.).

🛠️ Configuration and Installation

NumCpp is a header-only library, which means you do not need to compile it separately; you just need to ensure that the compiler can find the NumCpp header file path when compiling your project.

However, NumCpp depends on the Boost library (version 1.73 or higher), so you need to configure Boost first. Below are the steps to quickly configure NumCpp and Boost on an Ubuntu system:

  1. Create a project directory and obtain the header files for NumCpp and Boost:

    mkdir includes
     git clone https://github.com/dpilger26/NumCpp.git 
     mv NumCpp/include includes/NumCpp
     wget https://dl.bintray.com/boostorg/release/1.75.0/source/boost_1_75_0.zip
     unzip boost_1_75_0.zip
     mv boost_1_75_0/boost includes/NumCpp

    Here we placed the header files for both NumCpp and Boost in the includes/NumCpp directory. Please note that the version of Boost may be updated, and you can get the latest download link from the Boost official website.

  2. When compiling your code, ensure to use the C++14 or higher standard and specify the header file search path using the -I option:

    g++ your_code.cpp --std=c++14 -Iincludes/

If you are using other IDEs like Visual Studio, simply add the directories containing the NumCpp and Boost header files to the additional include directories in the project properties.

💻 Basic Usage of NumCpp and Code Examples

Below we will demonstrate the basic usage of NumCpp through some specific code examples. These examples will be compared with the familiar NumPy operations.

First, include the NumCpp header file:

#include "NumCpp.hpp"
namespace nc = nc; // Use nc as a namespace alias

1. Creating Arrays

In NumCpp, you use nc::NdArray<dtype> to create arrays, and the initialization method is very similar to NumPy.

Function Description NumPy Example NumCpp Equivalent Code
Create from list a = np.array([[1, 2], [3, 4]]) nc::NdArray<int> a = {{1, 2}, {3, 4}};
Change array shape a.reshape(2, 3) a.reshape(2, 3);
Convert data type a.astype(np.double) auto a2 = a.astype<double>();

Additionally, NumCpp also provides various convenient array initialization functions:

Function Description NumPy Example NumCpp Equivalent Code
Generate linear sequence np.linspace(1,10,5) auto a3 = nc::linspace<int>(1,10,5);
Generate arithmetic sequence np.arange(3,7) auto a4 = nc::arange<int>(3,7);
Generate identity matrix np.eye(4) auto a5 = nc::eye<int>(4);
Generate all-zero array np.zeros((3,4)) auto a6 = nc::zeros<int>(3,4);
Generate all-one array np.ones((3,4)) auto a8 = nc::ones<int>(3,4);

2. Array Operations and Slicing

NumCpp supports array slicing and logical operations similar to NumPy.

// Generate a random array for demonstration
auto a14 = nc::random::randInt<int>({10, 10}, 0, 100); // Create a 10x10 random integer array, range 0-100
// Access a single element
auto value = a14(2, 3); // Note: use parentheses instead of brackets
// Slicing operation
auto slice = a14({2, 5}, {2, 5}); // Get the area from row 2 to 5 (excluding 5), column 2 to 5 (excluding 5)
// Using logical indexing
auto values = a14[a14 > 50]; // Get all elements greater than 50
// Assign values to elements that meet the condition
a14.putMask(a14 > 50, 666); // Set all elements greater than 50 to 666

3. Mathematical and Statistical Operations

NumCpp provides a rich set of mathematical and statistical functions, supporting calculations across the entire array or along specific axes.

// Create two example arrays
auto a = nc::random::randInt<int>({3, 4}, 0, 10);
auto b = nc::random::randInt<int>({3, 4}, 0, 10);

// Basic arithmetic operations are element-wise
auto c = a * b;

// Reduction operations
auto sum_all = nc::sum(a); // Calculate the sum of all elements
auto sum_per_row = nc::sum(a, nc::Axis::ROW); // Calculate the sum of each row
auto mean_val = nc::mean(a); // Calculate the mean of all elements
auto max_val = nc::max(a); // Find the maximum value among all elements

// Comparison operations
auto a36 = nc::equal(a, b); // Element-wise comparison of a and b for equality
auto a37 = a == b; // Equivalent to the above

4. Random Number Generation

NumCpp’s random number generation functionality is under the nc::random namespace.

nc::random::seed(666); // Set random seed
auto a15 = nc::random::randN<double>({3, 4}); // Generate normally distributed random numbers of specified shape
auto a16 = nc::random::randInt<int>({3, 4}, 0, 10); // Generate random integers within the specified range

💡 Advanced Features and Performance Optimization

In addition to the basic features mentioned above, NumCpp also includes some advanced capabilities:

  • Array Concatenation: Supports operations like stack, vstack, hstack, append, etc.
  • Linear Algebra: Supports operations like dot product, matrix decomposition, etc.
  • File I/O: Supports saving arrays to files or loading from files.
    auto tempDir = boost::filesystem::temp_directory_path();
    auto tempTxt = (tempDir / "temp.txt").string();
    a.tofile(tempTxt); // Save array a to a text file
    auto a50 = nc::fromfile<int>(tempTxt); // Load array from text file

In terms of performance, NumCpp can optimize the computation process at compile time using expression templates, reducing the creation of temporary objects and thus improving execution efficiency. For example, this optimization can significantly enhance performance for complex operations on large arrays.

📚 Conclusion

NumCpp successfully brings the powerful array manipulation and numerical computation capabilities of NumPy into the C++ world, with its main advantages being:

  • Familiar API: Lowers the barrier for NumPy users to learn numerical computation in C++.
  • High Performance: Utilizes C++ compile-time optimizations and template metaprogramming to provide efficient execution speed.
  • Ease of Use: The header-only design makes integration into projects very simple.
  • Comprehensive Functionality: Covers a wide range of needs from basic array operations to advanced mathematical functions.

Leave a Comment