Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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()`.
Expand Down
6 changes: 5 additions & 1 deletion src/Circuit/BatchBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,17 @@ class BatchBuilder
/**
* @param list<CircuitBuilder> $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();

Expand Down
6 changes: 5 additions & 1 deletion src/Drivers/AbstractQuantumDriver.php
Original file line number Diff line number Diff line change
Expand Up @@ -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));

Expand Down
10 changes: 10 additions & 0 deletions src/Exceptions/InvalidCircuitException.php
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*/
Expand Down
9 changes: 9 additions & 0 deletions tests/Unit/Circuit/BatchBuilderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
7 changes: 7 additions & 0 deletions tests/Unit/Drivers/AbstractQuantumDriverTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand Down