Full Reference →

Q# Cheat Sheet

Install: pip install qsharp

Background and History

Q# (pronounced “Q sharp”) was announced by Microsoft in December 2017 as part of the Microsoft Quantum Development Kit. It is a domain-specific programming language designed exclusively for expressing quantum algorithms, developed by a team at Microsoft Research led by Krysta Svore. Microsoft’s approach was unusual in the quantum computing landscape: rather than building a Python library on top of existing tools, they created an entirely new language with a type system and compiler purpose-built for quantum concepts like qubit linearity (qubits cannot be copied or arbitrarily discarded) and automatic generation of adjoint and controlled operation variants.

The Q# language and QDK were part of Microsoft’s broader quantum computing strategy, which centered on topological qubits as the eventual hardware target. While Microsoft’s topological qubit research progressed more slowly than initially anticipated, Q# was designed to be hardware-independent and usable with any future quantum backend. The language shipped with a built-in full-state simulator, a Toffoli simulator, a sparse simulator, a resource estimator for projecting hardware requirements, and tight integration with Visual Studio and VS Code.

In early 2024, Microsoft released a modernized Q# compiler and runtime as part of the new Azure Quantum Development Kit. This overhaul, which accompanied the Q# 1.0 release, dropped the .NET dependency for the Python integration path, simplified the language in several areas, and added a browser-based compiler accessible through VS Code for the Web. The updated QDK includes a resource estimator that can project the physical qubit and runtime requirements of algorithms targeting fault-tolerant hardware — the most practically useful planning tool available for any quantum framework. In 2025, Microsoft announced Majorana 1, its first topological qubit chip, marking a milestone in the hardware strategy Q# was always designed to support.

Q# integrates with Azure Quantum, Microsoft’s cloud platform for quantum computing, which provides access to hardware from IonQ, Quantinuum, Rigetti, and others. As of 2025, Q# has a smaller user community than Qiskit or PennyLane, partly because its standalone language approach has a steeper learning curve than Python-based frameworks. However, it remains actively developed and is particularly valued for its resource estimation capabilities and its formal approach to quantum program correctness.

If you are coming from Python-first frameworks, the Q# vs Qiskit vs Cirq comparison on QuantumZeitgeist is a useful orientation. For hands-on learning, start with the Q# Hello World tutorial or work through the full Getting Started with Q# guide on this site.


Installation

Q# runs in three modes: VS Code extension (recommended for larger projects), Jupyter notebooks via Python (best for exploration), or the standalone qsharp CLI.

# Python integration (recommended for most users)
pip install qsharp

# VS Code: install "Azure Quantum Development Kit" extension
# Jupyter: qsharp kernel is included with the pip package

# Verify installation
python -c "import qsharp; print(qsharp.__version__)"

The Python package bundles the Q# compiler and simulator. No separate .NET installation is required as of Q# 1.x.


Q# Program Structure

Q# uses operations (with side effects, like quantum gates) and functions (pure classical computation). All quantum code lives inside operations. Namespaces group related operations and functions.

namespace MyProgram {
    open Microsoft.Quantum.Intrinsic;   // H, X, Y, Z, CNOT, M
    open Microsoft.Quantum.Canon;       // ApplyToEach, QFT, etc.
    open Microsoft.Quantum.Measurement; // MResetZ, MeasureEachZ

    operation BellState() : (Result, Result) {
        use (q0, q1) = (Qubit(), Qubit());
        H(q0);
        CNOT(q0, q1);
        return (MResetZ(q0), MResetZ(q1));
    }
}

Key design rules:

  • Qubits cannot be copied (no-cloning theorem enforced by the type system)
  • Qubits must be returned to |0⟩ before the use block ends
  • Operations can have side effects; functions cannot
  • The compiler auto-generates Adjoint and Controlled variants when declared

Types

TypeDescription
QubitA quantum bit — not copyable, must be released in
ResultMeasurement outcome: Zero or One
BoolClassical boolean: true or false
Int64-bit signed integer
BigIntArbitrary-precision integer
Double64-bit floating-point
StringImmutable string (for Message() output)
PauliPauliX, PauliY, PauliZ, PauliI
Qubit[]Qubit array
Result[]Measurement result array
'TGeneric type parameter
UnitVoid equivalent (return type when nothing is returned)
(Int, Bool)Tuple type

Qubit Allocation

use q = Qubit();                          // single qubit, starts in |0⟩
use qs = Qubit[4];                        // array of 4 qubits
use (a, b) = (Qubit(), Qubit());         // tuple allocation
use (ctrl, targets) = (Qubit(), Qubit[3]);  // mixed

// Qubits MUST be reset before release
Reset(q);          // reset to |0⟩
ResetAll(qs);      // reset entire array
MResetZ(q);        // measure, then reset -- returns Result

Qubits start in state |0⟩ and must be returned to |0⟩ before the use block ends. The compiler will warn if qubits are potentially released in a non-|0⟩ state. This enforces clean resource management and is one of Q#‘s strongest safety guarantees.

The Bloch sphere simulator on this site is a useful visual aid for understanding qubit states before working with them in Q#.


Gates (Intrinsic Operations)

Single-qubit gates

H(q)           // Hadamard: |0⟩ → (|0⟩+|1⟩)/√2
X(q)           // Pauli-X (NOT gate)
Y(q)           // Pauli-Y
Z(q)           // Pauli-Z (phase flip)
S(q)           // S gate (Z^½)
T(q)           // T gate (Z^¼) -- building block for fault-tolerant circuits
I(q)           // Identity

// Rotation gates (angles in radians)
Rx(theta, q)   // rotation around X axis
Ry(theta, q)   // rotation around Y axis
Rz(theta, q)   // rotation around Z axis
R1(theta, q)   // phase rotation: |1⟩ → e^(iθ)|1⟩

Two-qubit gates

CNOT(control, target)       // Controlled-NOT
CZ(q1, q2)                  // Controlled-Z
SWAP(q1, q2)                // SWAP

Multi-qubit gates

CCNOT(q1, q2, q3)           // Toffoli (doubly-controlled NOT)
CSWAP(control, q1, q2)      // Fredkin (controlled SWAP)

// General controlled versions
Controlled H([control], target)
Controlled X([c1, c2], target)      // multi-control X
Controlled Rz(theta, [c], target)

See the quantum gates explained tutorial for a deeper walkthrough of what each gate does to qubit state.


Measurement

let result = M(q);                  // measure in Z basis → Result (Zero or One)
let result = MResetZ(q);            // measure + reset to |0⟩ (preferred)
let result = MResetX(q);            // measure in X basis + reset
let result = MResetY(q);            // measure in Y basis + reset

// Multi-qubit
let results = MeasureEachZ(qs);     // Result[], measures each qubit independently
let parity = Measure([PauliZ, PauliZ], [q0, q1]);  // joint measurement

// Conditional on result
if result == One {
    X(q);   // classical feedforward
}
if result == Zero {
    Message("Measured zero");
}

Measurement collapses the qubit to a definite state. In Q#, measurement always returns a classical Result value — there is no way to inspect the quantum state directly in a real execution (only in simulation).


Classical Control Flow

// Conditionals
if condition {
    // ...
} elif other {
    // ...
} else {
    // ...
}

// For loops (bounded, required for adjoint generation)
for i in 0 .. n - 1 {
    H(qs[i]);
}
for q in qs {
    X(q);
}

// Repeat-until (for probabilistic algorithms)
repeat {
    H(q);
    let result = M(q);
} until result == Zero
fixup {
    X(q);   // runs if condition not met, then tries again
}

// While (for classical-only functions)
mutable i = 0;
while i < 10 {
    set i += 1;
}

Variables

let x = 5;              // immutable binding
mutable y = 0;          // mutable variable
set y = 10;             // update mutable

// Arrays
let arr = [1, 2, 3];
let first = arr[0];
let len = Length(arr);
let slice = arr[1 .. 2];   // [2, 3]

// Strings (for debugging output only)
Message($"Iteration {i}: result = {result}");

Adjoint and Controlled

Every operation can declare Adj, Ctl, or Adj + Ctl variants. The compiler generates them automatically when the body allows.

// Declaring an adjointable + controllable operation
operation MyOp(theta : Double, q : Qubit) : Unit is Adj + Ctl {
    Rx(theta, q);
    H(q);
    T(q);
}

// Using the variants
Adjoint MyOp(theta, q);              // inverse / uncomputation
Controlled MyOp([ctrl], theta, q);   // controlled version
Adjoint Controlled MyOp([ctrl], theta, q);

This is one of Q#‘s most powerful features. Writing an operation once gives you the forward pass, the inverse (essential for uncomputation in quantum algorithms), and the controlled version automatically. The Grover’s Algorithm in Q# tutorial makes heavy use of this for oracle construction.


Canon Library

The Microsoft.Quantum.Canon namespace provides higher-level building blocks:

open Microsoft.Quantum.Canon;
open Microsoft.Quantum.Arithmetic;

// Apply to each qubit
ApplyToEach(H, qs);                    // H to every qubit
ApplyToEachA(H, qs);                   // Adj variant
ApplyToEachCA(H, qs);                  // Adj + Ctl variant

// Quantum Fourier Transform
QFT(qs);
Adjoint QFT(qs);    // inverse QFT (used in QPE)

// Arithmetic
let (sum, carry) = RippleCarryAdderD(a, b);

The Quantum Phase Estimation in Q# tutorial demonstrates QFT and Adjoint QFT in a full working implementation.


Running from Python

The recommended workflow for exploration and testing:

import qsharp

# Evaluate a Q# expression directly
result = qsharp.eval("""
    use q = Qubit();
    H(q);
    let r = MResetZ(q);
    r
""")
print(result)   # qsharp.Result.Zero or qsharp.Result.One

# Run with multiple shots
results = qsharp.run("""
    use (q0, q1) = (Qubit(), Qubit());
    H(q0);
    CNOT(q0, q1);
    let r = (MResetZ(q0), MResetZ(q1));
    r
""", shots=1000)

zeros = sum(1 for r in results if r == (qsharp.Result.Zero, qsharp.Result.Zero))
ones  = sum(1 for r in results if r == (qsharp.Result.One,  qsharp.Result.One))
print(f"|00⟩: {zeros/10:.1f}%  |11⟩: {ones/10:.1f}%")
# expect: |00⟩: ~50%  |11⟩: ~50%

Loading a .qs file

import qsharp

# Load and compile a Q# source file
qsharp.eval(open("MyAlgorithm.qs").read())

# Run an operation from it
results = qsharp.run("MyNamespace.MyOperation()", shots=500)

See the Hello World in Q# tutorial for a complete working example from installation to first run.


Resource Estimation

One of Q#‘s flagship features — estimate how much fault-tolerant hardware an algorithm would require without running it on a real device:

import qsharp

# Estimate resources for a Q# operation
result = qsharp.estimate("""
    use qs = Qubit[10];
    // ... your algorithm ...
    ResetAll(qs);
""")

print(result["physicalCounts"]["physicalQubits"])
print(result["physicalCounts"]["runtime"])

The resource estimator accounts for error correction overhead, magic state distillation, T gate cost, and target hardware parameters. It is the most rigorous planning tool available for projecting real-world fault-tolerant requirements.


Azure Quantum

To run on real hardware through Azure:

import azure.quantum
from azure.quantum.target.microsoft import MicrosoftEstimator

workspace = azure.quantum.Workspace(
    subscription_id="your-subscription-id",
    resource_group="your-resource-group",
    name="your-workspace",
    location="eastus"
)

# List available targets (hardware providers)
for target in workspace.get_targets():
    print(target.name)
# ionq.simulator, ionq.qpu, quantinuum.hqs-lt-s1, rigetti.sim.qvm, ...

# Submit a Q# job to IonQ
job = workspace.submit_job(
    target_name="ionq.simulator",
    input_data=qsharp_source,
    input_data_format="qsharp.v2",
    shots=100
)
result = job.get_results()

Azure Quantum currently provides access to IonQ (trapped-ion), Quantinuum (trapped-ion), and Rigetti (superconducting) hardware. See Getting Started with Microsoft Azure Quantum on QuantumZeitgeist for a practical walkthrough of the platform.


Common Patterns

Bell state

operation BellPair(q0 : Qubit, q1 : Qubit) : Unit is Adj + Ctl {
    H(q0);
    CNOT(q0, q1);
}

operation RunBell() : (Result, Result) {
    use (q0, q1) = (Qubit(), Qubit());
    BellPair(q0, q1);
    return (MResetZ(q0), MResetZ(q1));
}

Phase kickback

Used in algorithms like Grover’s algorithm and Bernstein-Vazirani to transfer phase information from a target qubit to a control:

operation PhaseKickback(oracle : (Qubit => Unit is Adj + Ctl), control : Qubit) : Unit is Adj {
    use target = Qubit();
    X(target);                      // put target in |1⟩
    H(control);                     // control into superposition
    Controlled oracle([control], target);  // phase kickback
    H(control);
    Reset(target);
}

Repeat-until-success (RUS)

For probabilistic state preparation or non-deterministic algorithms:

operation PrepareArbitraryState(q : Qubit) : Unit {
    mutable done = false;
    repeat {
        H(q);
        let r = M(q);
        set done = (r == Zero);
    } until done
    fixup {
        Reset(q);   // reset and try again
    }
}

Quantum phase estimation (QPE) skeleton

See the full Quantum Phase Estimation in Q# tutorial for a complete implementation. The skeleton:

operation QuantumPhaseEstimation(
    oracle : ((Int, Qubit) => Unit is Adj + Ctl),
    eigenstate : Qubit,
    precision : Int
) : Double {
    use countingRegister = Qubit[precision];
    ApplyToEach(H, countingRegister);
    for (idx, ctrl) in Enumerated(countingRegister) {
        Controlled oracle([ctrl], (1 <<< idx, eigenstate));
    }
    Adjoint QFT(countingRegister);
    let results = MeasureEachZ(countingRegister);
    ResetAll(countingRegister);
    // convert results to phase estimate...
    return 0.0;
}

Debugging and Output

// Print to console (simulator only)
Message("Hello from Q#");
Message($"Qubit count: {Length(qs)}");
Message($"Result: {result}");

// Dump state (simulator only -- not available on hardware)
DumpMachine();          // prints full state vector
DumpRegister(qs);       // prints state of a specific register

// Assertions (fail loudly in simulation, stripped on hardware)
Fact(Length(qs) == 4, "Expected 4 qubits");
EqualityFactI(count, 3, "Count should be 3");

Error Correction in Q#

Q# has built-in support for quantum error correction primitives. See the Quantum Error Correction in Q# tutorial for a hands-on introduction to stabilizer codes and syndrome measurement in Q#.

open Microsoft.Quantum.ErrorCorrection;

// Encode a logical qubit using the 3-qubit bit-flip code
operation EncodeInBitFlipCode(physical : Qubit[]) : LogicalRegister {
    CNOT(physical[0], physical[1]);
    CNOT(physical[0], physical[2]);
    return LogicalRegister(physical);
}

Q# vs Other Frameworks

FeatureQ#QiskitPennyLane
LanguageStandalone DSLPython libraryPython library
Autodiff / gradientsNoPartialYes (core feature)
Resource estimationExcellentLimitedNo
Adjoint auto-generationYesManualManual
Hardware accessAzure QuantumIBM QuantumMultiple backends
Learning curveSteepModerateModerate
Best forFault-tolerant algorithms, resource planningGate-level control, IBM hardwareVariational algorithms, QML

If you are primarily interested in variational algorithms and machine learning, PennyLane is a better fit. If you want the deepest integration with IBM’s hardware ecosystem, Qiskit is the standard. Q# is the right choice if you care about long-term algorithm correctness, formal resource estimation, and eventual fault-tolerant hardware.


Learning Resources

On this site:

Courses:

External: