# [Learn Quantum Computing with Python and Q#](https://www.manning.com/books/learn-quantum-computing-with-python-and-q-sharp?a_aid=learn-qc-granade&a_bid=ee23f338)<br>Chapter 9 Exercise Solutions
----
> Copyright (c) Sarah Kaiser and Chris Granade.
> Code sample from the book "Learn Quantum Computing with Python and Q#" by
> Sarah Kaiser and Chris Granade, published by Manning Publications Co.
> Book ISBN 9781617296130.
> Code licensed under the MIT License.

### Preamble


```python
import numpy as np
import qutip as qt
import matplotlib.pyplot as plt
import qsharp
%matplotlib inline
```

### Exercise 9.1

**In Chapter 4, you used Python type annotations to represent the concept of a _strategy_ in the CHSH game.
User-defined types in Q# can be used in a similar fashion.
Give it a go by defining a new UDT for CHSH strategies and then use your new UDT to wrap the constant strategy from Chapter 4.**

*HINT*: Your and Eve's parts of the strategy can each be represented as operations that take a `Result` and output a `Result`.
That is, as operations of type `Result => Result`.


```python
strategy = qsharp.compile("""
    newtype Strategy = (
        PlayAlice: (Result => Result),
        PlayBob: (Result => Result)
    );
""")
strategy
```

----
### Exercise 9.2

**You can find the model for Lancelot's results if you use Born's rule!
We have put the definition from Chapter 2 below, see if you can plot the resulting value as a function of Lancelot's scale using Python.
Does your plot look like a trigonometric function?**

\begin{align}
    \Pr(\text{measurement} | \text{state}) = |\left\langle \text{measurement} \mid \text{state} \right\rangle|^2
\end{align}

*HINT*: For Lancelot's measurements, the $\left\langle \text{measurement} \right|$ part of Born's rule is given by $\left\langle 1 \right|$.
Immediately before measuring, his qubit will be in the state $H R_1(\theta * \textrm{scale}) H \left|0\right\rangle$.
You can simulate the `R1` operation in QuTiP by using the matrix form in the Q# reference at https://docs.microsoft.com/qsharp/api/qsharp/microsoft.quantum.intrinsic.r1.

For the purposes of illustration, let's choose $\theta = 0.456$ radians.


```python
theta = 0.456
```

Next, as the hint gives us, we'll need to define a matrix that we can use to simulate the `R1` operation:


```python
def r1_matrix(angle: float) -> qt.Qobj:
    return qt.Qobj([
        [1, 0],
        [0, np.exp(1j * angle)]
    ])
```


```python
r1_matrix(theta)
```

We can use this to find Lancelot's state after applying each hidden rotation:


```python
def lancelot_final_state(theta: float, scale: float) -> qt.Qobj:
    initial_state = qt.basis(2, 0)
    # Simulate the H Q# operation.
    state = qt.qip.operations.hadamard_transform() * initial_state
    # Simulate the R1 operation.
    state = r1_matrix(theta * scale) * state
    # Simulate undoing the H operation with another call to H.
    state = qt.qip.operations.hadamard_transform() * state
    
    return state
```


```python
lancelot_final_state(theta, 1.2)
```

We now have everything we need to predict the probability of a "1" outcome:


```python
def lancelot_pr1(theta: float, scale: float) -> float:
    ket1 = qt.basis(2, 1)
    
    # Apply Born's rule.
    return np.abs((ket1.dag() * lancelot_final_state(theta, scale))[0, 0]) ** 2
```


```python
lancelot_pr1(theta, 1.2)
```

Plotting for a variety of different scales, we see the expected sinusoidal shape:


```python
scales = np.linspace(0, 20, 201)
pr1s = [lancelot_pr1(theta, scale) for scale in scales]
plt.plot(scales, pr1s)
```

----
### Exercise 9.3

**Try writing Q# programs that use `AssertQubit` and `DumpMachine` to verify that:**

- $\left|+\right\rangle$ and $\left|-\right\rangle$ are both eigenstates of the `X` operation.
- $\left|0\right\rangle$ and $\left|1\right\rangle$ are both eigenstates of the `Rz` operation, regardless of what angle you choose to rotate by.

For even more practice, try figuring out what the eigenstates of the `Y` and `CNOT` operations and writing a Q# program to verify your guesses!

*HINT*: You can find the vector form of the eigenstates of a unitary operation using QuTiP.
For instance, the eigenstates of the `Y` operation are given by `qt.sigmay().eigenstates()`.
From there, you can use what you learned about rotations in Chapters 4 and 5 to figure out which Q# operations prepare those states.

Don't forget you can always test if a particular state is an eigenstate of an operation by just writing a quick test in Q#!  

Let's start by verifying that $\left|+\right\rangle$ and $\left|-\right\rangle$ are both eigenstates of the `X` operation.


```python
verify_x_eigenstates = qsharp.compile("""
    open Microsoft.Quantum.Diagnostics;
    
    operation VerifyXEigenstates() : Unit {
        use q = Qubit();
        // Prepare |+⟩.
        H(q);

        // Check that the X operation does nothing.
        X(q);
        Message("Checking that |+⟩ is an eigenstate of the X operation.");
        DumpMachine();
            
        // Reset so that we're ready for the next check.
        Reset(q);
            
        // Next, do the same with |−⟩.
        X(q);
        H(q);
            
        X(q);
        Message("");
        Message("Checking that |−⟩ is an eigenstate of the X operation.");
        DumpMachine();
           
        Reset(q);
    }
""")
```


```python
verify_x_eigenstates.simulate()
```

Notice that in both cases, we got back the same state (up to a global phase), confirming the first part of the exercise.
Doing the same for `Rz`, we add an input for the rotation angle:


```python
verify_rz_eigenstates = qsharp.compile("""
    open Microsoft.Quantum.Diagnostics;
    
    operation VerifyRzEigenstates(angle : Double) : Unit {
        use q = Qubit();
        // Prepare |0⟩ by doing nothing.

        // Check that the Rz operation does nothing.
        Rz(angle, q);
        Message("Checking that |0⟩ is an eigenstate of the Rz operation.");
        DumpMachine();
            
        // Reset so that we're ready for the next check.
        Reset(q);
            
        // Next, do the same with |1⟩.
        X(q);
            
        Rz(angle, q);
        Message("");
        Message("Checking that |1⟩ is an eigenstate of the Rz operation.");
        DumpMachine();
            
        Reset(q);
    }
""")
```


```python
verify_rz_eigenstates.simulate(angle=0.123)
```


```python
verify_rz_eigenstates.simulate(angle=4.567)
```

Using the hint, we can find what eigenstates we should try for the `Y` and `CNOT` operations:


```python
qt.sigmay().eigenstates()
```


```python
qt.qip.operations.cnot().eigenstates()
```

That is, $(|0\rangle + i |1\rangle) / \sqrt{2}$ and $(|0\rangle - i |1\rangle) / \sqrt{2}$ are eigenstates of the `Y` operation, while $|00\rangle$, $|01\rangle$, $|1+\rangle$ and $|1-\rangle$ are eigenstates of the `CNOT` operation.

----
### Exercise 9.4

**Verify that $\left|0\right\rangle\left\langle 0\right| \otimes \mathbb{1} + \left|1\right\rangle\left\langle{1}\right| \otimes X$ is the same as:**

\begin{align}
    U_{\mathrm{CNOT}} = \left(\begin{matrix}
        \mathbb{1} & 0 \\
        0 & X
    \end{matrix}\right).
\end{align}

*HINT*: You can verify this by hand, by using NumPy's `np.kron` function, or QuTiP's `qt.tensor` function.
If you need a refresher, check out how you simulated teleportation in Chapter 5, or check out the derivation of the Deutsch–Jozsa algorithm in Chapter 7.


```python
ket0 = qt.basis(2, 0)
ket1 = qt.basis(2, 1)
```


```python
projector_0 = ket0 * ket0.dag()
projector_0
```


```python
projector_1 = ket1 * ket1.dag()
projector_1
```


```python
qt.tensor(projector_0, qt.qeye(2)) + qt.tensor(projector_1, qt.sigmax())
```

----
### Exercise 9.5

**Either by hand or using QuTiP, verify that state dumped by running the Q# snippet below is the same as $\left|-1\right\rangle = \left|-\right\rangle \otimes \left|1\right\rangle$.**

```Q#
use (control, target) = (Qubit(), Qubit());
H(control);
X(target);

CZ(control, target);
DumpRegister((), [control, target]);

Reset(control);
Reset(target);
```

*NOTE*: If you seem to get the right answer other than that the order of the qubits are swapped, note that `DumpMachine` uses a _little-endian_ representation to order states.
In little-endian, |2⟩ is short-hand for |01⟩, not |10⟩.
If this seems confusing, blame the x86 processor architecture…

Let's first run the above snippet to see what output is generated.


```python
run_exercise_95 = qsharp.compile("""
    open Microsoft.Quantum.Diagnostics;

    operation RunExercise95() : Unit {
        use (control, target) = (Qubit(), Qubit());
        H(control);
        X(target);

        CZ(control, target);
        DumpRegister((), [control, target]);

        Reset(control);
        Reset(target);
    }
""")
```


```python
run_exercise_95.simulate()
```

Next, let's compute what $\left|-1\right\rangle = \left|-\right\rangle \otimes \left|1\right\rangle$ in vector notation by using QuTiP.


```python
ket_minus = qt.Qobj([
    [1],
    [-1]
]) / np.sqrt(2)
ket1 = qt.basis(2, 1)
```


```python
qt.tensor(ket_minus, ket1)
```

As the note suggests, these two outputs appear different at first, but the resolution is that Q# uses little-endian notation, such that "|2⟩" means the |01⟩ amplitude, which QuTiP prints as the second row.
We can make this more clear by manually telling IQ# to print out as bitstrings instead of little-endian notation.

**WARNING:** Calling the `%config` magic from Python is not officially supported, and may break in future versions of Q#.


```python
qsharp.client._execute('%config dump.basisStateLabelingConvention = "Bitstring"')
```


```python
run_exercise_95.simulate()
```

----
### Epilogue

_The following cell logs what version of the components this was last tested with._


```python
qsharp.component_versions()
```




    {'iqsharp': LooseVersion ('0.17.2105143879'),
     'Jupyter Core': LooseVersion ('1.5.0.0'),
     '.NET Runtime': LooseVersion ('.NETCoreApp,Version=v3.1'),
     'qsharp': LooseVersion ('0.17.2105.143879')}



c04be0da160d439950c99f6478933e7d2fea0e81
ipynb
Jupyter Notebook
ch09/ch09-exercise-solutions.ipynb
redxe/learn-qc-with-python-and-qsharp
3b0a493d49b1eed38e2e92a3acf463a58d85667f
MIT
2019-04-30T05:07:42.000Z
2022-03-26T10:18:21.000Z
ch09/ch09-exercise-solutions.ipynb
redxe/learn-qc-with-python-and-qsharp
3b0a493d49b1eed38e2e92a3acf463a58d85667f
MIT
2020-04-05T15:00:16.000Z
2022-02-11T18:42:22.000Z
ch09/ch09-exercise-solutions.ipynb
redxe/learn-qc-with-python-and-qsharp
3b0a493d49b1eed38e2e92a3acf463a58d85667f
MIT
2019-04-30T16:55:07.000Z
2022-03-26T01:00:55.000Z
Qwen/Qwen-72B
1. YES
2. YES