Parallel Execution

Scale JCAL to large grids with CellularAutomataParallelRule.

For large grids where the sequential executor becomes a bottleneck, JCAL provides CellularAutomataParallelRule — a drop-in replacement that distributes the transition function across multiple threads using Java parallel streams.


When to Use

The parallel executor is beneficial when:

  • Your grid is large (typically 500×500 or more in 2D, smaller thresholds in 3D/4D).
  • The transition function (transition) is computationally intensive per cell.
  • Your hardware has multiple CPU cores available.

For small grids or trivially cheap rules, the thread-management overhead of parallel streams can make the parallel executor slower than the sequential one. Benchmark first.


Usage

Extend CellularAutomataParallelRule instead of CellularAutomataRule. The transition method signature is identical — no API changes required.

public class GameOfLifeParallelRule extends CellularAutomataParallelRule {

    private static final CellState DEAD  = new CellState("dead",  "0");
    private static final CellState ALIVE = new CellState("alive", "1");

    @Override
    public Cell transition(Cell cell, List<Cell> neighbors) {
        long aliveCount = neighbors.stream()
            .filter(n -> n.getCurrentStatus().equals(ALIVE))
            .count();

        boolean isAlive = cell.getCurrentStatus().equals(ALIVE);
        Cell next = new Cell(DEAD, cell.getCol(), cell.getRow());

        if (!isAlive && aliveCount == 3) {
            next.setCurrentStatus(ALIVE);
        } else if (isAlive && (aliveCount == 2 || aliveCount == 3)) {
            next.setCurrentStatus(ALIVE);
        }

        return next;
    }
}

Run it exactly as you would the sequential executor:

CellularAutomata ca = new CellularAutomata(config);
GameOfLifeParallelRule executor = new GameOfLifeParallelRule();
ca = executor.run(ca);

Thread Safety

JCAL guarantees that each call to transition is independent: the method receives a snapshot of the grid taken before any cell is mutated. As long as your implementation of transition does not access shared mutable state outside of its arguments, it is thread-safe by design.


Parallel CCA (with Refinements)

The refinements hook is also available on CellularAutomataParallelRule:

public class HeatDiffusionParallel extends CellularAutomataParallelRule {

    @Override
    public Cell refinements(Cell cell) {
        // Clamp temperature — called in parallel for each cell
        HeatStatus s = (HeatStatus) cell.getCurrentStatus();
        s.temperature = Math.max(0.0, Math.min(s.temperature, 1000.0));
        cell.setCurrentStatus(s);
        return cell;
    }

    @Override
    public Cell transition(Cell cell, List<Cell> neighbors) {
        double avg = neighbors.stream()
            .mapToDouble(n -> ((HeatStatus) n.getCurrentStatus()).temperature)
            .average().orElse(0.0);

        double next = (((HeatStatus) cell.getCurrentStatus()).temperature + avg) / 2.0;
        return new Cell(new HeatStatus(next), cell.getCol(), cell.getRow());
    }
}

The refinement phase and the transition phase are each fully parallelized internally.


Internal Architecture

Internally, CellularAutomataParallelRule uses:

  • CellularAutomataRunner — a Callable that applies transition to a subset of cells in parallel.
  • CellularAutomataRefinementRunner — a Callable that applies refinements in parallel before the snapshot is taken.

These are internal classes; do not use them directly.


See Also