class iint

The iint class is AADC’s active integer type for tracking integer operations in computational graphs, particularly for array indexing and mathematical operations that involve integer calculations.

Overview

iint is defined in aadc/iint.h and serves as a drop-in replacement for native int64_t when integer expressions depend on kernel inputs and need to be tracked in the valuation graph. This enables AADC to handle complex mathematical models involving array lookups, interpolation, and other operations that require integer indices computed from active random variables.

assert(sizeof(int64_t) == sizeof(iint)); // Always true

When to Use iint vs int64_t

Understanding when to use iint versus native integer types is crucial for correct AADC implementation:

Scenario Use Type Reason
Dynamic indices iint Index computed from kernel inputs or derived values
Static indices int64_t Fixed array indices or loop counters

Dynamic vs Static Integer Operations

Dynamic integer operations involve calculations that depend on kernel inputs and can vary between evaluations:

// Dynamic - index depends on kernel input
idouble x(2.5);  // marked as input
iint index = aadc::toInt(x);  // Convert to integer index
idouble result = aadcArrayOps::get(lookup_table, index);

Static integer operations don’t depend on kernel inputs:

// Static - fixed array operations
for (int i = 0; i < array_size; ++i) {  // Loop counter
    process_element(array[i]);
}

Internal State Flags

Similar to idouble, iint objects maintain internal flag IsRandom to indicate if they depend on stochastic inputs. This flag is managed automatically by AADC Core and propagates through operations.

Core Operations

Arithmetic Operations

iint supports standard integer arithmetic:

iint a(10), b(3);
iint sum = a + b;        // Addition
iint diff = a - b;       // Subtraction  
iint product = a * b;    // Multiplication
iint negative = -a;      // Unary minus

Assignment Operations

iint result;
result = a;       // Assignment
result += b;      // Add-assign
result -= b;      // Subtract-assign
result *= b;      // Multiply-assign

Comparison Operations

All comparison operations return ibool:

iint x(5), y(10);
ibool less = (x < y);      // Less than
ibool equal = (x == y);    // Equal
ibool greater = (x > y);   // Greater than
ibool not_equal = (x != y); // Not equal
ibool less_eq = (x <= y);  // Less than or equal
ibool greater_eq = (x >= y); // Greater than or equal

Array Operations

Array Element Access

The aadcArrayOps::get() function provides safe array access with active indices:

#include <aadc/iint.h>
using namespace aadcArrayOps;

std::vector<idouble> data = {1.0, 2.5, 3.7, 4.2};
iint index(2);
idouble value = get(data, index);  // Returns data[2] = 3.7

// Also works with raw arrays
idouble array[4] = {1.0, 2.5, 3.7, 4.2};
idouble value2 = get(array, 4, index);  // Specify size explicitly

Integer Array Access

For arrays of integer-like types:

std::vector<int64_t> int_data = {10, 20, 30, 40};
iint index(1);
iint value = getInt(int_data, index);  // Returns 20

Type Conversion

From idouble to iint

Use aadc::toInt() for converting floating-point values to integers:

idouble x(3.7);
iint int_x = aadc::toInt(x);  // Truncates to 3

This conversion is tracked in the computational graph, enabling differentiation through the conversion.

Conditional Operations

Functional Form: iIf()

For conditional integer expressions:

iint value_a(100), value_b(200);
ibool condition = (x > threshold);
iint result = iIf(condition, value_a, value_b);

Mathematical Functions

namespace std {
    iint max(const iint& a, const iint& b);
    iint min(const iint& a, const iint& b);
    iint abs(const iint& x);
}

Search and Lookup Operations

Practical Example: Piecewise Constant Interpolation

template<typename mdouble>
class PiecewiseConst {
    std::vector<double> x_points;
    std::vector<mdouble> y_values;
    
public:
    PiecewiseConst(const std::vector<double>& x_pts) : x_points(x_pts) {}
    
    void setValues(const std::vector<double>& y_vals) {
        assert(y_vals.size() == x_points.size());
        y_values.resize(y_vals.size());
        for (size_t i = 0; i < y_vals.size(); ++i) {
            y_values[i] = y_vals[i];
        }
    }
    
    mdouble interpolate(const mdouble& x) {
        // Find the interval containing x
        iint index = std::min(aadc::lower_bound(x_points, x), iint(x_points.size() - 1));
        
        // Get the corresponding y-value
        return aadcArrayOps::get(y_values, index);
    }
};

// Usage in AADC recording
idouble x(2.5);
PiecewiseConst<idouble> interpolator({1.0, 2.0, 3.0, 4.0});
interpolator.setValues({10.0, 20.0, 30.0, 40.0});

auto x_arg = x.markAsInput();
std::vector<aadc::AADCArgument> y_args;
for (size_t i = 0; i < interpolator.y_values.size(); ++i) {
    y_args.push_back(interpolator.y_values[i].markAsDiff());
}

idouble result = interpolator.interpolate(x);
auto result_out = result.markAsOutput();

Integration Notes

  • Memory layout: iint maintains the same memory footprint as int64_t
  • Vector operations: Can be used in std::vector<iint> and other containers

Common Use Cases

  1. Array indexing with computed indices: When array positions depend on input variables
  2. Lookup table operations: For fast function approximation and interpolation
  3. Categorization: Assigning continuous inputs to discrete buckets
  4. Search operations: Binary search and related algorithms
  5. Integer arithmetic in mathematical models: When calculations naturally involve integer operations

Best Practices

  1. Use for truly dynamic indices: Only when the index depends on kernel inputs
  2. Prefer static indexing for fixed operations: Use native integers for loop counters and fixed array access
  3. Combine with array operations: Use the provided aadcArrayOps functions for safe access
  4. Test boundary conditions: Ensure array bounds are respected in your logic
  5. Document integer semantics: Make clear when truncation or rounding behavior is important

See Also