class ibool

The ibool class is AADC’s active boolean type for tracking logical operations in computational graphs that involve conditional logic.

Overview

ibool is defined in aadc/ibool.h and serves as a drop-in replacement for native bool when boolean expressions depend on kernel inputs and need to be tracked in the valuation graph. Unlike traditional automatic differentiation tools, AADC can record and execute conditional branches, making it suitable for complex mathematical models with conditional logic.

assert(sizeof(bool) == sizeof(ibool)); // Always true

When to Use ibool vs bool

Understanding when to use ibool versus native bool is crucial for correct AADC implementation:

Scenario Use Type Reason
Stochastic conditions ibool Condition depends on kernel inputs and varies between evaluations
Static conditions bool Condition is constant or doesn’t depend on kernel inputs
Diff-only conditions bool Condition only depends on markAsDiff() variables (not stochastic)

Stochastic vs Static Conditions

Stochastic conditions are boolean expressions that depend on kernel “random” inputs (markAsInput() or markAsInputNoDiff()). These conditions can evaluate differently for different input values and must be tracked:

// Stochastic - depends on kernel input
idouble temperature(25.0);
AADCArgument temp_arg = temperature.markAsInput();
idouble threshold(30.0);
ibool is_above_threshold = temperature > threshold; // Must use ibool

Static conditions don’t depend on kernel inputs and can use native bool:

// Static - doesn't depend on kernel inputs
bool use_iterative_method = true; // Algorithm choice
int max_iterations = 1000; // Fixed parameter
bool converged = (i == max_iterations); // Loop control

Important: Variables marked with markAsDiff() (diff-only inputs) do not make conditions stochastic since they don’t vary between kernel evaluations.

Internal State Flags

Similar to idouble, ibool 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.

Recording Phase Behavior

Context Condition Type Approach Notes
Outside recording Any Native bool or ibool No tracking needed
Inside recording Static Native bool or ibool Condition doesn’t vary
Inside recording Stochastic Must use ibool + iIf() Traditional if() not supported

Functional Form: iIf()

For stochastic conditions during recording, use the functional form iIf() instead of traditional if() statements:

// Traditional if-statement (only for static conditions)
if (static_condition) {
    result = value_a;
} else {
    result = value_b;
}

// Functional form (required for stochastic conditions)
result = iIf(stochastic_condition, value_a, value_b);

iIf() Overloads

iIf() supports various type combinations:

idouble iIf(const ibool& cond, const idouble& a, const idouble& b);
idouble iIf(const ibool& cond, const double a, const double b);
idouble iIf(const ibool& cond, const idouble& a, const double b);
idouble iIf(const ibool& cond, const double a, const idouble& b);
ibool iIf(const ibool& cond, const ibool& a, const ibool& b);

Boolean Operations

Comparison Operations

All comparison operations between idouble values return ibool:

idouble x(100.0), y(110.0);
ibool less_than = (x < y);        // Returns ibool
ibool greater_equal = (x >= y);   // Returns ibool
ibool equal = (x == y);           // Returns ibool

Logical Operations

Standard logical operations are available in the aadcBoolOps namespace:

using namespace aadcBoolOps;

ibool a = (x > 0.0);
ibool b = (y < 100.0);
ibool result = a && b;  // Logical AND
ibool result2 = a || b; // Logical OR
ibool not_a = !a;       // Logical NOT

Short-Circuit Evaluation Warning

Important: Unlike native C++ boolean operators, overloaded && and || operators for ibool do not provide short-circuit evaluation. Both operands are always evaluated:

// In native C++, computeExpensive() is not called if x == 0
if (x != 0 && computeExpensive(x) > threshold) { ... }

// With ibool, computeExpensive() is ALWAYS called
using namespace aadcBoolOps;
if (x != 0 && computeExpensive(x) > threshold) { ... } // computeExpensive called even if x == 0

Use aadcBoolOps only when it’s safe to evaluate all operands.

Type Conversion Control

Explicit Conversion Prevention

By default, ibool prevents implicit conversion to native bool:

ibool condition = (x > y);
if (condition) { ... }  // Compile error - no implicit conversion

Controlled Implicit Conversion

When AADC_ALLOW_TO_PASSIVE_BOOL is defined, implicit conversion is allowed with runtime checking:

#define AADC_ALLOW_TO_PASSIVE_BOOL
ibool condition = (x > y);
if (condition) { ... }  // Allowed - conversion is monitored

Each conversion is checked, and stochastic conversions are logged and can trigger custom handlers for debugging.

Utility Functions

Mathematical Functions

namespace std {
    idouble max(const idouble& a, const idouble& b);
    idouble min(const idouble& a, const idouble& b);
    idouble abs(const idouble& x);
    idouble fabs(const idouble& x);
    idouble copysign(const idouble& a, const idouble& b);
    
    // Boolean testing functions
    ibool isnan(const idouble& x);
    ibool isfinite(const idouble& x);
}

Conditional Assignment

void condAssign(idouble& target, const ibool& condition, const idouble& value);
// Equivalent to: target = iIf(condition, value, target);

Legacy Code Integration

For large existing codebases using global double to idouble replacement integration strategy:

  1. Enable controlled conversion:

    #define AADC_ALLOW_TO_PASSIVE_BOOL
  2. Use Active-to-Passive reporting to identify where conversions occur

  3. Modify only truly stochastic conditions to use iIf() and condAssign(). Prefer min/max/abs functions for simple comparisons.

  4. Use debugging tools to catch unintended conversions during development

Best Practices

  1. Start with explicit conversion prevention during development of a new project to catch all conditional logic
  2. Use the functional form iIf() for all stochastic conditions
  3. Enable Active-to-Passive reporting to monitor conversions in production
  4. Test thoroughly when enabling implicit conversion in legacy code
  5. Be cautious with short-circuit evaluation when using aadcBoolOps namespace

Example: Piecewise Function with Conditional Logic

// Mathematical function with conditional behavior
idouble x(2.5);        // Input variable
idouble threshold(3.0); // Boundary parameter
idouble slope_low(2.0); // Parameters for different regions
idouble slope_high(0.5);

// Mark inputs for differentiation
auto x_arg = x.markAsInput();
auto threshold_arg = threshold.markAsDiff();  // Diff-only
auto slope_low_arg = slope_low.markAsInput();
auto slope_high_arg = slope_high.markAsInput();

// Stochastic condition - x value determines behavior
ibool in_low_region = x < threshold;

// Static condition - algorithm choice
bool use_quadratic_form = false; // Configuration parameter

// Piecewise function using functional form
idouble result = iIf(in_low_region,
                    slope_low * x,           // Linear in low region
                    slope_high * (x - threshold) + slope_low * threshold); // Linear in high region

// Static branching for algorithm choice (outside or before recording)
if (use_quadratic_form) {
    // Alternative quadratic formulation
    idouble quadratic_term = x * x * 0.1;
    result = result + quadratic_term;
}

See Also