├── Chapter07 ├── bellstate.slq ├── decomposedcx.slq ├── ghz.slq ├── superdensecoding.slq ├── halfadder.slq ├── multiqubitgates.slq └── teleportation.slq ├── Chapter06 ├── geometric.slq ├── uniformsuperposition.slq ├── singlequbitgates.slq └── superposition.slq ├── Chapter09 ├── helpers │ ├── groverDiffusion.slq │ └── rand.slq ├── simon.slq ├── groverSimple.slq ├── groverMultiple.slq └── groverUnknown.slq ├── Chapter10 ├── qft.slq └── phaseEstimation.slq ├── LICENSE ├── Chapter08 ├── bernsteinVazirani.slq └── deutschJozsa.slq ├── Chapter11 ├── bitFlipCode.slq ├── phaseFlipCode.slq └── shorCode.slq ├── README.md └── Chapter12 └── qkd.slq /Chapter07/bellstate.slq: -------------------------------------------------------------------------------- 1 | def main() { 2 | return BellState(); 3 | } 4 | 5 | def BellState(){ 6 | b:=1:𝔹; 7 | b:=H(b); 8 | 9 | c:=1:𝔹; 10 | if b{ 11 | c := X(c); 12 | } 13 | 14 | return (b,c); 15 | 16 | } -------------------------------------------------------------------------------- /Chapter07/decomposedcx.slq: -------------------------------------------------------------------------------- 1 | def main() { 2 | return DecomposedCX(); 3 | } 4 | 5 | def DecomposedCX(){ 6 | b:=1:𝔹; 7 | b:=H(b); 8 | 9 | a:=1:𝔹; 10 | if a{ 11 | b := Z(b); 12 | } 13 | 14 | b:=H(b); 15 | 16 | return b; 17 | } -------------------------------------------------------------------------------- /Chapter06/geometric.slq: -------------------------------------------------------------------------------- 1 | def main() { 2 | return geometric(); 3 | } 4 | 5 | def geometric():!ℕ{ 6 | count := 0; 7 | ok := true; 8 | while ok{ 9 | count += 1; 10 | ok = measure(H(false)); 11 | } 12 | return count; 13 | } -------------------------------------------------------------------------------- /Chapter09/helpers/groverDiffusion.slq: -------------------------------------------------------------------------------- 1 | // Grover Diffusion Operator 2 | 3 | def groverDiffusion[n:!ℕ](cand:uint[n])mfree: uint[n]{ 4 | for k in [0..n) { cand[k] := H(cand[k]); } 5 | if cand!=0{ phase(π); } 6 | for k in [0..n) { cand[k] := H(cand[k]); } 7 | return cand; 8 | } 9 | -------------------------------------------------------------------------------- /Chapter07/ghz.slq: -------------------------------------------------------------------------------- 1 | def main() { 2 | return GHZ(); 3 | } 4 | 5 | def GHZ(){ 6 | a:=0:𝔹; 7 | b:=0:𝔹; 8 | c:=0:𝔹; 9 | 10 | a:=H(a); 11 | 12 | if a{ 13 | b := X(b); 14 | } 15 | 16 | if b{ 17 | c := X(c); 18 | } 19 | 20 | return (a,b,c); 21 | 22 | } -------------------------------------------------------------------------------- /Chapter06/uniformsuperposition.slq: -------------------------------------------------------------------------------- 1 | def main() { 2 | return UniformSuperposition[1](); 3 | } 4 | 5 | def UniformSuperposition[n:!ℕ]():𝔹^n{ 6 | qubits := vector(n,0:𝔹); // vector of length n filled with zeros 7 | for i in [0..n){ 8 | qubits[i] := H(qubits[i]); 9 | } 10 | return qubits; 11 | } -------------------------------------------------------------------------------- /Chapter06/singlequbitgates.slq: -------------------------------------------------------------------------------- 1 | def main() { 2 | return Hadamard(); 3 | } 4 | 5 | def Hadamard() { 6 | x:=0:𝔹; 7 | return H(x); 8 | } 9 | 10 | def PauliZ() { 11 | z:=1:𝔹; 12 | return Z(z); 13 | } 14 | 15 | def PauliY() { 16 | y:=1:𝔹; 17 | return Y(y); 18 | } 19 | 20 | def PauliX() { 21 | x:=0:𝔹; 22 | return X(x); 23 | } -------------------------------------------------------------------------------- /Chapter10/qft.slq: -------------------------------------------------------------------------------- 1 | // Quantum Fourier Transform 2 | 3 | 4 | def QFT[n:!ℕ](ψ: int[n])mfree: int[n]{ 5 | for k in [0..n div 2){ 6 | (ψ[k],ψ[n-k-1]) := (ψ[n-k-1],ψ[k]); 7 | } 8 | for k in [0..n){ 9 | ψ[k] := H(ψ[k]); 10 | for l in [k+1..n){ 11 | if ψ[l] && ψ[k]{ 12 | phase(2*π * 2^(k-l-1)); 13 | } 14 | } 15 | } 16 | return ψ; 17 | } 18 | 19 | 20 | def main(){ 21 | ψ := 2:int[2]; 22 | return QFT(ψ); 23 | } 24 | -------------------------------------------------------------------------------- /Chapter07/superdensecoding.slq: -------------------------------------------------------------------------------- 1 | def main() { 2 | return SuperDenseCoding(); 3 | } 4 | 5 | def SuperDenseCoding(){ 6 | a:=0:𝔹; 7 | b:=0:𝔹; 8 | 9 | // Bell State Preparation 10 | a:=H(a); 11 | if a{ 12 | b := X(b); 13 | } 14 | 15 | // Alice's Operation - 11 is sent 16 | a:=Z(a); 17 | a:=X(a); 18 | 19 | // Bob's Operation 20 | if a{ 21 | b := X(b); 22 | } 23 | 24 | a:=H(a); 25 | 26 | return(a,b); 27 | } -------------------------------------------------------------------------------- /Chapter07/halfadder.slq: -------------------------------------------------------------------------------- 1 | def main() { 2 | return HalfAdder(); 3 | } 4 | 5 | def HalfAdder(){ 6 | // Define 4 Qubits - a & b inputs, s & c are sum and carry - outputs 7 | a:=1:𝔹; 8 | b:=1:𝔹; 9 | s:=0:𝔹; 10 | c:=0:𝔹; 11 | 12 | // XOR Gate Operation 13 | 14 | // CX operation on b and s qubit 15 | if b{ 16 | s := X(s); 17 | } 18 | 19 | // CX operation on a and s 20 | if a{ 21 | s := X(s); 22 | } 23 | 24 | // AND Gate Operation 25 | 26 | if a && b{ 27 | c := X(c); 28 | } 29 | 30 | return (s,c); 31 | } -------------------------------------------------------------------------------- /Chapter09/helpers/rand.slq: -------------------------------------------------------------------------------- 1 | // Random number generators 2 | 3 | def uniformInt(range:!ℕ){ 4 | // returns x~{0,...range-1} 5 | n:=ceil(log(range)/log(2)) coerce !ℕ; 6 | r:=range; 7 | while (r>range-1) { 8 | // rerolls r if it is greater than range 9 | r=0; 10 | for k in [0..n){ 11 | // rolls each bit of r 12 | r+=2^k*rand(); 13 | } 14 | } 15 | return r; 16 | } 17 | 18 | def rand(){ 19 | // quantum number generator 20 | return measure(H(false)); 21 | } 22 | 23 | -------------------------------------------------------------------------------- /Chapter07/multiqubitgates.slq: -------------------------------------------------------------------------------- 1 | def CSWAP(x:𝔹, y:𝔹, z:𝔹) { 2 | if x{ 3 | a:=z; 4 | z:=y; 5 | y:=a; 6 | } 7 | return (x,y,z); 8 | } 9 | 10 | def SWAP(x:𝔹, y:𝔹) { 11 | a:=y; 12 | y:=x; 13 | x:=a; 14 | return (x,y); 15 | } 16 | 17 | def CCX(const x:𝔹,const y:𝔹,z:𝔹):𝔹{ 18 | if x && y{ 19 | z := X(z); 20 | } 21 | return (z); 22 | } 23 | 24 | def CZ(const x:𝔹,y:𝔹):𝔹{ 25 | if x{ 26 | y := Z(y); 27 | } 28 | return y; 29 | } 30 | 31 | def CX(const x:𝔹,y:𝔹):𝔹{ 32 | if x{ 33 | y := X(y); 34 | } 35 | return y; 36 | } -------------------------------------------------------------------------------- /Chapter10/phaseEstimation.slq: -------------------------------------------------------------------------------- 1 | // Phase Estimation algorithm 2 | 3 | import qft; 4 | 5 | def phaseEstimation[k:!ℕ]( 6 | U:int[k] !->mfree int[k], 7 | u:int[k], 8 | precision:!ℕ) { 9 | 10 | ancilla := 0:int[precision]; 11 | for i in [0..precision) { ancilla[i] := H(ancilla[i]); } 12 | 13 | for i in [0..precision) { 14 | if ancilla[i] { 15 | for l in [0..2^i) { 16 | u := U(u); 17 | } 18 | } 19 | } 20 | 21 | ancilla := reverse(QFT[precision])(ancilla); 22 | result := measure(ancilla); 23 | measure(u); 24 | return result; 25 | } 26 | -------------------------------------------------------------------------------- /Chapter07/teleportation.slq: -------------------------------------------------------------------------------- 1 | def main() { 2 | return Teleportation(); 3 | } 4 | 5 | def Teleportation(){ 6 | 7 | // Initialize Qubits - 'a' qubit is to be teleported by Alice to Bob 8 | a:=0:𝔹; 9 | b:=0:𝔹; 10 | c:=0:𝔹; 11 | 12 | // Alice's Operations 13 | 14 | // Creating the Bell State 15 | b:=H(b); 16 | 17 | if b{ 18 | c := X(c); 19 | } 20 | 21 | // Alice applies CX and H to 'a' qubit 22 | 23 | if a{ 24 | b := X(b); 25 | } 26 | 27 | a:=H(a); 28 | 29 | // Alice measures her qubits 'a' and 'b' 30 | 31 | ma1:=measure(a); 32 | ma2:=measure(b); 33 | print(ma1); 34 | print(ma2); 35 | 36 | // Bob's measurement 37 | 38 | //c:=X(c); 39 | //c:=Z(c); 40 | 41 | return (c); 42 | } -------------------------------------------------------------------------------- /Chapter09/simon.slq: -------------------------------------------------------------------------------- 1 | // Simon's algorithm 2 | 3 | def main() { 4 | return Simon(); 5 | } 6 | 7 | def Simon(){ 8 | // Initializing Qubits for Inputs - a,b,c and Oracle - d,e,f 9 | a:=0:𝔹; 10 | b:=0:𝔹; 11 | c:=0:𝔹; 12 | d:=0:𝔹; 13 | e:=0:𝔹; 14 | f:=0:𝔹; 15 | 16 | // Applying Hadamard to Inputs 17 | a:=H(a); 18 | b:=H(b); 19 | c:=H(c); 20 | 21 | // Encoding 011 secret key in the Oracle 22 | 23 | if b{ 24 | d := X(d); 25 | } 26 | 27 | if b{ 28 | e := X(e); 29 | } 30 | 31 | if c{ 32 | d := X(d); 33 | } 34 | 35 | if b{ 36 | f := X(f); 37 | } 38 | 39 | if c{ 40 | e := X(e); 41 | } 42 | 43 | if c{ 44 | f := X(f); 45 | } 46 | 47 | // Applying Hadamard to Inputs 48 | 49 | a:=H(a); 50 | b:=H(b); 51 | c:=H(c); 52 | 53 | // Measure the d,e,f qubits for variable consumption 54 | 55 | md:=measure(d); 56 | me:=measure(e); 57 | mf:=measure(f); 58 | 59 | return (a,b,c,md,me,mf); 60 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Packt 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Chapter09/groverSimple.slq: -------------------------------------------------------------------------------- 1 | // Grover's algorithm for a single solution 2 | // - Returns the only x for which f(x) = 1 3 | // 4 | // - More detailed description: https://www.scottaaronson.com/qclec/22.pdf 5 | 6 | import helpers.groverDiffusion; 7 | 8 | def grover[n:!ℕ](f: const uint[n] !→ lifted 𝔹):!ℕ{ 9 | nIterations:= round(π / 4 * sqrt(2^n)); 10 | cand:=0:uint[n]; 11 | for k in [0..n) {cand[k] := H(cand[k]);} 12 | 13 | for k in [0..nIterations){ 14 | if f(cand){ 15 | phase(π); 16 | } 17 | // state ignoring normalization: 18 | // ∑(v≠w)|v⟩ - |w*⟩ 19 | cand:=groverDiffusion(cand); 20 | // ∑(v≠w)γ₋|v⟩ + γ₊|w*⟩ 21 | } 22 | return measure(cand) as !ℕ; 23 | } 24 | 25 | /* EXAMPLE CALL */ 26 | 27 | def main() { 28 | f := λ(x:uint[5])lifted:𝔹{return x==3;}; // creates an oracle which outputs one only when x=3 29 | x := grover(f); 30 | assert(x==3); // verifies that grover finds the right solution 31 | 32 | return x; 33 | } 34 | 35 | /* TEST */ 36 | 37 | // This function defines a test for Grover 38 | def test_grover() { 39 | def f(x:uint[3])lifted:𝔹{ 40 | return x==7; 41 | } // creates an oracle which outputs one only when x=3 42 | x := grover(f); 43 | // verifies that grover finds the right solution 44 | assert(x==7); 45 | 46 | return x; 47 | } 48 | 49 | 50 | -------------------------------------------------------------------------------- /Chapter08/bernsteinVazirani.slq: -------------------------------------------------------------------------------- 1 | // Bernstein-Vazirani algorithm 2 | // - Returns s where f(x) = s·x mod 2 3 | // 4 | 5 | def bernstein_vazirani[n:!ℕ](f: const uint[n] !→ lifted 𝔹):!uint[n]{ 6 | cand := 0:uint[n]; 7 | for k in [0..n) { cand[k] := H(cand[k]); } 8 | 9 | // state ignoring normalization: 10 | // ∑ᵥ|v⟩ 11 | 12 | if f(cand) { 13 | phase(π); 14 | } 15 | 16 | // state ignoring normalization: 17 | // ∑ᵥ(-1)^f(v) |v⟩ 18 | // = ∑ᵥ(-1)^(s·v) |v⟩ 19 | // = (|0⟩+(-1)^(s₁)|1⟩) ⊗ ⋯ ⊗ (|0⟩+(-1)^(sₙ)|1⟩) 20 | 21 | for k in [0..n) { cand[k] := H(cand[k]); } 22 | 23 | s := measure(cand); 24 | return s; 25 | } 26 | 27 | /* TEST */ 28 | 29 | def f[n:!ℕ](s:!uint[n])(x:uint[n])lifted:𝔹{ 30 | y := scal(s, x)%2; 31 | return y==1; 32 | } 33 | 34 | def scal[n:!ℕ](const x:uint[n], const y:uint[n])qfree:uint[n] { 35 | // computes the scalar product x·y 36 | count := 0:uint[n]; 37 | for k in [0..n) { 38 | count+=x[k] && y[k]; 39 | } 40 | return count; 41 | } 42 | 43 | def main() { 44 | // test with all secret strings on 3 bits 45 | for i in [0..8) { 46 | s := i coerce !uint[3]; 47 | s₀ := bernstein_vazirani(f(s)); 48 | assert(s==s₀); 49 | } 50 | 51 | // test with secret string s = 01 52 | s := 1 coerce !uint[2]; 53 | s₀ := bernstein_vazirani(f(s)); 54 | return s₀; 55 | 56 | } 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /Chapter11/bitFlipCode.slq: -------------------------------------------------------------------------------- 1 | // Bit-Flip code (Quantum error correction) 2 | // - Returns the qubit ψ where ψ is encoded to a 3-qubit state and then 3 | // - passed through a noisy channel potentially flipping one of the qubits 4 | 5 | 6 | def encode(ψ:𝔹) mfree { // encode a single qubit ψ into a 3-qubit state 7 | ψ := (dup(ψ), dup(ψ), ψ); 8 | 9 | return ψ; 10 | } 11 | 12 | def correct(ψ:𝔹^3) { // correct a single bit-flip error if there is one 13 | // measure the error syndrome 14 | p1 := measure(ψ[0] ⊕ ψ[1]); 15 | p2 := measure(ψ[0] ⊕ ψ[2]); 16 | 17 | // apply the appropriate correction 18 | if (p1 && p2) { 19 | ψ[0] := X(ψ[0]); 20 | } else if (p1) { 21 | ψ[1] := X(ψ[1]); 22 | } else if (p2) { 23 | ψ[2] := X(ψ[2]); 24 | } 25 | 26 | return ψ; 27 | } 28 | 29 | def bit_flip_code(ψ: 𝔹, channel: 𝔹^3 !→ 𝔹^3) { // simulate the bit-flip code error-correcting process 30 | ψ := encode(ψ); 31 | ψ := channel(ψ); 32 | ψ := correct(ψ); 33 | ψ := reverse(encode)(ψ); 34 | 35 | return ψ; 36 | } 37 | 38 | /* EXAMPLES */ 39 | 40 | def channel(ψ:𝔹^3) { // flip the first qubit of a 3-qubit state 41 | ψ[0] := X(ψ[0]); 42 | return ψ; 43 | } 44 | 45 | def main() { 46 | // Example 1: basis state 47 | ψ := 1:𝔹; 48 | φ := bit_flip_code(ψ, channel); 49 | // verifies that φ = 1 50 | forget(φ = 1); 51 | 52 | // Example 2: superposition 53 | ψ := H(1:𝔹); 54 | φ := bit_flip_code(ψ, channel); 55 | // verifies that φ = H(|1>) 56 | return φ; 57 | } 58 | 59 | 60 | -------------------------------------------------------------------------------- /Chapter08/deutschJozsa.slq: -------------------------------------------------------------------------------- 1 | // Deutsch-Jozsa algorithm 2 | // - Returns : | 0 if f is balanced (#{x, f(x)=0} = #{x, f(x)=1}) 3 | // | 1 if f is constant (∀x f(x)=0 or ∀x f(x)=1) 4 | 5 | 6 | def deutsch_jozsa[n:!ℕ](f: const int[n] !→ lifted 𝔹):!𝔹{ 7 | cand := 0:int[n]; 8 | for k in [0..n) { cand[k] := H(cand[k]); } 9 | 10 | // state ignoring normalization: 11 | // ∑ᵥ|v⟩ 12 | 13 | if f(cand) { 14 | phase(π); 15 | } 16 | 17 | // state ignoring normalization: 18 | // ∑ᵥ(-1)^f(v)|v⟩ 19 | 20 | for k in [0..n) { cand[k] := H(cand[k]); } 21 | 22 | // state ignoring normalization: 23 | // ∑ᵥ(-1)^f(v) ∑ᵤ (-1)^(u·v)|u⟩ 24 | 25 | result := measure(cand); 26 | // probability to measure 0 is: 27 | // - 1 if f constant (constructive interference) 28 | // - 0 if f balanced (destructive interference) 29 | return result==0; 30 | } 31 | 32 | /* TEST */ 33 | 34 | def test_balanced() { 35 | def balanced(x:int[2])lifted:𝔹{ 36 | if (x[0]==1) { 37 | return 1:𝔹; 38 | } 39 | else { 40 | return 0:𝔹; 41 | } 42 | } // implements a balanced function (outputs half 0 and half 1) 43 | x := deutsch_jozsa(balanced); 44 | assert(x == 0); 45 | return x; 46 | } 47 | 48 | def test_constant() { 49 | def constant(x:int[2])lifted:𝔹{ 50 | return 1:𝔹; 51 | } // implements a constant function (outputs only 1) 52 | x := deutsch_jozsa(constant); 53 | assert(x == 1); 54 | return x; 55 | } 56 | 57 | def main() { 58 | print(test_balanced()); // DJ on balanced function should output 0 59 | print(test_constant()); // DJ on constant function should output 1 60 | return; 61 | } 62 | -------------------------------------------------------------------------------- /Chapter11/phaseFlipCode.slq: -------------------------------------------------------------------------------- 1 | // Phase-Flip code (Quantum error correction) 2 | // - Returns the qubit ψ where ψ is encoded to a 3-qubit state and then 3 | // - passed through a noisy channel potentially flipping the phase of one of the qubits 4 | 5 | 6 | def encode(ψ:𝔹) mfree { // encode a single qubit ψ into a 3-qubit state 7 | ψ := (dup(ψ), dup(ψ), ψ); 8 | 9 | return ψ;} 10 | 11 | def correct(ψ:𝔹^3) { // correct a single bit-flip error if there is one 12 | // measure the error syndrome 13 | p1 := measure(ψ[0] ⊕ ψ[1]); 14 | p2 := measure(ψ[0] ⊕ ψ[2]); 15 | 16 | // apply the appropriate correction 17 | if (p1 && p2) { 18 | ψ[0] := X(ψ[0]); 19 | } else if (p1) { 20 | ψ[1] := X(ψ[1]); 21 | } else if (p2) { 22 | ψ[2] := X(ψ[2]); 23 | } 24 | 25 | return ψ; 26 | } 27 | 28 | def phase_flip_code(ψ: 𝔹, channel: 𝔹^3 !→ 𝔹^3) { // simulate the phase-flip code error-correcting process 29 | ψ := encode(ψ); 30 | 31 | for k in [0..3) {ψ[k] := H(ψ[k]);} 32 | ψ := channel(ψ); 33 | for k in [0..3) {ψ[k] := H(ψ[k]);} 34 | 35 | ψ := correct(ψ); 36 | ψ := reverse(encode)(ψ); 37 | 38 | return ψ; 39 | } 40 | 41 | /* EXAMPLES */ 42 | 43 | def channel(ψ:𝔹^3) { // flip the phase of the second qubit of a 3-qubit state 44 | ψ[1] := Z(ψ[1]); 45 | return ψ; 46 | } 47 | 48 | def main() { 49 | // Example 1: basis state 50 | ψ := 0:𝔹; 51 | φ := phase_flip_code(ψ, channel); 52 | // verifies that φ = 0 53 | forget(φ = 0); 54 | 55 | // Example 2: superposition 56 | ψ := H(0:𝔹); 57 | φ := phase_flip_code(ψ, channel); 58 | // verifies that φ = H(|0>) 59 | return φ; 60 | } 61 | 62 | 63 | -------------------------------------------------------------------------------- /Chapter09/groverMultiple.slq: -------------------------------------------------------------------------------- 1 | // Grover's algorithm for a known number (M) of solutions 2 | // - Returns one of the x for which f(x) = 1 3 | // 4 | // - More detailed description: https://arxiv.org/ftp/arxiv/papers/0705/0705.4171.pdf 5 | 6 | 7 | import helpers.groverDiffusion; 8 | 9 | def grover_multiple[n:!ℕ](f: const uint[n] !→ lifted 𝔹, M:!ℕ):!ℕ{ 10 | nIterations:= round((π/4) * sqrt(2^n/M)); 11 | cand:=0:uint[n]; 12 | for k in [0..n) { cand[k] := H(cand[k]); } 13 | 14 | for k in [0..nIterations){ 15 | if f(cand){ 16 | phase(π); 17 | } 18 | cand:=groverDiffusion(cand); 19 | } 20 | return measure(cand) as !ℕ; 21 | } 22 | 23 | /* EXAMPLE CALL */ 24 | 25 | def main(){ 26 | f := λ(x:uint[6])lifted:𝔹{ return x==4 || x==5 || x==6; }; 27 | // creates an oracle which outputs one only when x is in {4,5,6} 28 | 29 | x := grover_multiple(f, 3); 30 | 31 | assert(x==4 || x==5 || x==6); 32 | // verifies that grover_multiple finds one of the right solutions 33 | 34 | return x; 35 | } 36 | 37 | /* TEST */ 38 | 39 | // This function defines tests for Grover with respectively 2, 3 and 4 solutions 40 | def test_grover_multiple() { 41 | n := 6; 42 | def f2(x:uint[n])lifted:𝔹{ 43 | return x==2 || x==3; 44 | } 45 | def f3(x:uint[n])lifted:𝔹{ 46 | return x==4 || x==5 || x==6; 47 | } 48 | def f4(x:uint[n])lifted:𝔹{ 49 | return x==7 || x==8 || x==9 || x==10; 50 | } // creates oracles with respectively 2, 3 and 4 solutions 51 | x := grover_multiple(f2, 2); 52 | y := grover_multiple(f3, 3); 53 | z := grover_multiple(f4, 4); 54 | // verifies that grover_multiple finds one of the right solutions 55 | assert(x==2 || x==3); 56 | assert(y==4 || y==5 || y==6); 57 | assert(z==7 || z==8 || z==9 || z==10); 58 | } 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /Chapter09/groverUnknown.slq: -------------------------------------------------------------------------------- 1 | // Grover's algorithm for an unknown number of solutions 2 | // - Returns one of the x for which f(x) = 1 3 | // 4 | // - More detailed description: https://arxiv.org/pdf/1709.01236.pdf 5 | 6 | import helpers.groverDiffusion; 7 | import helpers.rand; 8 | 9 | def grover_unknown[n:!ℕ](f: const uint[n] !→ lifted 𝔹):!ℕ{ 10 | m := 1:!ℚ; 11 | l := 6/5; 12 | 13 | while (m <= 2^(n/2)) { 14 | nIterations := uniformInt(floor(m) coerce !ℕ) + 1; 15 | 16 | cand := 0:uint[n]; 17 | for k in [0..n) {cand[k] := H(cand[k]);} 18 | 19 | for k in [0..nIterations){ 20 | if f(cand){ 21 | phase(π); 22 | } 23 | cand:=groverDiffusion(cand); 24 | } 25 | 26 | x := measure(cand); 27 | 28 | if f(x) {return x as !ℕ;} 29 | else {m=l*m;} 30 | } 31 | 32 | return 0; 33 | } 34 | 35 | /* EXAMPLE CALL */ 36 | 37 | def main(){ 38 | f := λ(x:uint[5])lifted:𝔹{ return x==1 || x==2 || x==5 || x==8; }; 39 | // creates an oracle which outputs one only when x is in {1,2,5,8} 40 | 41 | x := grover_unknown(f); 42 | 43 | assert(x==1 || x==2 || x==5 || x==8); 44 | // verifies that grover_unknown finds one of the right solutions 45 | 46 | return x; 47 | } 48 | 49 | 50 | 51 | /* TEST */ 52 | 53 | // This function defines tests for Grover with respectively 1, 2, 3 and 4 solutions 54 | def test_grover_unknown() { 55 | n := 5; 56 | def f1(x:uint[n])lifted:𝔹{ 57 | return x==1; 58 | } 59 | def f2(x:uint[n])lifted:𝔹{ 60 | return x==2 || x==3; 61 | } 62 | def f3(x:uint[n])lifted:𝔹{ 63 | return x==4 || x==5 || x==6; 64 | } 65 | def f4(x:uint[n])lifted:𝔹{ 66 | return x==7 || x==8 || x==9 || x==10; 67 | } 68 | // creates oracles with respectively 1, 2, 3 and 4 solutions 69 | x := grover_unknown(f1); 70 | y := grover_unknown(f2); 71 | z := grover_unknown(f3); 72 | w := grover_unknown(f4); 73 | // verifies that grover_unknown finds one of the right solutions 74 | assert(x==1); 75 | assert(y==2 || y==3); 76 | assert(z==4 || z==5 || z==6); 77 | assert(w==7 || w==8 || w==9 || w==10); 78 | } 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /Chapter11/shorCode.slq: -------------------------------------------------------------------------------- 1 | // Shor code (Quantum error correction) 2 | // - Returns the qubit ψ where ψ is encoded to a 9-qubit state and then 3 | // - passed through a noisy channel potentially altering the state of one of the qubits 4 | 5 | 6 | // Helper function 7 | 8 | def applyToEach[τ,τ'](triple:τ^3, f: τ !→ τ') { // apply a function to each element of a triple 9 | (h0,h1,h2) := triple; 10 | fTriple := (f(h0),f(h1),f(h2)); 11 | 12 | return fTriple; 13 | } 14 | 15 | 16 | def triplicate(ψ:𝔹) mfree { // entangle a qubit with two duplicates 17 | ψ := (dup(ψ), dup(ψ), ψ); 18 | 19 | return ψ; 20 | } 21 | 22 | def encode(ψ:B) { // encode a single qubit ψ into a 9-qubit state 23 | ψ := triplicate(ψ); 24 | ψ := applyToEach(ψ, H); 25 | ψ := applyToEach(ψ, triplicate); 26 | 27 | return ψ; 28 | } 29 | 30 | def correct(ψ:𝔹^3) { // correct a single bit-flip error if there is one 31 | // measure the error syndrome 32 | a1 := measure(ψ[0] ⊕ ψ[1]); 33 | a2 := measure(ψ[0] ⊕ ψ[2]); 34 | 35 | // apply the appropriate correction 36 | if (a1 && a2) { 37 | ψ[0] := X(ψ[0]); 38 | } else if (a1) { 39 | ψ[1] := X(ψ[1]); 40 | } else if (a2) { 41 | ψ[2] := X(ψ[2]); 42 | } 43 | 44 | return ψ; 45 | } 46 | 47 | def correctBitFlip(ψ:(B^3)^3) { // correct a single bit-flip error in each triplet of a triplet of triplets 48 | ψ := applyToEach(ψ, correct); 49 | 50 | return ψ; 51 | } 52 | 53 | def correctPhaseFlip(ψ:(B^3)^3) { // correct a single phase-flip error 54 | ψ := applyToEach(ψ, reverse(triplicate)); 55 | ψ := applyToEach(ψ, H); 56 | ψ := correct(ψ); 57 | 58 | return ψ 59 | } 60 | 61 | def shor_code(ψ:B, channel: (B^3)^3 !→ (B^3)^3){ // simulate the Shor code error-correcting process 62 | ψ := encode(ψ); 63 | 64 | ψ := channel(ψ); 65 | 66 | ψ := correctBitFlip(ψ); 67 | ψ := correctPhaseFlip(ψ); 68 | 69 | ψ := reverse(triplicate)(ψ); 70 | 71 | return ψ; 72 | } 73 | 74 | /* EXAMPLES */ 75 | 76 | def channel(ψ:(𝔹^3)^3) { // flip both one qubit and its phase in a 3x3 structure 77 | ψ[0][1] := Z(ψ[0][1]); 78 | ψ[0][1] := X(ψ[0][1]); 79 | return ψ; 80 | } 81 | 82 | def main() { 83 | // Example 1: basis state 84 | ψ := 1:𝔹; 85 | φ := shor_code(ψ, channel); 86 | // verifies that φ = 1 87 | forget(φ=1); 88 | 89 | // Example 2: superposition 90 | ψ := H(1:𝔹); 91 | φ := shor_code(ψ, channel); 92 | // verifies that φ = H(|1>) 93 | return φ; 94 | } -------------------------------------------------------------------------------- /Chapter06/superposition.slq: -------------------------------------------------------------------------------- 1 | // Superposition generation 2 | // - Generates the (normalized) superposition |0⟩ + ... + |N-1⟩ on k qubits 3 | // 4 | // - Requires N < 2^k 5 | 6 | def superposition[k:!ℕ](N:!ℕ, qs:𝔹^k)mfree:𝔹^k{ 7 | // generates the (normalized) superposition |0⟩ + ... + |N-1⟩ on k qubits 8 | 9 | assert(N <= 2^k); 10 | // ensures that the biggest state |N-1⟩ requires less than k qubits to be written 11 | 12 | n := floor(log(N)/log(2)) coerce !ℕ; 13 | r := N - 2^n coerce !ℕ; 14 | // decomposition N = 2^n + r 15 | 16 | // rotate first qubit 17 | (head,)~tail := qs; 18 | θ := 2*asin(sqrt(r)/sqrt(N)); 19 | // sin(θ/2) = sqrt(r/N), cos(θ/2) = sqrt((N-r)/N) = sqrt(2^n/N) 20 | 21 | head := rotY(θ, head); 22 | // |0⟩ ↦ cos(θ/2)|0⟩ + sin(θ/2)|1⟩ = sqrt(2^n/N) |0⟩ + sqrt(r/N)|1⟩ 23 | 24 | // state : 25 | // (sqrt(2^n)/sqrt(N))|0⟩⊗(|0⟩⊗ ⋯ ⊗|0⟩) + (sqrt(r)/sqrt(N))|1⟩⊗(|0⟩⊗ ⋯ ⊗|0⟩) 26 | 27 | // conditionally on first qubit either prepare 28 | // the uniform distribution on n qubits or 29 | // (recursively) the remainder |0⟩ + ... + |r-1⟩ 30 | if head { 31 | tail := superposition(r, tail); // (|0⟩ + ... + |r-1⟩)/sqrt(r) 32 | } 33 | else { 34 | for i in [0..n) { tail[i] := H(tail[i]); } // ∑ᵥ|v⟩/sqrt(2^n) 35 | } 36 | 37 | // tail = (x₁, ..., xₙ, xₙ₊₁, ..., xₖ) and 38 | // head (= x₀) is the MSB of (x₁, ..., xₙ, x₀) 39 | for i in [0..n div 2) { (tail[i], tail[n-1-i]) := (tail[n-1-i], tail[i]); } 40 | // tail = (xₙ, ..., x₁, xₙ₊₁, ..., xₖ) 41 | 42 | qs:=(head,)~tail; 43 | // qs = (x₀, xₙ, ..., x₁, xₙ₊₁, ..., xₖ) 44 | 45 | for i in [0..(n+1) div 2) { (qs[i], qs[n-i]) := (qs[n-i], qs[i]); } 46 | // qs = (x₁, ..., xₙ, x₀, xₙ₊₁, ..., xₖ) 47 | 48 | // state : 49 | // (sqrt(2^n)/sqrt(N))|0⟩⊗(∑ᵥ|v⟩/sqrt(2^n)) + 50 | // (sqrt(r)/sqrt(N))|1⟩⊗((|0⟩ + ... + |r-1⟩)/sqrt(r) 51 | // = (1/sqrt(N))(|0⟩ + ... + |N-1⟩) 52 | 53 | return qs; 54 | } 55 | 56 | /* TEST */ 57 | 58 | def test_superposition() { 59 | k := 5; 60 | qs := vector(k, 0:𝔹); 61 | qs := superposition(19, qs); 62 | measure(qs); 63 | } 64 | 65 | /* EXAMPLE CALL */ 66 | 67 | def main() { 68 | N := 19; 69 | // generates the normalized superposition |0⟩ + ... + |18⟩ on 5 qubits 70 | qs := superposition(N, vector(5, 0:𝔹)) as uint[5]; 71 | 72 | // measures 0 <= n < 19 73 | n := measure(qs); 74 | assert(n < N); 75 | return(n); 76 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | # Quantum Computing with Silq Programming 5 | 6 | Quantum Computing with Silq Programming 7 | 8 | This is the code repository for [Quantum Computing with Silq Programming](https://www.packtpub.com/in/programming/quantum-computing-with-silq-programming?utm_source=github&utm_medium=repository&utm_campaign=9781800569669), published by Packt. 9 | 10 | **Get up and running with quantum computing with the simplicity of this new high-level programming language** 11 | 12 | ## What is this book about? 13 | Quantum computing is a growing field, with many research projects focusing on programming quantum computers in the most efficient way possible. One of the biggest challenges faced with existing languages is that they work on low-level circuit model details and are not able to represent quantum programs accurately. Developed by researchers at ETH Zurich after analyzing languages including Q# and Qiskit, Silq is a high-level programming language that can be viewed as the C++ of quantum computers! Quantum Computing with Silq Programming helps you explore Silq and its intuitive and simple syntax to enable you to describe complex tasks with less code. 14 | 15 | This book covers the following exciting features: 16 | Identify the challenges that researchers face in quantum programming 17 | Understand quantum computing concepts and learn how to make quantum circuits 18 | Explore Silq programming constructs and use them to create quantum programs 19 | Use Silq to code quantum algorithms such as Grover's and Simon’s 20 | Discover the practicalities of quantum error correction with Silq 21 | Explore useful applications such as quantum machine learning in a practical way 22 | 23 | If you feel this book is for you, get your [copy](https://www.amazon.com/dp/1800569661) today! 24 | 25 | https://www.packtpub.com/ 27 | 28 | ## Instructions and Navigations 29 | All of the code is organized into folders. For example, Chapter02. 30 | 31 | The code will look like the following: 32 | ``` 33 | def geometric():!?? { 34 | count := 0; 35 | ok := true; 36 | while ok{ 37 | count += 1; 38 | ``` 39 | 40 | **Following is what you need for this book:** 41 | This Silq quantum computing book is for students, researchers, and scientists looking to learn quantum computing techniques and software development. Quantum computing enthusiasts who want to explore this futuristic technology will also find this book useful. Beginner-level knowledge of any programming language as well as mathematical topics such as linear algebra, probability, complex numbers, and statistics is required. 42 | 43 | With the following software and hardware list you can run all code files present in the book (Chapter 1-13). 44 | ### Software and Hardware List 45 | | No. | Software required | OS required | 46 | | -------- | ------------------------------------ | ----------------------------------- | 47 | | 1 | Visual Studio with Silq plugin | Windows, Mac OS X, and Linux (Any) | 48 | 49 | 50 | We also provide a PDF file that has color images of the screenshots/diagrams used in this book. [Click here to download it](https://static.packt-cdn.com/downloads/9781800569669_ColorImages.pdf). 51 | 52 | ### Related products 53 | * Dancing with Qubits [[Packt]](https://www.packtpub.com/product/dancing-with-qubits/9781838827366?utm_source=github&utm_medium=repository&utm_campaign=9781838827366) [[Amazon]](https://www.amazon.com/dp/1838827366) 54 | 55 | * Learn Quantum Computing with Python and IBM Quantum Experience [[Packt]](https://www.packtpub.com/product/learn-quantum-computing-with-python-and-ibm-quantum-experience/9781838981006?utm_source=github&utm_medium=repository&utm_campaign=9781838981006) [[Amazon]](https://www.amazon.com/dp/1838981004) 56 | 57 | ## Get to Know the Author 58 | **Srinjoy Ganguly** 59 | Srinjoy Ganguly is the founder and CEO of AdroitERA, an EdTech firm, and possesses a Master's in quantum computing technology from the Technical University of Madrid, and a Master's in artificial intelligence from the University of Southampton. He has over 4 years' experience in quantum computing and 5 years' experience in machine learning and deep learning. He currently leads the Quantum Machine Learning (QML) study space at QWorld. He has given an expert talk on QML at IEEE SPS. His research interests include QML, Quantum Image Processing, Quantum Natural Language Processing (QNLP), machine learning, and deep learning. 60 | 61 | **Thomas Cambier** 62 | Thomas Cambier is a French programmer and software developer. After studying top-level mathematics, physics, and computer science at Ecole Polytechnique, France's leading engineering school, he obtained a Master's degree in computer science at ETH Zürich on a broad range of subjects going from algorithmics and cryptography to object-oriented programming. Deeply interested in the promising possibilities resulting from quantum computing, he wrote his Master's thesis on the design of quantum algorithms with Silq, a brand-new high-level quantum language created by a research group at ETH Zürich. 63 | 64 | ### Download a free PDF 65 | 66 | If you have already purchased a print or Kindle version of this book, you can get a DRM-free PDF version at no cost.
Simply click on the link to claim your free PDF.
67 |

https://packt.link/free-ebook/9781800569669

-------------------------------------------------------------------------------- /Chapter12/qkd.slq: -------------------------------------------------------------------------------- 1 | // Quantum Key Distribution 2 | // - Simulates the generation of a secure key through an insecure channel 3 | 4 | 5 | // Helper functions 6 | 7 | // Returns 0 or 1 with equal probability 8 | def rand(){ 9 | return measure(H(false)); 10 | } 11 | 12 | // Random number generator 13 | def uniformInt(range:!ℕ){ 14 | // returns x~{0,...range-1} 15 | n:=ceil(log(range)/log(2)) coerce !ℕ; 16 | r:=range; 17 | while (r>range-1) { 18 | // rerolls r if it is greater than range 19 | r=0; 20 | for k in [0..n){ 21 | // rolls each bit of r 22 | r+=2^k*rand(); 23 | } 24 | } 25 | return r; 26 | } 27 | 28 | def prepareState(n:!ℕ) { 29 | // prepares n random bits each encoded as a qubit in a random base 30 | bitsA := 0:!int[n]; 31 | basesA := 0:!int[n]; 32 | 33 | for i in [0..n){ // generates random bits and random bases 34 | bitsA[i] = rand(); 35 | basesA[i] = rand(); 36 | } 37 | 38 | qubitsA := bitsA:int[n]; 39 | for i in [0..n){ // encodes each qubit to be sent in the corresponding base 40 | if basesA[i] {qubitsA[i] = H(qubitsA[i]);} 41 | } 42 | 43 | return(bitsA, basesA, qubitsA); 44 | } 45 | 46 | def measureB[n:!ℕ](qubitsA:int[n]) { 47 | // generates n random bases to measure the received bits 48 | basesB := 0:!int[n]; 49 | 50 | for i in [0..n){ // generates random bases 51 | basesB[i] = rand(); 52 | } 53 | 54 | for i in [0..n){ // decodes each received qubit according to the bases 55 | if basesB[i] {qubitsA[i] := H(qubitsA[i]);} 56 | } 57 | bitsB := measure(qubitsA); 58 | return (bitsB, basesB); 59 | } 60 | 61 | def compareBases[n:!ℕ](basesA:!int[n], basesB:!int[n]) { 62 | // returns the correctly guessed bases 63 | indices := 0:!int[n]; 64 | nInfo := 0:!ℕ; 65 | indices = ~(basesA ⊕ basesB); // keeps indices where bases coincide 66 | for i in [0..n){ 67 | nInfo+=indices[i]; 68 | } 69 | return (indices, nInfo); 70 | } 71 | 72 | def computePresumablySharedInfo[n:!ℕ](indices:!int[n], bitsA:!int[n], 73 | bitsB:!int[n], nInfo:!ℕ) { 74 | 75 | infoA := 0:!int[nInfo]; // contains the presumbaly shared information on A part 76 | infoB := 0:!int[nInfo]; 77 | count := 0:!ℕ; 78 | i := 0:!ℕ; 79 | while (count < nInfo) { // computes the presumably shared information 80 | if indices[i] { 81 | infoA[count] = bitsA[i]; 82 | infoB[count] = bitsB[i]; 83 | count+=1; 84 | } 85 | i+=1; 86 | } 87 | return (infoA, infoB); 88 | } 89 | 90 | def checkEavesdropper[n:!ℕ](infoA:!int[n], 91 | infoB:!int[n], 92 | nSharedBits:!ℕ) { 93 | bitsRevealed := 0:!ℕ; 94 | noE := true; 95 | count := 0:!ℕ; 96 | i := 0:!ℕ; 97 | notAlreadyChecked := 2^n - 1 coerce !int[n]; 98 | while (bitsRevealed < n - nSharedBits) { 99 | // checks that no eavesdropper has gained info about the shared secret 100 | // by comparing some bits 101 | i = uniformInt(n); 102 | if notAlreadyChecked[i] { 103 | // checks that random bits coincide 104 | noE &= (infoA[i] == infoB[i]); 105 | notAlreadyChecked[i] = false; 106 | bitsRevealed+=1; 107 | } 108 | } 109 | return (notAlreadyChecked, noE); 110 | } 111 | 112 | def computeSharedInfo[n:!ℕ](indices:!int[n], bitsA:!int[n], 113 | bitsB:!int[n], nInfo:!ℕ, nSharedBits:!ℕ) { 114 | // computes shared information 115 | 116 | // nSharedBits is the number of remaining bits after 'burning' some of them 117 | // to detect the presence of an eavesdropper 118 | assert (nSharedBits < nInfo); 119 | 120 | (infoA, infoB) := computePresumablySharedInfo(indices, bitsA, bitsB, nInfo); 121 | 122 | (notAlreadyChecked, noE) := checkEavesdropper(infoA, infoB, nSharedBits); 123 | 124 | sharedInfo := 0:!uint[nSharedBits]; 125 | // contains the shared information to be used if no eavesdroper has been detected 126 | 127 | count := 0:!ℕ; 128 | i := 0:!ℕ; 129 | while (count < nSharedBits) { // computes the shared information 130 | if notAlreadyChecked[i] { 131 | sharedInfo[count] = infoA[i]; 132 | count+=1; 133 | } 134 | i+=1; 135 | } 136 | 137 | return (sharedInfo, noE); 138 | // returns the shared information and the detection or not of an eavesdropper 139 | } 140 | 141 | def qkd[n:!ℕ](channel: int[n] !→ int[n]){ 142 | (bitsA, basesA, qubitsA) := prepareState(n); 143 | // prepares the random bases and bits and the qubits to be send to B 144 | 145 | qubitsA := channel(qubitsA); 146 | // simulates the transfer through a potentially malicious channel 147 | 148 | (bitsB, basesB) := measureB(qubitsA); 149 | // generates the guesses from B 150 | 151 | (indices, nInfo) := compareBases(basesA, basesB); 152 | // compares the bases between A and B 153 | 154 | 155 | nSharedBits := floor((2/3)*nInfo) coerce !ℕ; 156 | 157 | (sharedInfo, noE) := computeSharedInfo(indices, bitsA, bitsB, nInfo, nSharedBits); 158 | // computes the shared information obtained 159 | // and the potential detection of an eavesdropper 160 | 161 | return (noE, nSharedBits); 162 | // returns whether an eavesdropper was detected (0 if detection) 163 | // and how many bits of info are shared 164 | } 165 | 166 | /* TEST */ 167 | 168 | def testQKDnoE() { // tests QKD with no eavesdropper 169 | n := 12; 170 | channel := λ(b:int[n]). b; // channel = id 171 | (noE, nSharedBits) := qkd[n](channel); 172 | assert(noE); // no detection of eavesdropper (noE=1) 173 | print(noE, nSharedBits); 174 | } 175 | 176 | def testQKDE() { // tests QKD with an eavesdropper 177 | n := 12; 178 | channel := λ(b:int[n]){ for i in [0..n) {b[i] := H(b[i])}; return b; }; 179 | // channel alters the qubits 180 | (noE, nSharedBits) := qkd[n](channel); 181 | print(noE, nSharedBits); 182 | } 183 | 184 | /* EXAMPLE CALL */ 185 | 186 | def main() { 187 | // tests QKD both with and without an eavesdropper 188 | 189 | // test without eavesdropper should always be of the form (1,n) 190 | testQKDnoE(); 191 | 192 | // test with an eavesdropper may be of the form (0,n) if its presence is detected 193 | testQKDE(); 194 | } 195 | --------------------------------------------------------------------------------