Skip to content

Source code for the "Introduction to Quantum Computing with Q# and QDK" book

Notifications You must be signed in to change notification settings

filipw/intro-to-qc-with-qsharp-book

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

20 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Introduction to Quantum Computing with Q# and QDK

Code samples from the Introduction to Quantum Computing with Q# and QDK book, published by Springer Nature on 7 May 2022.

About the book

This book introduces the fundamentals of the theory of quantum computing, illustrated with code samples written in Q#, a quantum-specific programming language, and its related Quantum Development Kit. Quantum computing (QC) is a multidisciplinary field that sits at the intersection of quantum physics, quantum information theory, computer science and mathematics, and which may revolutionize the world of computing and software engineering.

The book begins by covering historical aspects of quantum theory and quantum computing, as well as offers a gentle, algebra-based, introduction to quantum mechanics, specifically focusing on concepts essential for the field of quantum programming. Quantum state description, state evolution, quantum measurement and the Bell’s theorem are among the topics covered. The readers also get a tour of the features of Q# and familiarize themselves with the QDK.

Next, the core QC topics are discussed, complete with the necessary mathematical formalism. This includes the notions of qubit, quantum gates and quantum circuits. In addition to that, the book provides a detailed treatment of a series of important concepts from quantum information theory, in particular entanglement and the no-cloning theorem, followed by discussion about quantum key distribution and its various protocols. Finally, the canon of most important QC algorithms and algorithmic techniques is covered in-depth - from the Deutsch-Jozsa algorithm, through Grover’s search, to Quantum Fourier Transform, quantum phase estimation and Shor’s algorithm.

The book is an accessible introduction into the vibrant and fascinating field of quantum computing, offering a blend of academic diligence with pragmatism that is so central to software development world. All of the discussed theoretical aspects of QC are accompanied by runnable code examples, providing the reader with two different angles - mathematical and programmatic - of looking at the same problem space.

Compatibility notes

The main branch in this repository contains the sample code that matches the content of the book as it was published. The code was written using the version 0.21.2112180703 of the QDK and the Q# language, which was released on 14th December 2021. The code should also work fine with all the newer versions of QDK lower than 1.0 (the last pre-1.0 release was 0.28.302812 on 15 September 2023).

QDK 1.0 was released on 12 January 2024 and contains numerous breaking changes and feature gaps in the libraries and in the language itself. The code samples ported to that release can be found on the qdk-1.x branch.

You can also use the table below to navigate the samples, and switch between the versions in the book (QDK 0.2x) and QDK 1.x.

List of examples

Name Chapter Description QDK 0.2x QDK 1.x
basic 3 Basic Q# language examples link link
basic-qubits 4 Basic qubit examples link link
superposition 4 Superposition examples link link
single qubit gates 4 Single qubit gates examples link link
multi qubit gates 4 Multi qubit gates examples link link
entanglement 5 Basic Bell states examples link link
bell 5 Bell's inequality examples link link
chsh 5 CHSH game example link link
ghz 5 GHZ game example link link
teleportation 5 Teleportation example link link
entanglement swapping 5 Entanglement swapping example link link
superdense 5 Superdense coding example link link
bb84 6 BB84 protocol example link link
b92 6 B92 protocol example link link
eprqkd 6 EPR QDK protocol example link link
deutsch-jozsa 7 Deutsch-Jozsa algorithm link link
bernstein-vazirani 7 Bernstein-Vazirani algorithm link link
grover 7 Grover algorithm link link
qft 7 Quantum Fourier Transform example link n/a
qpe 7 Quantum phase estimation example link link
shor 7 Shor's algorithm link link

QDK 1.x migration notes

Below is the list of breaking changes that occur in QDK 1.x, compared to QDK 0.2x. Note that this is not a comprehensive list, but one that focuses on the content and the code used in this book. It effectively summarizes that changes that have been applied between the main and qdk-1.x branches.

The grouping into chapters helps identify when a given braking change can be first encountered in the book. However, once it has been mentioned, it is not repeated again.

General

Mandatory qubit release

The explicit release of qubits is now mandatory (it used to happen implicitly). Use Reset, ResetAll or measurement with a built-in reset (for example MResetZ instead of M).

Namespaces

Q# now has implicit namespaces (file is a de facto namespace) so the code no longer must be wrapped in a namespace explicitly. Reusable functions and operations must be explicitly exported if they are needed in another file.

Additionally, all Microsoft.* namespaces got renamed to Std.*. Namespaces are now imported using the import keyword, instead of open. Individual callabales can be explicitly imported, or the entire namespace can be imported using import * syntax.

Finally, all the intrinsic operations are implicitly imported, so there is no need to import them explicitly.

For example:

namespace Microsoft.Quantum.Samples {
    open Microsoft.Quantum.Intrinsic;
    open Microsoft.Quantum.Math;
    open Microsoft.Quantum.Canon;
    open Microsoft.Quantum.Diagnostics;
    open Microsoft.Quantum.Arrays;
}

Can be simplified to:

import Std.Arrays.*;
import Std.Diagnostics.*;
import Std.Math.*;

Explicit return is not necessary anymore

This is not a breaking change, but an alternative new syntax. A return statement on the last line of a function or operation may be omitted for brevity. In such cases the trailing semicolon is also omitted. For example:

function Hello() : String {
    return "Hello, world!";
}

Can be simplified to:

function Hello() : String {
    "Hello, world!"
}

Lambdas

When the book was written, the QDK did not support lambdas yet. Those are now supported. As a result, helper functions are not needed where lambdas can be used. For example:

let allZeroes = All(ResultIsZero, MeasureEachZ(x));

function ResultIsZero(result : Result) : Bool {
    return result == Zero;
}

Can become:

let allZeroes = All(result -> result == Zero, MeasureEachZ(x));

Tuple decomposition now supported

Q# 1.x supports direct tuple decomposition, eliminating the need for accessing tuple elements with the ::Item1, ::Item2, etc. syntax.

let tuple = SomeFunction();
let firstItem = tuple::Item1;
let secondItem = tuple::Item2;

Can be simplified to:

let (firstItem, secondItem) = SomeFunction();

Chapter 3

@Attribute() missing

This cannot be ported as user-defined attributes are no longer supported in Q#.

BoolAsString missing

Can be replaced with simple string interpolation i.e. BoolAsString(flag) -> $“{flag}”

DoubleAsInt missing

Can be replaced with Truncate function from the Microsoft.Quantum.Math namespace.

Chapter 4

MultiM missing

operation MultiM is missing. However, internally it was really just a loop of measurements.

Can be replaced with MeasureEachZ or a manual measurement loop.

LittleEndian and BigEndian types missing

Wherever needed, we can now just pass qubit array in little-endian or big-endian format instead - ideally with the ordering explained in comments when necessary.

MeasureInteger

MeasureInteger takes a qubit array now, instead of a LittleEndian register It is newly in the Microsoft.Quantum.Measurement namespace.

OperationPow is missing

This can be replaced with:

function OperationPow<'T> (op : ('T => Unit), power : Int) : ('T => Unit) {
    return ApplyOperationRepeatedly(op, power, _);
}

internal operation ApplyOperationRepeatedly<'T> (op : ('T => Unit), power : Int, target : 'T)
: Unit {
    for idxApplication in 0 .. power - 1 {
        op(target);
    }
}

or simply by calling the operation the required (N) amount of times.

Chapter 5

IsResultZero and IsResultOne are missing

These can be replaced with simple checks: result == Zero or result == One.

DoubleAsStringWithFormat missing

Replaced with DoubleAsStringWithPrecision(number, precision).

PrepareEntangledState is missing

This was just a shorthand for creating entanglement using H + CNOT - can be added manually wherever needed.

Chapter 6

EqualA, EqualB missing

This is not necessary as most types are now comparable to each other.

For example: if (EqualA(EqualB, aliceBits, bobBits)) can become if aliceBits == bobBits

IntAsString missing

Not needed, can be solved with just interpolation or concatenation to a string.

For example simple string interpolation i.e. IntAsString(number) -> $“{number}”

Chapter 7

CControlledCA is missing

Can be re-added by hand. Here is the polyfill:

function CControlledCA<'T> (op : ('T => Unit is Ctl + Adj)) : ((Bool, 'T) => Unit is Ctl + Adj) {
    return ApplyIfCA(_, op, _);
}

operation ApplyIfCA<'T> (bit : Bool, op : ('T => Unit is Ctl + Adj), target : 'T) : Unit is Ctl + Adj {
    if (bit) {
        op(target);
    }
}

QuantumPhaseEstimation missing

Phase estimation has to be done manually. See the sample code in this repo.

QFTLE and QFT missing

There is now ApplyQFT but it behaves differently (no swap) and is little endian by default. It’s very easy to polyfill though:

operation QFTLE(qs : Qubit[]) : Unit is Adj + Ctl {
    // reversal needed since we want to use little endian order
    ApproximateQFT(Length(qs), Reversed(qs));
}

// original QDK QFT implementation for big-endian
operation ApproximateQFT (a : Int, qs : Qubit[]) : Unit is Adj + Ctl {
     let nQubits = Length(qs);
     Fact(nQubits > 0, "`Length(qs)` must be least 1");
     Fact(a > 0 and a <= nQubits, "`a` must be positive and less than `Length(qs)`");
     for i in 0 .. nQubits - 1 {
         for j in 0 .. i - 1 {
             if i - j < a {
                 Controlled R1Frac([qs[i]], (1, i - j, (qs)[j]));
             }
         }
         H(qs[i]);
     }
     SwapReverseRegister(qs);
}

Fraction type missing

The Fraction type has been removed in QDK 1.x. Calculations with fractions can now be handled using tuples directly. For example:

let input = Fraction(numerator, denominator);
let result = ContinuedFractionConvergentI(input, modulus);
let period = AbsI(result::Denominator);

Can be replaced with:

// New code
let (_, period) = ContinuedFractionConvergentI((numerator, denominator), modulus);
let periodAbs = AbsI(period);

DiscreteOracle type missing

The DiscreteOracle type is no longer available in QDK 1.x. Instead, functions and operations that previously used it should be refactored to accept and use controlled operations directly.

let oracle = DiscreteOracle(PrepareOracle(a, N, _, _));
QuantumPhaseEstimation(oracle, target, BigEndian(source));

Can be replaced with a direct implementation of phase estimation that takes the appropriate operations as parameters.

About

Source code for the "Introduction to Quantum Computing with Q# and QDK" book

Topics

Resources

Stars

Watchers

Forks