# Measurements Kata

The **Measurements** quantum kata is a series of exercises designed
to get you familiar with the concept of measurements and with programming in Q#.
It covers the following topics:
* single-qubit measurements,
* discriminating orthogonal and nonorthogonal states.

Each task is wrapped in one operation preceded by the description of the task.
Your goal is to fill in the blank (marked with `// ...` comments)
with some Q# code that solves the task. To verify your answer, run the cell using Ctrl/⌘+Enter.

The tasks are given in approximate order of increasing difficulty; harder ones are marked with asterisks.

To begin, first prepare this notebook for execution (if you skip the first step, you'll get "Syntax does not match any known patterns" error when you try to execute Q# code in the next cells; if you skip the second step, you'll get "Invalid test name" error):

```qsharp
%package Microsoft.Quantum.Katas::0.10.1911.1607
```

Microsoft.Quantum.Standard::0.10.1911.1607

Microsoft.Quantum.Katas::0.10.1911.1607

> The package versions in the output of the cell above should always match. If you are running the Notebooks locally and the versions do not match, please install the IQ# version that matches the version of the `Microsoft.Quantum.Katas` package.
>
> How to install the right IQ# version
> For example, if the version of `Microsoft.Quantum.Katas` package above is 0.1.2.3, the installation steps are as follows:
>
> 1. Stop the kernel.
> 2. Uninstall the existing version of IQ#:
>        dotnet tool uninstall microsoft.quantum.iqsharp -g
> 3. Install the matching version:
>        dotnet tool install microsoft.quantum.iqsharp -g --version 0.1.2.3
> 4. Reinstall the kernel:
>        dotnet iqsharp install
> 5. Restart the Notebook.
>

```qsharp
%workspace reload
```

Quantum.Kata.Measurements.ABC_DistinguishStates_MultiQubit_Threshold

Quantum.Kata.Measurements.AllZerosOrWState

Quantum.Kata.Measurements.AllZerosOrWState_Alternate

Quantum.Kata.Measurements.AllZerosOrWState_Reference

Quantum.Kata.Measurements.ApplyDiag

Quantum.Kata.Measurements.AreBitstringsEqual

Quantum.Kata.Measurements.BasisStateMeasurement

Quantum.Kata.Measurements.BasisStateMeasurement_Reference

Quantum.Kata.Measurements.BellState

Quantum.Kata.Measurements.BellState_Reference

Quantum.Kata.Measurements.CheckSuperpositionBitstringsMeasurement

Quantum.Kata.Measurements.CheckSuperpositionBitstringsOneMeasurement

Quantum.Kata.Measurements.CheckTwoBitstringsMeasurement

Quantum.Kata.Measurements.DistinguishStates_MultiQubit

Quantum.Kata.Measurements.DistinguishStates_MultiQubit_Threshold

Quantum.Kata.Measurements.DistinguishTwoStates_OneQubit

Quantum.Kata.Measurements.FindFirstDiff_Reference

Quantum.Kata.Measurements.FindFirstSuperpositionDiff_Reference

Quantum.Kata.Measurements.GHZ_State_Reference

Quantum.Kata.Measurements.GHZOrWState

Quantum.Kata.Measurements.GHZOrWState_Alternate

Quantum.Kata.Measurements.GHZOrWState_Reference

Quantum.Kata.Measurements.InitializeQubit

Quantum.Kata.Measurements.InitializeQubit_Reference

Quantum.Kata.Measurements.IsQubitA

Quantum.Kata.Measurements.IsQubitA_Reference

Quantum.Kata.Measurements.IsQubitNotInABC

Quantum.Kata.Measurements.IsQubitNotInABC_Reference

Quantum.Kata.Measurements.IsQubitOne

Quantum.Kata.Measurements.IsQubitOne_Reference

Quantum.Kata.Measurements.IsQubitPlus

Quantum.Kata.Measurements.IsQubitPlus_Reference

Quantum.Kata.Measurements.IsQubitPlusOrZero

Quantum.Kata.Measurements.IsQubitPlusOrZero_Reference

Quantum.Kata.Measurements.IsQubitPlusZeroOrInconclusiveSimpleUSD

Quantum.Kata.Measurements.IsQubitPlusZeroOrInconclusiveSimpleUSD_Reference

Quantum.Kata.Measurements.StatePrep_AllZerosOrWState

Quantum.Kata.Measurements.StatePrep_BasisStateMeasurement

Quantum.Kata.Measurements.StatePrep_BellState

Quantum.Kata.Measurements.StatePrep_Bitstring

Quantum.Kata.Measurements.StatePrep_BitstringSuperposition

Quantum.Kata.Measurements.StatePrep_FindFirstDiff

Quantum.Kata.Measurements.StatePrep_GHZOrWState

Quantum.Kata.Measurements.StatePrep_IsQubitA

Quantum.Kata.Measurements.StatePrep_IsQubitNotInABC

Quantum.Kata.Measurements.StatePrep_IsQubitOne

Quantum.Kata.Measurements.StatePrep_IsQubitPlus

Quantum.Kata.Measurements.StatePrep_IsQubitZeroOrPlus

Quantum.Kata.Measurements.StatePrep_SuperpositionMeasurement

Quantum.Kata.Measurements.StatePrep_ThreeQubitMeasurement

Quantum.Kata.Measurements.StatePrep_TwoBitstringsMeasurement

Quantum.Kata.Measurements.StatePrep_TwoQubitState

Quantum.Kata.Measurements.StatePrep_TwoQubitStatePartTwo

Quantum.Kata.Measurements.StatePrep_ZeroZeroOrOneOne

Quantum.Kata.Measurements.SuperpositionMeasurement

Quantum.Kata.Measurements.SuperpositionMeasurement_Alternate

Quantum.Kata.Measurements.SuperpositionMeasurement_Reference

Quantum.Kata.Measurements.SuperpositionOneMeasurement

Quantum.Kata.Measurements.SuperpositionOneMeasurement_Reference

Quantum.Kata.Measurements.T101_IsQubitOne_Test

Quantum.Kata.Measurements.T102_InitializeQubit_Test

Quantum.Kata.Measurements.T103_IsQubitPlus_Test

Quantum.Kata.Measurements.T104_IsQubitA_Test

Quantum.Kata.Measurements.T105_ZeroZeroOrOneOne_Test

Quantum.Kata.Measurements.T106_BasisStateMeasurement_Test

Quantum.Kata.Measurements.T107_TwoBitstringsMeasurement_Test

Quantum.Kata.Measurements.T108_SuperpositionOneMeasurement_Test

Quantum.Kata.Measurements.T109_SuperpositionMeasurement_Test

Quantum.Kata.Measurements.T110_AllZerosOrWState_Test

Quantum.Kata.Measurements.T111_GHZOrWState_Test

Quantum.Kata.Measurements.T112_BellState_Test

Quantum.Kata.Measurements.T113_TwoQubitState_Test

Quantum.Kata.Measurements.T114_TwoQubitStatePartTwo_Test

Quantum.Kata.Measurements.T115_ThreeQubitMeasurement_Test

Quantum.Kata.Measurements.T201_IsQubitZeroOrPlus_Test

Quantum.Kata.Measurements.T202_IsQubitZeroOrPlusSimpleUSD_Test

Quantum.Kata.Measurements.T203_IsQubitNotInABC_Test

Quantum.Kata.Measurements.ThreeQubitMeasurement

Quantum.Kata.Measurements.ThreeQubitMeasurement_Reference

Quantum.Kata.Measurements.ThreeQubitMeasurement_SimpleMeasurement

Quantum.Kata.Measurements.TwoBitstringsMeasurement

Quantum.Kata.Measurements.TwoBitstringsMeasurement_Reference

Quantum.Kata.Measurements.TwoQubitState

Quantum.Kata.Measurements.TwoQubitState_Reference

Quantum.Kata.Measurements.TwoQubitStatePartTwo

Quantum.Kata.Measurements.TwoQubitStatePartTwo_Alternate

Quantum.Kata.Measurements.TwoQubitStatePartTwo_Reference

Quantum.Kata.Measurements.USD_DistinguishStates_MultiQubit_Threshold

Quantum.Kata.Measurements.WState_Arbitrary_Reference

Quantum.Kata.Measurements.ZeroZeroOrOneOne

Quantum.Kata.Measurements.ZeroZeroOrOneOne_Reference

## Part I. Discriminating Orthogonal States

### Task 1.1. $|0\rangle$ or $|1\rangle$?

**Input:** A qubit which is guaranteed to be in either the $|0\rangle$ or the $|1\rangle$ state.

**Output:**  `true` if the qubit was in the $|1\rangle$ state, or `false` if it was in the $|0\rangle$ state. The state of the qubit at the end of the operation does not matter.

```qsharp
%kata T101_IsQubitOne_Test

operation IsQubitOne (q : Qubit) : Bool {
    // The operation M will measure a qubit in the Z basis (|0⟩ and |1⟩ basis)
    // and return Zero if the observed state was |0⟩ or One if the state was |1⟩.
    // To answer the question, you need to perform the measurement and check whether the result
    // equals One - either directly or using library function IsResultOne.
    //
    // Type the following: return M(q) == One;
    // Then run the cell using Ctrl/⌘+Enter.

    return M(q) == One;
}
```

    Success!

### Task 1.2. Set the qubit to the $|0\rangle$ state.

**Input:** A qubit in an arbitrary state.

**Goal:**  Change the state of the qubit to $|0\rangle$.

```qsharp
%kata T102_InitializeQubit_Test

operation InitializeQubit (q : Qubit) : Unit {
    if (M(q) == One) {
        X(q);
    }
}
```

    Success!

### Task 1.3. $|+\rangle$ or $|-\rangle$?

**Input:** A qubit which is guaranteed to be in either the $|+\rangle$ or the $|-\rangle$ state. As a reminder, $|+\rangle = \frac{1}{\sqrt{2}} \big(|0\rangle + |1\rangle\big)$, $|-\rangle = \frac{1}{\sqrt{2}} \big(|0\rangle - |1\rangle\big)$.

**Output:** `true` if the qubit was in the $|+\rangle$ state, or `false` if it was in the $|-\rangle$ state. The state of the qubit at the end of the operation does not matter.

```qsharp
%kata T103_IsQubitPlus_Test

operation IsQubitPlus (q : Qubit) : Bool {
    H(q);
    return M(q) == Zero;
}
```

    Success!

### Task 1.4. $|A\rangle$ or $|B\rangle$?

**Inputs:**

1. Angle $\alpha$, in radians, represented as a `Double`.
2. A qubit which is guaranteed to be in either the $|A\rangle$ or the $|B\rangle$ state, where $|A\rangle = \cos \alpha |0\rangle + \sin \alpha |1\rangle$ and $|B\rangle = - \sin \alpha |0\rangle + \cos \alpha |1\rangle$.

**Output:** `true` if the qubit was in the $|A\rangle$ state, or `false` if it was in the $|B\rangle$ state. The state of the qubit at the end of the operation does not matter.

📝 $
R_y^{-1}(2\alpha) = R_y(-2\alpha) = \begin{bmatrix} \cos\alpha & \sin\alpha \\ -\sin\alpha & \cos\alpha \end{bmatrix}
$ をかけると、

$$
\begin{align}
R_y(-2\alpha) |A\rangle &= |0\rangle, \\
R_y(-2\alpha) |B\rangle &= |1\rangle
\end{align}
$$

となる。

```qsharp
%kata T104_IsQubitA_Test

operation IsQubitA (alpha : Double, q : Qubit) : Bool {
    Ry(-2.0 * alpha, q);
    return M(q) == Zero;
}
```

    Success!

### Task 1.5. $|00\rangle$ or $|11\rangle$?

**Input:** Two qubits (stored in an array of length 2) which are guaranteed to be in either the $|00\rangle$ or the $|11\rangle$ state.

**Output:** 0 if the qubits were in the $|00\rangle$ state, or 1 if they were in the $|11\rangle$ state. The state of the qubits at the end of the operation does not matter.

📝 もつれているので、1つのキュビットを測定するだけで判別できる。

```qsharp
%kata T105_ZeroZeroOrOneOne_Test

operation ZeroZeroOrOneOne (qs : Qubit[]) : Int {
    return M(qs[0]) == Zero ? 0 | 1;
}
```

    Success!

### Task 1.6. Distinguish four basis states.

**Input:** Two qubits (stored in an array of length 2) which are guaranteed to be in one of the four basis states ($|00\rangle$, $|01\rangle$, $|10\rangle$, or $|11\rangle$).

**Output:**

* 0 if the qubits were in the $|00\rangle$ state,
* 1 if they were in the $|01\rangle$ state,
* 2 if they were in the $|10\rangle$ state,
* 3 if they were in the $|11\rangle$ state.

In this task and the subsequent ones the order of qubit states in task description matches the order of qubits in the array (i.e., $|10\rangle$ state corresponds to `qs[0]` in state $|1\rangle$ and `qs[1]` in state $|0\rangle$).

The state of the qubits at the end of the operation does not matter.

```qsharp
%kata T106_BasisStateMeasurement_Test

operation BasisStateMeasurement (qs : Qubit[]) : Int {
    return (M(qs[0]) == One ? 2 | 0) + (M(qs[1]) == One ? 1 | 0);
}
```

    Success!

### Task 1.7. Distinguish two basis states given by bit strings

**Inputs:**

1. $N$ qubits (stored in an array of length $N$) which are guaranteed to be in one of the two basis states described by the given bit strings.
2. Two bit strings represented as `Bool[]`s.

**Output:**

* 0 if the qubits were in the basis state described by the first bit string,
* 1 if they were in the basis state described by the second bit string.

Bit values `false` and `true` correspond to $|0\rangle$ and $|1\rangle$ states. You are guaranteed that both bit strings have the same length as the qubit array, and that the bit strings differ in at least one bit.

You can use exactly one measurement. The state of the qubits at the end of the operation does not matter.

> Example:  for bit strings `[false, true, false]` and `[false, false, true]` return 0 corresponds to state $|010\rangle$, and return 1 corresponds to state $|001\rangle$.

📝 Superpostion の Task 14 と同じ考え方。

```qsharp
%kata T107_TwoBitstringsMeasurement_Test

open Microsoft.Quantum.Arrays;
open Microsoft.Quantum.Logical;

operation TwoBitstringsMeasurement (qs : Qubit[], bits1 : Bool[], bits2 : Bool[]) : Int {
    let different = IndexOf(NotEqualB, Zip(bits1, bits2));

    if (M(qs[different]) == One) {
        return bits1[different] ? 0 | 1;
    } else {
        return bits1[different] ? 1 | 0;
    }
}
```

    Success!

### Task 1.8. Distinguish two superposition states given by two arrays of bit strings - 1 measurement

**Inputs:**

1. $N$ qubits (stored in an array of length $N$) which are guaranteed to be in one of the two superposition states described by the given arrays of bit strings.
2. Two arrays of bit strings represented as `Bool[][]`s.
The arrays have dimensions $M_1 \times N$ and $M_2 \times N$ respectively, where $N$ is the number of qubits and $M_1$ and $M_2$ are the numbers of bit strings in each array. Note that in general $M_1 \neq M_2$.
An array of bit strings `[b₁, ..., bₘ]` defines a state that is an equal superposition of all basis states defined by bit strings $b_1, ..., b_m$.
For example, an array of bit strings `[[false, true, false], [false, true, true]]` defines a superposition state $\frac{1}{\sqrt2}\big(|010\rangle + |011\rangle\big)$.

You are guaranteed that there exists an index of a qubit Q for which:
 - all the bit strings in the first array have the same value in this position (all `bits1[j][Q]` are the same),
 - all the bit strings in the second array have the same value in this position (all `bits2[j][Q]` are the same),
 - these values are different for the first and the second arrays.

> For example, for arrays `[[false, true, false], [false, true, true]]` and `[[true, false, true], [false, false, true]]` return 0 corresponds to state $\frac{1}{\sqrt2}\big(|010\rangle + |011\rangle\big)$, return 1 - to state $\frac{1}{\sqrt2}\big(|101\rangle + |001\rangle\big)$, and you can distinguish these states perfectly by measuring the second qubit.

**Output:**

* 0 if qubits were in the superposition state described by the first array,
* 1 if they were in the superposition state described by the second array.

The state of the qubits at the end of the operation does not matter.

You are allowed to use exactly one measurement.

```qsharp
// bits1 と bits2 を区別できるインデックス（問題文中の Q）を探す
function FindDistinguishableIndex(bits1 : Bool[][], bits2 : Bool[][]) : Int {
    for (Q in 0 .. Length(bits1[0]) - 1) {

        // continue 文や break 文がないので、フラグで管理する
        mutable sameInBits1 = true;
        let valueOfBits1 = bits1[0][Q];
        for (j in 0 .. Length(bits1) - 1) {
            if (bits1[j][Q] != valueOfBits1) {
                set sameInBits1 = false;
            }
        }

        mutable sameInBits2 = true;
        let valueOfBits2 = bits2[0][Q];
        for (j in 0 .. Length(bits2) - 1) {
            if (bits2[j][Q] != valueOfBits2) {
                set sameInBits2 = false;
            }
        }

        if (sameInBits1 and sameInBits2 and valueOfBits1 != valueOfBits2) {
           return Q;
        }
    }
    return -1; // これが無いと Not all code paths return a value. と怒られる
}
```

FindDistinguishableIndex

```qsharp
%kata T108_SuperpositionOneMeasurement_Test

open Microsoft.Quantum.Convert;

operation SuperpositionOneMeasurement (qs : Qubit[], bits1 : Bool[][], bits2 : Bool[][]) : Int {
    // Message($"bits1: {bits1}");
    // Message($"bits2: {bits2}");

    let Q = FindDistinguishableIndex(bits1, bits2);

    // if (M(qs[Q]) == One) {
    //     return bits1[0][Q] ? 0 | 1;
    // } else {
    //     return bits1[0][Q] ? 1 | 0;
    // }
    return ResultAsBool(M(qs[Q])) == bits1[0][Q] ? 0 | 1;
}
```

    Success!

📝 `$""` に `{変数名}` を入れると文字列補間ができるので、`Message()` と合わせてデバッグに使えそう。

> The Q# syntax for string interpolations is a subset of the C# 7.0 syntax; Q# does not support verbatim (multi-line) interpolated strings. See [Interpolated Strings](https://docs.microsoft.com/dotnet/csharp/language-reference/keywords/interpolated-strings) for the C# syntax.

https://docs.microsoft.com/en-us/quantum/language/expressions?view=qsharp-preview#string-expressions

### Task 1.9. Distinguish two superposition states given by two arrays of bit strings

**Inputs:**

1. $N$ qubits (stored in an array of length $N$) which are guaranteed to be in one of the two superposition states described by the given arrays of bit strings.
2. Two arrays of bit strings represented as `Bool[][]`s.
The arrays describe the superposition states in the same way as in the previous task,
i.e. they have dimensions $M_1 \times N$ and $M_2 \times N$ respectively, $N$ being the number of qubits.

The only constraint on the bit strings is that **all bit strings in the two arrays are distinct**.

> Example:  for bit strings `[[false, true, false], [false, false, true]]` and `[[true, true, true], [false, true, true]]` return 0 corresponds to state $\frac{1}{\sqrt2}\big(|010\rangle + |001\rangle\big)$, return 1 to state $\frac{1}{\sqrt2}\big(|111\rangle + |011\rangle\big)$.

**Output:**

* 0 if qubits were in the superposition state described by the first array,
* 1 if they were in the superposition state described by the second array.

The state of the qubits at the end of the operation does not matter.

You can use as many measurements as you wish.

📝 何回測定しても良いということなので、`MultiM` ですべてのキュビットを測定してしまう。

```qsharp
open Microsoft.Quantum.Arrays;
open Microsoft.Quantum.Logical;

function AreBitStringsEqual (bits1 : Bool[], bits2 : Bool[]) : Bool {
    return All(EqualB, Zip(bits1, bits2));
}
```

AreBitStringsEqual

```qsharp
%kata T109_SuperpositionMeasurement_Test

open Microsoft.Quantum.Convert;
open Microsoft.Quantum.Measurement;

operation SuperpositionMeasurement (qs : Qubit[], bits1 : Bool[][], bits2 : Bool[][]) : Int {
    let bitString = ResultArrayAsBoolArray(MultiM(qs));

    // ラムダ式やカリー化が無い？ので面倒…
    for (bs in bits1) {
        if (AreBitStringsEqual(bitString, bs)) {
            return 0;
        }
    }
    return 1;
}
```

    Success!

### Task 1.10. $|0...0\rangle$ state or W state ?

**Input:** $N$ qubits (stored in an array of length $N$) which are guaranteed to be either in the $|0...0\rangle$ state or in the [W state](https://en.wikipedia.org/wiki/W_state).

**Output:**

* 0 if the qubits were in the $|0...0\rangle$ state,
* 1 if they were in the W state.

The state of the qubits at the end of the operation does not matter.

📝 前問同様、全キュビットを測定する

```qsharp
%kata T110_AllZerosOrWState_Test

open Microsoft.Quantum.Convert;
open Microsoft.Quantum.Measurement;

operation AllZerosOrWState (qs : Qubit[]) : Int {
    let k = ResultArrayAsInt(MultiM(qs));
    return k == 0 ? 0 | 1;
}
```

    Success!

### Task 1.11. GHZ state or W state ?

**Input:** $N \ge 2$ qubits (stored in an array of length $N$) which are guaranteed to be either in the [GHZ state](https://en.wikipedia.org/wiki/Greenberger%E2%80%93Horne%E2%80%93Zeilinger_state) or in the [W state](https://en.wikipedia.org/wiki/W_state).

**Output:**

* 0 if the qubits were in the GHZ state,
* 1 if they were in the W state.

The state of the qubits at the end of the operation does not matter.

📝 前問同様。GHZ は $1/\sqrt{2} \big( |00 \dots 0 \rangle + |11 \dots 1 \rangle \big)$

```qsharp
%kata T111_GHZOrWState_Test

open Microsoft.Quantum.Convert;
open Microsoft.Quantum.Measurement;

operation GHZOrWState (qs : Qubit[]) : Int {
    let k = ResultArrayAsInt(MultiM(qs));
    return k == 0 or k == 2 ^ Length(qs) - 1 ? 0 | 1;
}
```

    Success!

### Task 1.12. Distinguish four Bell states.

**Input:** Two qubits (stored in an array of length 2) which are guaranteed to be in one of the four Bell states.

**Output:**

* 0 if they were in the state $|\Phi^{+}\rangle = \frac{1}{\sqrt{2}} \big(|00\rangle + |11\rangle\big)$,
* 1 if they were in the state $|\Phi^{-}\rangle = \frac{1}{\sqrt{2}} \big(|00\rangle - |11\rangle\big)$,
* 2 if they were in the state $|\Psi^{+}\rangle = \frac{1}{\sqrt{2}} \big(|01\rangle + |10\rangle\big)$,
* 3 if they were in the state $|\Psi^{-}\rangle = \frac{1}{\sqrt{2}} \big(|01\rangle - |10\rangle\big)$.

The state of the qubits at the end of the operation does not matter.

📝 Bell state を作ったとき（Superposition の Task 6, 7）は

```
H(qs[0]);
CNOT(qs[0], qs[1]);
```

としたが、これの逆操作を行えばよいらしい。なるほどー！

$$
\begin{align}
|\Phi^{+}\rangle &= \frac{1}{\sqrt{2}} \big(|00\rangle + |11\rangle\big)
\xrightarrow{\text{CNOT}} \frac{1}{\sqrt{2}} \big(|00\rangle + |10\rangle\big)
\xrightarrow{\text{H}} |00\rangle \\
|\Phi^{-}\rangle &= \frac{1}{\sqrt{2}} \big(|00\rangle - |11\rangle\big)
\xrightarrow{\text{CNOT}} \frac{1}{\sqrt{2}} \big(|00\rangle - |10\rangle\big)
\xrightarrow{\text{H}} |10\rangle \\
|\Psi^{+}\rangle &= \frac{1}{\sqrt{2}} \big(|01\rangle + |10\rangle\big)
\xrightarrow{\text{CNOT}} \frac{1}{\sqrt{2}} \big(|01\rangle + |11\rangle\big)
\xrightarrow{\text{H}} |01\rangle \\
|\Psi^{-}\rangle &= \frac{1}{\sqrt{2}} \big(|01\rangle - |10\rangle\big)
\xrightarrow{\text{CNOT}} \frac{1}{\sqrt{2}} \big(|01\rangle - |11\rangle\big)
\xrightarrow{\text{H}} |11\rangle \\
\end{align}
$$

```qsharp
%kata T112_BellState_Test

open Microsoft.Quantum.Convert;
open Microsoft.Quantum.Measurement;

operation BellState (qs : Qubit[]) : Int {
    CNOT(qs[0], qs[1]);
    H(qs[0]);

    return ResultArrayAsInt(MultiM(qs));
}
```

    Success!

### Task 1.13. Distinguish four orthogonal 2-qubit states.

**Input:** Two qubits (stored in an array of length 2) which are guaranteed to be in one of the four orthogonal states.

**Output:**

* 0 if they were in the state $|S_0\rangle = \frac{1}{2} \big(|00\rangle + |01\rangle + |10\rangle + |11\rangle\big)$,
* 1 if they were in the state $|S_1\rangle = \frac{1}{2} \big(|00\rangle - |01\rangle + |10\rangle - |11\rangle\big)$,
* 2 if they were in the state $|S_2\rangle = \frac{1}{2} \big(|00\rangle + |01\rangle - |10\rangle - |11\rangle\big)$,
* 3 if they were in the state $|S_3\rangle = \frac{1}{2} \big(|00\rangle - |01\rangle - |10\rangle + |11\rangle\big)$.

The state of the qubits at the end of the operation does not matter.

📝 いずれも $|+\rangle$ や $|-\rangle$ のテンソル積に分解できる。

$$
\begin{align}
|S_0\rangle
    &= \frac{1}{2} \big(|00\rangle + |01\rangle + |10\rangle + |11\rangle\big)
    = |+\rangle \otimes |+\rangle
    \xrightarrow{\text{H} \otimes \text{H}} |00\rangle \\
|S_1\rangle
    &= \frac{1}{2} \big(|00\rangle - |01\rangle + |10\rangle - |11\rangle\big)
    = |+\rangle \otimes |-\rangle
    \xrightarrow{\text{H} \otimes \text{H}} |01\rangle \\
|S_2\rangle
    &= \frac{1}{2} \big(|00\rangle + |01\rangle - |10\rangle - |11\rangle\big)
    = |-\rangle \otimes |+\rangle
    \xrightarrow{\text{H} \otimes \text{H}} |10\rangle \\
|S_3\rangle
    &= \frac{1}{2} \big(|00\rangle - |01\rangle - |10\rangle + |11\rangle\big)
    = |-\rangle \otimes |-\rangle
    \xrightarrow{\text{H} \otimes \text{H}} |11\rangle \\
\end{align}
$$

```qsharp
%kata T113_TwoQubitState_Test

open Microsoft.Quantum.Arrays;
open Microsoft.Quantum.Convert;
open Microsoft.Quantum.Measurement;

operation TwoQubitState (qs : Qubit[]) : Int {
    ApplyToEach(H, qs);
    return ResultArrayAsInt(Reversed(MultiM(qs)));
}
```

    Success!

### Task 1.14*. Distinguish four orthogonal 2-qubit states, part 2.

**Input:** Two qubits (stored in an array of length 2) which are guaranteed to be in one of the four orthogonal states.

**Output:**

* 0 if they were in the state $|S_0\rangle = \frac{1}{2} \big(+ |00\rangle - |01\rangle - |10\rangle - |11\rangle\big)$,
* 1 if they were in the state $|S_1\rangle = \frac{1}{2} \big(- |00\rangle + |01\rangle - |10\rangle - |11\rangle\big)$,
* 2 if they were in the state $|S_2\rangle = \frac{1}{2} \big(- |00\rangle - |01\rangle + |10\rangle - |11\rangle\big)$,
* 3 if they were in the state $|S_3\rangle = \frac{1}{2} \big(- |00\rangle - |01\rangle - |10\rangle + |11\rangle\big)$.

The state of the qubits at the end of the operation does not matter.

📝 後ろのキュビットに H を適用すると、Task 1.12 で出てきた Bell 状態になる。

$$
\begin{align}
|S_0\rangle
    &= \frac{1}{2} \big(+ |00\rangle - |01\rangle - |10\rangle - |11\rangle\big)
    = \frac{1}{\sqrt{2}} \big(+ |0\rangle |-\rangle - |1\rangle |+\rangle \big)
    \xrightarrow{\text{I} \otimes \text{H}} \frac{1}{\sqrt{2}} \big(|01\rangle - |10\rangle\big)
    = |\Psi^{-}\rangle
    \\
|S_1\rangle
    &= \frac{1}{2} \big(- |00\rangle + |01\rangle - |10\rangle - |11\rangle\big)
    = \frac{1}{\sqrt{2}} \big(- |0\rangle |-\rangle - |1\rangle |+\rangle \big)
    \xrightarrow{\text{I} \otimes \text{H}} - \frac{1}{\sqrt{2}} \big(|01\rangle + |10\rangle\big)
    = - |\Psi^{+}\rangle
    \\
|S_2\rangle
    &= \frac{1}{2} \big(- |00\rangle - |01\rangle + |10\rangle - |11\rangle\big)
    = \frac{1}{\sqrt{2}} \big(- |0\rangle |-\rangle + |1\rangle |+\rangle \big)
    \xrightarrow{\text{I} \otimes \text{H}} - \frac{1}{\sqrt{2}} \big(|00\rangle - |11\rangle\big)
    = - |\Phi^{-}\rangle
    \\
|S_3\rangle
    &= \frac{1}{2} \big(- |00\rangle - |01\rangle - |10\rangle + |11\rangle\big)
    = \frac{1}{\sqrt{2}} \big(- |0\rangle |-\rangle - |1\rangle |+\rangle \big)
    \xrightarrow{\text{I} \otimes \text{H}} - \frac{1}{\sqrt{2}} \big(|00\rangle + |11\rangle\big)
    = - |\Phi^{+}\rangle
\end{align}
$$

ただし、Task 1.12（$|\Phi^{+}\rangle, |\Phi^{-}\rangle, |\Psi^{+}\rangle, |\Psi^{-}\rangle$）とは順序が逆なので注意。測定では全体の符号は無視できる。

```qsharp
%kata T114_TwoQubitStatePartTwo_Test

open Microsoft.Quantum.Convert;
open Microsoft.Quantum.Measurement;

operation TwoQubitStatePartTwo (qs : Qubit[]) : Int {
    H(qs[1]);
    return 3 - BellState(qs);
}
```

    Success!

### Task 1.15**. Distinguish two orthogonal states on three qubits.

**Input:** Three qubits (stored in an array of length 3) which are guaranteed to be in one of the two orthogonal states.

**Output:**

* 0 if they were in the state $|S_0\rangle = \frac{1}{\sqrt{3}} \big(|100\rangle + \omega |010\rangle + \omega^2 |001\rangle \big)$,
* 1 if they were in the state $|S_1\rangle = \frac{1}{\sqrt{3}} \big(|100\rangle + \omega^2 |010\rangle + \omega |001\rangle \big)$.

Here $\omega = e^{2i \pi/ 3}$.

The state of the qubits at the end of the operation does not matter.

```qsharp
%kata T115_ThreeQubitMeasurement_Test

operation ThreeQubitMeasurement (qs : Qubit[]) : Int {
    // ...
}
```

## Part II*. Discriminating Nonorthogonal States

Variations of quantum state discrimination tasks are covered in the paper ["Quantum State Discrimination"](https://arxiv.org/pdf/quant-ph/0010114.pdf).
* Task 2.1 is an example of hypothesis testing for two pure states.
* Task 2.2 is an example of unambiguous state discrimination. See also the paper ["Unambiguous quantum measurement of nonorthogonal states"](https://www.researchgate.net/publication/13375059_Unambiguous_quantum_measurement_of_nonorthogonal_states)
  for further information and hints about how to implement the unambiguous measurements required for this task.
* Task 2.3 is the so-called Wootters/Peres game. See the following three references for more information and in particular
  the book [3, p. 287] for a nice description of the optimal POVM.

  [1] A. Holevo, “Information-theoretical aspects of quantum measurement,” Problems of Information Transmission, vol. 9, no. 2, pp. 110–118 (1973)

  [2] A. Peres and W. K. Wootters, “Optimal detection of quantum information,” Phys. Rev. Lett., vol. 66, pp. 1119-1122, Mar. 1991.

  [3] A. Peres, “Quantum Theory: Concepts and Methods,” Kluwer Academic Publishers, 2002.

### Task 2.1*. $|0\rangle$ or $|+\rangle$?

**Input:** A qubit which is guaranteed to be in either the $|0\rangle$ or the $|+\rangle$ state.

**Output:**  `true` if the qubit was in the $|0\rangle$ state, or `false` if it was in the $|+\rangle$ state. The state of the qubit at the end of the operation does not matter.

In this task your solution will be called multiple times, with one of the states picked with equal probability every time. You have to get overall accuracy of at least 80%.

This task is an example of quantum hypothesis testing, or state discrimination with minimum error.

```qsharp
%kata T201_IsQubitZeroOrPlus_Test

operation IsQubitPlusOrZero (q : Qubit) : Bool {
    // ...
}
```

### Task 2.2**. $|0\rangle$, $|+\rangle$ or inconclusive?

**Input:** A qubit which is guaranteed to be in either the $|0\rangle$ or the $|+\rangle$ state.

**Output:**

* 0 if the qubit was in the $|0\rangle$ state,
* 1 if it was in the $|+\rangle$ state,
* -1 if you can't decide, i.e., an "inconclusive" result.

Your solution:

* should never give 0 or 1 answer incorrectly (i.e., identify $|0\rangle$ as 1 or $|+\rangle$ as 0),
* will be called multiple times, with one of the states picked with equal probability every time,
* may give an inconclusive (-1) answer in at most 80% of all the cases,
* must correctly identify the $|0\rangle$ state as 0 in at least 10% of all the cases,
* must correctly identify the $|1\rangle$ state as 1 in at least 10% of all the cases.

The state of the qubit at the end of the operation does not matter.

This task is an example of unambiguous state discrimination.

  Need a hint? Click here
  You can use extra qubit(s) is your solution.

```qsharp
%kata T202_IsQubitZeroOrPlusSimpleUSD_Test

operation IsQubitPlusZeroOrInconclusiveSimpleUSD (q : Qubit) : Int {
    // ...
}
```

### Task 2.3**.  Peres/Wooters game

**Input:** A qubit which is guaranteed to be in one of the three states:

* $|A\rangle = \frac{1}{\sqrt{2}} \big( |0\rangle + |1\rangle \big)$,
* $|B\rangle = \frac{1}{\sqrt{2}} \big( |0\rangle + \omega |1\rangle \big)$,
* $|C\rangle = \frac{1}{\sqrt{2}} \big( |0\rangle + \omega^2 |1\rangle \big)$,

Here $\omega = e^{2i \pi/ 3}$.

**Output:**

* 1 or 2 if the qubit was in the $|A\rangle$ state,
* 0 or 2 if the qubit was in the $|B\rangle$ state,
* 0 or 1 if the qubit was in the $|C\rangle$ state.

You are never allowed to give an incorrect answer. Your solution will be called multiple times, with one of the states picked with equal probability every time.

The state of the qubit at the end of the operation does not matter.

```qsharp
%kata T203_IsQubitNotInABC_Test

operation IsQubitNotInABC (q : Qubit) : Int {
    // ...
}
```


**Submission deadline:**
* **Regular problems: last lab session before or on Monday, 9.13.2020b**
* **Bonus problems: Last lab during semester**

**Points: 5 + 9 bonus points**

Please note: some of the assignments are tedious or boring if you are already a NumPy ninja. The bonus problems were designed to give you a more satisfying alternative.

## Heads Up!

This assignment comes with starter code, but you are not forced to use it, as long as you execute all analysis demanded in the problems.

## A note about plots!

Plots are a way of communication. Just lke text, they can be paraphrased. You do not have to exactly reproducy my plots, but you must try to make suer yourp plots tell a similar story:
- label axis
- add titles
- choose plot type properly
- choose a color scale, limits, ticks

so that you can describe what is happening!

## Bugs?!

Please submit Github PRs or email us about any problems with the notebook - we will try to correct them quickly.

```python
# Standard IPython notebook imports
%matplotlib inline

import os

from io import StringIO
import itertools
import httpimport
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from tqdm import tqdm_notebook

import scipy.stats as sstats
import scipy.optimize as sopt

import seaborn as sns

import sklearn.datasets
import sklearn.ensemble
import sklearn.svm
import sklearn.tree

import cvxopt

# In this way we can import functions straight from github
with httpimport.github_repo('janchorowski', 'nn_assignments',
                            module='common', branch='nn18'):
     from common.plotting import plot_mat

sns.set_style('whitegrid')
```

# SVM Theory
A linear SVM assigns points $x^{(i)}\in\mathbb{R}^n$ to one of two
classes,  $y^{(i)}\in\{-1,1\}$ using the decision rule:

\begin{equation}
y = \text{signum}(w^T x + b).
\end{equation}

SVM training consists of finding weights $w\in\mathbb{R}^n$
and bias $b\in\mathbb{R}$ that maximize the separation margin. This
corresponds to solving the following quadratic optimization problem:

\begin{equation}
\begin{split}
  \min_{w,b,\xi}  &\frac{1}{2}w^Tw  + C\sum_{i=1}^m \xi_i  \\
  \text{s.t. } & y^{(i)}(w^T x^{(i)} + b) \geq 1- \xi_i\;\; \forall_i \\
  & \xi_i \geq 0 \;\; \forall_i.
\end{split}
\end{equation}

# Problem 1 [2p]

Load the iris dataset.

1. [1p] Using the `sklearn.svm.SVC` library train a linear SVM that
separates the Virginica from the Versicolor class using the
petal length and petal width features. Plot the obtained decision boundary and
the support vectors (their locations and weights - coefficients $\alpha$).

2. [.5p] Now train a nonlinear SVM using the Gaussian kernel. Tune the parameetrs `C` and `gamma` (for the kernel) to reach maximum training accurracy. Plot the decision boundary and supprt vectors.

3. [.5p] Answer the following questions:
    - When the SVM is forced to maximally accurate on the train set, roughly how many support vectors do we get ?\
    ans: 80%
    - what is the relationship between the regularization constant `C` and the support vector weights `alpha`?\
    ans: The bigger C, the bigger difference between weights

```python
# load iris, extract petal_length and petal_width of versicolors and virginicas

iris = sklearn.datasets.load_iris()
print('Features: ', iris.feature_names)
print('Targets: ', iris.target_names)
petal_length = iris.data[:,iris.feature_names.index('petal length (cm)')]
petal_width = iris.data[:, iris.feature_names.index('petal width (cm)')]

IrisX = np.array(iris.data.T)
IrisX = IrisX[:, iris.target!=0]

IrisX2F = np.vstack([petal_length, petal_width])
IrisX2F = IrisX2F[:, iris.target!=0]

# Set versicolor=0 and virginia=1
IrisY = (iris.target[iris.target!=0]-1).reshape(1,-1).astype(np.float64)

plt.scatter(IrisX2F[0,:], IrisX2F[1,:], c=IrisY.ravel(),
            cmap='spring', edgecolors='k')
plt.xlabel('petal_length')
plt.ylabel('petal_width')
```

```python
#
# Fit a linear SVM using libsvm
#
from sklearn.svm import SVC
svm_model = SVC(kernel="linear")
svm_model.fit(IrisX2F.T, IrisY.ravel())

print("libsvm error rate: %f" % ((svm_model.predict(IrisX2F.T)!=IrisY).mean(),))
```

    libsvm error rate: 0.050000

```python
#
# Plot the decision boundary
#

petal_lengths, petal_widths = np.meshgrid(np.linspace(IrisX2F[0,:].min(), IrisX2F[0,:].max(), 100),
                                          np.linspace(IrisX2F[1,:].min(), IrisX2F[1,:].max(), 100))

IrisXGrid = np.vstack([petal_lengths.ravel(), petal_widths.ravel()])
predictions_Grid = svm_model.predict(IrisXGrid.T)

plt.contourf(petal_lengths, petal_widths, predictions_Grid.reshape(petal_lengths.shape), cmap='spring')

plt.scatter(IrisX2F[0,:], IrisX2F[1,:], c=IrisY.ravel(),
            cmap='spring', edgecolors='k')

plt.xlabel('petal_length')
plt.ylabel('petal_width')
plt.title('Decision boundary found by libsvm')
```

```python
#
# Plot the decision boundary and the support vectors.
#
# You can extract the indices of support vectors and their weights from fielfs of the
# svm object. Display the loaction of support vectors and their weights (by changing the
# size in the scatterplot)
#
# TODO
#

support_vector_indices = svm_model.support_
support_vector_coefficients = svm_model.dual_coef_
plt.contourf(petal_lengths, petal_widths, predictions_Grid.reshape(petal_lengths.shape), cmap='spring')
plt.scatter(
    IrisX2F[0,support_vector_indices],
    IrisX2F[1,support_vector_indices],
    c=IrisY.ravel()[support_vector_indices],
    s=(np.abs(support_vector_coefficients)*10)**2,
    cmap='spring',
    edgecolors='k')

plt.xlabel('petal_length')
plt.ylabel('petal_width')
plt.title('Decision boundary found by libsvm')
```

```python
#
# Fit a nonlinear SVM with a Gaussian kernel using libsvm.
# Optimize the SVM to make
#

svm_gauss_model = SVC(C=3, gamma=100)
svm_gauss_model.fit(IrisX2F.T, IrisY.ravel())

print("libsvm error rate: %f" % ((svm_gauss_model.predict(IrisX2F.T)!=IrisY).mean(),))

petal_lengths, petal_widths = np.meshgrid(np.linspace(IrisX2F[0,:].min(), IrisX2F[0,:].max(), 100),
                                          np.linspace(IrisX2F[1,:].min(), IrisX2F[1,:].max(), 100))

IrisXGrid = np.vstack([petal_lengths.ravel(), petal_widths.ravel()])
predictions_Grid = svm_gauss_model.predict(IrisXGrid.T)

plt.contourf(petal_lengths, petal_widths, predictions_Grid.reshape(petal_lengths.shape), cmap='spring')

sizes = np.zeros(IrisY.shape[-1])
sizes[svm_gauss_model.support_] = np.abs(svm_gauss_model.dual_coef_) **2 *10
# sizes[svm_gauss_model.support_[np.abs(svm_gauss_model.dual_coef_).argsort()[:, -3:]]]*=10
plt.scatter(IrisX2F[0,:], IrisX2F[1,:], c=IrisY.ravel(), s=sizes,
            cmap='spring', edgecolors='k')

print(IrisY.ravel().shape, svm_gauss_model.dual_coef_.shape)

plt.xlabel('petal_length')
plt.ylabel('petal_width')
plt.title('Decision boundary found by libsvm')
```

# Problem 2 [1p]

Reimplement the linear SVM using the use `cvxopt.solvers.qp`
Quadratic Programming (QP) solver. You will need to define the matrices
that define the problem. Compare the obtained solutions. Extract the
support vectors from the LIBSVM solution and plot the support vectors.

The `cvxopt.solvers.qp` solves the following optimization problem:

\begin{align}
\text{minimize over } x \text{: }& \frac{1}{2} x^T P x + q^T x \\
\text{subject to: } & Gx \leq h \\
& Ax = b
\end{align}

\begin{equation}
\begin{split}
  \min_{w,b,\xi}  &\frac{1}{2}w^Tw  + C\sum_{i=1}^m \xi_i  \\
  \text{s.t. } & - y^{(i)} * w^T x^{(i)} + -y^{(i)} * b - \xi_i \leq -1\;\; \forall_i \\
  & \xi_i \geq 0 \;\; \forall_i.
\end{split}
\end{equation}

To solve the SVM problem you need to encode the weights $W$, biases $b$, and slack variables $\xi$ as elements of the vector $x$, then properly fill the matrices and vectors $P$, $q$, $G$, $h$. We can ignore setting the $A$ and $b$ parametrs, since there are no linear constraints.

```python
IrisX2F.shape
```

    (2, 100)

```python
#
# Now solve the SVM using the QP solver
#

n, m = IrisX2F.shape
C=10.0

#x: w | b | ksi
P = np.zeros((n+1+m, n+1+m)) #w, bias, xi
q = np.zeros((n+1+m,1))
G = np.zeros((2*m, n+1+m)) # we have two constrains for each data point:
                           # that the margin is equal to 1-xi
                           # and that xi is nonnegative
h = np.zeros((2*m,1))
#
# TODO: fill in P, q, G, h
#

P[:n, :n] = np.eye(n)
q[n+1:] = np.ones((m,1)) * C

G[:m,:n+1] = -np.ones((m,n+1))
G[:m,:n+1] *= IrisY.T * 2 - 1
G[:m,:n] *= IrisX2F.T

G[:m,n+1:] = -np.eye(m)
G[m:,n+1:] = -np.eye(m)

h[:m,:] = -np.ones((m, 1))

#
# Now run the solver
#
ret  = cvxopt.solvers.qp(cvxopt.matrix(P), cvxopt.matrix(q),
                         cvxopt.matrix(G), cvxopt.matrix(h), )

ret = np.array(ret['x'])

#
# extract the weights and biases
#
W = ret[:n].reshape(-1,1)
b = ret[n]

#
# Extract the weight and bias from libsvm for comparison
#
Wlibsvm = svm_model.coef_
blibsvm = svm_model.intercept_
print()
print('W', W.T, 'Wlibsvm', Wlibsvm)
print('b', b, 'blibsvm', blibsvm)

petal_lengths, petal_widths = np.meshgrid(np.linspace(IrisX2F[0,:].min(), IrisX2F[0,:].max(), 100),
                                          np.linspace(IrisX2F[1,:].min(), IrisX2F[1,:].max(), 100))

IrisXGrid = np.vstack([petal_lengths.ravel(), petal_widths.ravel()])

# predictions_Grid = svm_model.predict(IrisXGrid.T)

plt.contourf(petal_lengths, petal_widths, (W.T @ IrisXGrid + b >= 0).astype(int).reshape(petal_lengths.shape), cmap='spring')

plt.scatter(IrisX2F[0,:], IrisX2F[1,:], c=IrisY.ravel(),
            cmap='spring', edgecolors='k')

plt.xlabel('petal_length')
plt.ylabel('petal_width')
plt.title('Decision boundary found by QP solver')
None
```

# Problem 3 [2p]

Repeat 100 bootstrap experiments to establish the effect of constant $C$ on SVM.

For each experiment do the following:

1. Sample (with replacement) a bootstrap dataset equal in size to the
    training dataset. This will be this experiment's training dataset.
2. Prepare the experiment's testing dataset by using samples not
    inluded in the bootstrap dataset.
3. For all $C$ from the set $\{10^{-4}, 10^{-3.5}, 10^{-3.}, \ldots, 10^{6}\}$
    fit a nonlinear SVM (Gaussian kernel, called \texttt{rbf} in
    LIBSVM using the default $\gamma$) and record the training and
    testing errors.

Analyze a box plot of errors as a function of $C$. Can you see its
influence on the training and testing error, as well as on the
testing error variability?

**Indicate regions of overfitting and underfitting.**

```python
res = []
for rep in range(100):
    bootstrap_sel = np.random.randint(0, IrisY.shape[1], IrisY.shape[1])
    test_sel = np.setdiff1d(np.arange(IrisY.shape[1]), np.unique(bootstrap_sel))

    bootstrap_IrisX = IrisX[:,bootstrap_sel]
    bootstrap_IrisY = IrisY[:,bootstrap_sel]

    test_IrisX = IrisX[:,test_sel]
    test_IrisY = IrisY[:,test_sel]

    #
    # TODO: Loop over a list of exponents.
    #
    for Cexponent in np.arange(-4, 6.5, 0.5):
        C = 10.0**Cexponent
        svm_model = SVC(C=C, gamma='auto')
        svm_model.fit(bootstrap_IrisX.T, bootstrap_IrisY.ravel())
        train_acc = (svm_model.predict(bootstrap_IrisX.T)==bootstrap_IrisY).mean()
        test_acc = (svm_model.predict(test_IrisX.T)==test_IrisY).mean()

        res.append(dict(Cexponent=Cexponent, err=1-test_acc, subset='test'))
        res.append(dict(Cexponent=Cexponent, err=1-train_acc, subset='train'))

res = pd.DataFrame(res)
chart = sns.catplot(kind='box', x='Cexponent', y='err', col='subset',
            color='blue', data=res)
chart.set_xticklabels(rotation=45)
None
```

# Problem 4 [3p bonus]

Implement a nonlinear SVM by solving the dual problem using the Quadratic Programming solver. Compare results with LIBSVM.

Please see [page 20 if CS229 lecture notes](http://cs229.stanford.edu/notes/cs229-notes3.pdf) for problem formulation.

# Problem 5 [2p bonus]
Compare two ways to implement a multi-class
SVM: by training ``1-vs-1`` classifier for each class combination,
and by training a ``1-vs-rest`` classifier for each clas. See
http://www.csie.ntu.edu.tw/~cjlin/papers/multisvm.pdf for
details.

```python

```

# Problem 6 [4p bonus]

Implement a Kernelized linear regression. Train it on Iris using a Gaussian kernel. Compare to the non-linear SVM.


```python
%load_ext autoreload
%autoreload 2

import math
import cmath
import numpy as np
import scipy
import sympy

import matplotlib
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
import pandas as pd
import seaborn as sns
from pylab import cm
import json

import os
import sys
import re
from copy import deepcopy

import matplotlib.patches as patches
from matplotlib import colors as m2colors

import plotly
import plotly.graph_objects as go

from MMfxns import *
from MMplotting import *
from pyDOE2 import lhs

cwd = os.getcwd()
sns.set(style="ticks", font_scale=1.5)

mcolors = dict(m2colors.BASE_COLORS, **m2colors.CSS4_COLORS)

saveall = True

print(plotly.__version__)
print(matplotlib.__version__)
import warnings
warnings.filterwarnings("ignore", category=np.VisibleDeprecationWarning)

pd.set_option('display.expand_frame_repr', False, 'display.max_columns', None)
```

    4.13.0
    3.3.2

```python
# ## testing LHS
# test = lhs(4,10)
# rang = np.array([100,800])

# print(test)

# print(test[:,0] * np.diff(rang) + np.amin(rang))
class noise:
    def __init__(self, mean=0., std = 1., mag = 0.1):
        self.mean = mean
        self.std = std
        self.mag = mag
        self.rg = Generator(PCG64())
    def draw(self):
        return self.mag * self.rg.normal(self.mean, self.std)

test = {}

test['eps'] = noise(0., 1., 1.)
a = []
for ai in np.arange(5000):
    a.append(test['eps'].draw())
plt.hist(np.array(a))

```

```python
#### Energy dependent alpha increase and decrease

resultsDF = pd.DataFrame(columns=['m_profile','t_space','x_prof','alpha_prof','active_region','deltaV'])
resultsDF2 = pd.DataFrame(columns=['m_profile','t_space','x_prof','alpha_prof','active_region','deltaV'])

def set_params(file=None):
    params = {}
    if file:
        with open(file, 'r') as f:
            params = json.load(f)
    else:
        params['tau'] = 1.05
        params['tau_F'] = params['tau'] * 5 # params['tau'] * 2
        params['tau_SG'] = 360 #params['tau'] * 150
        params['tau_SR'] = params['tau_SG']

        params['m0'] = 6.
        params['x0'] = 0.3
        params['a0'] = .8
        params['a_max'] = 50
        params['n'] = 4.
        params['resolution'] = 1.
        params['type'] = 'stiff'
        params['color'] = None
        params['input_m'] = []

        params['tau_R0'] = 200 # params['tau_SG'] #* 2
        params['TV0SR'] = 0.1
        params['TV0SG'] = 3.

        params['dynamics'] = 'exp_dynamicTS'
#         params['eps'] = noise(0., 1., 0.02)

    return params

ins = np.array(
    [
        [24, 2.],
        [240, 10.], #320
        [480, 2.],
    ]
        )

params = set_params()
# params = set_params('./figures/figure4_dynamics_profiles/perm_mem_v2.png.json')
# ins = np.array(params['input_m'][0])
print(params)
resultsDF, params, priming_times, memory_times, stiffP, stiffA = run_profile(integrate_profile, ins, params, resultsDF)
mech_stats = np.abs(stiffP - stiffA) / params['m0']

fig, ax = plt.subplots(2, 2, figsize=(14,9), gridspec_kw={'height_ratios': [1, .5]})
fig.suptitle('Energy Dependent \u03b1')

if 6 in pd.unique(resultsDF['active_region']) or 5 in pd.unique(resultsDF['active_region']):
#     print('blue')
    params['color'] = (0., 0., 1.)
#     params['color'] = (212/255, 0., 0.)
elif 4 in pd.unique(resultsDF2['active_region']):
#     print('red')
    params['color'] = (212/255, 0., 0.)
else:
#     print('pink')
    params['color'] = (1., 0., 1.)

params, fig, fig2, axs = plot_profile(fig, ax[:,0], resultsDF, params, colors =
                                      [mcolors['darkorange'], mcolors['red'], mcolors['navy'],
                                           mcolors['navy'], mcolors['springgreen'], mcolors['springgreen']])

params['input_m'].append(ins.tolist())

fig3, ax3 = plt.subplots(1,1,figsize=(fig.get_size_inches()[0] - fig2.get_size_inches()[0], fig2.get_size_inches()[1]))
labels = ['${t_{prime}}/{t_{memory}}$', '$\Delta_{tot}$']
if np.amax(memory_times > 0):
    barlist = ax3.bar(labels, [np.mean(priming_times / memory_times), mech_stats])
    barlist[0].set_color('g')
    barlist[1].set_color('k')

print ('*********************')

# params = set_params()

# ins = np.array(
#     [
#         [24, 2.3],
#         [168, 10.],
#         [400, 2.3],
#     ]
#         )

# resultsDF2, params, priming_times, memory_times, stiffP, stiffA = run_profile(integrate_profile, ins, params, resultsDF2)
# mech_stats = np.abs(stiffP - stiffA) / params['m0']

# params['input_m'].append(ins.tolist())

# if 6 in pd.unique(resultsDF2['active_region']) or 5 in pd.unique(resultsDF2['active_region']):
# #     print('red')
#     params['color'] = (212/255, 0., 0.)
# elif 4 in pd.unique(resultsDF2['active_region']):
# #     print('blue')
#     params['color'] = (0., 0., 1.)
# else:
# #     print('pink')
#     params['color'] = (1., 0., 1.)

# params['color'] = (212/255, 0., 0.)
# params, fig, _, _ = plot_profile(fig, ax[:,1], resultsDF2, params, colors =
#                                  [mcolors['darkorange'], mcolors['red'], mcolors['navy'],
#                                   mcolors['navy'], mcolors['springgreen'], mcolors['springgreen']], plot_PD = False)

# for ai, aa in enumerate(axs):
#     _,_,_,_ = add_m_traj(aa, resultsDF2['alpha_prof'].values,
#                          resultsDF2['m_profile'].values, resultsDF2['t_space'].values, params)

saveall = False
if saveall:
    direc = os.getcwd() + '/figures/figure4_dynamics_profiles/'
#     direc = os.getcwd() + '/figures/figure6_exp_fits/'

    fname1 = 'profiles_' + params['dynamics'] + "_perm_mem_v2.png"
#     fname1 = "anseth_match_NN_v1.png"
    print(fname1)
    fig.savefig(direc + fname1, format='png', dpi=600)

#     fname2 = direc + "PD_perm_mem_v1.png"
    fname2 = 'PD_' + fname1
    print(fname2)
    fig2.savefig(direc + fname2, format='png', dpi=600)

#     fname3 = direc + "barz_EnergyWell_stiff_v3.png"
#     print(fname3)
#     fig3.tight_layout()
#     fig3.savefig(fname3, format='png', dpi=600)

    with open(direc+fname1 + '.json', 'w') as f:
        f.write(json.dumps(params))

```

```python
# %%capture
import multiprocessing as mp
import timeit
def run_sim(ins, params, trials):

    memout = []
#     params = deepcopy(params)
    for pi in np.arange(len(trials)):

        resultsDF = pd.DataFrame(columns=['m_profile','t_space','x_prof','alpha_prof','active_region','deltaV'])
        resultsDF, params, priming_times, memory_times, stiffP, stiffA = run_profile(integrate_profile, ins, params, resultsDF)
        memout.append(memory_times)

    return memout

ins = np.array(
    [
        [24, 2.],
        [24, 10.], #320
        [480, 2.],
    ]
        )

params = set_params()
# params['input_m'].append(ins.tolist())
start = timeit.default_timer()

numtrials = np.arange(1000)

memappend = [];

# for ni, nn in enumerate(numtrials):
#     params = set_params()
#     out = run_sim(ins, params)
#     print(out)
#     memappend.append(out)

print(memappend)
n_proc = mp.cpu_count() # // 2
chunksize = len(numtrials) // n_proc
proc_chunks = []
for i_proc in range(n_proc):
    chunkstart = i_proc * chunksize
    # make sure to include the division remainder for the last process
    chunkend = (i_proc + 1) * chunksize if i_proc < n_proc - 1 else None

    proc_chunks.append(numtrials[chunkstart:chunkend])

# assert sum(map(len, proc_chunks)) == len(outputDF)
# print(proc_chunks)
with mp.Pool(processes=n_proc) as pool:

    proc_results = [pool.apply_async(run_sim, args=(ins, params, chunk,)) for chunk in proc_chunks]

    # blocks until all results are fetched
    result_chunks = [r.get() for r in proc_results]

results = [item for sublist in result_chunks for item in sublist]
results = np.array([i[0] for i in results])

# print(results)

np.save('../figures_v2/figure6_exp_fits/24.npy', results)
with open('../figures_v2/figure6_exp_fits/24.json', 'w') as f:
    f.write(json.dumps(params))

end = timeit.default_timer()
# print((end - start)/3600, end = ' '); print(' hours')
```

```python
print((end - start)/3600, end = ' '); print(' hours')
# np.save('../figures_v2/figure6_exp_fits/168.npy', results)
# with open('../figures_v2/figure6_exp_fits/168.json', 'w') as f:
#     f.write(json.dumps(params))
plt.hist(results / 24, density=True, cumulative=-1, bins=20);
```

```python
ptime = 240
bins = np.arange(-0.5,30.,1.)

fig, ax = plt.subplots(1,1,figsize=(6,6))

memory = np.load('../figures_v2/figure6_exp_fits/'+ str(ptime) +'.N127.npy')
print(len(memory))
ax.hist(memory / 24, density=True, cumulative=-1, bins=bins, color='blue', alpha = 0.7, label='model');

expdata = pd.read_excel('../experiments/bar_plot.xlsx')

subdata = expdata.loc[expdata['prime'].isin([0,ptime])]
subdata.at[subdata['type'] == 'soft', 'relaxation time'] = 16 * 24 # np.amax(bins * 24) + 3*24
subdata.at[subdata['type'] == 'stiff', 'relaxation time'] = -2 * 24
print(subdata)

ax.bar(subdata.loc[subdata['units']=='hr','relaxation time'] / 24, subdata.loc[subdata['units']=='hr','YAP']/100.,
       width=1.5, color='g', alpha=0.7, label='prime 10')
ax.bar(subdata.loc[subdata['units']=='control','relaxation time'] / 24, subdata.loc[subdata['units']=='control','YAP']/100.,
       width=1.5, color='k', alpha=1., label='controls')
# plt.bar(subdata['relaxation time'] / 24, subdata['RUNX2']/100., color='r', alpha=0.7)

N = 1500
gausscompare = np.abs(np.random.normal(np.mean(memory/24), np.std(memory/24), N))
# gausscompare = np.random.normal(np.mean(memory/24), np.std(memory/24), N)
# gausscompare[gausscompare < 0.] = 0.
print(np.mean(memory/24))
print(np.std(memory/24))
print(np.std(memory/24)/np.sqrt(N))

ax.hist(gausscompare, density=True, cumulative=-1, bins=bins, label='normal CDF',
         histtype='step', alpha=0.8, color='k', linewidth=2.)

ax.set_xlim([-3.5,18])
ax.set_xlabel('memory (days)')
ax.set_ylabel('fraction of cells w/ elevated $x_{nuc}$')
ax.legend(bbox_to_anchor=(0, 1.2), loc='upper left', ncol=2)
```

```python
ptime = 168
bins = np.arange(-0.5,20.,.5)

fig, ax = plt.subplots(1,1,figsize=(6,6))

memory = np.load('../figures_v2/figure6_exp_fits/'+ str(ptime) +'.N127.npy') # '
print(len(memory))
ax.hist(memory / 24, density=True, cumulative=-1, bins=bins, color='blue', alpha = 0.7, label='model');

expdata = pd.read_excel('../experiments/bar_plot.xlsx')

subdata = expdata.loc[expdata['prime'].isin([0,ptime])]
subdata.at[subdata['type'] == 'soft', 'relaxation time'] = 8*24 # np.amax(bins * 24) + 3*24
subdata.at[subdata['type'] == 'stiff', 'relaxation time'] = -1.5 * 24
print(subdata)

ax.bar(subdata.loc[subdata['units']=='hr','relaxation time'] / 24, subdata.loc[subdata['units']=='hr','YAP']/100.,
       width=1.5, color='g', alpha=.9, label='prime 7')
ax.bar(subdata.loc[subdata['units']=='control','relaxation time'] / 24, subdata.loc[subdata['units']=='control','YAP']/100.,
       width=1.5, color='k', alpha=1., label='controls')
# plt.bar(subdata['relaxation time'] / 24, subdata['RUNX2']/100., color='r', alpha=0.7)

N = 1500
gausscompare = np.random.normal(np.mean(memory/24), np.std(memory/24), N)
gausscompare[gausscompare < 0.] = 0.
print(np.mean(memory/24))
print(np.std(memory/24))
print(np.std(memory/24)/np.sqrt(N))

ax.hist(gausscompare, density=True, cumulative=-1, bins=bins, label='normal CDF',
         histtype='step', alpha=0.8, color='k', linewidth=2)

ax.set_xlim([-3.5,10])
ax.set_xlabel('memory (days)')
ax.set_ylabel('fraction of cells w/ elevated $x_{nuc}$')
ax.legend(bbox_to_anchor=(0, 1.2), loc='upper left', ncol=2)
```

```python
# ## test cell for broken axis

# fig, ax = plt.subplots(2, 2, figsize=(14,9), gridspec_kw={'height_ratios': [1, .75]})
# fig.suptitle('Energy \u03b1')

# params['color'] = mcolors['red']
# params, fig, fig2, axs = plot_profile(fig, ax[:,0], resultsDF, params, colors =
#                                       [mcolors['darkorange'], mcolors['red'], mcolors['navy'],
#                                            mcolors['navy'], mcolors['springgreen'], mcolors['springgreen']])

# params, fig, _, _ = plot_profile(fig, ax[:,1], resultsDF2, params, colors =
#                                  [mcolors['darkorange'], mcolors['red'], mcolors['navy'],
#                                   mcolors['navy'], mcolors['springgreen'], mcolors['springgreen']], plot_PD = False)
# params['color'] = mcolors['black']
# for ai, aa in enumerate(axs):
#     _,_,_,_ = add_m_traj(aa, resultsDF2['alpha_prof'].values, resultsDF2['m_profile'].values, resultsDF2['t_space'].values, params)
```

```python
#### figure 4 stiff genes
#### integrate profile DOES NOT DEPEND ON WELL DEPTH
## setting time units to hours
params = {}
resultsDF = pd.DataFrame(columns=['m_profile','t_space','x_prof','alpha_prof','active_region','deltaV'])
resultsDF2 = pd.DataFrame(columns=['m_profile','t_space','x_prof','alpha_prof','active_region','deltaV'])

def set_params(file=None):
    params = {}
    if file:
        with open(file, 'r') as f:
            params = json.load(f)
    else:
        params['tau'] = .94
        params['tau_F'] = params['tau'] * 2 # params['tau'] * 2

        params['m0'] = 5.47
        params['x0'] = 0.2
        params['a0'] = 0.2
        params['a_max'] = 60.5
        params['n'] = 3.46
        params['resolution'] = 0.2
        params['type'] = 'stiff'
        params['color'] = None
        params['input_m'] = []

        params['tau_SG'] = 170.1 # params['tau'] * 180
        params['tau_SR'] = 380.0 # params['tau_SG'] * 6

        params['dynamics'] = 'exp_staticTS'

    return params

params = set_params()
# 480 hours = 20 days
# time in hours | # stiffness
ins = np.array(
    [
        [24, 2.],
        [240, 10.],
        [500, 2.],
    ]
        )

resultsDF, params, priming_times, memory_times, stiffP, stiffA = run_profile(integrate_profile, ins, params, resultsDF)
mech_stats = np.abs(stiffP - stiffA) / params['m0']

fig, ax = plt.subplots(2, 2, figsize=(14,9), gridspec_kw={'height_ratios': [1, .5]})
fig.suptitle('Static \u03b1')

if 6 in pd.unique(resultsDF['active_region']) or 5 in pd.unique(resultsDF['active_region']):
#     print('red')
    params['color'] = (212/255, 0., 0.)
elif 4 in pd.unique(resultsDF2['active_region']):
#     print('blue')
    params['color'] = (0., 0., 1.)
else:
#     print('pink')
    params['color'] = (1., 0., 1.)

params, fig, fig2, axs = plot_profile(fig, ax[:,0], resultsDF, params, colors =
                                      [mcolors['darkorange'], mcolors['red'], mcolors['navy'],
                                           mcolors['navy'], mcolors['springgreen'], mcolors['springgreen']])

fig3, ax3 = plt.subplots(1,1,figsize=(fig.get_size_inches()[0] - fig2.get_size_inches()[0], fig2.get_size_inches()[1]))
labels = ['${t_{prime}}/{t_{memory}}$', '$\Delta_{tot}$']
if np.amax(memory_times > 0):
    barlist = ax3.bar(labels, [np.mean(priming_times / memory_times), mech_stats])
    barlist[0].set_color('g')
    barlist[1].set_color('k')

params['input_m'].append(ins.tolist())

print ('*********************')

params = set_params()

ins = np.array(
    [
        [24, 2],
        [110, 10],
        [300, 2],
    ]
        )

resultsDF2, params, priming_times, memory_times, stiffP, stiffA = run_profile(integrate_profile, ins, params, resultsDF2)
mech_stats = np.abs(stiffP - stiffA) / params['m0']

params['input_m'].append(ins.tolist())

if 6 in pd.unique(resultsDF2['active_region']) or 5 in pd.unique(resultsDF2['active_region']):
#     print('blue')
    params['color'] = (0., 0., 1.)
elif 4 in pd.unique(resultsDF2['active_region']):
    #     print('red')
    params['color'] = (212/255, 0., 0.)
else:
#     print('pink')
    params['color'] = (1., 0., 1.)

params, fig, _, _ = plot_profile(fig, ax[:,1], resultsDF2, params, colors =
                                 [mcolors['darkorange'], mcolors['red'], mcolors['navy'],
                                  mcolors['navy'], mcolors['springgreen'], mcolors['springgreen']], plot_PD = False)

for ai, aa in enumerate(axs):
    _,_,_,_ = add_m_traj(aa, resultsDF2['alpha_prof'].values,
                         resultsDF2['m_profile'].values, resultsDF2['t_space'].values, params)

# plt.plot(np.arange(len(deltaVlist)), deltaVlist)
saveall = False
if saveall:
    direc = os.getcwd() + '/figures/figure5_profile_NoEdependent/'

    fname1 = direc + "profiles_noEnergyWell_stiff_2panel_v2.png"
    print(fname1)
    fig.savefig(fname1, format='png', dpi=600)

    fname2 = direc + "PD_noEnergyWell_stiff_2panel_v2.png"
    print(fname2)
    fig2.savefig(fname2, format='png', dpi=600)

    fname3 = direc + "barz_noEnergyWell_stiff_v2.png"
    print(fname3)
    fig3.tight_layout()
    fig3.savefig(fname3, format='png', dpi=600)

    with open(fname1 + '.json', 'w') as f:
        f.write(json.dumps(params))

```

```python
#### figure 4 stiff genes
#### integrate profile constant
## setting time units to hours
params = {}
resultsDF = pd.DataFrame(columns=['m_profile','t_space','x_prof','alpha_prof','active_region','deltaV'])
resultsDF2 = pd.DataFrame(columns=['m_profile','t_space','x_prof','alpha_prof','active_region','deltaV'])

def set_params(file=None):
    params = {}
    if file:
        with open(file, 'r') as f:
            params = json.load(f)
    else:
        params['tau'] = 0.94
        params['tau_F'] = params['tau'] * 2
        params['tau_SG'] = 170.1 # params['tau'] * 180
        params['tau_SR'] = 380.0 # params['tau_SG'] * 6

        params['m0'] = 5.47
        params['x0'] = 0.2
        params['a0'] = 0.8
        params['a_max'] = 60.5
        params['n'] = 3.46
        params['resolution'] = 0.4 # 0.05
        params['type'] = 'stiff'
        params['color'] = None
        params['input_m'] = []
#         params['tau_SG'] = 120.0 # params['tau'] * 180
#         params['tau_SR'] = 720.0 # params['tau_SG'] * 6
        params['dynamics'] = 'constant'

    return params

params = set_params()

# 480 hours = 20 days
# time in hours | # stiffness
ins = np.array(
    [
        [24, 2.],
        [96, 10.],
        [300, 2.],
    ]
        )
print(params)
resultsDF, params, priming_times, memory_times, stiffP, stiffA = run_profile(integrate_profile, ins, params, resultsDF)
mech_stats = np.abs(stiffP - stiffA) / params['m0']

print(params['t_prime'])

fig, ax = plt.subplots(2, 2, figsize=(14,9), gridspec_kw={'height_ratios': [1, .5]})
fig.suptitle('Static \u03b1')

if 6 in pd.unique(resultsDF['active_region']) or 5 in pd.unique(resultsDF['active_region']):
#     print('blue')
    params['color'] = (0., 0., 1.)
elif 4 in pd.unique(resultsDF2['active_region']):
    #     print('red')
    params['color'] = (212/255, 0., 0.)
else:
#     print('pink')
    params['color'] = (1., 0., 1.)

params, fig, fig2, axs = plot_profile(fig, ax[:,0], resultsDF, params, colors =
                                      [mcolors['darkorange'], mcolors['red'], mcolors['navy'],
                                           mcolors['navy'], mcolors['springgreen'], mcolors['springgreen']])
params['input_m'].append(ins.tolist())
print(params['time_to_AC']/24)

print ('*********************')

# params = set_params()

# ins = np.array(
#     [
#         [24, 2],
#         [110, 10],
#         [300, 2],
#     ]
#         )

# resultsDF2, params, priming_times, memory_times, stiffP, stiffA = run_profile(integrate_profile, ins, params, resultsDF2)
# mech_stats = np.abs(stiffP - stiffA) / params['m0']

# params['input_m'].append(ins.tolist())

# if 6 in pd.unique(resultsDF2['active_region']) or 5 in pd.unique(resultsDF2['active_region']):
# #     print('red')
#     params['color'] = (212/255, 0., 0.)
# elif 4 in pd.unique(resultsDF2['active_region']):
# #     print('blue')
#     params['color'] = (0., 0., 1.)
# else:
# #     print('pink')
#     params['color'] = (1., 0., 1.)

# params, fig, _, _ = plot_profile(fig, ax[:,1], resultsDF2, params, colors =
#                                  [mcolors['darkorange'], mcolors['red'], mcolors['navy'],
#                                   mcolors['navy'], mcolors['springgreen'], mcolors['springgreen']], plot_PD = False)

# for ai, aa in enumerate(axs):
#     _,_,_,_ = add_m_traj(aa, resultsDF2['alpha_prof'].values,
#                          resultsDF2['m_profile'].values, resultsDF2['t_space'].values, params)

# # plt.plot(np.arange(len(deltaVlist)), deltaVlist)
saveall = True
if saveall:
    direc = os.getcwd() + '/figures/figure4_dynamics_profiles/'

    fname1 = 'profiles_' + params['dynamics'] + '_stiff_nomem.png'
    print(fname1)
    fig.savefig(direc + fname1, format='png', dpi=600)

    fname2 = 'PD_' + fname1
    print(fname2)
    fig2.savefig(direc + fname2, format='png', dpi=600)

    with open(fname1 + '.json', 'w') as f:
        f.write(json.dumps(params))

```

```python
#### figure 4 heat map of equilibrium value of x

params = {}
resultsDF = pd.DataFrame(columns=['m_profile','t_space','x_prof','alpha_prof','active_region','deltaV'])
resultsDF2 = pd.DataFrame(columns=['m_profile','t_space','x_prof','alpha_prof','active_region','deltaV'])

def set_params(file=None):
    params = {}
    if file:
        with open(file, 'r') as f:
            params = json.load(f)
    else:
        params['tau'] = 1. # for the equation
        params['tau_F'] = 1. # for alpha
        params['tau_SG'] = 170.1 # params['tau'] * 180
        params['tau_SR'] = 380.0 # params['tau_SG'] * 6

        params['m0'] = 1.
        params['x0'] = 0.2
        params['a0'] = 0.8
        params['a_max'] = 2.5
        params['n'] = 4.
        params['resolution'] = 0.4 # 0.05
        params['type'] = 'stiff'
        params['color'] = None
        params['input_m'] = []
#         params['tau_SG'] = 120.0 # params['tau'] * 180
#         params['tau_SR'] = 720.0 # params['tau_SG'] * 6
        params['dynamics'] = 'constant'

    return params

params = set_params()

x_c = x_crit(params['n'])
a_c = alpha_crit(params['n'], params['tau'])
params['x_c'] = x_c; params['a_c'] = a_c;
print(a_c)

# a_space, mtst = calc_PD(params)
# print(a_space)
# print(mtst)

m_c = scipy.optimize.fsolve(m_crit_general, 0.5, args=(params), xtol=1e-10)[0] / params['m0']
params['m_c'] = m_c
print(m_c)

Ndim = 200
x_space = np.linspace(0, 5., Ndim)
m_space = np.linspace(0.05,params['m_c']*3,Ndim) * params['m0']
a_space = np.linspace(0.01, params['a_c']*3, Ndim)
print(np.amax(m_space))
a_ind = np.where(np.abs(a_space - a_c) == np.amin(np.abs(a_space - a_c)))[0]

U_data = np.zeros((len(x_space), len(a_space), len(m_space)))
for mi, mm in enumerate(m_space):
    for ai, aa in enumerate(a_space):
        for xi, xx in enumerate(x_space):
            U_data[xi, ai, mi] = U(f_m, mm, xx, aa, params)

x_arr_max = np.zeros((Ndim,Ndim))
x_arr_min = np.zeros((Ndim,Ndim))
m0coords = []; m1coords = []; m2coords = []
for mi, mm in enumerate(m_space):
    for ai, aa in enumerate(a_space):
        xargs = find_peaks(-np.abs(np.diff(U_data[:,ai,mi])))[0]
        Utest = U_data[xargs,ai,mi]
        xmins = x_space[xargs]

        if len(xmins) > 0:
            x_arr_max[mi, ai] = xmins[np.argmin(Utest)]
            x_arr_min[mi, ai] = np.amin(xmins) # xmins[np.argmin(Utest)]
        else:
            try:
                x_arr_max[mi, ai] = np.amax(xmins)
#                 x_arr_min[mi, ai] = np.amin(xmins)
            except:
                x_arr_max[mi, ai] = 0.
#                 x_arr_min[mi, ai] = 0.

fig, ax = plt.subplots(1,1, figsize=(6,8))

## absolute m
# cf = ax.contourf(a_space, m_space, x_arr, levels=100, cmap=cm.Purples_r)
# ylims = ax.get_ylim()
# ax.plot([params['a_c'], params['a_c']],[np.amin(m_space), params['m_c']*params['m0']*2], mcolors['green'], linewidth=3.5, linestyle='-.', label='\u03b1$_{c}$')
# ax.plot([np.amin(a_space), np.amax(a_space)], [params['m_c']*params['m0'], params['m_c']*params['m0']], mcolors['deeppink'], linewidth=3.5, linestyle='-.', lab

## m over m0
cf = ax.contourf(a_space, m_space / params['m0'], x_arr_max, levels=100, cmap=cm.Purples_r)
ylims = ax.get_ylim()
ax.plot([params['a_c'], params['a_c']],[ylims[0], ylims[1]], mcolors['green'], linewidth=3.5, linestyle='-.', label='\u03b1$_{c}$')
ax.plot([np.amin(a_space), np.amax(a_space)], [params['m_c'], params['m_c']], mcolors['deeppink'], linewidth=3.5, linestyle='-.', label='m$_{c}$')

ax.set_ylim(ylims)

cb = plt.colorbar(cf, ax=ax, format='%.2f', orientation='horizontal')
tick_locator = ticker.MaxNLocator(nbins=5)
cb.locator = tick_locator
cb.update_ticks()

randcurves = np.floor(np.linspace(0, np.amax(a_space), 4))
randcurves = np.array([20,50,75,99])

fig3, ax3 = plt.subplots(1,1, figsize=(8,8))

colors = ['b','g','r','c','y','k']

for di, dd in enumerate(randcurves):

    ax3.scatter(m_space / params['m0'], x_arr_max[:,int(dd)], color=colors[di])
ax3.scatter(m_space / params['m0'], x_arr_max[:,a_ind], color=colors[di+1])

for di, dd in enumerate(randcurves):

    ax3.scatter(m_space / params['m0'], x_arr_min[:,int(dd)], color=colors[di])
ax3.scatter(m_space / params['m0'], x_arr_min[:,a_ind], color=colors[di+1])

plt.show()

fig2, ax2 = plt.subplots(1,1, figsize=(6,6))
baseline = f_m(m_space, params)
cf2 = ax2.contourf(a_space, m_space, x_arr_max - baseline[:,np.newaxis], levels=100, cmap=cm.Purples_r)
plt.colorbar(cf, ax=ax2)

saveall = False
if saveall:
    direc = os.getcwd() + '/figures/figure4_dynamics_profiles/'

    fname1 = direc + "x_overlay_phasediagram_values.png"
    print(fname1)
    fig.tight_layout()
    fig.savefig(fname1, format='png', dpi=600)

    with open(fname1 + '.json', 'w') as f:
        f.write(json.dumps(params))

```

```python
## figure 6
## data maps for parameters, static and Energy dependent.
## spearman correlation map
## heat map for priming time, memory time, distance from m_c.

# goal: assemble different inputs, get profiles, collocate summary_stats output, make a grid of the inputs and plot.
#(probably very sensitive)

direc = './stiff_results/staticTS/'
fname = 'static_LHS_SG_SR_ng'
# fname2 = 'static_LHS_SG_SR_n4'

allDF = pd.read_csv(direc + fname +'.csv')
# allDF2 = pd.read_csv(direc + fname2 +'.csv')
input_prof = np.load(direc + fname + '_inputs.npy', allow_pickle=True)
# input_prof2 = np.load(direc + fname2 + '_inputs.npy', allow_pickle=True)

# allDF = pd.concat((allDF, allDF2))

allDF = allDF.reset_index(drop=True)
print(allDF.head(1))
print(len(allDF))
# print(input_prof.shape)
# print(input_prof)
prettylabel = {
#                         'prime_time':'$t_{prime}$',
                        'tau_SG':'${\\tau}_{SG}$',
                        'tau_SR':'${\\tau}_{SR}$',
                        # 'm0':'${m}_{0}$',
                        'tau':'${\\tau}$',
                        'delta_prime':'${\Delta}_{prime}$',
                        'delta_mem':'${\Delta}_{mem}$',
                        'delta_tot':'${\Delta}_{tot}$',
                        'n':'n'
                        }

allDF['delta_prime'] = (allDF['prime_stiff'] - allDF['m_c'] * allDF['m0']) / allDF['m0']
allDF['delta_mem'] = (allDF['m_c'] * allDF['m0'] - allDF['mem_stiff']) / allDF['m0']
allDF['delta_tot'] = allDF['delta_prime'] + allDF['delta_mem']
allDF['tm_over_tp'] = allDF['mem_time'] / allDF['prime_time']

memDF = allDF.loc[allDF['mem_time'] > 0.].loc[allDF['mem_time'].notna()]

subcols = ['tau','tau_SG','tau_SR', 'delta_prime', 'delta_mem', 'delta_tot','n'] # 'm0','tau',
fig, ax = plt.subplots(1,1,figsize=(6, 6))

mem_corr = plot_memcorr(memDF, subcols, prettylabel, ax)
mem_corr = mem_corr.iloc[mem_corr.abs().argsort()]
heat_cols = mem_corr.index.values[-2:]
print(heat_cols)

heat_cols = ['delta_prime', 'tau_SG']
fig2 = plot_groupedDF_heatmap(memDF[subcols + ['tm_over_tp']], heat_cols, prettylabel, [None, [120, 400]])

fig2.show()

saveall = False
if saveall:
    direc = os.getcwd() + '/figures/figure6/'

    fname1 = direc + "staticNG_spearman_corrs_v1.png"
    print(fname1)
    fig.savefig(fname1, format='png', dpi=600)

    fname2 = direc + "static_heatmap_v1.png"
    fig2.write_image(fname2, scale = 1.5, engine='kaleido')

```

```python
## figure 6
## data maps for parameters, Energy DEPENDENT

direc = './stiff_results/dynamicTS/'
# fname = 'energy_dependent_LHS_n3'
fname = 'energy_dependent_LHS_ng_all'
# fname2 = 'energy_dependent_LHS_ng2'
# fname2 = 'static_paramtest_SG_SR_tautest_n4'

allDF = pd.read_csv(direc + fname +'.csv')
# allDF2 = pd.read_csv(direc + fname2 +'.csv')
input_prof = np.load(direc + fname + '_inputs.npy', allow_pickle=True)
# input_prof2 = np.load(direc + fname2 + '_inputs.npy', allow_pickle=True)

# allDF = pd.concat((allDF, allDF2))
# input_prof = np.concatenate((input_prof, input_prof2))

allDF = allDF.reset_index(drop=True)
print(allDF.head(1))
print(len(allDF))

# allDF.to_csv('energy_dependent_LHS_ng_all'+'.csv')
# np.save('energy_dependent_LHS_ng_all'+'_inputs.npy', input_prof)

# sys.exit()

prettylabel = {
                        #'prime_time':'$t_{prime}$',
                        'tau_R0':'${\\tau}_{SGR}$',
                        'TV0SG':'${V}_{SG}$',
                        'TV0SR':'${V}_{SR}$',
                        'm0':'${m}_{0}$',
                        'tau':'${\\tau}$',
                        'delta_prime':'${\Delta}_{prime}$',
                        'delta_mem':'${\Delta}_{mem}$',
                        'delta_tot':'${\Delta}_{tot}$',
                        'n':'n'
                        }

allDF['delta_prime'] = (allDF['prime_stiff'] - allDF['m_c'] * allDF['m0']) / allDF['m0']
allDF['delta_mem'] = (allDF['m_c'] * allDF['m0'] - allDF['mem_stiff']) / allDF['m0']
allDF['delta_tot'] = allDF['delta_prime'] + allDF['delta_mem']
allDF['tm_over_tp'] = allDF['mem_time'] / allDF['prime_time']

# allDF.to_csv('./stiff_results/energy_dependent_LHS_ng_addcols.csv')
# sys.exit()

memDF = allDF.loc[allDF['mem_time'] > 0.].loc[allDF['mem_time'].notna()]

# memDF[subcols].apply(lambda x: x - x.mean()/x.std(), axis=0)

subcols = ['tau', 'tau_R0', 'TV0SG', 'TV0SR', 'delta_prime', 'delta_mem', 'delta_tot', 'n'] # 'm0','tau',
# subcols = ['tau', 'tau_R0', 'TV0SG', 'TV0SR', 'delta_prime', 'delta_mem', 'delta_tot']

# memDF[subcols + ['tm_over_tp']] = memDF[subcols + ['tm_over_tp']].apply(lambda x: x - x.mean()/x.std(), axis=0)

fig, ax = plt.subplots(1,1,figsize=(7, 6))
mem_corr = plot_memcorr(memDF, subcols, prettylabel, ax)
mem_corr = mem_corr.iloc[mem_corr.abs().argsort()]
heat_cols = mem_corr.index.values[-2:]
print(heat_cols)

heat_cols = ['TV0SG', 'tau_R0']
fig2 = plot_groupedDF_heatmap(memDF[subcols + ['tm_over_tp']], heat_cols, prettylabel, [None, [150,700]])

plt.show()
fig2.show()

saveall = False
if saveall:
    direc = os.getcwd() + '/figures/figure6/'

    fname1 = direc + "energyNG_spearman_corrs_v2.png"
    print(fname1)
    fig.savefig(fname1, format='png', dpi=600)

    fname2 = direc + "energyNG_heatmap_v2.png"
    fig2.write_image(fname2, scale = 1.5, engine='kaleido')
```

```python
## test cell for failures

failU = np.load('failureU2.npy', allow_pickle=True)

print(failU)

failU = failU[0:10000]

# print(failU[0:100])
fig, ax = plt.subplots(3, 1, figsize=(6,10))

ax[0].plot(np.arange(len(failU)-1), np.abs(np.diff(failU)))
ax[0].scatter(diff_algo, np.abs(np.diff(failU))[diff_algo])

print(argrelextrema(np.abs(np.diff(failU)), np.less)[0])
print(find_peaks(-np.abs(np.diff(failU)))[0])

diff_algo = find_peaks(-np.abs(np.diff(failU)))[0]
algo = find_peaks((-failU))[0]

ax[1].plot(np.arange(len(failU)), failU)
ax[1].scatter(diff_algo, failU[diff_algo])

ax[2].plot(np.arange(len(failU)), failU)
ax[2].scatter(algo, failU[algo])

params = {}
params['tau'] = 1.
params['m0'] = 6.
params['n'] = 3
params['type'] = 'stiff'

m = 3.
a = 1.192
xUd = np.linspace(0,3,int(1e6))

x_current = 0.73

U_data = U(f_m, m, xUd, a, params)

x_args = find_peaks(-np.abs(np.diff(U_data)))[0]
x_argmin = x_args[np.abs(xUd[x_args]-x_current) == np.amin(np.abs(xUd[x_args]-x_current))]

plt.plot(xUd, U_data)
plt.scatter(xUd[x_args], U_data[x_args])
```

```python
## test cell for broken axis

fig, ax = plt.subplots(2, 2, figsize=(14,9), gridspec_kw={'height_ratios': [1, .75]})
fig.suptitle('Static \u03b1')

params['color'] = mcolors['red']
params, fig, fig2, axs = plot_profile(fig, ax[:,0], resultsDF, params, colors =
                                      [mcolors['darkorange'], mcolors['red'], mcolors['navy'],
                                           mcolors['navy'], mcolors['springgreen'], mcolors['springgreen']])

params, fig, _, _ = plot_profile(fig, ax[:,1], resultsDF2, params, colors =
                                 [mcolors['darkorange'], mcolors['red'], mcolors['navy'],
                                  mcolors['navy'], mcolors['springgreen'], mcolors['springgreen']], plot_PD = False)
params['color'] = mcolors['black']
for ai, aa in enumerate(axs):
    _,_,_,_ = add_m_traj(aa, resultsDF2['alpha_prof'].values, resultsDF2['m_profile'].values, resultsDF2['t_space'].values, params)
```