Skip to content

Fix #44: reject non-numeric qubit and cost ceilings instead of treating them as zero - #89

Open
corgab wants to merge 2 commits into
mainfrom
fix/44-validate-numeric-ceilings
Open

Fix #44: reject non-numeric qubit and cost ceilings instead of treating them as zero#89
corgab wants to merge 2 commits into
mainfrom
fix/44-validate-numeric-ceilings

Conversation

@corgab

@corgab corgab commented Sep 6, 2026

Copy link
Copy Markdown
Owner

Summary

max_qubits and max_cost_per_run are read from env() without a cast, and the guards hard-cast them at the point of use: (int) $ceiling in assertWithinQubitCeiling(), (float) $ceiling in assertWithinCostCeiling(). (int) "abc" is 0, so a typo or an unset shell variable in .env turned into a ceiling of zero that rejected every circuit with an InvalidCircuitException blaming the circuit, not the configuration.

Plan

Validate at the point of use rather than in config/aether.php, so a published config that still carries the raw env() call is covered too, and keep the blank-means-unlimited behaviour intact. This is the targeted fix for the symptom; #66 (typed driver configuration) remains the place for the broader refactor and can absorb these helpers.

Changes

  • AbstractQuantumDriver: a private filteredConfig($key, $filter, $options, $expected) does the blank check (null/'' mean "no ceiling"), refuses booleans (filter_var(true, FILTER_VALIDATE_INT) would yield 1) and runs filter_var; on rejection it throws InvalidDriverConfigException::invalidValue() naming the driver, key, value and expected shape. Two protected readers sit on top: positiveIntegerConfig() (FILTER_VALIDATE_INT, min_range 1) and nonNegativeNumberConfig() (FILTER_VALIDATE_FLOAT, min_range 0). Numeric strings are accepted.
  • validateCircuits() resolves max_qubits once per call and passes the integer to assertWithinQubitCeiling(); its docblock now lists InvalidDriverConfigException.
  • AwsBraketDriver::assertWithinCostCeiling() uses nonNegativeNumberConfig(); the estimate comparison no longer casts.
  • InvalidDriverConfigException::invalidValue() (same signature and message as the one introduced in Fix #69: derive synchronous safety from the device ARN by default #74, so the two branches merge cleanly).
  • config/aether.php: one comment on each option stating the accepted shapes. README "Qubit Ceiling" and "Cost Estimation": one sentence each listing what is rejected (typos, 0, negatives, decimals for qubits; non-numeric or negative for cost).

No new dependencies, no behaviour change for valid or blank values.

Tests

  • AbstractQuantumDriverTest: dataset ('abc', '2.5', '0', -3, true) throws InvalidDriverConfigException mentioning [max_qubits] without touching the bridge; a numeric string '4' is still honoured as the ceiling.
  • AwsBraketDriverTest: dataset ('abc', '-1', true) throws mentioning [max_cost_per_run]; a numeric string '0.50' is still honoured.
  • The existing blank-value tests keep passing, proving the unlimited case is unchanged.

Local: pint passed, pest 801 tests passed.

Closes #44

…hem as zero

max_qubits and max_cost_per_run come straight from env() as strings, and
(int) "abc" is 0 in PHP, so a typo in .env silently became a ceiling of
zero that rejected every circuit with a message blaming the circuit. The
drivers now read both options through validating helpers: blank stays
"no ceiling", a positive integer or a non-negative number is accepted
(numeric strings included), and anything else throws
InvalidDriverConfigException naming the key and the value it got.

Closes #44
positiveIntegerConfig() and nonNegativeNumberConfig() now share a single
filteredConfig() that handles the blank check, the boolean exclusion and
the filter_var call, so the two readers differ only in filter and range.
validateCircuits() resolves max_qubits once per call instead of once per
circuit and documents the InvalidDriverConfigException it can raise; the
README lists every rejected shape, not only non-numeric strings.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[config] Non-numeric AETHER_MAX_QUBITS / AETHER_AWS_MAX_COST silently becomes a ceiling of 0

1 participant