Skip to the content.

Frequently Asked Questions (FAQ)

Common questions about FSharp.Azure.Quantum.

đŸ”„ Quick Troubleshooting Guide

Start here if something isn’t working!

Symptom Quick Fix
“Distance matrix must be square” Ensure rows = columns = number of cities
“Budget 
 is insufficient to purchase any asset” Budget must be ≄ cheapest asset price
Compiler error on solution.Result Use match on the Result first (see Getting Started)
Error has no .Contains / %s rejects it Errors are QuantumError values, not strings: use err.Message
Very slow first run Normal - .NET JIT compilation. Second run will be fast.
Suboptimal solutions Raise the QAOA shot counts in the solver config, or run several times and keep the best (see below)
“needs N qubits 
 at most M qubits” The encoding is too wide for the backend; shrink the problem or use a larger backend (see problem sizes)
Type inference error Add explicit type annotations: list<string * float * float>
“MinHolding 
 cannot exceed MaxHolding” Check constraint values: MinHolding ≀ MaxHolding ≀ Budget

Still stuck? See detailed Errors and Troubleshooting below.

General Questions

What is FSharp.Azure.Quantum?

FSharp.Azure.Quantum is a quantum-first F# library for solving combinatorial optimization problems using quantum algorithms (QAOA, VQE, QFT). It provides:

Do I need an Azure account to use this library?

No! The LocalBackend (default) provides quantum simulation entirely offline without Azure credentials. You only need Azure access if you want to use cloud quantum backends for real quantum hardware or provider simulators.

Is this production-ready?

The current package version is 1.6.2. It is suitable for:

LocalBackend provides free quantum simulation; its width is derived from available memory (hard ceiling 30 qubits), and iterative algorithms such as QAOA are practical up to about 20 qubits. For more, cloud backends are available via Azure Quantum.

Should I use quantum computing to solve my problem? Why not AI?

Quantum computing is for small-data, big-compute problems: the input fits on a page, but the number of possible answers explodes (routes, schedules, portfolios, molecular energies). For big-data problems, where the answer has to be learned from many examples, traditional AI could work better.

Technical Questions

When should I use quantum vs HybridSolver?

Quick Comparison Table

Aspect Direct Quantum API HybridSolver (with classical fallback)
Approach QAOA/VQE quantum algorithms QuantumAdvisor picks classical or quantum per problem
When quantum runs Always Only when the advisor strongly recommends it (≄ 50 variables by default) and you pass a backend, or when you force Some HybridSolver.Quantum
Cost LocalBackend: free; Cloud: provider pricing Classical runs are free; optional budget guard for quantum
Problem Size Limited by the backend’s qubit count (LocalBackend: memory-derived, QAOA practical to ~20 qubits) Classical path has no qubit limit
Best For Learning, consistent quantum API Variable-size workloads where small cases should stay classical
Backend LocalBackend (default) or a cloud backend Same, passed to the solve*WithBackendAsync functions
Reproducible ⚠ Probabilistic (quantum nature) Classical path: ✅ Deterministic

Decision Criteria

Use Direct Quantum API when:

Use HybridSolver when:

Example:

open System.Threading
open FSharp.Azure.Quantum
// QuantumMaxCutSolver lives in the Quantum namespace
open FSharp.Azure.Quantum.Quantum

let problem =
    MaxCut.createProblem ["A"; "B"; "C"; "D"]
        [ ("A", "B", 1.0); ("B", "C", 2.0); ("C", "D", 1.0); ("D", "A", 1.0) ]

// Direct Quantum API: QAOA on the local simulator
task {
    match! MaxCut.solveAsync problem None CancellationToken.None with
    | Ok solution -> printfn "Cut value: %.1f" solution.CutValue
    | Error err -> eprintfn "Error: %s" err.Message
}

// HybridSolver takes the solver-level problem type
let hybridProblem: QuantumMaxCutSolver.MaxCutProblem =
    { Vertices = problem.Vertices; Edges = problem.Edges }

task {
    match! HybridSolver.solveMaxCutAsync hybridProblem None None None CancellationToken.None with
    | Ok solution -> 
        printfn "Method: %A" solution.Method  // Classical or Quantum
        printfn "Reasoning: %s" solution.Reasoning
        printfn "Cut value: %.1f" solution.Result.CutValue
    | Error err -> eprintfn "Error: %s" err.Message
}

Crossover Point: With QuantumAdvisor.defaultThresholds, HybridSolver routes to classical below 50 variables. From 50 up it uses quantum if you supplied a backend (and the estimated cost is within any budget you set); otherwise it still runs classically and says so in Reasoning.

How accurate are the solutions?

Quantum algorithms (QAOA/VQE) provide:

Solution quality improves with:

Classical path (via HybridSolver) provides:

How many shots do I need?

It depends on what the shots are for.

Estimating a number (an expectation value, a probability, an option price): the statistical error falls as 1/√shots, so four times the shots halves it. Primitives.sampledExpectation and the Monte Carlo results report their standard error; raise shots until that error is small against the difference you need to resolve.

Finding a good solution (QAOA, Grover-style search): what matters is the chance p that one shot returns an acceptable answer. The chance of seeing at least one in N shots is 1 − (1 − p)^N, so

Wanted confidence Shots needed
95% about 3 / p
99% about 4.6 / p

Read p off the solution. Every QAOA solver solution carries Sampling, the standing of the returned answer among the final samples:

open FSharp.Azure.Quantum.Core.QaoaExecutionHelpers

// call as: report solution.Sampling
let report (sampling: SampleStatistics option) =
    match sampling with
    | Some s ->
        printfn "%d of %d shots returned this answer (p ≈ %.1f%%); %d were valid" s.Hits s.Shots (100.0 * s.HitRate) s.Valid
        printfn "uniform guessing: %.2f%% per shot; shots for 95%%: %A" (100.0 * s.UniformRate) (s.ShotsFor 0.95)
    | None -> printfn "no single sampling run produced this answer (decomposed, or no circuit needed)"

On the 8-route supply-chain example the optimised circuit returns the optimum in 4–5% of shots, and 100 shots found it in 147 of 150 runs.

Start small, then scale. While you are still getting the model right (the encoding, the constraints, the backend configuration), run 10–50 shots on the local simulator: a wrong model shows up as an error or as Valid = 0 however many shots you take, and short runs keep the loop fast. Once the answers are right, take a pilot run of 100–200 shots, read HitRate, and size the production run from the table.

Check that the circuit is doing the work. A problem on n qubits has 2^n bitstrings. If your shots approach or exceed that number, sampling is close to exhaustive and would find the answer with any circuit; random guessing finds a unique optimum with probability 1/2^n per shot (UniformRate). Compare HitRate with that baseline, and expect p to shrink as the problem grows — more layers and optimised angles raise it, more shots only compensate.

Will my Azure bill grow exponentially with more shots?

No. The price of a job is linear in its shots: twice the shots costs about twice as much, never more. Each provider on Azure Quantum bills in proportion to shots — IonQ per gate and shot, Quantinuum in credits where the shot count multiplies the gate count, Rigetti by execution time — some with a minimum charge per job. CostEstimation.estimateCost gives a figure before you submit, and the Azure Quantum pricing page has the current rates. The local simulator is free.

Two things do need watching:

What problem sizes can I solve?

With a quantum backend the limit is the number of qubits the encoding needs:

Problem Qubits needed
TSP citiesÂČ (4 cities = 16 qubits)
Graph Coloring nodes × colors
MaxCut one per vertex
Knapsack one per item + ⌈log₂(capacity + 1)⌉ slack bits (capacity in whole weight units)
Portfolio one per affordable asset (+ slack bits when lots limit the number of holdings)
Network Flow one per route
Task Scheduling tasks × time slots

The local simulator holds up to StateVector.maxQubits (derived from memory, at most 30), and QAOA is practical up to about 20 qubits by default. A problem wider than the backend is split into circuits that fit where its structure allows (next question); otherwise the solver returns an error that names the qubit count.

With the classical path of HybridSolver there is no qubit limit: the classical TSP heuristic accepts up to 10,000 cities (TspSolver.maxCities).

My problem is wider than the backend. Can it still run on it?

Often, by trading qubits for circuit runs. QuboSplitting cuts a problem into pieces that fit, runs every piece on the backend, and joins the answers classically. Three exact splits exist, and one heuristic search for problems none of them cuts:

Split Works when Cost Used by
Conditioning the QUBO is sparse: fixing variables breaks it into pieces that fit, and the fixed variables couple narrowly enough to join (chains, trees, ladders, strips) a piece runs once per assignment of the fixed variables it touches: 2^b runs for b of them (at most 256 runs by default) MaxCut, vertex cover, clique, matching, set cover, MAX-SAT, binary ILP and the drug-discovery solvers
Blocks a linear objective under one additive integer limit (a knapsack) one run per block and share of the capacity (at most 256 by default) Knapsack (solveAsync, or solveInBlocksAsync to ask for it)
Halves every subset that sums to a target is wanted (subset-sum) one search per half and partial sum (at most 256 by default) QuantumKnapsackSolver.findAllExactCombinationsAsync and the Knapsack.findAll
Async functions with a backend
Neighbourhood search (heuristic, off by default) any QUBO, dense ones included: conditioning does not cut it and the backend cannot hold it linear in the problem: one run per block of the backend’s width per sweep, MaxSweeps sweeps at most, all within MaxShareRuns the conditioning solvers above, when MaxSweeps is above 0; any problem through QuboSplitting.searchNeighbourhoodsAsync

The neighbourhood search keeps a best assignment so far (the all-zero one to start), samples one block of variables at a time with the rest fixed, and takes a sample of lower energy; a sweep samples every block once, and the search ends after a sweep that improved nothing. Nothing is joined exactly, so the answer is the best sample seen, not a proven optimum. searchNeighbourhoodsAsync takes any problem whose blocks you can write as QUBOs (its Neighbourhoods record: the blocks of a sweep, a block’s QUBO, how a sample is written back, the score, and an Accept gate for the problem’s own constraints, which the search moves into before it starts demanding it), so a 5000-drone assignment can be improved a block of drones at a time without a 5000ÂČ-variable QUBO ever existing (see the SwarmChoreography example’s BlockAssignment).

When it happens. Only when the problem needs more qubits than the backend runs (or than MaxPieceQubits, when you set it), and only where the split settings allow it. The default is SplitPolicy.OnSimulators:

Policy Simulators (local, topological) Backends that bill every circuit (Azure Quantum, Braket)
Never one circuit; the backend accepts or refuses it one circuit
OnSimulators (default) split one circuit, so billed jobs never multiply unasked
Always split split: every piece is a billed job

How to set it. The settings are the Splitting field of QaoaSolverConfig and of the MaxCut and Knapsack configurations:

open FSharp.Azure.Quantum.Core

let splitting: QaoaExecutionHelpers.SplitSettings =
    { QaoaExecutionHelpers.defaultSplitSettings with
        Policy = QaoaExecutionHelpers.SplitPolicy.Always // also on billed hardware
        MaxPieceQubits = ValueSome 12 // widest circuit: wider problems are split even if the backend could run them
        MaxShareRuns = 100         // at most 100 circuit runs per split
        MaxBlockItems = 10         // at most 10 items per block
        MaxSweeps = 3 }            // a QUBO that neither cuts nor fits: 3 sweeps of neighbourhood search (default 0: refuse it)

let config =
    { QaoaExecutionHelpers.defaultConfig with
        Splitting = splitting }

The business builders (coverageOptimizer, resourcePairing, socialNetwork, 
) and the MaxCut and Knapsack builders use the default settings; call the solver’s solveWithConfigAsync (or QuantumMaxCutSolver.solveAsync / QuantumKnapsackSolver.solveAsync) to pass your own.

How to see it. A solution that was split carries Split, and has no Sampling:

open FSharp.Azure.Quantum.Core

let describe (split: QaoaExecutionHelpers.SplitReport option) =
    match split with
    | Some s -> printfn "%d runs, widest %d qubits, %d fixed variables, %d blocks, %d sweeps" s.Runs s.WidestPieceQubits s.FixedVariables s.Blocks s.Sweeps
    | None -> printfn "ran as one circuit"

Sweeps above 0 means a neighbourhood search: the answer is the best sample seen, not an exact join.

What to expect:

Can I use my own distance calculations?

Yes! Build the distance matrix yourself and pass it to HybridSolver.solveTspAsync:

open FSharp.Azure.Quantum

let cities = [| (0.0, 0.0); (1.0, 0.5); (2.0, 1.5); (3.0, 3.0) |]

// Custom distance function (Manhattan distance here)
let myDistance (x1, y1) (x2, y2) = abs (x1 - x2) + abs (y1 - y2)

// Build matrix
let n = cities.Length
let distances = 
    Array2D.init n n (fun i j ->
        if i = j then 0.0
        else myDistance cities.[i] cities.[j])

task {
    match! HybridSolver.solveTspAsync distances None None None CancellationToken.None with
    | Ok solution -> printfn "Tour length: %.2f (%A)" solution.Result.TourLength solution.Method
    | Error err -> printfn "Error: %s" err.Message
}

Errors and Troubleshooting

“Distance matrix must be square”

Problem: Your distance matrix has different number of rows and columns.

Solution:

// ❌ Wrong: 3x2 matrix
let wrong = array2D [[0.0; 1.0]; [2.0; 0.0]; [3.0; 4.0]]

// ✅ Correct: 3x3 matrix
let correct = array2D [
    [0.0; 1.0; 2.0]
    [1.0; 0.0; 3.0]
    [2.0; 3.0; 0.0]
]

“Distance matrix contains negative values”

Problem: Negative distances aren’t supported.

Solution: Ensure all distances are >= 0.0:

// Normalize or shift if needed
let normalized = 
    distances 
    |> Array2D.map (fun d -> max 0.0 d)

Solutions seem suboptimal

Try these improvements:

  1. Give QAOA more shots (quantum TSP; 4 cities = 16 qubits). Only a few percent of the shots are valid tours at 4 cities, and the result is the shortest of them, so more shots raise the chance that the optimum is among them. NumLayers = 3 raises the share of valid tours and makes the optimization slower: ```fsharp open FSharp.Azure.Quantum.Quantum open FSharp.Azure.Quantum.Core.BackendAbstraction open FSharp.Azure.Quantum.Backends.LocalBackend

let config = { QuantumTspSolver.defaultConfig with OptimizationShots = 500 FinalShots = 4000 }

let backend = LocalBackend() :> IQuantumBackend

task { match! HybridSolver.solveTspWithBackendAndConfigAsync distances None None (Some HybridSolver.Quantum) (Some backend) config CancellationToken.None with | Ok solution -> printfn “Tour length: %.2f” solution.Result.TourLength | Error err -> printfn “Error: %s” err.Message }


2. **Compare with the classical heuristic:**
```fsharp
task {
    match! HybridSolver.solveTspAsync distances None None (Some HybridSolver.SolverMethod.Classical) CancellationToken.None with
    | Ok solution -> printfn "Tour length: %.2f" solution.Result.TourLength
    | Error err -> printfn "Error: %s" err.Message
}
  1. Run the quantum solver several times and keep the best:
    task {
     let solutions = ResizeArray()
     for _ in 1..10 do
         match! HybridSolver.solveTspWithBackendAsync distances None None (Some HybridSolver.Quantum) (Some backend) CancellationToken.None with
         | Ok solution -> solutions.Add solution
         | Error _ -> ()
     return solutions |> Seq.minBy (fun sol -> sol.Result.TourLength)
    }
    

How do I debug slow performance?

Profile your problem:

open System.Diagnostics

task {
    let sw = Stopwatch.StartNew()
    match! HybridSolver.solveTspAsync distances None None None CancellationToken.None with
    | Ok solution -> 
        sw.Stop()
        printfn "Size: %d cities" (distances.GetLength(0))
        printfn "Time: %d ms" sw.ElapsedMilliseconds
        printfn "Method: %A" solution.Method
        printfn "Solver time: %.1f ms" solution.ElapsedMs
        printfn "2-opt iterations: %d" solution.Result.Iterations
    | Error err -> printfn "Error: %s" err.Message
}

Feature Questions

Is quantum backend available yet?

Status: ✅ Quantum algorithms are implemented and available via LocalBackend (default) and Azure Quantum cloud backends (IonQ, Rigetti, Quantinuum, Atom Computing, IQM).

Direct Quantum API:

HybridSolver: Chooses a classical solver for problems below the advisor’s thresholds (50 variables by default), where quantum circuit overhead isn’t beneficial.

What quantum algorithms are used?

Optimization Problems (QAOA-based):

Quantum Chemistry (VQE):

QFT-Based Applications:

Classical Fallback (HybridSolver only):

Can I add my own optimization problems?

Yes! The library is designed to be extensible:

  1. Encode your problem as a QUBO (see QUBO Encoding Strategies)
  2. Run it with QAOA on any IQuantumBackend (see the QaoaCircuit / QaoaOptimizer modules and the existing solvers under Solvers/Quantum)
  3. Use QuantumAdvisor.getRecommendation if you want a quantum-vs-classical recommendation

See API Reference for the building blocks.

Does it support GPU acceleration?

Not in the local simulator, which runs on the CPU. CudaQBridge can hand circuits to NVIDIA CUDA-Q as an external tool.

Integration Questions

How do I integrate with my existing F# code?

// Add package reference
// dotnet add package FSharp.Azure.Quantum

// Open namespaces
open System.Threading
open FSharp.Azure.Quantum

// Use in your code
let optimizeTour (distances: float[,]) (cancellationToken: CancellationToken) =
    task {
        match! HybridSolver.solveTspAsync distances None None None cancellationToken with
        | Ok solution -> return Some solution.Result
        | Error _ -> return None
    }

Can I use this from C#?

Yes! F# libraries are interoperable; F# option parameters take null for None:

using System.Threading;
using FSharp.Azure.Quantum;

var distances = new double[,] {
    {0.0, 2.0, 9.0},
    {1.0, 0.0, 6.0},
    {15.0, 7.0, 0.0}
};

var result = await HybridSolver.solveTspAsync(distances, null, null, null, CancellationToken.None);

if (result.IsOk) {
    var solution = result.ResultValue;
    Console.WriteLine($"Tour length: {solution.Result.TourLength}");
} else {
    Console.WriteLine($"Error: {result.ErrorValue.Message}");
}

The CSharpBuilders class and the C# extension methods offer a more idiomatic surface for the problem builders; see API Reference.

Does it work with .NET 8/9/10?

Targets: .NET 10.0

Compatible with: .NET 10.0 or later

Not compatible: .NET Framework, .NET Core 3.1, .NET 5–9

Cost and Licensing

How much does it cost?

Library: Free and open source (Unlicense license)

LocalBackend (Quantum Simulation): Free - runs entirely local; width derived from available memory

Cloud Quantum Backends: Azure Quantum pricing applies

What license is it under?

Unlicense - public domain equivalent:

Support Questions

Where can I get help?

How do I report a bug?

  1. Check existing issues
  2. Create minimal reproduction
  3. Include:
    • F#/.NET version
    • Library version
    • Code sample
    • Expected vs actual behavior

How can I contribute?

Contributions welcome!

See CONTRIBUTING.md (if available) or open an issue to discuss.

Performance Questions

Why is my first call slow?

.NET JIT compilation - first call includes:

Solution: Warm up with a small problem first:

task {
    // Warm up JIT with a tiny problem
    let! _ = MaxCut.solveAsync (MaxCut.createProblem ["A"; "B"] [ ("A", "B", 1.0) ]) None CancellationToken.None

    // Now solve the real problem
    let! solution = MaxCut.solveAsync problem None CancellationToken.None
    return solution
}

How do I parallelize multiple solves?

Start each run as a task on the thread pool and await them together; give each run its own backend instance:

open System.Threading
open System.Threading.Tasks
open FSharp.Azure.Quantum.Core

// Ten independent QAOA runs; keep the best cut
task {
    let! runs =
        [1..10]
        |> List.map (fun _ -> 
            Task.Run<QuantumResult<MaxCut.Solution>>(fun () ->
                let runBackend = LocalBackend() :> IQuantumBackend
                MaxCut.solveAsync problem (Some runBackend) CancellationToken.None))
        |> Task.WhenAll
    let best =
        runs
        |> Array.choose (function Ok s -> Some s | Error _ -> None)
        |> Array.maxBy (fun s -> s.CutValue)
    return best
}

Each run holds its own state vector, so memory grows with the number of parallel runs.

See Backend Switching for more task patterns.


Still have questions?