Fix #66: read driver options through a typed DriverConfig value object - #103
Open
corgab wants to merge 2 commits into
Open
Fix #66: read driver options through a typed DriverConfig value object#103corgab wants to merge 2 commits into
corgab wants to merge 2 commits into
Conversation
…alue object Driver config used to travel as a bare array, read at each use site with $this->config['key'] ?? default plus an inline cast: (int) "abc" is 0, so a typo'd env value became a ceiling of zero that rejected every circuit without saying why. AbstractQuantumDriver now builds a Config\DriverConfig once in its constructor (AwsBraketDriver builds an AwsDriverConfig through the new makeConfig() hook). The object validates and casts max_qubits, entropy_qubits and synchronous_safe — and, for aws, the pricing rates and max_cost_per_run — throwing InvalidDriverConfigException::invalidValue() for a value that is neither blank nor of the documented shape. Blank still means default, so existing configs are unaffected. The raw array is kept verbatim and still reaches Python as driver_config, so custom providers and untyped keys such as python_provider keep working; DriverConfig::get() exposes them to custom drivers. Closes #66
… constructor ordering config/aether.php pre-cast entropy_qubits and the pricing rates with (int)/(float), so a garbage env value reached the validator as 0 and passed. The value object now owns the casting, so the casts are gone. makeConfig() runs inside the base constructor and calls driverName(); the docblocks and README now say so, so a custom driver does not return a property its own constructor sets after parent::__construct().
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Driver configuration travelled as a bare
array $config, read at seven use sites with$this->config['key'] ?? defaultplus an inline cast.(int) "abc"is 0, so a typo'd env value became a ceiling of zero that rejected every circuit without saying why. This PR introduces a typed value object built once per driver, so every option is validated and cast in one place and a garbage value fails at resolution time with a message naming the driver, the key and the value.Plan
Config\DriverConfigfor the shared options (max_qubits,entropy_qubits,synchronous_safe) andConfig\AwsDriverConfigon top of it for thepricingrates andmax_cost_per_run.AbstractQuantumDriverbuild the object in its constructor through an overridablemakeConfig()hook;AwsBraketDriveroverrides it to returnAwsDriverConfig.?? default+ cast pair in the drivers to a typed property access.driver_config, and untyped keys (python_provider, provider-specific settings) stay reachable throughDriverConfig::get().requiredConfig()lazy, soestimateCost()still works on a driver without a complete remote setup; only values that are present but of the wrong shape throw at construction.Changes
src/Config/DriverConfig.php(new): readonly value object with$maxQubits,$entropyQubits,$synchronousSafe, plusget(),isBlank(),blankKeys(),toArray(). Blank (absent, null, empty string) means default. Validation viafilter_varwith booleans and non-scalars refused;synchronous_safeaccepts the env spellings ("false","0","off", ...).src/Config/AwsDriverConfig.php(new): adds$maxCostPerRun,$perTaskRate,$perShotRate,$currencyandmissingRates(); rejects apricingthat is not an array.src/Exceptions/InvalidDriverConfigException.php: newinvalidValue(driver, key, value, expected)factory.src/Drivers/AbstractQuantumDriver.php:protected readonly DriverConfig $config(generic@template TConfig),makeConfig()hook,validateCircuits()resolves the ceiling once,assertConfigured()usesblankKeys(),payload()and the fiveexecute()calls passtoArray(),generateEntropy()reads$entropyQubits.src/Drivers/AwsBraketDriver.php:makeConfig()returnsAwsDriverConfig;beforeExecution(),estimateCost()andassertWithinCostCeiling()read typed properties; the cost guard's rate check usesmissingRates().src/Drivers/LocalSimulatorDriver.php:@extends AbstractQuantumDriver<DriverConfig>.config/aether.php,README.md,CLAUDE.md: document the accepted shapes and the new failure mode; README tells custom-driver authors to read$this->config->maxQubits/->get('key').tests/Unit/Config/DriverConfigTest.phpandAwsDriverConfigTest.php(new, dataset-driven: defaults, blank handling, env string casts, every rejection path with its message); driver tests for construction-time rejection, string rates from env, and the raw array still reaching Python untouched.Behaviour preserved
max_qubits/max_cost_per_runstill mean unlimited; blanksynchronous_safestill means safe.entropy_qubitsstill falls back to 16 (the existing clamp tests pass unchanged); only a non-numeric value now throws.missingKeys('aws', ['pricing.per_task', 'pricing.per_shot']).Overlap
Supersedes the driver-side numeric helpers in #89 (#44): the same
invalidValue()message and the same accepted shapes, now living in the value object. Touches the samerequiredConfig()region as #100 (#68). Whichever merges second will need a trivial conflict resolution in favour of this file layout.Tests
vendor/bin/pint --testpasses.vendor/bin/pest --compact: 847 tests, 1411 assertions, 1 skipped (791 before: +56).Closes #66
Generated by Claude Code