420 строки
17 KiB
Plaintext
420 строки
17 KiB
Plaintext
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
// Licensed under the MIT license.
|
|
|
|
namespace Quantum.Kata.RippleCarryAdder {
|
|
|
|
open Microsoft.Quantum.Intrinsic;
|
|
open Microsoft.Quantum.Canon;
|
|
open Microsoft.Quantum.Diagnostics;
|
|
open Microsoft.Quantum.Convert;
|
|
open Microsoft.Quantum.Math;
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////
|
|
// Welcome!
|
|
//////////////////////////////////////////////////////////////////
|
|
|
|
// The "Ripple Carry Adder" quantum kata is a series of exercises designed
|
|
// to get you familiar with ripple-carry addition on a quantum computer,
|
|
// walking you through the steps to build two different adders.
|
|
// It covers the following topics:
|
|
// - Adapting a classical adder to a quantum environment
|
|
// - Modifying the adder to re-use input qubits
|
|
// - An alternate, simplified quantum adder
|
|
// - A simple subtractor
|
|
// - Performing arithmetic modulo 2ᴺ
|
|
|
|
// Each task is wrapped in one operation preceded by the description of the task.
|
|
// Each task (except tasks in which you have to write a test) has a unit test associated with it,
|
|
// which initially fails. Your goal is to fill in the blank (marked with // ... comment)
|
|
// with some Q# code to make the failing test pass.
|
|
|
|
// Within each section, tasks are given in approximate order of increasing difficulty;
|
|
// harder ones are marked with asterisks.
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////
|
|
// Part I. Simple adder outputting to empty qubits
|
|
//////////////////////////////////////////////////////////////////
|
|
|
|
// This section adapts the classical binary adder to a quantum computer.
|
|
// It starts with simple sum and carry gates, and works up to an N-bit full adder.
|
|
|
|
// Task 1.1. Summation of two bits
|
|
// Inputs:
|
|
// 1) qubit "a" in an arbitrary state |φ⟩,
|
|
// 2) qubit "b" in an arbitrary state |ψ⟩,
|
|
// 3) qubit "sum" in state |0⟩.
|
|
// Goal: transform the "sum" qubit into the lowest bit of the binary sum of φ and ψ
|
|
// |0⟩ + |0⟩ → |0⟩
|
|
// |0⟩ + |1⟩ → |1⟩
|
|
// |1⟩ + |0⟩ → |1⟩
|
|
// |1⟩ + |1⟩ → |0⟩
|
|
// Any superposition should map appropriately.
|
|
// Example:
|
|
// |+⟩ = (|0⟩ + |1⟩) / sqrt(2)
|
|
// |-⟩ = (|0⟩ - |1⟩) / sqrt(2)
|
|
// |+⟩ ⨂ |-⟩ ⨂ |0⟩ → (|000⟩ + |101⟩ - |011⟩ - |110⟩) / 2
|
|
operation LowestBitSum (a : Qubit, b : Qubit, sum : Qubit) : Unit is Adj {
|
|
// ...
|
|
|
|
}
|
|
|
|
|
|
// Task 1.2. Carry of two bits
|
|
// Inputs:
|
|
// 1) qubit "a" in an arbitrary state |φ⟩,
|
|
// 2) qubit "b" in an arbitrary state |ψ⟩,
|
|
// 3) qubit "carry" in state |0⟩.
|
|
// Goal: set the "carry" qubit to |1⟩ if the binary sum of φ and ψ produces a carry, and leave it in state |0⟩ otherwise.
|
|
// |0⟩ and |0⟩ → |0⟩
|
|
// |0⟩ and |1⟩ → |0⟩
|
|
// |1⟩ and |0⟩ → |0⟩
|
|
// |1⟩ and |1⟩ → |1⟩
|
|
// Any superposition should map appropriately.
|
|
// Example:
|
|
// |+⟩ ⨂ |-⟩ ⨂ |0⟩ → (|000⟩ + |100⟩ - |010⟩ - |111⟩) / 2
|
|
operation LowestBitCarry (a : Qubit, b : Qubit, carry : Qubit) : Unit is Adj {
|
|
// ...
|
|
|
|
}
|
|
|
|
|
|
// Task 1.3. One-bit adder
|
|
// Inputs:
|
|
// 1) qubit "a" in an arbitrary state |φ⟩,
|
|
// 2) qubit "b" in an arbitrary state |ψ⟩,
|
|
// 3) two qubits "sum" and "carry" in state |0⟩.
|
|
// Goals:
|
|
// 1) transform the "sum" qubit into the lowest bit of the binary sum of φ and ψ,
|
|
// 2) transform the "carry" qubit into the carry bit produced by that sum.
|
|
operation OneBitAdder (a : Qubit, b : Qubit, sum : Qubit, carry : Qubit) : Unit is Adj {
|
|
// ...
|
|
|
|
}
|
|
|
|
|
|
// Task 1.4. Summation of 3 bits
|
|
// Inputs:
|
|
// 1) qubit "a" in an arbitrary state |φ⟩,
|
|
// 2) qubit "b" in an arbitrary state |ψ⟩,
|
|
// 3) qubit "carryin" in an arbitrary state |ω⟩,
|
|
// 4) qubit "sum" in state |0⟩.
|
|
// Goal: transform the "sum" qubit into the lowest bit of the binary sum of φ, ψ and ω.
|
|
operation HighBitSum (a : Qubit, b : Qubit, carryin : Qubit, sum : Qubit) : Unit is Adj {
|
|
// ...
|
|
|
|
}
|
|
|
|
|
|
// Task 1.5. Carry of 3 bits
|
|
// Inputs:
|
|
// 1) qubit "a" in an arbitrary state |φ⟩,
|
|
// 2) qubit "b" in an arbitrary state |ψ⟩,
|
|
// 3) qubit "carryin" in an arbitrary state |ω⟩,
|
|
// 4) qubit "carryout" in state |0⟩.
|
|
// Goal: transform the "carryout" qubit into the carry bit produced by the sum of φ, ψ and ω.
|
|
operation HighBitCarry (a : Qubit, b : Qubit, carryin : Qubit, carryout : Qubit) : Unit is Adj {
|
|
// ...
|
|
|
|
}
|
|
|
|
|
|
// Task 1.6. Two-bit adder
|
|
// Inputs:
|
|
// 1) two-qubit register "a" in an arbitrary state |φ⟩,
|
|
// 2) two-qubit register "b" in an arbitrary state |ψ⟩,
|
|
// 3) two-qubit register "sum" in state |00⟩,
|
|
// 4) qubit "carry" in state |0⟩.
|
|
// Goals:
|
|
// 1) transform the "sum" register into the binary sum of φ and ψ,
|
|
// 2) transform the "carry" qubit into the carry bit produced by that sum.
|
|
// Note: All qubit registers in this kata are in little-endian order.
|
|
// This means the least significant bit comes first, then the next least significant, and so on.
|
|
// In this exercise, for example, 1 would be represented as |10⟩, while 2 would be represented as |01⟩.
|
|
// The sum of |10⟩ and |11⟩ would be |001⟩, with the last qubit being the carry qubit.
|
|
operation TwoBitAdder (a : Qubit[], b : Qubit[], sum : Qubit[], carry : Qubit) : Unit is Adj {
|
|
// Hint: don't forget that you can allocate extra qubits.
|
|
|
|
// ...
|
|
|
|
}
|
|
|
|
|
|
// Task 1.7. N-bit adder
|
|
// Inputs:
|
|
// 1) N-qubit register "a" in an arbitrary state |φ⟩,
|
|
// 2) N-qubit register "b" in an arbitrary state |ψ⟩,
|
|
// 3) N-qubit register "sum" in state |0...0⟩,
|
|
// 4) qubit "carry" in state |0⟩.
|
|
// Goals:
|
|
// 1) transform the "sum" register into the binary sum of φ and ψ,
|
|
// 2) transform the "carry" qubit into the carry bit produced by that sum.
|
|
// Challenge: can you do this without allocating extra qubits?
|
|
operation ArbitraryAdder (a : Qubit[], b : Qubit[], sum : Qubit[], carry : Qubit) : Unit is Adj {
|
|
// ...
|
|
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////
|
|
// Part II. Simple in-place adder
|
|
//////////////////////////////////////////////////////////////////
|
|
|
|
// The adder from the previous section requires empty qubits to accept the result.
|
|
// This section adapts the previous adder to calculate the sum in-place,
|
|
// that is, to reuse one of the numerical inputs for storing the output.
|
|
|
|
// Task 2.1. In-place summation of two bits
|
|
// Inputs:
|
|
// 1) qubit "a" in an arbitrary state |φ⟩,
|
|
// 2) qubit "b" in an arbitrary state |ψ⟩.
|
|
// Goal: transform qubit "b" into the lowest bit of the sum of φ and ψ.
|
|
// Leave qubit "a" unchanged.
|
|
operation LowestBitSumInPlace (a : Qubit, b : Qubit) : Unit is Adj {
|
|
// ...
|
|
|
|
}
|
|
|
|
|
|
// Something to think about: can we re-use one of the input bits to calculate the carry in-place as well? Why or why not?
|
|
|
|
|
|
// Task 2.2. In-place one-bit adder
|
|
// Inputs:
|
|
// 1) qubit "a" in an arbitrary state |φ⟩,
|
|
// 2) qubit "b" in an arbitrary state |ψ⟩,
|
|
// 3) qubit "carry" in state |0⟩.
|
|
// Goals:
|
|
// 1) transform the "carry" qubit into the carry bit from the addition of φ and ψ,
|
|
// 2) transform qubit "b" into the lowest bit of φ + ψ.
|
|
// Leave qubit "a" unchanged.
|
|
operation OneBitAdderInPlace (a : Qubit, b : Qubit, carry : Qubit) : Unit is Adj {
|
|
// Hint: think carefully about the order of operations.
|
|
|
|
// ...
|
|
|
|
}
|
|
|
|
|
|
// Task 2.3. In-place summation of three bits
|
|
// Inputs:
|
|
// 1) qubit "a" in an arbitrary state |φ⟩,
|
|
// 2) qubit "b" in an arbitrary state |ψ⟩,
|
|
// 3) qubit "carryin" in an arbitrary state |ω⟩.
|
|
// Goal: transform qubit "b" into the lowest bit from the addition of φ and ψ and ω.
|
|
// Leave qubits "a" and "carryin" unchanged.
|
|
operation HighBitSumInPlace (a : Qubit, b : Qubit, carryin : Qubit) : Unit is Adj {
|
|
// ...
|
|
|
|
}
|
|
|
|
|
|
// Task 2.4. In-place two-bit adder
|
|
// Inputs:
|
|
// 1) two-qubit register "a" in an arbitrary state |φ⟩,
|
|
// 2) two-qubit register "b" in an arbitrary state |ψ⟩,
|
|
// 3) qubit "carry" in state |0⟩.
|
|
// Goals:
|
|
// 1) transform register "b" into the state |φ + ψ⟩,
|
|
// 2) transform the "carry" qubit into the carry bit from the addition.
|
|
// Leave register "a" unchanged.
|
|
operation TwoBitAdderInPlace (a : Qubit[], b : Qubit[], carry : Qubit) : Unit is Adj {
|
|
// ...
|
|
|
|
}
|
|
|
|
|
|
// Task 2.5. In-place N-bit adder
|
|
// Inputs:
|
|
// 1) N-qubit register "a" in an arbitrary state |φ⟩,
|
|
// 2) N-qubit register "b" in an arbitrary state |ψ⟩,
|
|
// 3) qubit "carry" in state |0⟩.
|
|
// Goals:
|
|
// 1) transform register "b" into the state |φ + ψ⟩,
|
|
// 2) transform the "carry" qubit into the carry bit from the addition.
|
|
// Leave register "a" unchanged.
|
|
operation ArbitraryAdderInPlace (a : Qubit[], b : Qubit[], carry : Qubit) : Unit is Adj {
|
|
// ...
|
|
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////
|
|
// Part III*. Improved in-place adder
|
|
//////////////////////////////////////////////////////////////////
|
|
|
|
// The in-place adder doesn't require quite as many qubits for the inputs and outputs,
|
|
// but it still requires an array of extra ("ancillary") qubits to perform the calculation.
|
|
// A relatively recent algorithm allows you to perform the same calculation
|
|
// using only one additional qubit.
|
|
|
|
// Task 3.1. Majority gate
|
|
// Inputs:
|
|
// 1) qubit "a" in an arbitrary state |φ⟩,
|
|
// 2) qubit "b" in an arbitrary state |ψ⟩,
|
|
// 3) qubit "c" in an arbitrary state |ω⟩.
|
|
// Goal: construct the "in-place majority" gate:
|
|
// 1) transform qubit "a" into the carry bit from the addition of φ, ψ and ω,
|
|
// 2) transform qubit "b" into |φ + ψ⟩,
|
|
// 3) transform qubit "c" into |φ + ω⟩.
|
|
operation Majority (a : Qubit, b : Qubit, c : Qubit) : Unit is Adj {
|
|
// ...
|
|
|
|
}
|
|
|
|
|
|
// Task 3.2. "UnMajority and Add" gate
|
|
// Inputs:
|
|
// 1) qubit "a" storing the carry bit from the sum φ + ψ + ω,
|
|
// 2) qubit "b" in state |φ + ψ⟩,
|
|
// 3) qubit "c" in state |φ + ω⟩.
|
|
// Goal: construct the "un-majority and add", or "UMA" gate
|
|
// 1) restore qubit "a" into state |φ⟩,
|
|
// 2) transform qubit "b" into state |φ + ψ + ω⟩,
|
|
// 3) restore qubit "c" into state |ω⟩.
|
|
operation UnMajorityAdd (a : Qubit, b : Qubit, c : Qubit) : Unit is Adj {
|
|
// ...
|
|
|
|
}
|
|
|
|
|
|
// Task 3.3. One-bit majority-UMA adder
|
|
// Inputs:
|
|
// 1) qubit "a" in an arbitrary state |φ⟩,
|
|
// 2) qubit "b" in an arbitrary state |ψ⟩,
|
|
// 3) qubit "carry" in state |0⟩.
|
|
// Goal: construct a one-bit binary adder from task 2.2 using Majority and UMA gates.
|
|
operation OneBitMajUmaAdder (a : Qubit, b : Qubit, carry : Qubit) : Unit is Adj {
|
|
// Hint: Allocate an extra qubit to hold the carry bit used in Majority and UMA gates during the computation.
|
|
// It's less efficient here, but it will help in the next tasks.
|
|
|
|
// ...
|
|
|
|
}
|
|
|
|
|
|
// Task 3.4. Two-bit majority-UMA adder
|
|
// Inputs:
|
|
// 1) two-qubit register "a" in an arbitrary state |φ⟩,
|
|
// 2) two-qubit register "b" in an arbitrary state |ψ⟩,
|
|
// 3) qubit "carry" in state |0⟩.
|
|
// Goal: construct a two-bit binary adder from task 2.4 using Majority and UMA gates.
|
|
operation TwoBitMajUmaAdder (a : Qubit[], b : Qubit[], carry : Qubit) : Unit is Adj {
|
|
// Hint: think carefully about which qubits you need to pass to the two gates.
|
|
|
|
// ...
|
|
|
|
}
|
|
|
|
|
|
// Task 3.5. N-bit majority-UMA adder
|
|
// Inputs:
|
|
// 1) N-qubit register "a" in an arbitrary state |φ⟩,
|
|
// 2) N-qubit register "b" in an arbitrary state |ψ⟩,
|
|
// 3) qubit "carry" in state |0⟩.
|
|
// Goal: construct an N-bit binary adder from task 2.5 using only one extra qubit.
|
|
operation ArbitraryMajUmaAdder (a : Qubit[], b : Qubit[], carry : Qubit) : Unit is Adj {
|
|
// ...
|
|
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////
|
|
// Part IV*. In-place subtractor
|
|
//////////////////////////////////////////////////////////////////
|
|
|
|
// Subtracting a number is the same operation as adding a negative number.
|
|
// As such, the binary adder we just built can be easily adapted to act as a subtractor instead.
|
|
|
|
// Task 4.1. N-bit subtractor
|
|
// Inputs:
|
|
// 1) N-qubit register "a" in an arbitrary state |φ⟩,
|
|
// 2) N-qubit register "b" in an arbitrary state |ψ⟩,
|
|
// 3) qubit "borrowBit" in state |0⟩.
|
|
// Goal: construct a binary subtractor:
|
|
// 1) transform register "b" into the state |ψ - φ⟩ ,
|
|
// 2) set the "borrowBit" qubit to |1⟩ if that subtraction required a borrow.
|
|
// Leave register "a" unchanged.
|
|
operation Subtractor (a : Qubit[], b : Qubit[], borrowBit : Qubit) : Unit is Adj {
|
|
// Hint: use the adder you already built,
|
|
// and experiment with inverting the registers before and after the addition.
|
|
|
|
// ...
|
|
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////
|
|
// Part V. Addition and subtraction modulo 2ᴺ
|
|
//////////////////////////////////////////////////////////////////
|
|
|
|
// In the previous parts we considered "normal" arithmetic, in which
|
|
// the sum of two numbers can have more bits than each of the summands.
|
|
// In these tasks we have used an extra qubit to store the "carry" or "borrow" bit (the most significant bit).
|
|
// If we want to perform our computation modulo 2ᴺ instead, in classical computing
|
|
// we can easily discard this "carry" qubit and get the result modulo 2ᴺ automatically.
|
|
// However, in quantum computing information cannot be erased that easily:
|
|
// this extra qubit is changed after the operation, and "discarding" it can affect the state of the other qubits.
|
|
// We need to modify the computation itself so that the last carry qubit is not involved at all.
|
|
// In this part we will learn to implement operations modulo 2ᴺ which do either not use this extra qubit or uncompute it after use.
|
|
|
|
// Task 5.1. Adder modulo 2ᴺ
|
|
// Inputs:
|
|
// 1) N-qubit register "a" in an arbitrary state |φ⟩,
|
|
// 2) N-qubit register "b" in an arbitrary state |ψ⟩,
|
|
// 3) N-qubit register "sum" in state |0...0⟩.
|
|
// Goal: transform the register "sum" into the state |(φ + ψ) mod 2ᴺ⟩.
|
|
// Leave registers "a" and "b" unchanged.
|
|
operation AdderModuloN (a : Qubit[], b : Qubit[], sum : Qubit[]) : Unit is Adj {
|
|
// Hint: Consider task 1.7; can you reuse the same logic here?
|
|
// Which parts of that computation you don't need in this case?
|
|
// ...
|
|
}
|
|
|
|
|
|
// Task 5.2. Two's complement
|
|
// Input: N qubit register "a" in an arbitrary state |φ⟩.
|
|
// Goal: Transform the register "a" into two's complement of φ
|
|
// (https://en.wikipedia.org/wiki/Two's_complement).
|
|
// Note that the numbers encoded in the register will range from -2ᴺ to 2ᴺ-1, inclusive.
|
|
operation TwosComplement (a : Qubit[]) : Unit is Adj {
|
|
// ...
|
|
}
|
|
|
|
|
|
// Task 5.3. Subtractor modulo 2ᴺ
|
|
// Inputs:
|
|
// 1) N-qubit register "a" in an arbitrary state |φ⟩,
|
|
// 2) N-qubit register "b" in an arbitrary state |ψ⟩,
|
|
// 3) N-qubit register "diff" in state |0...0⟩.
|
|
// Goal: transform the register "diff" into the state |(ψ - φ) mod 2ᴺ⟩.
|
|
// Leave registers "a" and "b" unchanged.
|
|
operation SubtractorModuloN (a : Qubit[], b : Qubit[], diff : Qubit[]) : Unit is Adj {
|
|
// ...
|
|
}
|
|
|
|
|
|
// Task 5.4. In-place adder modulo 2ᴺ
|
|
// Inputs:
|
|
// 1) N-qubit register "a" in an arbitrary state |φ⟩,
|
|
// 2) N-qubit register "b" in an arbitrary state |ψ⟩.
|
|
// Goal: transform the register "b" into the state |(φ + ψ) mod 2ᴺ⟩.
|
|
// Leave register "a" unchanged.
|
|
operation InPlaceAdderModuloN (a : Qubit[], b : Qubit[]) : Unit is Adj {
|
|
// ...
|
|
}
|
|
|
|
|
|
// Task 5.5. In-place subtractor modulo 2ᴺ
|
|
// Inputs:
|
|
// 1) N-qubit register "a" in an arbitrary state |φ⟩,
|
|
// 2) N-qubit register "b" in an arbitrary state |ψ⟩.
|
|
// Goal: transform the register "b" into the state |(ψ - φ) mod 2ᴺ⟩.
|
|
// Leave register a unchanged.
|
|
operation InPlaceSubtractorModuloN (a : Qubit[], b : Qubit[]) : Unit is Adj {
|
|
// ...
|
|
}
|
|
|
|
}
|