Janus 2.0.0
High-performance C++20 dual-mode numerical framework
Loading...
Searching...
No Matches
Loop Patterns in Janus

The Core Distinction: Structural vs Dynamic

Structural Loops (✅ Work in Symbolic Mode)

Loop bounds are known at graph-building time (int, const):

for (int i = 0; i < 10; i++) {
// Loop index 'i' is structural
// Values computed can be symbolic
result += symbolic_value * i;
}

Dynamic Loops (⚠️ Numeric Mode ONLY)

Loop continuation depends on runtime values:

while (error > tolerance) { // ❌ Can't evaluate symbolic condition!
error = update(error);
}

Common Patterns

1. Simple For Loop

template <typename Scalar>
Scalar sum_of_squares(int n) {
Scalar sum = 0.0;
for (int i = 1; i <= n; ++i) { // n is structural
Scalar value = static_cast<Scalar>(i);
sum += value * value; // values can be symbolic
}
return sum;
}

2. Nested Loops (Matrix Operations)

template <typename Scalar>
Matrix<Scalar> multiply(const Matrix<Scalar>& A, const Matrix<Scalar>& B) {
int m = A.rows(), n = A.cols(), p = B.cols();
Matrix<Scalar> C(m, p);
for (int i = 0; i < m; ++i) { // Structural bounds
for (int j = 0; j < p; ++j) {
for (int k = 0; k < n; ++k) {
C(i,j) += A(i,k) * B(k,j); // Symbolic values
}
}
}
return C;
}

3. While Loop → Fixed Iterations

// ❌ DOESN'T WORK (Symbolic):
while (error > tol) { ... }
// ✅ WORKS (Both modes):
for (int i = 0; i < max_iterations; ++i) {
// Fixed iterations instead of convergence check
x = update(x);
}

4. Break/Continue → Conditional Accumulation

// ❌ DOESN'T WORK (Symbolic):
for (int i = 0; i < n; ++i) {
if (values(i) > threshold) break; // Dynamic condition!
}
// ✅ WORKS (Both modes):
Scalar result = default_value;
for (int i = 0; i < n; ++i) {
result = janus::where(values(i) > threshold,
values(i), // Use this value
result); // Keep previous
}
auto where(const Cond &cond, const T1 &if_true, const T2 &if_false)
Select values based on condition (ternary operator) Returns: cond ? if_true : if_false Supports mixed...
Definition Logic.hpp:43

When You Need Dynamic Loops

Use Case: Time-stepping simulation until convergence

Option 1: Numeric-Only Simulation (RECOMMENDED for pure simulation)

double simulate_until_steady_state(double x0) {
// ✅ Use dynamic loops freely in numeric mode!
double x = x0;
double error = 1000.0;
while (error > 1e-6) { // Dynamic - totally fine!
double x_new = physics_update(x);
error = std::abs(x_new - x);
x = x_new;
}
return x;
}

Option 2: Fixed Iterations (for symbolic compatibility)

template <typename Scalar>
Scalar simulate_fixed_steps(const Scalar& x0, int n_steps) {
// ✅ Works in both modes
Scalar x = x0;
for (int i = 0; i < n_steps; ++i) {
x = physics_update(x);
}
return x;
}

Option 3: Hybrid Approach (best of both worlds)

// 1. Run numeric simulation to determine iteration count
int n_steps = determine_steps_numerically();
// 2. Use that fixed count for symbolic version
template <typename Scalar>
Scalar simulate_hybrid(const Scalar& x0) {
return simulate_fixed_steps(x0, n_steps); // Can now differentiate!
}

Advanced Patterns

Conditional Logic Inside Loops

template <typename Scalar>
Scalar selective_sum(const Vector<Scalar>& values,
const Vector<Scalar>& thresholds) {
Scalar sum = 0.0;
for (int i = 0; i < values.size(); ++i) {
// Use where() for conditions on values
sum += janus::where(values(i) > thresholds(i),
values(i),
Scalar(0.0));
}
return sum;
}

Element-wise Transformations

template <typename Scalar>
Vector<Scalar> apply_function(const Vector<Scalar>& input) {
int n = input.size();
Vector<Scalar> output(n);
for (int i = 0; i < n; ++i) {
output(i) = complex_function(input(i));
}
return output;
}

Key Takeaways

Feature Numeric Mode Symbolic Mode
for (int i=0; i<N; i++) ✅ Yes ✅ Yes
while (symbolic > val) ✅ Yes ❌ No
if (symbolic) break ✅ Yes ❌ No
Loop values Any Symbolic OK
Loop bounds Any Must be structural

When to use which:

  • Pure simulation/analysis: Use dynamic loops freely!
  • Optimization/AutoDiff: Use fixed iterations
  • Both: Hybrid approach (numeric determines N, symbolic uses fixed N)

See Also

  • examples/loop_patterns.cpp - Comprehensive examples
  • examples/numeric_intro.cpp - Time-stepping example
  • docs/patterns/branching_logic.md - Conditional patterns