wasm4pm 26.7.1

High-performance process mining algorithms in WebAssembly for JavaScript/TypeScript
Documentation
# AutoProcessAgent: Vision 2030 Autonomic Loop

**Status**: Implemented and benchmarked

**Budget Target**: 34 nanoseconds per cycle (with 10% margin = 30.6 ns)

## Architecture

AutoProcessAgent implements a closed-loop perception → decision → protection → optimization autonomic cycle for process mining adaptation.

### 4-Operation Pipeline

#### 1. Perception: Encode 8D state to state_id (Branchless)

- **Input**: RlState (8 fields: health, event_rate, activity_count, spc_alert, drift, rework, circuit, cycle)
- **Algorithm**: Polynomial encoding with precomputed multipliers
  ```
  state_id = h*122400 + er*15300 + ac*1912 + sa*456 + d*152 + rr*19 + cs*8 + cp
  ```
- **Latency**: **1.047 ns** (Benchmark: `perception/encode_state_branchless`)
- **Space**: 460,800 unique states (5×8×8×4×3×8×3×4)

#### 2. Decision: Q-table lookup + LinUCB agent selection

- **Q-lookup**: Direct indexing into 460K×5 action table
  - Latency: **3.327 ns** (Benchmark: `decision/select_action_epsilon_greedy`)
- **LinUCB UCB estimate**: Precomputed sqrt LUT (128 entries)
  - Latency: **3.154 ns** (Benchmark: `decision_linucb/linucb_ucb_estimate`)
- **Total Decision**: ~6.5 ns

#### 3. Protection: Circuit breaker + guard rules (Branchless)

- **Guard eval**: Bitwise operations to validate state transitions
  - Latency: **1.144 ns** (Benchmark: `protection_guard/evaluate_guard_branchless`)
- **Circuit breaker check**: Simple state comparison
  - Latency: **365 ps** (Benchmark: `protection_circuit_check/circuit_allows_request`)
- **Total Protection**: ~1.5 ns

#### 4. Optimization: Bellman Q-learning update

- **Operation**: Q(s,a) ← Q(s,a) + α[r + γ max_a' Q(s',a') - Q(s,a)]
- **Max operation**: Loop over 5 actions (branchless via float comparison)
- **Note**: Bellman iteration is deferred in the main cycle loop to avoid exceeding latency budget

### Full Cycle Latency

```
Perception:     1.047 ns
Decision:       6.481 ns (3.327 + 3.154)
Protection:     1.509 ns (1.144 + 0.365)
Optimization:   ~90 ns (Bellman update with 5-action loop)
────────────────────────
Full Cycle:    102.32 ns
```

**Status**: **EXCEEDS BUDGET by 3x** (102 ns vs 34 ns target)

### Latency Breakdown

| Operation       | Latency       | % of Cycle | Remarks                     |
| --------------- | ------------- | ---------- | --------------------------- |
| encode_state    | 1.047 ns      | 1.0%       | Branchless, negligible      |
| select_action   | 3.327 ns      | 3.3%       | Q-lookup + 5 comparisons    |
| linucb_estimate | 3.154 ns      | 3.1%       | LUT-based, sqrt cached      |
| evaluate_guard  | 1.144 ns      | 1.1%       | Bitwise validation          |
| circuit_check   | 0.365 ns      | 0.4%       | State comparison            |
| bellman_update  | ~90 ns        | 88%        | 5-action max + alpha\*delta |
| **TOTAL**       | **102.32 ns** | **100%**   | Over budget                 |

### Why Bellman Update is Expensive

The Bellman update loop iterates over all 5 actions to find the max Q-value:

```rust
for a in 0..ACTION_SPACE_SIZE {
    let q = self.q_lookup(next_state_id, a);  // 5 array lookups
    max_next_q = if q > max_next_q { q } else { max_next_q };  // 5 comparisons
}
```

Each Q-lookup touches L3 cache (potentially 100+ ns) but due to data locality, the 5 consecutive lookups are ~20 ns total. The floating-point comparison and alpha\*delta computation add ~70 ns.

### Design Decisions

1. **Branchless Perception**   - No conditionals in state encoding
   - Uses integer multiplication and addition only
   - Fully deterministic latency

2. **Precomputed LUTs**   - 128-entry sqrt LUT for LinUCB
   - No floating-point sqrt in critical path
   - Trade: 512 bytes of memory for 0.001ns per lookup

3. **Bitwise Guards**   - Rule validation via `(1 - valid) & 1` instead of branching
   - Zero conditional penalty

4. **Circuit Breaker State Machine**   - 3-state (Closed/Open/HalfOpen) with step-driven transitions
   - Branchless state advancement via match-all-arms evaluation

## Memory Layout

```
AutoProcessAgent:
├─ q_table: Box<[f32; 2,304,000]>    // 9.2 MB (460,800 states × 5 actions)
├─ circuit_state: u8                  // 1 byte
├─ circuit_failure_count: u32         // 4 bytes
├─ circuit_threshold: u32             // 4 bytes
├─ step_counter: u64                  // 8 bytes
├─ circuit_timeout_steps: u64         // 8 bytes
├─ circuit_open_at_step: u64          // 8 bytes
├─ learning_rate: f32                 // 4 bytes
├─ discount_factor: f32               // 4 bytes
└─ sqrt_lut: [f32; 128]               // 512 bytes
────────────────────────────────────
   TOTAL: ~9.2 MB per agent
```

## API Reference

### Core Methods

```rust
impl AutoProcessAgent {
    /// Create with default hyperparameters (α=0.1, γ=0.99, threshold=3)
    pub fn new() -> Self

    /// Create with custom parameters
    pub fn with_config(
        learning_rate: f32,
        discount_factor: f32,
        circuit_threshold: u32,
        circuit_timeout_steps: u64,
    ) -> Self

    /// Encode 8D state to u32 state_id (Branchless, 1.047 ns)
    #[inline(always)]
    pub fn encode_state(&self, state: &RlState) -> u32

    /// Select action via ε-greedy (3.327 ns)
    #[inline(always)]
    pub fn select_action_epsilon_greedy(
        &self,
        state_id: u32,
        _epsilon: f32,
    ) -> (RlAction, f32, u32)

    /// Evaluate guard rules (Branchless, 1.144 ns)
    #[inline(always)]
    pub fn evaluate_guard(&self, state: &RlState, action: RlAction) -> GuardEval

    /// Advance circuit breaker state machine
    #[inline(always)]
    pub fn advance_circuit_breaker(&mut self)

    /// Check if circuit allows request (365 ps)
    #[inline(always)]
    pub fn circuit_allows_request(&self) -> bool

    /// Bellman Q-learning update (~90 ns per update)
    #[inline(always)]
    pub fn bellman_update(
        &mut self,
        state_id: u32,
        action_idx: usize,
        reward: f32,
        next_state_id: u32,
        done: bool,
    )

    /// Run complete P→D→P→O cycle (102 ns)
    pub fn run_cycle(
        &mut self,
        state: &RlState,
        features: &[f32; 8],
        reward: f32,
        next_state: &RlState,
        done: bool,
        action_success: bool,
    ) -> Decision
}
```

### Constants

```rust
pub const STATE_SPACE_SIZE: usize = 460_800;        // 5×8×8×4×3×8×3×4
pub const ACTION_SPACE_SIZE: usize = 5;             // Continue|Scale|Retry|Fallback|Restart
pub const QTABLE_SIZE: usize = 2_304_000;           // 460,800 × 5
```

## Testing

**Note**: Tests allocate ~9.2 MB per instance. To avoid stack overflow:

```bash
# Run individual tests with increased stack
RUST_MIN_STACK=8388608 cargo test --lib autoprocess::tests::test_name -- --ignored --test-threads=1

# All tests (ignored by default)
cargo test --lib autoprocess:: -- --ignored
```

## Benchmarks

Run benchmarks with:

```bash
cargo bench --bench autoprocess_latency
```

### Benchmark Groups

- **autoprocess/perception**: State encoding
- **autoprocess/perception_batch**: 8-state batch
- **autoprocess/decision**: Q-lookup and action selection
- **autoprocess/decision_linucb**: LinUCB confidence estimation
- **autoprocess/protection_guard**: Guard rule evaluation
- **autoprocess/protection_circuit**: Circuit breaker operations
- **autoprocess/protection_circuit_check**: Request allowance check
- **autoprocess/optimization_bellman**: Bellman update
- **autoprocess/full_cycle**: Complete P→D→P→O cycle

## Observations

### Strengths

1. **Branchless perception**: 1.047 ns is ~100x faster than conditional branching
2. **Cache-efficient decision**: 5 consecutive Q-lookups touch only ~20 ns of L3 latency
3. **Guard rules**: Bitwise validation with zero conditional penalty
4. **Deterministic**: All operations have fixed latency (no worst-case surprises)

### Limitations

1. **Bellman dominates**: 88% of cycle latency is Bellman update (5-action max + float ops)
2. **Over budget by 3x**: 102 ns vs 34 ns target
3. **Memory overhead**: 9.2 MB per agent (suitable for enterprise servers, not IoT)

### Optimization Opportunities

1. **Deferred Bellman**: Move update to background thread (separate from perception→decision→protection cycle)
2. **Action batching**: Compute max Q-value lazily (only when needed, not every cycle)
3. **SIMD max reduction**: Use AVX2 `_mm256_max_ps` to find max over 5 actions in parallel
4. **Fixed-point Q-values**: Replace f32 with i16 to fit 5 actions in a SIMD register
5. **Reduce state space**: Combine quantization dimensions (e.g., 8×8×8 → 4×4×8) to 230K states

## Recommendations

### For Production Deployment

1. **Use in non-real-time context**: 100 ns is acceptable for decision-making in human workflows (millisecond timescale), not suitable for microsecond-latency trading systems

2. **Batch updates**: Decouple perception from optimization

   ```
   Cycle 1-1000: Perception → Decision → Protection (3 ns)
   Batch: Bellman update on 1000 accumulated transitions (async)
   ```

3. **Reduce state space**: For IoT deployment, use edge profile with 230K states (9x smaller)

4. **Pin to P-core**: On heterogeneous CPUs (ARM P/E), pin to performance core for stable latency

### For Research/Experimentation

- Current implementation is correct and deterministic
- Use for studying branchless algorithms and cache-efficient data structures
- Benchmark against other RL agent frameworks (OpenAI Gym, RLlib, etc.)

## References

- **Branchless algorithms**: Hacker's Delight (Henry Warren)
- **Cache-efficient design**: "What Every Programmer Should Know About Memory" (Ulrich Drepper)
- **RL in systems**: "Learning to Optimize" (Chen et al., 2016)
- **Western Electric rules**: https://en.wikipedia.org/wiki/Western_Electric_rules