diff --git a/README.md b/README.md index a59f1ac..70e99a9 100644 --- a/README.md +++ b/README.md @@ -95,8 +95,9 @@ $result->outcomes(); // ['00', '11'] — bitstrings sorted by count, desc ```php $builder = Quantum::circuit()->qubits(2)->h(0)->cnot(0, 1)->measure(); -$builder->gateCount(); // 2 — number of gates, excluding measurement -$builder->depth(); // 2 — number of sequential layers, excluding measurement +$builder->gateCount(); // 2 — number of gates, excluding measurement +$builder->depth(); // 2 — number of sequential layers, excluding measurement +$builder->hasMeasurement(); // true — whether any gate is a measurement ``` ### Entropy Generation diff --git a/src/Circuit/CircuitBuilder.php b/src/Circuit/CircuitBuilder.php index 0d7f565..831355a 100644 --- a/src/Circuit/CircuitBuilder.php +++ b/src/Circuit/CircuitBuilder.php @@ -38,8 +38,6 @@ class CircuitBuilder /** @var Gate[] */ private array $gates = []; - private bool $hasMeasurement = false; - private int $shots = 1000; final public function __construct( @@ -619,7 +617,7 @@ public function validate(): static throw InvalidCircuitException::noQubits(); } - if (! $this->hasMeasurement) { + if (! $this->hasMeasurement()) { throw InvalidCircuitException::noMeasurement(); } @@ -627,9 +625,26 @@ public function validate(): static } /** - * Validate a gate against the circuit's qubit range, append it, and - * track measurement state. The single append primitive behind every - * fluent gate method and fromArray(). + * Whether any gate in the circuit is a measurement. + * + * Derived from $gates on demand, like gateCount() and depth(), rather + * than tracked in a flag push() would have to keep in step; circuits are + * tens of gates at most, so the scan costs nothing measurable. + */ + public function hasMeasurement(): bool + { + foreach ($this->gates as $gate) { + if ($gate->isMeasurement()) { + return true; + } + } + + return false; + } + + /** + * Validate a gate against the circuit's qubit range and append it. The + * single append primitive behind every fluent gate method and fromArray(). * * @throws InvalidCircuitException */ @@ -639,10 +654,6 @@ private function push(Gate $gate): static $this->gates[] = $gate; - if ($gate->isMeasurement()) { - $this->hasMeasurement = true; - } - return $this; } diff --git a/tests/Unit/Circuit/CircuitBuilderTest.php b/tests/Unit/Circuit/CircuitBuilderTest.php index 6ac6459..9827eb5 100644 --- a/tests/Unit/Circuit/CircuitBuilderTest.php +++ b/tests/Unit/Circuit/CircuitBuilderTest.php @@ -249,6 +249,25 @@ // run() — validation: no measurement // ------------------------------------------------------------------------- +it('reports whether the circuit measures anything, derived from its gates', function () use (&$builder): void { + $builder->qubits(2)->h(0)->cnot(0, 1); + + expect($builder->hasMeasurement())->toBeFalse(); + + $builder->measure(1); + + expect($builder->hasMeasurement())->toBeTrue(); +}); + +it('does not count a measurement dropped from an appended fragment', function () use (&$device, &$builder): void { + $fragment = (new CircuitBuilder($device))->qubits(1)->x(0)->measure(); + + $builder->qubits(1)->append($fragment); + + expect($builder->hasMeasurement())->toBeFalse() + ->and(fn () => $builder->run())->toThrow(InvalidCircuitException::class); +}); + it('run throws when no measurement', function () use (&$builder): void { expect(fn () => $builder->qubits(1)->h(0)->run()) ->toThrow(InvalidCircuitException::class);