Parameters
How to declare parameters in qsolve.yaml: every field, the five types and their exact checks, ranges and choices, how defaults and overrides are validated, how the run form shows them and how they reach your code.
Updated 13 November 2026 View as markdown
On this page
Parameters are settings a user can change without changing the problem: shots, circuit depth, an iteration count, a mode switch. Declare each under parameters in qsolve.yaml; Qsolve checks every value before your code runs. Parameters are separate from the problem input, which is described by the input schema.
Declaring a parameter
parameters:
- {name: layers, label: Circuit depth, type: int, default: 3, min: 1, max: 8, description: "More layers can find better answers but take longer to train"}
- {name: shots, label: Final shots, type: int, default: 4000, min: 100, max: 20000, description: Final sampling shots}
- {name: train_on, label: Train angles on, type: enum, default: sim, choices: [sim, backend], description: "sim trains noise-free and uses the device only to measure"}
- {name: penalty_scale, label: Penalty strength, type: float, default: 1.0, min: 0.1, max: 20, description: Cardinality penalty multiplier}
The first three come from max-cut, the last from portfolio-optimization. Every key is one of these seven, and any other key in an entry fails the manifest load:
| Key | Type | Required | Default | Meaning and constraints |
|---|---|---|---|---|
name |
string | Yes | The key your code reads from self.params. Unique within the manifest (parameter names must be unique). The model puts no restriction on the characters, so use a Python identifier (optimizer_iters) to keep it usable as a key and in -p name=value. |
|
type |
string | Yes | Exactly int, float, bool, str or enum. |
|
default |
any | Yes | Used when the user gives no value. It is checked with the same rules as a user's value, so a default that fails them makes the manifest invalid with that rule's message. There is no way to declare "no default". | |
label |
string | No | "" |
The name shown in the form, at most 60 characters. When empty, the form shows the name with _ and - replaced by spaces and the first letter capitalised (optimizer_iters shows as "Optimizer iters"). |
description |
string | No | "" |
What changing it does. No length limit in the model. Shown as an info tooltip next to the label. |
min |
number | No | none | Inclusive lower bound. Read only for int and float; ignored for other types. |
max |
number | No | none | Inclusive upper bound. Same rules as min. |
choices |
list | For enum |
none | The allowed values. Read only for enum; ignored for other types. |
Checked when the manifest loads (a failure makes the manifest invalid with the message shown): min greater than max (parameter 'x': min 5.0 is greater than max 1.0), an enum with no or an empty choices list (parameter 'x': an enum needs a non-empty choices list), and a min or max that is NaN or infinite. A default is checked like any value. The choices of an enum are not required to be unique.
The five types
| Type | Accepts | Rejects | Error message ('x' is the parameter name) |
|---|---|---|---|
int |
whole numbers, including negative | booleans, numbers with a fractional part or written as 3.0, strings |
parameter 'x' must be an int |
float |
any finite number, integers included | booleans, NaN, infinity, strings | parameter 'x' must be a number (NaN and infinity: must be a finite number) |
bool |
true or false |
anything else, including 0 and 1 |
parameter 'x' must be a bool |
str |
text, any length, including empty | anything else | parameter 'x' must be a string |
enum |
a value equal to one of choices (a bool never equals the int 1: true is not the choice 1) |
any other value | parameter 'x' must be one of ['sim', 'backend'] |
For int and float the bounds are checked after the type, and a value outside them is rejected with parameter 'shots' must be >= 100.0 or parameter 'shots' must be <= 20000.0. Bounds are stored as numbers, so the message shows a decimal point even for whole bounds. Bounds never apply to bool, str or enum. NaN and infinity never pass for int or float, so a NaN cannot slip through a bound.
Values are not converted. A float parameter keeps an integer as an integer (default: 1 reaches your code as 1, not 1.0), so convert in your code when it matters: float(self.params["penalty_scale"]). A str parameter has no length limit or pattern. If you need a closed set of strings, use enum.
How values reach your code
Qsolve resolves the parameters before it builds your class: it starts from the defaults, overlays the user's overrides and checks every value. The result is a dictionary holding every declared parameter, which is passed to __init__ and stored as self.params:
p, shots = int(self.params["layers"]), int(self.params["shots"])
- Every declared name is present. You can index
self.paramswithout checking for missing keys. - Every value has passed the checks above. Do not re-validate type and range.
- Undeclared names never arrive. An override naming an undeclared parameter is rejected with
unknown parameters: ['name'](the names are sorted). - The resolved set is recorded. It is stored with the run, shown on the run page and kept in the receipt (
provenance.params), so a run records the values it really used, defaults included. - Parameters are also passed to
estimate()throughself.params, becauseestimate()is a method of the same instance.
The seed is not a parameter. It is a separate field of every run and reaches your code as ctx.seed.
Where overrides come from
| Place | How | When a value is wrong |
|---|---|---|
| Local run | qsolve run . -i input.json -p layers=1 -p train_on=backend. The text after = is parsed as JSON first, so 500 is a number, true is a bool, 0.5 is a float and fast (not valid JSON) stays text. |
Error: plus the message, exit code 1. A -p without = is a usage error: --param expects key=value. |
| Benchmark case | params: in benchmarks/cases.yaml. Missing names take the default. See Benchmarks. |
The case fails with the message. |
| Run form | Every declared parameter gets a control (see below). | The API answers 422 invalid_params with the message. |
| API | The params object of POST /v1/runs; names omitted take the default. |
422 invalid_params, message as above. |
A string value such as "500" is a str, not an int. Over the API and in YAML, write numbers as numbers.
How the run form shows them
The run page collects the parameters in a collapsible section called "Solver settings", closed by default and showing "Tuned defaults" until something differs from the default, then "N changed". The controls follow the type:
| Type | Control | Notes |
|---|---|---|
enum |
A row of buttons, one per choice | Shows each choice as text. |
bool |
A switch | |
int, float |
A number box; for int a typed value is rounded to a whole number |
When both min and max are set, the range is printed beside it ("100 to 20,000"); outside the range the box is marked invalid. |
str |
A text box |
Every control is labelled with label (or the generated name) and carries description as a tooltip. A "Reset to defaults" button appears once any value is changed. The form always submits all parameters, defaults included, in declared order of the manifest.
Two behaviours to design around:
- Give both bounds, or the range is not shown. With only a
minor only amax, the box is still marked invalid when you leave the bound, but no range text appears. - The form does not stop an out-of-range number by itself. The input form's own validity check covers the problem input; the parameter values are checked on the server, which refuses the run with
invalid_paramsand the message above. Put bounds in the manifest and rely on that check. - Use string choices for an
enum. The form sends the clicked choice as text, so an enum declared with numeric choices such as[1, 2]would be submitted as"1"and refused withmust be one of [1, 2]. Use[one, two]-style text, and convert in your code.
Parameter controls have no x-ui hints: those apply to the problem input only. They are described in the input schema reference.
Choosing parameters well
Expose the settings that change cost, accuracy or run time, and give each a bound that keeps a run inside your resources.timeout_s and at a sensible cost. A shots parameter with no max lets a user ask for a run you did not plan for, and the platform runs at most 100,000 shots per circuit (qsolve.MAX_SHOTS_PER_CIRCUIT): lint E025 is an error when a shots parameter has no max, a max above that or a default above it.
- Name the shots parameter
shots. When a workflow gives noestimate(), the default estimate usesparams["shots"](and0if there is none) as its total shots, with one circuit, and is marked as a default; see Writing the workflow. - Use an
enumfor modes, not a string.constraint_mode: [mixer, penalty]is checked, a free string is not. - Make the default the setting you would recommend. Researchers run defaults. In
max-cutthe defaults are 3 layers, 4000 final shots and noise-free training (train_on: sim). - If changing a setting changes which problem is solved, it is an input, not a parameter.
- Reflect cost-driving parameters in your
estimate(). A run that sends many more circuits than estimated fails theestimate_honestgate. In the examples,train_on: backendsends one circuit per optimiser step to the target, and the estimate counts them, which is whytrain_shotsandoptimizer_itersare bounded (max: 8000andmax: 400inmax-cut). - Bound by the limits you advertise.
quantum-kernel-classifierusesmin: 0with the documented meaning "0 = auto-tune" forrepsandbandwidth; a sentinel like this is a normal int or float, so say what it means indescription. - Defaults must be cheap enough to validate. The platform runs your benchmark cases with their
params, falling back to the defaults, inside the validation time limit.
See Writing the workflow for estimate() and The manifest for the rest of qsolve.yaml. Every lint code is in Lint codes.