Your first workflow
Build, test and submit a Qsolve workflow from scratch: install the SDK, scaffold with qsolve init, run, validate, lint, benchmark, push to GitHub and submit.
Updated 13 November 2026 View as markdown
On this page
qsolve init generates a passing workflow that solves a small QUBO with QAOA. Run it unchanged first, submit it, then swap in your own problem. Every command used here is specified in The qsolve command line, and every Python name in the SDK reference.
1. Install the SDK
You need Python 3.12 or newer. The ibm extra installs the device noise models that qsolve test uses for its noise-model benchmark case, so install it even if you never run on hardware.
pip install "qsolve-sdk[ibm]"
qsolve --version
qsolve --help
With the extra you can also run on a real device from your machine with --target ibm:<device> and your own token in QSOLVE_IBM_TOKEN. Simulators and noise-model targets need no token.
2. Scaffold a workflow
qsolve init depot-picker --summary "Pick the best depots"
cd depot-picker
The name is 2 to 63 lowercase letters, digits and dashes, starting with a letter or digit. It becomes the workflow's identifier on Qsolve. The summary is written into qsolve.yaml unquoted, so avoid a colon followed by a space and # in it. The command creates:
depot-picker/
qsolve.yaml
README.md
benchmarks/cases.yaml
examples/qubo_5.json
schemas/input.json
schemas/output.json
src/__init__.py
src/workflow.py
tests/test_workflow.py
src/workflow.py is about 120 lines of Qiskit: it builds a QAOA circuit, trains the angles on a local statevector simulator, samples the final circuit through ctx.sampler and compares the result with an exhaustive search. The class is a qsolve.Workflow subclass (The Workflow class). Project layout explains each file. tests/test_workflow.py is a pytest file that runs the example through qsolve.run_workflow and asserts the quantum result matches the best classical answer; run it with pytest.
3. Validate the manifest and inputs
qsolve validate .
OK depot-picker v0.1.0
validate loads qsolve.yaml, reads both schemas and checks every example input against the input schema.
4. Lint
qsolve lint .
WARNING W051 no LICENSE file found
0 error(s), 1 warning(s)
Lint is the static check the platform runs before it executes any of your code. The scaffold sets license: Apache-2.0 but does not create the file, hence the warning: add a LICENSE file with the license text to clear it. Every code is explained in Lint codes; Troubleshooting covers common fixes. Errors exit 1, warnings do not.
5. Run it locally
qsolve run . -i examples/qubo_5.json --seed 1
depot-picker v0.1.0 on aer-cpu
┌───────────────────────────────┬─────────────────────┐
│ Metric │ Value │
├───────────────────────────────┼─────────────────────┤
│ Objective (quantum) │ -10 │
│ Objective (Exhaustive search) │ -10 │
│ Gap vs baseline │ 0.000% │
│ Uncertainty │ exact (stderr None) │
│ Shots │ 4000 │
│ Run time (s) │ 2.22 │
└───────────────────────────────┴─────────────────────┘
QAOA (quantum approximate optimisation): -10; Exhaustive search: -10 (accuracy 100.00%).
Override a parameter with -p layers=1; write the full JSON result with --out result.json. The file holds a result and a provenance, described in Results and events. The same seed gives the same result every time.
6. Check the cost estimate
qsolve estimate . -i examples/qubo_5.json
{
"qubits": 5,
"circuits_on_target": 1,
"shots_on_target": 4000,
"classical_cost_class": "low",
"runtime_s": null,
"notes": [],
"by_default": false
}
Nothing runs. The gate estimate_honest fails if this is off by more than a factor of two from what a run sends. qsolve estimate has no --param, so it uses the manifest defaults. The numbers come from the estimate() method of your class (Metering and cost).
7. Run the gates and benchmarks
qsolve test .
Gates
┌──────────────────┬────────┬────────────────────────────────────────────┐
│ Gate │ Result │ Detail │
├──────────────────┼────────┼────────────────────────────────────────────┤
│ manifest │ pass │ depot-picker v0.1.0 │
│ example_inputs │ pass │ 1 valid │
│ benchmark_cases │ pass │ 3 cases defined │
│ methods_declared │ pass │ 3 methods declared │
│ fidelity_checked │ pass │ a case sets min_fidelity on a noisy target │
│ cases_pass │ pass │ 3/3 passed │
│ reproducible │ pass │ identical result hash │
│ estimate_honest │ pass │ estimate within 2x of what was sent │
└──────────────────┴────────┴────────────────────────────────────────────┘
Cases on sim-cpu
┌────────────────────┬────────┬───────┬──────────┬──────────┐
│ Case │ Result │ Gap │ Fidelity │ Time (s) │
├────────────────────┼────────┼───────┼──────────┼──────────┤
│ unconstrained-5 │ pass │ 0.00% │ 0.998 │ 1.5 │
│ dense-random-8 │ pass │ 0.00% │ 0.982 │ 0.4 │
│ noise-model-qubo-5 │ pass │ 0.00% │ 0.974 │ 10.1 │
└────────────────────┴────────┴───────┴──────────┴──────────┘
ALL GATES PASSED
These are the gates and cases the platform runs on submission. (reproducible and estimate_honest need at least one case that ran, so a workflow whose cases all fail shows six gates.) qsolve test does not lint, so run qsolve lint . as well. The third case runs on fake:fez@1.6, a device noise model with every error rate multiplied by 1.6. Benchmarks explains each gate.
8. Make it yours
Change these in order, running qsolve test . after each step:
schemas/input.jsonandexamples/: the input a user provides.__init__,sizeandevaluateinsrc/workflow.py: parse the input and score a solution. RaiseWorkflowErrorfor a bad input andInvalidSolutionfor a solution that breaks a rule.quantum: your circuits, run throughctx.sampler. See Writing the workflow.referenceand anyclassical_<id>methods: the strongest classical method that is practical.qsolve.yaml: method descriptions, metric and envelope (The manifest).benchmarks/cases.yaml: cases with known answers, plus one noise-model case (Benchmarks).schemas/output.jsonandanswer(): what people read.
Also update tests/test_workflow.py so pytest keeps passing. Bump version in qsolve.yaml whenever anything changes after a submission; see Versioning.
9. Push to GitHub
git init
git add .
git commit -m "First version"
git branch -M main
git remote add origin https://github.com/<you>/depot-picker.git
git push -u origin main
The repository can be public, or private if you connect the Qsolve GitHub app (Private repositories). It needs qsolve.yaml at the root, no symbolic links or submodules, and must be under 50 MB.
10. Submit
Sign in to Qsolve, open the Publish a workflow page, paste https://github.com/<you>/depot-picker into the repository field and press Submit. The branch, tag or commit field is optional and defaults to the default branch. Qsolve pins the exact commit it fetched.
The submission moves from Waiting to Checking, then Published or Needs changes; open it to see lint findings and gates. A published workflow is runnable by its link at once and listed publicly once approved. See Validation and publishing.