diff --git a/README.md b/README.md index a59f1ac..b3be70c 100644 --- a/README.md +++ b/README.md @@ -127,6 +127,7 @@ $batch[0]->probabilities(); ``` * **Validation**: every circuit is validated like a single `->run()` would, so a circuit without qubits or without `measure()` throws `InvalidCircuitException` before anything is executed. +* **Empty batch**: `Quantum::batch([])` throws `InvalidCircuitException` immediately. A list filtered down to nothing is almost always a mistake, and running it would only trigger an empty execution to return no results. * **Per-circuit shots**: each circuit keeps its own `->shots()`. On AWS the whole batch is submitted at once with one shot count per task; the local simulator does not support that, so with mixed shot counts the circuits run sequentially inside the same Python process. * **Driver mismatch**: a circuit pinned to another driver (e.g. `Quantum::circuit('aws')`) cannot be run in a batch targeting a different driver — `InvalidCircuitException::batchDriverMismatch` is thrown. * **QPU safety**: `synchronous_safe` applies to batches too. A batch `->run()` on a driver marked `synchronous_safe: false` throws, exactly like a single `->run()`. diff --git a/src/Circuit/BatchBuilder.php b/src/Circuit/BatchBuilder.php index 97bf8e3..e8482f7 100644 --- a/src/Circuit/BatchBuilder.php +++ b/src/Circuit/BatchBuilder.php @@ -18,13 +18,17 @@ class BatchBuilder /** * @param list $circuits * - * @throws InvalidCircuitException When a circuit is pinned to a different driver than the batch. + * @throws InvalidCircuitException When the batch is empty, or a circuit is pinned to a different driver than the batch. */ public function __construct( private readonly QuantumDevice $device, private readonly array $circuits, private readonly string $driverName, ) { + if ($circuits === []) { + throw InvalidCircuitException::emptyBatch(); + } + foreach ($circuits as $circuit) { $pinnedDriver = $circuit->driverName(); diff --git a/src/Drivers/AbstractQuantumDriver.php b/src/Drivers/AbstractQuantumDriver.php index af06efa..ac99710 100644 --- a/src/Drivers/AbstractQuantumDriver.php +++ b/src/Drivers/AbstractQuantumDriver.php @@ -176,10 +176,14 @@ protected function payload(array $data): array * * @param CircuitBuilder[] $circuits * - * @throws InvalidCircuitException + * @throws InvalidCircuitException When the batch is empty or a circuit fails validation. */ public function executeBatch(array $circuits): BatchResult { + if ($circuits === []) { + throw InvalidCircuitException::emptyBatch(); + } + $this->preflight(); $this->validateCircuits(array_values($circuits)); diff --git a/src/Exceptions/InvalidCircuitException.php b/src/Exceptions/InvalidCircuitException.php index ed110ef..c226413 100644 --- a/src/Exceptions/InvalidCircuitException.php +++ b/src/Exceptions/InvalidCircuitException.php @@ -96,6 +96,16 @@ public static function unknownGateType(string $type): self ); } + /** + * Create an exception for a batch that contains no circuits. + */ + public static function emptyBatch(): self + { + return new self( + 'Quantum::batch() needs at least one circuit. An empty batch has nothing to run and would only trigger an empty execution to return no results.' + ); + } + /** * Create an exception when a circuit in a batch has a driver pinned that does not match the batch driver. */ diff --git a/tests/Unit/Circuit/BatchBuilderTest.php b/tests/Unit/Circuit/BatchBuilderTest.php index a948106..27ff147 100644 --- a/tests/Unit/Circuit/BatchBuilderTest.php +++ b/tests/Unit/Circuit/BatchBuilderTest.php @@ -38,6 +38,15 @@ $device->assertCircuitNotRan(); }); +it('rejects an empty batch before touching the device', function () { + $device = new QuantumFake; + + expect(fn () => new BatchBuilder($device, [], 'local')) + ->toThrow(InvalidCircuitException::class, 'at least one circuit'); + + $device->assertBatchNotRan(); +}); + it('rejects a circuit without qubits', function () { $device = new QuantumFake; diff --git a/tests/Unit/Drivers/AbstractQuantumDriverTest.php b/tests/Unit/Drivers/AbstractQuantumDriverTest.php index 1c79b88..8ee74b0 100644 --- a/tests/Unit/Drivers/AbstractQuantumDriverTest.php +++ b/tests/Unit/Drivers/AbstractQuantumDriverTest.php @@ -464,6 +464,13 @@ protected function driverName(): string expect($result)->toBeInstanceOf(CircuitResult::class); }); +it('refuses an empty batch without spawning the bridge', function () { + $this->bridge->expects($this->never())->method('execute'); + + expect(fn () => $this->driver->executeBatch([])) + ->toThrow(InvalidCircuitException::class, 'at least one circuit'); +}); + it('throws InvalidCircuitException on executeBatch when any circuit exceeds max_qubits', function () { $driver = new class($this->bridge, ['max_qubits' => 5]) extends AbstractQuantumDriver {