Skip to the content.

Quantum Random Number Generator (QRNG) API

Overview

The Quantum Random Number Generator (QRNG) models random number generation via quantum measurement. On real quantum hardware, measurements are fundamentally non-deterministic and yield true (quantum) randomness.

⚠️ Important — local simulation is not quantum randomness. By default this module simulates the measurements classically. The unseeded local path draws its outcomes from the OS cryptographically secure RNG (System.Security.Cryptography.RandomNumberGenerator) — CSPRNG-quality, suitable for cryptographic key material, but still classical randomness. True quantum randomness comes from generateWithBackendAsync on a cloud QPU target created with shots = 1: the bits are then the one measured shot of that job.

Key Features:

When to Use:

When NOT to Use (use System.Random instead):

For reproducible experiments, pass a seed to generateBits (see below); the seeded path uses System.Random(seed) and is not suitable for cryptographic use.


Quick Start

Generate Random Bytes (e.g., Cryptographic Key)

open FSharp.Azure.Quantum.Algorithms

// Generate 256-bit (32-byte) cryptographic key
let keyBytes = QRNG.generateBytes 32
printfn "Key: %s" (System.Convert.ToBase64String(keyBytes))

Generate Random Integer in Range

// Simulate 6-sided die roll
let diceRoll = QRNG.generateInt 6 + 1  // Returns 1-6
printfn "Rolled: %d" diceRoll

// Random index for array of 100 elements
let randomIndex = QRNG.generateInt 100  // Returns 0-99

Generate Random Float [0.0, 1.0)

// Monte Carlo sampling
let randomSample = QRNG.generateFloat()  // Returns float in [0.0, 1.0)
printfn "Sample: %.6f" randomSample

Core Functions

generate

val generate : numBits:int -> QRNGResult

Generates random bits from the simulated Hadamard + measurement model. Same as generateBits numBits None, so the bits come from the OS CSPRNG.

Parameters:

Returns: QRNGResult containing:

Example:

let result = QRNG.generate 8
printfn "Bits: %A" result.Bits
printfn "As byte: %d" result.AsBytes.[0]
printfn "Entropy: %.3f" result.Entropy

generateBits

val generateBits : numBits:int -> seed:int option -> QRNGResult

Like generate, with a choice of randomness source.

Parameters:

Example:

let a = QRNG.generateBits 16 (Some 42)
let b = QRNG.generateBits 16 (Some 42)
printfn "Reproducible: %b" (a.Bits = b.Bits)  // true

generateInt

val generateInt : maxValue:int -> int

Generates random integer in range [0, maxValue) using rejection sampling.

Parameters:

Returns: Random integer n where 0 ≤ n < maxValue

Example:

// Random number from 0-99
let randomPercent = QRNG.generateInt 100

// Random array index
let arr = [|1; 2; 3; 4; 5|]
let idx = QRNG.generateInt arr.Length
let randomElement = arr.[idx]

generateFloat

val generateFloat : unit -> float

Generates random float in range [0.0, 1.0) with 53-bit precision (IEEE 754 double mantissa).

Returns: Random float in [0.0, 1.0)

Example:

// Monte Carlo estimation of π
let estimatePi samples =
    [1..samples]
    |> List.map (fun _ ->
        let x = QRNG.generateFloat()
        let y = QRNG.generateFloat()
        if x*x + y*y <= 1.0 then 1.0 else 0.0)
    |> List.average
    |> (*) 4.0

let pi = estimatePi 10000
printfn "π ≈ %.4f" pi

generateBytes

val generateBytes : numBytes:int -> byte[]

Generates random byte array (8 bits per byte).

Parameters:

Returns: byte[] with random values

Example:

// Generate 256-bit AES key
let aesKey = QRNG.generateBytes 32

// Generate random salt for password hashing
let salt = QRNG.generateBytes 16

Backend Integration

generateWithBackendAsync

val generateWithBackendAsync : 
    numBits:int -> 
    backend:IQuantumBackend -> 
    cancellationToken:CancellationToken -> 
    Task<QuantumResult<QRNGResult>>

Generates random bits by executing a numBits-qubit H-superposition circuit through the specified backend.

⚠️ Randomness source depends on the backend:

⚠️ Cost Warning: On a cloud backend every call is one billed job, so n draws are n jobs. Cap them with a JobBudget (see Backend Switching).

Parameters:

Returns: Task<QuantumResult<QRNGResult>> (QuantumResult<'T> is Result<'T, QuantumError>). Invalid numBits and backend failures come back as Error, not exceptions.

Example:

open System.Threading
open FSharp.Azure.Quantum.Backends

task {
    // Use local simulator (free, fast)
    let backend = LocalBackendFactory.createUnified()
    
    let! result = QRNG.generateWithBackendAsync 16 backend CancellationToken.None
    
    match result with
    | Ok qrng -> 
        printfn "Generated 16 bits with entropy: %.3f" qrng.Entropy
    | Error err -> 
        printfn "Error: %s" err.Message
}

On hardware, create the backend with one shot per job:

open System.Threading
open FSharp.Azure.Quantum
open FSharp.Azure.Quantum.Core
open FSharp.Azure.Quantum.Backends.CloudBackends

let credential = Authentication.CredentialProviders.createDefaultCredential ()
let httpClient = Authentication.createAuthenticatedClient credential
let workspaceUrl =
    "https://<location>.quantum.azure.com/subscriptions/<sub>/resourceGroups/<rg>/providers/Microsoft.Quantum/workspaces/<ws>"

// shots = 1: the 16 bits are the one measured shot of one billed job
let qpu = CloudBackendFactory.createIonQ httpClient workspaceUrl "ionq.qpu.aria-1" 1

task {
    match! QRNG.generateWithBackendAsync 16 qpu CancellationToken.None with
    | Ok qrng -> printfn "Hardware bits: %A" qrng.Bits
    | Error err -> printfn "Error: %s" err.Message
}

Statistical Quality Testing

testRandomness

val testRandomness : bits:bool[] -> RandomnessTest

Performs basic statistical tests on generated bits to assess randomness quality.

Tests Performed:

  1. Frequency Test: Ratio of 1s vs 0s (should be ~0.5)
  2. Run Test: Count of alternations between 0 and 1
  3. Entropy: Shannon entropy (should be ~1.0 for perfect randomness)

Returns: RandomnessTest with:

Example:

let result = QRNG.generate 10000
let test = QRNG.testRandomness result.Bits

printfn "Frequency Ratio: %.3f (expect ~0.500)" test.FrequencyRatio
printfn "Entropy: %.3f (expect ~1.000)" test.Entropy
printfn "Quality: %s" test.Quality

Expected Output:

Frequency Ratio: 0.498 (expect ~0.500)
Entropy: 0.997 (expect ~1.000)
Quality: EXCELLENT

Technical Details

Algorithm

QRNG models the simplest quantum circuit for randomness:

  1. Initialize qubits to 0⟩ state
  2. Apply Hadamard gate to each qubit → Creates uniform superposition: ψ⟩ = ( 0⟩ + 1⟩)/√2
  3. Measure in computational basis → Each qubit collapses to 0 or 1 with exactly 50% probability
  4. Extract bits from measurement outcomes

On quantum hardware the measurement outcome is physically non-deterministic, unlike a pseudo-random number generator (PRNG), which is deterministic given its seed. generate, generateBits, generateInt, generateFloat and generateBytes simulate the measurement locally. generateWithBackendAsync on a one-shot cloud backend returns the bits the hardware measured; on a simulator it samples the returned state locally (see above).

Entropy Calculation

Shannon entropy for binary sequence:

H = -p₀ log₂(p₀) - p₁ log₂(p₁)

Where:

Perfect randomness: H = 1.0 (maximum entropy for binary)

Memory Use

The local functions never build a multi-qubit state vector. Qubits in this circuit are independent, so the seeded path simulates one single-qubit measurement per bit, and the unseeded path draws one CSPRNG bit per measurement. Memory grows linearly with numBits, which is why up to 1,000,000 bits per call is practical. generateWithBackendAsync is different: it runs one numBits-qubit circuit, so its cost depends on the backend (on a cloud backend, one billed job per call).


References


See Also