# Notebook from RutvikM3/dymos
Path: docs/getting_started/defining_odes.ipynb

try:
    import openmdao.api as om
    import dymos as dm
except ImportError:
    !python -m pip install openmdao[notebooks]
    !python -m pip install dymos[docs]
    import openmdao.api as om
    import dymos as dm_____no_output_____

# Defining ODEs

Optimal control problems contain ordinary differential equations (ODE) or differential algebraic equations (DAE) that dictate the evolution of the state of the system.
Typically this evolution occurs in time, and the ODE represents equations of motion (EOM).
The equations of motion can define a variety of systems, not just mechanical ones.
In other fields they are sometimes referred to as *process equations*.

\begin{align}
    \dot{\bar{x}} = f_{ode}(\bar{x},t,\bar{u},\bar{d})
\end{align}

To represent EOM, Dymos uses a standard OpenMDAO System (a Group or Component).
This System takes some set of variables as input and computes outputs that include the time-derivatives of the state variables $\bar{x}$.
The ODE may also be a function of the current time $t$.

Finally, the dynamics may be subject to some set of controllable parameters.
In Dymos these are broken into the dynamic controls $\bar{u}$ and the static parameters $\bar{d}$.

##  System Options

ODE Systems in Dymos need provide values at numerous points in time we call nodes.
For performance reasons, it's best if it can do so using vectorized mathematical operations to compute the values simultaneously rather than using the loop to perform the calculation at each node.
Different optimal control transcriptions will need to have computed ODE values at different numbers of nodes, so each ODE system in Dymos is required to support the option `num_nodes`, which is defined in the `initialize` method.

ODE system may define initial options as well.
Since these options in OpenMDAO are typically provided as arguments to the instantiation of the ODE system, the user has the ability to provide additional input keyword arguments using the `ode_init_kwargs` option on Phase.

## Variables of the Optimal Control Problem

Dymos needs to know how state, time, and control variables are to be connected to the System, and needs to know which outputs to use as state time-derivatives.

### Time

The following time options can be set via the `set_time_options` method of Phase:_____no_output_____

from IPython.display import display, HTML, IFrame, Code
from dymos.phase.options import TimeOptionsDictionary
obj = TimeOptionsDictionary()
display(HTML(obj.to_table(fmt='html')))_____no_output_____

### States

States have the following options set via the `add_state` and `set_state_options` methods of Phase:_____no_output_____

from dymos.phase.options import StateOptionsDictionary
obj = StateOptionsDictionary()
display(HTML(obj.to_table(fmt='html')))_____no_output_____

###  Controls

Inputs to the ODE which are to be dynamic controls are added via the `add_control` and `set_control_options` methods of Phase. The available options are as follows:_____no_output_____

from dymos.phase.options import ControlOptionsDictionary
obj = ControlOptionsDictionary()
display(HTML(obj.to_table(fmt='html')))_____no_output_____

###  Parameters

Inputs to the ODE which are non-time-varying can be specified using the `add_parameter` method of Phase.
Parameters may be used as design variables (by setting `opt = True`), or they may be connected to an output external to the Phase or Trajectory.
The available options are as follows:_____no_output_____

from dymos.phase.options import ParameterOptionsDictionary
obj = ParameterOptionsDictionary()
display(HTML(obj.to_table(fmt='html')))_____no_output_____

By default, Dymos assumes that the ODE is defined such that a value of the parameter is provided at each node.
This makes it easier to define the partials for the ODE in a way such that some inputs may be used interchangeably as either controls or parameters.
If an ODE input is only to be used as a static variable (and not sized as `num_nodes` in the first dimension), then the user may specify option `static_target = True` to override this behavior.

### Tagging Variables

Dymos will also automatically find and add any states that have been declared in components in the ODE. The syntax
for declaring them is as follows._____no_output_____```python
self.add_output('vdot', val=np.zeros(nn), desc='acceleration magnitude', units='m/s**2',
                tags=['dymos.state_rate_source:v', 'dymos.state_units:m/s'])
```_____no_output_____The state is defined by adding tags to the state rate's output.
The tag 'dymos.state_rate_source:v' declares that 'v' is the state for which this output ('vdot') is the rate source.
You can also optionally use a tag in the format 'dymos.state_units:m/s' to define units for that state.
If you need to set any other options, then use `set_state_options` at the phase level.

Inputs which are sized as 'static' in the ODE (the first dimension is _not_ the number of nodes), must be made known to Dymos when using them as parameters.
This is normally accomplished with the 'static_target=True' option when calling add_parameter.
To avoid repetitive use of this option, users can add the tag 'dymos.static_target' to any input which should always be considered static.
When connecting to multiple targets, a 'dymos.static_target' tag on one of them will result in all of them being considered static.
Inputs tagged with 'dymos.static_target' cannot be used as controls or polynomial controls.

| Tag                                  | Applies to | Description                                                                                |
|--------------------------------------|------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| dymos.state_rate_source:{state_name} | Outputs    | Specifies that the tagged output provides the rate used to integrate the state of the given name.                                                                                |
| dymos.state_units:{units}            | Outputs    | Specifies the default units to be used for the state whose rate is provided by the tagged output. May only be used on outputs also tagged with 'dymos.state_rate_source:{state_name}'.                                      |
| dymos.static_target                  | Inputs     | Specifies that the input is _not_ shaped with num_nodes as the first dimension. This effects the way in which parameters are connected to the input. Inputs tagged with 'dymos.static_target' cannot be used as controls.   |

## Automatic target detection

Dymos will attempt to automatically connect variables to targets in the same name if those targets
exist at the top-level of the ODE and have the same name as the state, control, parameter, or 'time'.

## Vectorizing the ODE

In addition to specifying the ODE Options, a system used as an ODE is required to have a metadata
entry called `num_nodes`.  When evaluating the dynamics, these systems will receive time, states,
controls, and other inputs as *vectorized* values, where item in the vector represents the variable
value at a discrete time in the trajectory.

The nodes are discretization or collocation locations in the polynomials which represent
each segment.  The number of nodes in a given phase (to be evaluated by the ODE system) is determined
by the number of segments in the phase and the polynomial order in each segment.  When Dymos instantiates
the ODE system it provides the total number of nodes at which evaluation is required to the ODE system.
Thus, at a minimum, the `initialize` method of components for an ODE system typically look something
like this:

The inputs and outputs of the system are expected to provide a scalar or dimensioned
value *at each node*.  Vectorization of the component via numpy adds a significant performance increase
compared to using a for loop to cycle through calculations at each node.  It's important to remember
that vectorized data is going to be coming in, this is especially important for defining partials.
From the perspective of the ODE system, the outputs at some time `t` only depend on the values
of the input variables at time `t`.  When the output variables are scalar at any given time, this
results in components whose Jacobian matrices are diagonal.  This large degree of sparsity leads
to computational advantages when using sparse-aware optimizers like SNOPT.  Users should declare
the partial derivatives of their components to be sparse (by specifying nonzero rows and columns)
whenever possible._____no_output_____

class MyODESystem(om.ExplicitComponent):

    def initialize(self):
        self.metadata.declare('num_nodes', types=int)_____no_output_____

For example, if `MyODEComponent` is to compute the linear function $y = a * x + b$ then the
setup, compute, and compute partials methods might look like this:_____no_output_____

def setup(self):
    nn = self.metadata['num_nodes']

    self.add_input('a', shape=(nn,), units='m')
    self.add_input('x', shape=(nn,), units='1/s')
    self.add_input('b', shape=(nn,), units='m/s')

    self.add_output('y', shape=(nn,), units='m/s')

    r = c = np.arange(nn)
    self.declare_partials(of='y', wrt='a', rows=r, cols=c)
    self.declare_partials(of='y', wrt='x', rows=r, cols=c)
    self.declare_partials(of='y', wrt='b', rows=r, cols=c, val=1.0)

def compute(self, inputs, outputs):
    a = inputs['a']
    x = inputs['x']
    b = inputs['b']

    outputs['y'] = a * x + b

def compute_partials(self, inputs, outputs, partials):
    a = inputs['a']
    x = inputs['x']
    b = inputs['b']

    partials['y', 'a'] = x
    partials['y', 'x'] = a_____no_output_____

A few things to note here.  We can use the `shape` or `val` argument of `add_input` and `add_output`
to dimension each variable.  In this case each variable is assumed to be a scalar at each point in
time (each node).  We use the `rows` and `cols` arguments of `declare_partials` to provide the sparsity.
Here using `arange(nn)` for both gives us a diagonal jacobian with `nn` rows and `nn` columns.  Since
the number of nonzero values in the jacobian is `nn`, we only need to provide `nn` values in the
`compute_partials` method.  It will automatically fill them into the sparse jacobian matrix, in
row-major order.

In this example, the partial of `y` with respect to `b` is linear, so we can simply provide it in
the `declare_partials` call rather than reassigning it every time `compute_partials` is called.
The provided scalar value of `1.0` is broadcast to all `nn` values of the Jacobian matrix.

## Dimensioned Inputs and Outputs

The above example assumes all inputs and outputs are scalar at each node.  Sometimes the user may
encounter a situation in which the inputs and/or outputs are vectors, matrices, or tensors at
each node.  In this case the dimension of the variable is `num_nodes`, with the dimension of the
variable at a single node filling out the remaining indices. A 3-vector is thus dimensioned
`(num_nodes, 3)`, while a 3 x 3 matrix would be sized `(num_nodes, 3, 3)`.

##  Non-Vector Inputs

Declaring inputs as vectors means that they have the potential to be used either as parameters or as dynamic controls which can assume a different value at each node.
For some quantities, such as gravitational acceleration in the Brachistochrone example, we can assume that the value will never need to be dynamic.
To accommodate this, parameters can be declared static with the argument `static_target=True`.
This prevents Dymos from "fanning out" the static value to the *n* nodes in the ODE system.

##  Providing the ODE to the Phase

Phases in Dymos are instantiated with both the `ode_class` and the `transcription` to be used.
Internally, Dymos needs to instantiate the ODE multiple times.
This instantiation takes the form:_____no_output_____```python
ode_instance = ode_class(num_nodes=<int>, **ode_init_kwargs)
```_____no_output_____This allows an OpenMDAO ExecComp to be used as an ODE via a lambda.
For instance, the brachistochrone ODE can be written as:_____no_output_____

ode = lambda num_nodes: om.ExecComp(['vdot = g * cos(theta)',
                                     'xdot = v * sin(theta)',
                                     'ydot = -v * cos(theta)'],
                                    g={'value': 9.80665, 'units': 'm/s**2'},
                                    v={'shape': (num_nodes,), 'units': 'm/s'},
                                    theta={'shape': (num_nodes,), 'units': 'rad'},
                                    vdot={'shape': (num_nodes,),
                                          'units': 'm/s**2',
                                          'tags': ['state_rate_source:v']},
                                    xdot={'shape': (num_nodes,),
                                          'units': 'm/s',
                                          'tags': ['state_rate_source:v']},
                                    ydot={'shape': (num_nodes,),
                                          'units': 'm/s',
                                          'tags': ['state_rate_source:v']},
                                    has_diag_partials=True)

t = dm.Radau(num_segments=10, order=3)
phase = dm.Phase(ode_class=ode, transcription=t)_____no_output_____

Note the use of `has_diag_partials=True` to provide more efficient graph coloring for the derivatives.

In theory, this also means you can implement Python's `__call__` method for an ODE.
The following code will return a copy of the brachistochrone ODE with the appropriate number of nodes.
Note that the implementation below does not deal with any options provided via the `ode_init_kwargs`._____no_output_____

class CallableBrachistochroneODE(om.ExplicitComponent):

    def initialize(self):
        self.options.declare('num_nodes', types=int)

    def __call__(self, num_nodes, **kwargs):
        from copy import deepcopy
        ret = deepcopy(self)
        ret.options['num_nodes'] = num_nodes
        return ret

    def setup(self):
        nn = self.options['num_nodes']

        # Inputs
        self.add_input('v', val=np.zeros(nn), desc='velocity', units='m/s')

        self.add_input('g', val=9.80665, desc='grav. acceleration', units='m/s/s')

        self.add_input('theta', val=np.ones(nn), desc='angle of wire', units='rad')

        self.add_output('xdot', val=np.zeros(nn), desc='velocity component in x', units='m/s',
                        tags=['state_rate_source:x', 'state_units:m'])

        self.add_output('ydot', val=np.zeros(nn), desc='velocity component in y', units='m/s',
                        tags=['state_rate_source:y', 'state_units:m'])

        self.add_output('vdot', val=np.zeros(nn), desc='acceleration magnitude', units='m/s**2',
                        tags=['state_rate_source:v', 'state_units:m/s'])

        self.declare_partials(of='*', wrt='*', method='cs')
        self.declare_coloring(wrt='*', tol=1.0E-12)_____no_output_____

An instance of the above ODE can then be provided to the phase upon instantiation._____no_output_____

ode = CallableBrachistochroneODE(num_nodes=1)
t = dm.Radau(num_segments=10, order=3)
phase = dm.Phase(ode_class=ode, transcription=t)_____no_output_____

This can potentially lead to unintended behavior if multiple copeis of the ODE are intended to share data.
See [the Python docs](https://docs.python.org/3/library/copy.html) for some of the limitations of deepcopy.
_____no_output_____


# Notebook from stefanluedtke/WS19AI2Exercises
Path: .ipynb_checkpoints/03-LDA-checkpoint.ipynb

# Quadratic Discriminant Analysis

In this exercise, you will implement a QDA on your own. The QDA computes $p(\omega|x)=\frac{p(x|\omega)*p(\omega)}{p(x)}$, and assumes that the likelihood $p(x|\omega)$ follows a normal distribution (conditional on each class $\omega$). _____no_output_____## Exercise
A fisher asks you for help in classifying fish. Recently, he catched the following fish:

| Length (m)    | Type          |
| ------------- |-------------  |
| 1.3           | sea bass       |
| 0.7           | salmon       |
| 0.62           | salmon      |
| 0.9           | salmon       |
| 0.91          | sea bass       |
| 0.31          | herring       |
| 0.26           | herring       |

* Calculate the class priors $p(\omega)$ for each class of fish
* Calculate the parameters $\mu$ and $\sigma^2$ for the likelihoods $p(x|\omega)$.
* The fisher catches a new fish with length $x = 0.82 m$. Calculate the posteriori probability $p(\omega|x)$ for each class. Which class is most likely?_____no_output_____## Exercise

Implement a function `priors(classes)` that calculates a prior $p(x)$ for each class, given a vector of class labels.
The input should be an array of classes, e.g. `np.array(["stand","sit","sit","stand"])`. The output should be a pandas data frame with columns `class` and `prior`.
_____no_output_____## Exercise

Implement a function `likelihood(data)`, that takes a data frame consisting of a column $omega$ and a column $x$, and estimates the parameters of the likelihood $p(x|\omega)$ for each class $\omega$, i.e. that returns a mean and a variance for each class. Thus, the output should be a data frame with columns `omega`, `mean` and `variance`.

Plot the likelihoods for each class.
_____no_output_____## Exercise
Implement a function `myqda(newdat,lik,priors)`, that returns the most likely class for a new observation `newdat`.
Test your implementation on the dataset `features1.arff`. Train your QDA (i.e. compute priors and likelihood parameters), and then classify the data. How good is your classification?
_____no_output_____


# Notebook from kifumi/platypus
Path: notebooks/ch-algorithms/quantum-walk-search-algorithm.ipynb

# Quantum Walk Search Algorithm_____no_output_____Quantum walks are the quantum equivalence of a classical Markov chain and have become key in many quantum algorithms. In this section, we implement a quantum walk search algorithm that finds marked elements in a graph. The algorithm has a quadratic speedup compared to its classical counterpart.

## 1.Classical Markov chains
A Markov chain is a stochastic process often used to model real-life processes. It consists of states and associated transition probabilities, which describe the probabilities to move between the states in each time step. In discrete-time Markov chains, which we work with here, the time steps are discrete. Markov chains satisfy the Markov property, which means that the next step of the process only depends on the current and not any of the previous steps. A Markov chain has an associated transition matrix, P, which describes the probability to move between each of the states. We show an example of a Markov chain and its associated transition matrix $P$ below.
\begin{equation}
P=
\begin{pmatrix}
0.1 & 0.3 & 0.3\\
0.1 & 0.1 & 0.2 \\
0.8 & 0.6 & 0.5
\end{pmatrix}
\label{eq:matrix_example}
\end{equation}

Given a transition matrix $P$, we can obtain the probability distribution after $t$ time steps by $P^t$.

## 2.Quantum walks
Quantum walks are the quantum equivalent of the classical Markov chain. Due to superposition, a quantum walk will take all possible paths simultaneously until we measure the circuit. Due to quantum interference, some state will cancel out. This makes quantum walk algorithms faster than classical ones, since we can design them in such way that wrong answers quickly cancells out. There are two common models for quantum walks, coined quantum walks and Szegedy quantum walks, which are equivalent under certain circumstances. A coined quantum walk is a walk on the vertices of the graph while Szegedy quantum walk takes place on the edges. Before we show how we can implement a quantum walk, we will introduce both models.

 $\newcommand{\ket}[1]{\left|{#1}\right\rangle}$
 $\newcommand{\bra}[1]{\left\langle{#1}\right|}$
### 2.A Coined quantum walks
A simple example of a coined quantum walk is a walk on the infinite integer line. In this case, we represent the walker's position with an integer, $\{\ket{j} : j \in  \mathbb{Z} \}$, since the walker can walk all integers in $\mathbb{Z}$. A coin decides how the walker should move. On the integer line, the walker can go either left or right. Therefore, the coin's computational basis is $\{\ket{0}, \ket{1}\}$, and we move the walker in one direction if the coin is $\ket{0}$ and in the other direction if the coin is $\ket{1}$.

A coined quantum is a walk on the nodes in a graph, and we refer to the nodes as states. The walker can move between states that are connected with an edge. In the coin model, we have two quantum states and two operators. The first state is the position state, which represents the walker's position. For the walk above, this is an integer since the walker can be anywhere on the integer line. The other state is the coin state. The coin state decides how the walker should move in the next step. We can represent both the coin state and the position state by vectors in Hilbert space. If we can express the coin state by a vector in $\mathcal{H}_C$ and the position state with a vector in $\mathcal{H}_P$, we can express the quantum space for the entire walker as $\mathcal{H} = \mathcal{H}_C \otimes \mathcal{H}_P$.

As we mentioned, the model also has two operators; the coin operator, $C$, and the shift operator, $S$. The coin operator acts on $\mathcal{H}_C$ during each time step and puts the walker in superposition, so it walks all possible paths simultaneously. For the walk on the integer line, this means that it moves both to the left and right in each time step. There are different coin operators, but the most common ones are the Hadamard coin and the Grover coin. A Hadamard coin is a Hadamard gate that puts the walker in an equal superposition:

\begin{equation}
    H = \frac{1}{\sqrt{2}}
\begin{bmatrix}
1 & 1 \\
1 & -1
\end{bmatrix}
\end{equation}

A Grover coin is the Grover diffusion operator from Grover's algorithm. We define it as

\begin{equation}
    G =
    \begin{bmatrix}
\frac{2}{n} -1 & \frac{2}{n} & \ldots & \frac{2}{n}\\
\frac{2}{n} & \frac{2}{n} - 1  & \ldots & \frac{2}{n} \\
\vdots & \vdots & \ddots & \vdots \\
\frac{2}{n} & \frac{2}{n} & \ldots & \frac{2}{n} -1
\end{bmatrix}
\end{equation}

Like the Hadamard coin, a Grover coin puts the walker in superposition. However, it behaves a bit differently. If we apply a Grover coin to a walker in position $\ket{000}$, we obtain the state vector probabilities shown in the figure below. As we can see, the coin does not put the walker in an equal superposition as the Hadamard coin does. Instead, $\ket{000}$ has a much larger probability than the other states.

The other operator in the model, the shift operator, acts on $\mathcal{H}_P$ and moves the walker to the next position. For the walk on the integer line, the shift operator moves the walker to the left of the coin is $\ket{0}$ and to the right if the coin is $\ket{1}$:

\begin{equation}
    S \ket{0}\ket{j} = \ket{0}\ket{j+1}
\end{equation}

\begin{equation}
    S \ket{1}\ket{j} = \ket{1}\ket{j-1}
\end{equation}

With the shift operator defined as above, we can represent one step of the coined quantum as the unitary operator $U$ given by
\begin{equation}
    U = SC,
\end{equation}

where C is the coin operator. For the quantum walk on the integer line we use the Hadamard coin, but C can be either Hadamard coin, Grover coin or any other coin operator.

We can also look several steps ahead. We can express the quantum state $\ket{\psi}$ after $t$ time steps as
\begin{equation}
    \ket{\psi (t)} = U^t \ket{\psi(0)},
\end{equation}
where $\ket{\psi(0)}$ is the initial state and U is the operator for one step of the walk [1].

Coined quantum walks are most suitable for regular graphs, graphs where all nodes have the same number of neighbors [2]. An alternative quantum walk model that is more convenient for non-regular graphs is the Szegedy model that we will look at next.

### 2.B Szedgedy quantum walk
While a coined walk is a walk on the graph's nodes, a Szegedy walk is a walk on the edges on a bipartite double-cover of the original graph. The double-cover graph is a graph with twice as many vertices as the original graph. Two nodes in the bipartite double-cover graph are connected with an edge if and only if the nodes are also connected in the original graph. To create this model, we start with the transition probability matrix P for the classical walk. As described in section 1, we represent a classical discrete-time random walk by a transition matrix $P$. For any $N$-vertex graph with $N \times N$ transition matrix $P$, we can define the corresponding discrete-time quantum walk as a unitary operation on the Hilbert space $\mathcal{H}^N \otimes \mathcal{H}^N$. Let $P_{jk}$ define the probability that we make a transition from state $j$ to $k$. Before we define the walk, we define the normalized states

\begin{equation}
    \ket{\psi_j} := \sum_{k=1}^N \sqrt{P_{kj}} \ket{j,k}, \; j=1,...,N
\end{equation}

and the projection onto ${\ket{\psi_j}}:j=1,...,N$

\begin{equation}
    \Pi := \sum_{j=1}^N \ket{\psi_j} \bra{\psi_j}
\label{eq:sz_pi}
\end{equation}

We also introduce the shift operator S:

\begin{equation}
    S := \sum_{j,k=1}^N \ket{j,k} \bra{k,j}
\label{eq:sz_s}
\end{equation}

With $S$ and $\Pi$ defined as above we can introduce one step of the discrete-time quantum walk:

\begin{equation}
    U := S(2 \Pi - 1),
\label{eq:sz_op}
\end{equation}

where $(2 \Pi - 1)$ is the reflection operator. We also define $t$ steps of the walk as $U^t$ [2].

### 2.C Equivalence of coined and Szegedy quantum walks
It is known that a coined walk with Grover coin is equivalent to Szegedy quantum walk. For more details, we refer to this article [3] by Thomas G. Wong, where he also shows the equivalence between the operators in the two models.

## 3. Example: Implementing quantum walk on a hypercube
A hypercube is the $n$-dimensional counterpart of the $3$-dimensional cube. All nodes have degree $n$, and the hypercube has a total of $N=2^n$ nodes. We can represent the nodes in a hypercube graph by $n$-tuples of binary numbers. The binary representation of the neighbors of a node will differ by only one binary number. For example, in the 4-dimensional hypercube, the neighbors to $0000$ are $0001$, $0010$, $0100$, and $1000$. Thus, a node is connected to all nodes to which the Hamming distance is 1. The edges are also labeled. Two neighboring nodes that differ in the a:th bit are connected by the edge labeled $a$.

The Hilbert space representing a coined quantum walk on the hypercube is $\mathcal{H} = \mathcal{H}^n \otimes \mathcal{H}^{2^n}$, where $\mathcal{H}^n$ denotes the coin space and $\mathcal{H}^{2^n}$ the walker's position. The computational basis is

\begin{equation}
    \big\{ \ket{a,\vec{v}}, 0 \leq a \leq n-1, \vec{v} \in  \{(00...00), (00...01), ....., (11...11 )\} \big\}.
\end{equation}

The value of the coin computational basis $a$, which is associated with edge $a$, decides where the walker should move. If the $a=0$, the walker will go to the node where the first binary value differs from the current node. If $a=1$, the walker will move to the node in which the second value differs from the current, et cetera.  Let $\vec{e}_a$ be an n-tuple where all binary values, except the value with index $a$, are $0$. Then the shift operator $S$ moves the walker from the state $\ket{a} \ket{\vec{v}}$ to $\ket{\vec{v} \oplus \vec{e}_a}$:

\begin{equation}
    S \ket{a} \ket{\vec{v}} = \ket{a} \ket{\vec{v} \oplus \vec{e}_a}.
\end{equation}

We use the Grover coin, $G$, for this walk. Thus, the evolution operator is

\begin{equation}
    U = SG.
\end{equation}

We will now show how we can implement a quantum walk on a 4-dimensional hypercube. We need to implement the coin operator and the shift operator. We start by importing all necessary libraries from Qiskit. _____no_output_____

# Importing standard Qiskit libraries
from qiskit import QuantumCircuit, execute, Aer, IBMQ, QuantumRegister, ClassicalRegister
from qiskit.compiler import transpile, assemble
from qiskit.tools.jupyter import *
from qiskit.visualization import *
from qiskit.circuit.library import QFT
from numpy import pi
from qiskit.quantum_info import Statevector
from matplotlib import pyplot as plt
import numpy as np
# Loading your IBM Q account(s)
provider = IBMQ.load_account()_____no_output_____

The circuit will have 6 qubits, 4 that represents the position and 2 that represents the coin. As we mentioned previously, the coin is a Grover coin, which is the diffuser in Grover's algorithm. We start by implementing this. _____no_output_____

one_step_circuit = QuantumCircuit(6, name=' ONE STEP')
# Coin operator
one_step_circuit.h([4,5])
one_step_circuit.z([4,5])
one_step_circuit.cz(4,5)
one_step_circuit.h([4,5])
one_step_circuit.draw() _____no_output_____

Now, let us implement the shift operator. We know that the walker can only move to neighboring nodes, and all neighboring nodes differ by only one bit. We want to move the walker according to the coin, and we move the walker by applying a NOT gate to one of the node qubits. If the coin is in state $\ket{11}$, we move the walker to the state in which the first node qubit differ. If the coin is $\ket{10}$ or $\ket{01}$, the walker moves to the state where the second and third qubit, respectively, differ. Finally, if the Grover coin is $\ket{00}$, we flip the fourth qubit. We implement this with CCNOT- and NOT gates after the Grover coin. Together, they are one step of a quantum walk on a 4-dimensional hypercube. _____no_output_____

# Shift operator function for 4d-hypercube
def shift_operator(circuit):
    for i in range(0,4):
        circuit.x(4)
        if i%2==0:
            circuit.x(5)
        circuit.ccx(4,5,i)

shift_operator(one_step_circuit)

one_step_gate = one_step_circuit.to_instruction()
one_step_circuit.draw() _____no_output_____

#### 4.Quantum walk search algorithm
We will now implement a quantum walk search algorithm that finds a marked vertex in a graph. First, we describe the algorithm, then we go through its implementation. The quantum walk search algorithm solves the problem of finding marked vertices in a graph by a quantum walk. That is, we mark some set of vertices $|M|$, start at an arbitrary node in the graph and move according to the walk until we find the marked nodes. The basis states in the quantum walk search algorithm have two registers, one corresponding to the current node and the other corresponding to the previous node. That is, the basis states corresponds to the edges in the graph. We denote the quantum walk based on the classical Markov chain with transition matrix $P$ by the unitary operation $W(P)$ on $\mathcal{H}$. We also define $\ket{p_x} = \sum_y \sqrt{P_{xy}}\ket{y}$ as the uniform superposition over the node $x$'s neighbors. Let $\ket{x}\ket{y}$ be a basis state. We define the basis state $\ket{x}\ket{y}$ as ''good'' if $x$ is a marked node. Otherwise, we refer to it as ''bad''. We now introduce ''good'' and ''bad'' states:

\begin{equation}
    \ket{G} = \frac{1}{\sqrt{|M|}} \sum_{x \in M} \ket{x} \ket{p_x}, \;
    \ket{B} = \frac{1}{\sqrt{N-|M|}} \sum_{x \notin M} \ket{x} \ket{p_x},
\end{equation}

which are the superpositions over good and bad basis states. Next, let us define $\epsilon = |M|/N$ and $\theta = \arcsin(\sqrt{\epsilon})$.

In short, the algorithm consists of three steps:

1. Set up the initial state $\ket{U} = \frac{1}{\sqrt{N}} \sum_{x} \ket{x} \ket{p_x} = \sin{\theta} \ket{G} + \cos{\theta} \ket{B}$, a uniform superposition over all edges
2. Repeat $O(1/\sqrt{\epsilon})$ times:

    (a) Reflect through $\ket{B}$

    (b) Reflect through $\ket{U}$

3. Do a measurement in the computational basis

We can easily implement step $1$ with Hadamard gates and the reflection through $\ket{B}$ with a phase oracle that shifts the phase of $x$ if $x$ is in the first register, and leaves the circuit unchanged otherwise.

Step 2(b) is equivalent to finding a unitary $R(P)$ that performs the following mapping:
\begin{align}
\label{eq:mapping_1}
    \ket{U} &\mapsto \ket{U}, \: \text{and} \\
    \ket{\psi} &\mapsto -\ket{\psi}, \: \forall \ket{\psi} \text{in the span of eigenvectors of $W(P)$ that are orthogonal to $\ket{U}$}
\label{eq:mapping_2}
\end{align}

To find this operator we apply phase estimation on $W(P)$. Above we defined $W(P)$ as the evolution operator for the random walk. As we saw in section 2.A, this is a unitary operator. From this follows that the eigenvalues of $W(P)$ have norm $1$. Because of this, we can write the eigenvalues of $W(P)$ on the form $e^{\pm 2i\theta_j}$. The unitary $W(P)$ has one eigenvector with corresponding eigenvalue $1$, which is $\ket{U}$. This is given by $\theta_1=0$. $R(P)$ will find this vector $\ket{U}$ by adding a register with auxiliary qubits and perform phase estimation with precision $O(1/\sqrt{\delta})$, where $\delta$ is the spectral gap of $P$. To do this, we need to apply $W(P)$ $O(1/\sqrt{\delta})$ times. Let $\ket{w}$ be an eigenvector of $W(P)$ with eigenvalue $e^{\pm 2i\theta_j}$. Assume that $\tilde{\theta_j}$ is the best approximation to $\theta_j$ given by the phase estimation. The operation $R(P)$ that performs the mappings in for $\ket{w}$ in step 2(b) is given by [4]

\begin{equation}
    \ket{w} \ket{0} \mapsto \ket{w} \ket{\tilde{\theta_j}} \mapsto (-1)^{|\tilde{\theta_j} \neq 0|} \ket{w} \ket{\tilde{\theta_j}} \mapsto (-1)^{|\tilde{\theta_j} \neq 0|} \ket{w} \ket{0}
\end{equation}

## 5.Example: Quantum walk search on 4-dimensional hypercube

The quantum walk search algorithm makes it possible to find a marked set of nodes in $O(1/\sqrt{\epsilon})$ steps, $\epsilon =  |M|/N$, where $M$ is the number of marked nodes and $N$ is the total number of nodes. This algorithm is originally used with Szegedy quantum walks, where we use two node registers to represent the quantum state. However, a coined walk with a Grover coin is equivalent to a Szegedy quantum walk and since implementations of coined walks are less complex in general, we choose to implement the algorithm with a coined walk. We will use the 4-dimensional hypercube that we implemented in section 3.

In short, we will implement the algorithm as follows. We achieve step 1, a uniform superposition over all edges, by applying Hadamard gates to the node qubits as well as the coin qubits. For step 2(a), we implement a phase oracle. Step 2(b) is implemented by a phase estimation on one step of the quantum walk on the hypercube followed by marking all quantum states where $\theta \neq 0$. We do this by rotating an auxiliary qubit. In the last part of this step, we reverse the phase estimation. The number of theta qubits depends on the precision of $\theta$.

Below, we implement the quantum walk search algorithm on the 4-dimensional hypercube. _____no_output_____For this algorithm we will need to use the inverse of the one step gate implemented earlier. We get this by using the built in circuit function inverse()._____no_output_____

one_step_circuit.inverse().draw() _____no_output_____

The inversed one step gate will be used to reverse the phase estimation later. We need to make controlled gates from both the one step gate that we implemented in section 3 and its inverse. We will later use them depending on the value of the control qubit.  _____no_output_____

# Make controlled gates
inv_cont_one_step = one_step_circuit.inverse().control()
inv_cont_one_step_gate = inv_cont_one_step.to_instruction()
cont_one_step = one_step_circuit.control()
cont_one_step_gate = cont_one_step.to_instruction()_____no_output_____

Both the controlled one step gate and the controlled inversed one step gate will be used in the phase estimation. Another thing we will use in the phase estimation is the Quantum Fourier Transform. Qiskit has a function, QFT, which implements the Quantum Fourier Transform. The phase estimation uses the inverse Quantum Fourier Transform but we also will need to use the ordinary QFT to reverse the phase estimation._____no_output_____

inv_qft_gate = QFT(4, inverse=True).to_instruction()
qft_gate = QFT(4, inverse=False).to_instruction()

QFT(4, inverse=True).decompose().draw("mpl")_____no_output_____

Before we implement the phase estimation, we implement a phase oracle that marks the states 1011 and 1111. Then, we make it a circuit. This is step 2(a) of the algorithm._____no_output_____

phase_circuit =  QuantumCircuit(6, name=' phase oracle ')
# Mark 1011
phase_circuit.x(2)
phase_circuit.h(3)
phase_circuit.mct([0,1,2], 3)
phase_circuit.h(3)
phase_circuit.x(2)
# Mark 1111
phase_circuit.h(3)
phase_circuit.mct([0,1,2],3)
phase_circuit.h(3)
phase_oracle_gate = phase_circuit.to_instruction()
# Phase oracle circuit
phase_oracle_circuit =  QuantumCircuit(11, name=' PHASE ORACLE CIRCUIT ')
phase_oracle_circuit.append(phase_oracle_gate, [4,5,6,7,8,9])
phase_circuit.draw() _____no_output_____

We will now implement a gate that rotates an auxiliary qubit if the other qubits are non-zero. We will use this gate in the phase estimation, where it will rotate the auxiliary qubit if $\theta \neq 0$. _____no_output_____

# Mark q_4 if the other qubits are non-zero
mark_auxiliary_circuit = QuantumCircuit(5, name=' mark auxiliary ')
mark_auxiliary_circuit.x([0,1,2,3,4])
mark_auxiliary_circuit.mct([0,1,2,3], 4)
mark_auxiliary_circuit.z(4)
mark_auxiliary_circuit.mct([0,1,2,3], 4)
mark_auxiliary_circuit.x([0,1,2,3,4])

mark_auxiliary_gate = mark_auxiliary_circuit.to_instruction()
mark_auxiliary_circuit.draw()_____no_output_____

Now, we will implement step 2(b) of the algorithm. This step consists of a phase estimation one step of the quantum walk followed by an auxiliary qubit that we rotate if $\theta \neq 0$. For this, we use the mark_auxiliary_gate that we just created.  Thereafter, we reverse the phase estimation. _____no_output_____

# Phase estimation
phase_estimation_circuit = QuantumCircuit(11, name=' phase estimation ')
phase_estimation_circuit.h([0,1,2,3])
for i in range(0,4):
    stop = 2**i
    for j in range(0,stop):
        phase_estimation_circuit.append(cont_one_step, [i,4,5,6,7,8,9])

# Inverse fourier transform
phase_estimation_circuit.append(inv_qft_gate, [0,1,2,3])

# Mark all angles theta that are not 0 with an auxiliary qubit
phase_estimation_circuit.append(mark_auxiliary_gate, [0,1,2,3,10])

# Reverse phase estimation
phase_estimation_circuit.append(qft_gate, [0,1,2,3])

for i in range(3,-1,-1):
    stop = 2**i
    for j in range(0,stop):
        phase_estimation_circuit.append(inv_cont_one_step, [i,4,5,6,7,8,9])
phase_estimation_circuit.barrier(range(0,10))
phase_estimation_circuit.h([0,1,2,3])

# Make phase estimation gate
phase_estimation_gate = phase_estimation_circuit.to_instruction()
phase_estimation_circuit.draw() _____no_output_____

Now we implement the whole quantum walk search algorithm using the gates we made previously. We start by applying Hadamard gates to node and coin qubits, which is step 1 in the algorithm. Thereafter, we iteratively apply the phase oracle gate and the phase estimation gate (steps 2(a) and 2(b)). We need $O(1/\sqrt{\epsilon})$ iterations as stated in the description of the algorithm in section 4. Lastly, we measure the node qubits. _____no_output_____

# Implementation of the full quantum walk search algorithm
theta_q = QuantumRegister(4, 'theta')
node_q = QuantumRegister(4, 'node')
coin_q = QuantumRegister(2, 'coin')
auxiliary_q = QuantumRegister(1, 'auxiliary')
creg_c2 = ClassicalRegister(4, 'c')
circuit = QuantumCircuit(theta_q, node_q, coin_q, auxiliary_q, creg_c2)
# Apply Hadamard gates to the qubits that represent the nodes and the coin
circuit.h([4,5,6,7,8,9])
iterations = 2

for i in range(0,iterations):
    circuit.append(phase_oracle_gate, [4,5,6,7,8,9])
    circuit.append(phase_estimation_gate, [0,1,2,3,4,5,6,7,8,9,10])

circuit.measure(node_q[0], creg_c2[0])
circuit.measure(node_q[1], creg_c2[1])
circuit.measure(node_q[2], creg_c2[2])
circuit.measure(node_q[3], creg_c2[3])
circuit.draw()_____no_output_____

Finally we run the implementation on the qasm simulator. We see that the circuit collapse to the marked states a clear majority of the times. _____no_output_____

backend = Aer.get_backend('qasm_simulator')
job = execute( circuit, backend, shots=1024 )
hist = job.result().get_counts()
plot_histogram( hist )_____no_output_____

## 6. References

1. Renato Portugal. Quantum Walks and Search Algorithms. New York, NY: Springer New York, 2013
2. Markus G. Kuhn.Some Introductory Notes on Quantum Computing. Apr. 2000
3. Thomas G. Wong. “Equivalence of Szegedy’s and coined quantum walks”. In: Quantum InformationProcessing 16.9 (July 2017). ISSN: 1573-1332. DOI:10.1007/s11128-017-1667-y. URL:http://dx.doi.org/10.1007/s11128-017-1667-y.37
4. Ronald de Wolf. Quantum Computing: Lecture Notes. 2021. arXiv:1907.09415 [quant-ph]_____no_output_____

import qiskit.tools.jupyter
%qiskit_version_table/usr/local/anaconda3/envs/terra-unstable/lib/python3.9/site-packages/qiskit/aqua/__init__.py:86: DeprecationWarning: The package qiskit.aqua is deprecated. It was moved/refactored to qiskit-terra For more information see <https://github.com/Qiskit/qiskit-aqua/blob/main/README.md#migration-guide>
  warn_package('aqua', 'qiskit-terra')


# Notebook from Mazzafish/neoantigen
Path: antigen_discovery/.ipynb_checkpoints/Immune Stealth  - Ranked Similarity -  New Workflow 10-19-2016-checkpoint.ipynb

# Conservation Analysis and Epitope Prediction

#### Author: C. Mazzaferro, K. Fisch
#### Email: cmazzafe@ucsd.edu
#### Date: October 2016

## Outline of Notebook

1. Background
2. High Affinity Binding Prediction
    * Data Aggregation
    * Similarty Score Calculation
    * Visualize_____no_output_____Directions (from Mali's e-mail sent a couple of weeks ago):

Determine Cas9 orthologs that are orthogonal in the 'immunogenicity space'. This will allow us to prescribe a sequential regimen of Cas9s for therapeutic interventions. I've been thinking some more about how to do this, and I believe a simple strategy as follows might work:

- step i: build a database of Cas9 orthologs; #n.
- step ii: pick a seed Cas9 ortholog, say Sp, and then do a pairwise alignment with every other ortholog (n-1 alignments total). note that aligning all together will not be useful for our objective.
- step iii: overlay the immunogenicity profiles and score direct peptide overlaps (adding +1 for each overlap and 0 otherwise). sum the scores and retain orthologs (#m) for which the summation score = 0.
- step iv: randomly pick a second Cas9 from the orthologs retained in step iii and iterate back to step ii but now do just m-1 alignments with the retained orthologs.

This should quickly converge and give a handful of orthologs. We should ideally be able to dial the immunogenicity thresholds in the workflow as we could potentially land up with too many or too few options. We might also get different orthogonal lists based on the choice of seed ortholog -- but their should still be substantial overlaps, and so we should ideally iterate through all orthologs in step i to establish robustness of the lists.

This is just a suggestion for a potential workflow, so let me know what you'll think and absolutely feel free to pick this apart!
_____no_output_____
# Background_____no_output_____CRISPR (clustered regularly interspaced short palindromic repeat) is an adaptive immune system that provides protection against mobile genetic elements (viruses, transposable elements and conjugative plasmids).
CRISPR clusters contain spacers, sequences complementary to antecedent mobile elements, and target invading nucleic acids. CRISPR clusters are transcribed and processed into CRISPR RNA (crRNA). In type II CRISPR systems correct processing of pre-crRNA requires a trans-encoded small RNA (tracrRNA), endogenous ribonuclease 3 (rnc) and this protein. The tracrRNA serves as a guide for ribonuclease 3-aided processing of pre-crRNA; Cas9 only stabilizes the pre-crRNA:tracrRNA interaction and has no catalytic function in RNA processing. Subsequently Cas9/crRNA/tracrRNA endonucleolytically cleaves linear or circular dsDNA target complementary to the spacer; Cas9 is inactive in the absence of the 2 guide RNAs (gRNA). The target strand not complementary to crRNA is first cut endonucleolytically, then trimmed 3'-5' exonucleolytically. DNA-binding requires protein and both gRNAs, as does nuclease activity. Cas9 recognizes the protospacer adjacent motif (PAM) in the CRISPR repeat sequences to help distinguish self versus nonself, as targets within the bacterial CRISPR locus do not have PAMs. DNA strand separation and heteroduplex formation starts at PAM sites; PAM recognition is required for catalytic activity (PubMed:24476820). Confers immunity against a plasmid with homology to the appropriate CRISPR spacer sequences (CRISPR interference) (PubMed:21455174)_____no_output_____

from IPython.display import Image
Image("/Users/carlomazzaferro/Desktop/BINF Int Rand Pics/workflow_epitope.png")_____no_output_____

The pipeline presented here is aimed at identify the most immunogenic peptides in Cas9. By looking at multiple sequence alignment scores for the possible peptides and their associated predicted MHC affinity, we can determine which peptides within a Cas9 orthologue can be swapped in order to reduce overall immunogenecity.

The steps are constructed as follows:

1. Select proteins [from reference papaer](http://nar.oxfordjournals.org/content/suppl/2013/10/29/gkt1074.DC1/nar-02672-z-2013-File008.pdf)
2. Predict the MHC affinity of each peptide using [CBS's prediction services](http://www.cbs.dtu.dk/services/), in particular [netMHCcons Server](http://www.cbs.dtu.dk/services/NetMHCcons/)
3. Rank proteins according to their similarity bases on a seed*

*See attached file: `workflow_and_results.txt` for a lengthier description of how the scoring was done_____no_output_____
# MHC Binding Affinity Prediction
### Ran on netMHCcons for n-mers of 8, 9, 10, 11 amino acids and for the 12 allele supertype families

Run for each protein sequence retrieved from reference paper._____no_output_____## Load file_____no_output_____

from nepitope import mhc_utils
import pandas
import glob
import importlib
importlib.reload(mhc_utils)
#files saved from netMHC in  multiple different files, one per each query (nmer, allele)

fasta_file = '/Users/carlomazzaferro/Desktop/Test_IEDB/OtherProtsPrashant/prots_in_fasta.fasta'
filepath = '/Users/carlomazzaferro/Desktop/Test_IEDB/OtherProtsPrashant/mhc_preds/'

file_names = glob.glob(filepath + '*')
file_names = [file_name.split('/')[-1] for file_name in file_names]

aggregate_all = mhc_utils.FileConsolidation.load_batches(filepath, file_names, fasta_file)_____no_output_____#returns a dataframe per file used (just one in this case)
df_list = aggregate_all.return_df_list()_____no_output_____#Would run this. But too slow. The 3 cells below are speed-optimized.
lsss_1 = aggregate_all.optimized_list_df_by_prot(df_list[0:5])_____no_output_____

## Load original proteins_____no_output_____

from nepitope import pep_utils
fasta_all_prots = '/Users/carlomazzaferro/Desktop/BINF_rand_files/CAS9_stuff/Protein align from study/fasta_from_study_dealigned.fasta'
peps, prots = pep_utils.create_separate_lists(fasta_all_prots)_____no_output_____ref_spy = peps[-1][1:16]
ref_spy_prot = prots[-1] _____no_output_____peps_short_named = [pep.replace('>', '')[0:15] for pep in peps]
peps_and_prots = list(zip(peps_short_named, prots))[:-1]
peps_and_prots.append((ref_spy, ref_spy_prot))_____no_output_____

### Data Aggregation
Join data: return data in a list of dataframes, where each dataframe contains data about a protein (May take some time)_____no_output_____

ref_prot= 'StreptococcusPy'
from nepitope import ranked_similarity
importlib.reload(ranked_similarity)
sim_score = ranked_similarity.SimilartiyScore(lsss_1, fasta_all_prots, ref_prot)/Users/carlomazzaferro/Documents/Code/neoantigen/antigen_discovery/nepitope/ranked_similarity.py:234: SettingWithCopyWarning:
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: http://pandas.pydata.org/pandas-docs/stable/indexing.html#indexing-view-versus-copy
  df['Peptide'] = df['Peptide'].str.replace('X', '-')
/Users/carlomazzaferro/Documents/Code/neoantigen/antigen_discovery/nepitope/ranked_similarity.py:72: SettingWithCopyWarning:
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: http://pandas.pydata.org/pandas-docs/stable/indexing.html#indexing-view-versus-copy
  df_to_append['Group'] = idx
seed_df = sim_score.filtered_df[-1]
filt_dfs = sim_score.filtered_df
#seed_df.Range = seed_df.Range.apply(lambda x: set(x[0]))        _____no_output_____from nepitope import pairwise_comp
import importlib
importlib.reload(pairwise_comp)_____no_output_____

### Pipeline subprocess call_____no_output_____

pwcomp = pairwise_comp.PairwiseComp(filt_dfs, 5, peps_and_prots)
'227494853_Actin' in pwcomp.list_of_scores_dicts.keys()
ls_dc_five = pwcomp._iteration_run() _____no_output_____

### Reformat DF as (key1, key2) : matches per len._____no_output_____

lsls = []
for i in ls_dc_five:
    reform = {(outerKey, str(innerKey)): values for outerKey, innerDict in i[0].items() for innerKey, values in innerDict.items()}
    lsls.append(pandas.Series(reform, name=i[-1][0]))
df = pandas.concat(lsls, axis=1).sort_index(axis=1)  _____no_output_____def rank_by_tuple(df):
    running_dict = {}
    cols = list(df.columns.values)
    for col_idx in cols:
        for col_col in cols:
            vals = df[col_col].loc[col_idx]
            running_dict[(col_idx, col_col)] =  vals
    return running_dict

rn_dc = rank_by_tuple(df)
#pandas.DataFrame(rn_dc).T.to_csv('/Users/carlomazzaferro/Desktop/by_pep_len_unsorted.csv')_____no_output_____df_by_protein = pandas.DataFrame(rn_dc).T_____no_output_____#df_by_protein[(df_by_protein.Total > 0) & df_by_protein['Num High AA'] > 0].sort_values(by=['Total', 'Num High AA'])
df_by_protein_____no_output_____#######RELABEL########
#protein blast
_____no_output_____

### SimilarityScoring
Class used to process the raw data from netMHCcons into reusable structures that allow for easy manipulation. It includes the method `run_pipe()`, that is the method that performs the entire scoring process and returns a list of (ordered) proteins and their associated score._____no_output_____

from nepitope import ranked_similarity
%matplotlib inline
prot = 'StreptococcusPy'
fasta_file = '/Users/carlomazzaferro/Desktop/fastafastafastapasta.fasta'
ranking_utils = ranked_similarity.SimilartiyScore(lsss_1, fasta_file, prot)/Users/carlomazzaferro/Documents/Code/neoantigen/antigen_discovery/nepitope/ranked_similarity.py:281: SettingWithCopyWarning:
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: http://pandas.pydata.org/pandas-docs/stable/indexing.html#indexing-view-versus-copy
  df['Peptide'] = df['Peptide'].str.replace('X', '-')
/Users/carlomazzaferro/Documents/Code/neoantigen/antigen_discovery/nepitope/ranked_similarity.py:272: SettingWithCopyWarning:
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: http://pandas.pydata.org/pandas-docs/stable/indexing.html#indexing-view-versus-copy
  i['Final Pos'] = i['Pos'] + i['n-mer']
/Users/carlomazzaferro/Documents/Code/neoantigen/antigen_discovery/nepitope/ranked_similarity.py:273: SettingWithCopyWarning:
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: http://pandas.pydata.org/pandas-docs/stable/indexing.html#indexing-view-versus-copy
  i['Range'] = i[['Pos', 'Final Pos']].apply(lambda x: [list(range(x[0], x[1]))], axis=1)
/Users/carlomazzaferro/Documents/Code/neoantigen/antigen_discovery/nepitope/ranked_similarity.py:72: SettingWithCopyWarning:
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: http://pandas.pydata.org/pandas-docs/stable/indexing.html#indexing-view-versus-copy
  df_to_append['Group'] = idx
pep_list, scores  = ranking_utils.run_pipe()/Users/carlomazzaferro/Documents/Code/neoantigen/antigen_discovery/nepitope/ranked_similarity.py:149: SettingWithCopyWarning:
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: http://pandas.pydata.org/pandas-docs/stable/indexing.html#indexing-view-versus-copy
  subset_df['Similarity To Ref'] = subset_df['Peptide'].apply(self.similar, args=(ref_pep[0],))

### Plotter
Class that takes scores, proteins, and general information about such proteins and creates a nice visualization_____no_output_____

from nepitope import plotting_utils
df = ranking_utils.filtered_df
viz = plotting_utils.Plotter(pep_list, scores, df)_____no_output_____viz.plotting_function_updated()_____no_output_____from nepitope import mhc_utils

filepath = '/Users/carlomazzaferro/Desktop/Test_IEDB/newer_pipeline_files/SPy_CAS9_MHC_preds.xls'

aggregate_all = mhc_utils.FileConsolidation.load_full_file(filepath)/Users/carlomazzaferro/anaconda/envs/py35/lib/python3.5/site-packages/matplotlib/__init__.py:1035: UserWarning: Duplicate key in file "/Users/carlomazzaferro/.matplotlib/matplotlibrc", line #2
  (fname, cnt))
df2 = aggregate_all.return_df_list()
lsss_2 = aggregate_all.list_df_by_prot(df2)/Users/carlomazzaferro/Documents/Code/neoantigen/antigen_discovery/nepitope/mhc_utils.py:268: SettingWithCopyWarning:
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: http://pandas.pydata.org/pandas-docs/stable/indexing.html#indexing-view-versus-copy
  list_dfs[i]['Allele'] = allele_list[i]
path = '/Users/carlomazzaferro/Desktop/Test_IEDB/newer_pipeline_files/high_affinity_SPy_all.xls'
df = lsss_2[0][lsss_2[0]['Affinity Level'] == 'High'].sort_values(by='Pos')_____no_output_____df.to_csv(path_or_buf=path, sep='\t')_____no_output_____


# Notebook from johnnyzhang295/lightkurve
Path: docs/source/tutorials/2.01-how-to-detrend.ipynb

# How to remove K2 motion systematics with SFF_____no_output_____You can use `lightkurve` to remove the motion from K2 data. Targets in K2 data move over multiple pixels during the exposure due to thruster firings. This can be corrected using the self flat fielding method (SFF), which you can read more about [here](LINK?). There is a full tutorial on how SFF works [here](http://lightkurve.keplerscience.org/tutorials/motion-correction/replicate-vanderburg-2014-k2sff.html). Here we will show a quick tutorial on how to use the correct function for light curves.

Let's download a K2 light curve of an exoplanet host star. Remember that for K2 data, we can use the same Kepler functions `KeplerTargetPixelFile` and `KeplerLightCurveFile` as they are the same instrument._____no_output_____

from lightkurve import KeplerLightCurveFile
lcf = KeplerLightCurveFile.from_archive(247887989)   #KeplerLightCurveFile
lc = lcf.PDCSAP_FLUX                                 #KeplerLightCurve

#Remove nans and outliers
lc = lc.remove_nans().remove_outliers()

#Remove long term trends
lc = lc.flatten(window_length=401)Downloading URL https://mast.stsci.edu/api/v0/download/file?uri=mast:K2/url/missions/k2/lightcurves/c13/247800000/87000/ktwo247887989-c13_llc.fits to ./mastDownload/K2/ktwo247887989-c13_lc/ktwo247887989-c13_llc.fits ... [Done]
lc.plot();_____no_output_____

This light curve is of the object [K2-133b](https://exoplanetarchive.ipac.caltech.edu/cgi-bin/DisplayOverview/nph-DisplayOverview?objname=K2-133+b&type=CONFIRMED_PLANET), which has a period of 3.0712 days. Let's plot the folded version of it to see what it looks like._____no_output_____

lc.fold(period=3.0712).plot(alpha=0.2);_____no_output_____

We can see the hint of an exoplanet transit close to the center, but the motion of the spacecraft has made it difficult to make out above the noise. We can use the `correct` function on the lightcurve to remove this motion. An in-depth look into how the algorithm works can be found [here](http://lightkurve.keplerscience.org/tutorials/motion-correction/replicate-vanderburg-2014-k2sff.html). You can pass the following keywords to the `correct` function:

* `polyorder` : *int*
            Degree of the polynomial which will be used to fit one centroid as a function of the other.
* `niters` : *int*
            Number of iterations
* `bins` : *int*
            Number of bins to be used to create the piece-wise interpolation of arclength vs flux correction.
* `windows` : *int*
            Number of windows to subdivide the data.  The SFF algorithm is run independently in each window.

This [tutorial](gully-tutorial) will teach you more about how to tune these parameters. For this problem, we will use the defaults, but increase the number of windows to 20._____no_output_____

corr_lc = lc.correct(windows=20)100%|██████████| 20/20 [00:00<00:00, 25.46it/s]

Now when we compare the two light curves we can see the clear signal from the exoplanet._____no_output_____

ax = lc.fold(period=3.0712).plot(color='C0', alpha=0.2, label='With Motion')
ax = corr_lc.fold(period=3.0712).plot(ax=ax, color='C3', alpha=0.2, label='Motion Corrected');_____no_output_____


# Notebook from cosmhology/Jupyter-Notebook-Practice-Physical-Constants-Ratios
Path: Jupyter-Notebook Practice Physical Constants Ratios.ipynb

# ***Jupyter-Notebook to Practice Physical Constants Ratios***

## Dr Francis M. Sanchez's Physical Constants Ratios in Cosmology and MicroPhysics

### Physical Constants Ratios

Introduction
------------

`Jupyter-Notebook Practice Physical Constants Ratios` provides common physical constants ratios.

Installation
------------

Run the Jupyter-notebook in [Mybinder.org](https://mybinder.org/v2/gh/LaGuer/Jupyter-Notebook-Practice-Physical-Constants-Ratios/master) will be available for Julia 0.7 and later versions, and can be
installed with
[Github.com source code](https://github.com/LaGuer/Jupyter-Notebook-Practice-Physical-Constants-Ratios/blob/master/Jupyter-Notebook%20Practice%20Physical%20Constants%20Ratios.ipynb).
In a terminal session run the command

```
jupyter-notebook

```

### Updated universal constants and particle properties ( thanks to Jean Maruani / Francis M. Sanchez)
* [The_Dirac_Electron_From_Quantum_Chemistry_to_Holistic_Cosmology @ researchgate](https://www.researchgate.net/publication/287808070_The_Dirac_Electron_From_Quantum_Chemistry_to_Holistic_Cosmology)
* [Dirac_Electron_From_Quantum_Chemistry_to_Holistic_Cosmology @ Wiley](https://onlinelibrary.wiley.com/doi/abs/10.1002/jccs.201500374)

| Symbol  | Name                                | Formula                  | Dimension      | Value              | Unit            |
| ------  | ----                                | -------                  | ---------      | -----              | ----            |
| `π`     | pi                                  | -                        | -              | 3.1415926535897932 | `pure number`   |
| `e`     | e                                   | -                        | -              | 2.7182817284590452 | `pure number`   |
| `γ`     | gamma                               | -                        | -              | 0.5772156649015329 | `pure number`   |
| `26SGP` | 26 sporadic group Product           | -                        | -              | exp(841.2869278)   | `pure number`   |
| `HFP`   | Happy Family product                | -                        | -              | exp(674.5210288)   | `pure number`   |
| `O_M`   | Monster Group Cardinal              | -                        | -              | 8.080174248 × 1053 | `pure number`   |
| `O_B`   | Baby Monster Group Cardinal         | -                        | -              | 4.15478148 × 1033  | `pure number`   |
| `c`     | Speed of light in vacuum            | -                        | -              | 2.99792458e8       | `m.s^-1`        |
| `ħ`     | Reduced Planck constant             | ħ=h/2pi                  | -              | 1.054571800e-34    | `m^2 kg s^-1`   |
| `G'`    | constant of gravitation CODATA2014  | -                        | M^-1L^3T^-2    | 6.67408(31)e-11    | `m^3 kg^-1 s^-2`|
| `G`     | F.M.Sanchez constant of gravitation | F_gr=Gmm'/ d^2           | M^-1L^3T^-2    | 6.675453818e-11    | `m^3 kg^-1 s^-2`|
| `a`     | Electric constant                   | a=α^-1  F_qq=ħc/ad^2     | dimensionless  | 137.035999139^-1   | `pure number`   |
| `R_U`   | Universe Hubble radius (Sz)         | R_U=2G.M_U/c)^2          | L              | 1.3065e26          | `m`             |
| `G_F`   | Fermi Coupling Constant (Sz)        | G_F=(ħc)^3/E_F^2         |   ML^5T^-2     | 1.4358509(7)e-62   | `J.m^3`         |
| `a_G`   |Gravit Sanchez Coupling Constant     | a_G=ħc/Gm_pm_H           | -              | 1.691936465e38     | `pure number`   |
| `M_U`   | Universe Sanchez Mass               | M_U=(ħc/G)^2/m_e.m_p.m_H | M              | 8.7936e52          | `kg`            |
| `t_cc`  | Kotov Cosmic Periodicity            | -                        | s              | 9600.061(2)        | `s`             |
|`r_H(0)` | Bare Hydrogen radius                |    aħ/m_ec               | L              | 5.291772103e-11    | `m`             |
| `H`     |Hydrogen-electron mass ratio         |    m_H/m_e               | dimensionless  | 1837.152645        | `pure number`   |
| `p`     |Proton-electron mass ratio           |    m_p/m_e               | dimensionless  | 1836.152672        | `pure number`   |
| `n`     |Neutron-electron mass ratio          |    m_n/m_e               | dimensionless  | 1838.683659        | `pure number`   |
| `m_e`   |electron mass                        | -                        | M              |9.10938356(11)e-31  | `kg`            |
| `μ'`    |Muon electron mass ratio CODATA2014  | -                        | dimensionless  |206.7682826(41)     | `pure number`   |
| `μ`     |Muon mass F.M.SANCHEZ  (Sz)          | -                        | dimensionless  |206.7682869         | `pure number`   |
| `f'`    |Strg Nuc Coupling Constant CODATA2014| -                        | dimensionless  |1/0.1181(11)        | `pure number`   |
| `f`     |Strg Nuc Coupling Constant C.Bizouard|    F^2/2pi(pH)^3/2       | dimensionless  |8.434502892         | `pure number`   |
| `F'`    |Fermi Constant reduced mass CODAT2014|   E_F / m_ec^2           | dimensionless  |573007.33(25)       | `pure number`   |
| `F`     |Fermi Constant reduc mass F.M.SANCHEZ| -                        | dimensionless  |573007.3652         | `pure number`   |
| `d_e`   | electron magnetic moment excess     | -                        | dimensionless  |1.00115965218091(26)| `pure number`   |
| `θ'`    |Cosmic Temperature CODATA2014        | -                        | -              |  2.7255(6) K       | `K`             |
| `θ`     |Cosmic Temperature F.M.Sanchez       | -                        | -              |  2.725820831 K     | `K`             |
| `k_B`   |Boltzman Energy-temperature Convers° | -                        | -              | 1.3806488e10-23    | `J K^-1`        |
| `W`     |Intermediate vector boson Red. Mass  | -                        | dimensionless  | 157298(23)         | `pure number`   |
| `Z`     |Intermediate vector boson Red. Mass  | -                        | dimensionless  | 178450.0(41)       | `pure number`   |
| `Π±`    |Intermediate vector boson Red. Mass  | -                        | dimensionless  | 273.13203(68)      | `pure number`   |
| `Π0`    |Intermediate vector boson Red. Mass  | -                        | dimensionless  | 264.1426(18)       | `pure number`   |

## Latex and notations

- Put your LaTeX math inside ```$$ $$```

- Example of notation for:

- $$\sqrt{a_W} = \frac{E_F}{m_e \cdot c^2}$$ type ```$$\sqrt{a_W} = \frac{E_F}{m_e \cdot c^2}$$```

- Write your equation in a dedicated cell with latex

Exercice of notation for:

- $$\sqrt{G'G_F} = \frac{\frac{ƛ_e^2}{t_cc} \cdot ħ}{\sqrt{m_p \cdot m_H}}$$ ```$$\sqrt{G'G_F} = \frac{\frac{ƛ_e^2}{t_cc} \cdot ħ}{\sqrt{m_p \cdot m_H}}$$```

- This translates ```\begin{equation}
  \frac{\pi_Pt}{\pi_Bb}\ = 6\cdot\frac{\pi^5}{p_H^9}
  \label{eq:sample}
\end{equation}
```

- to this:
    \begin{equation}
  \frac{\pi_Pt}{\pi_Bb}\ = 6\cdot\frac{\pi^5}{p_H^9}
  \label{eq:sample}
\end{equation}

```
%%latex
\begin{align}
\R_U = \frac{2\cdot G\cdot M_U}{c^2} \\
\M_U = \frac{({ħc}{G})^2}{m_em_pm_h} \\
\r_H/ƛ_e = frac{\sum\frac{1}{n}{\sum(\frac{1}{n^2})}\\
\end{align}
```

- $$\e^{i\pi} = -1$$ type ```$$\e^{i\pi} = -1$$```
```

- to this:
- $$\sum{n}(\frac{1}{n})\ = \frac{137}{60} = 2 \times (\pi-2)$$ type ```$$\sum{n}(\frac{1}{n})\ = \frac{137}{60} = 2 \times (\pi-2)$$```

##_____no_output_____

<code>
%%timeit
# %matplotlib inline
%matplotlib notebook
import matplotlib
import numpy as np
import matplotlib.pyplot as plt

# using PhysicalConstants.CODATA2014
## m_p/m_e
## R_U=2G.M_U/c)^2_____no_output_____%%latex
\begin{align}
\frac{m_p^4}{m_p\cdot m_H\cdot\ m_e} = M \\
\end{align}_____no_output_____# %%timeit
# Inclusions
from math import *
values = []
exponents = []

########################################################
#     Physical Constants Ratios                        #
########################################################

progression_display = True # True / False
exponent_minmax = 3
precision = 0.00000001 # add or remove 0 for precision's accuracy
number_min_Zero = 1

# exercice 2
#exponent_minmax = 4
#precision = 0.00000002
#values.append(['a/137',log(137.0359991)-log(137)])
#values.append(['pg',log(1831.531308)-log(1836.152672)*0])
#values.append(['pK',log(1842.604127)-log(1836.118109)*0])
#values.append(['H',log(1837.152645)-log(1836.118109)*0])
#values.append(['p',log(1836.152672)+log(1836.118109)*0])
#values.append(['n',log(1838.683659)])
#values.append(['1sH-p',-log(0.999973)])
#values.append(['d',log(1.001159652)])

# exercice 2
values.append(['pi',1.144729886])
values.append(['a',4.920243658])
values.append(['137',4.919980926])
values.append(['F',13.25865385])
values.append(['H/p',0.00054445428])
values.append(['x',0.002792343634])
values.append(['n',7.516805193])
values.append(['8',2.079441542])
values.append(['d',0.001158980123])
values.append(['Ggamma',3.22597446])

########################################################
#     The Program itself                               #
########################################################

print ("Starting CosmHology...")
length = len(values)
precedent=-exponent_minmax

# Min exponent set for each value
for i in range (0,length):
    exponents.append(-exponent_minmax)
    print(values[i][0],end='\t')

print("Zeros",end='\t');
print("Precision");

nbSolutions = 0
#<= 0 Do not make twice the same calculation with the inverses
while exponents[length-1]<=0:
    # Calculate and process the result
    result= 0
    j=0
    while j<length:
        result=result + (values[j][1])*(exponents[j])
        j+=1
    #if result<0:
    #    result*=(-1)

    # Display or not the results
    if result<=precision and result>=-precision:
        # counting zeros
        nbzero=0
        for j in range (0,length):
            if (exponents[j] == 0):
                nbzero+=1
        if nbzero >= number_min_Zero:
            for j in range (0,length):
                print(exponents[j],end='\t')
            print(nbzero,end='\t')
            print(int(result*1000000000))
            nbSolutions = nbSolutions + 1

    # Add + Exponents Rotation
    exponents[0]+=1
    for j in range (0,length-1):
        if exponents[j]>exponent_minmax:
            exponents[j]=-exponent_minmax
            exponents[j+1]+=1
    if (progression_display):
        if (exponents[length-2]!=precedent):
            precedent=exponents[length-2]
            #print("Status:",end=' ')

print(exponents[j+1]);
print ("All results lead to nbSolutions =", nbSolutions)
print("Precision", precision)
print("Used Values are:", values)
Starting CosmHology...
a/137	pg	pK	H	p	n	1sH-p	d	Zeros	Precision
1	2	2	1	-3	-2	-1	0	1	-16
0	0	0	0	0	0	0	0	8	0
-1	-2	-2	-1	3	2	1	0	1	16
All results lead to nbSolutions = 3
Precision 2e-08
Used Values are: [['a/137', 0.00026273263604270625], ['pg', 7.51290767625786], ['pK', 7.518935136442214], ['H', 7.515972176482602], ['p', 7.515427722407373], ['n', 7.5168051918630026], ['1sH-p', 2.7000364506560378e-05], ['d', 0.0011589801229981887]]

</code>

In the following example, one can write the equation with Latex:_____no_output_____

<code>
from IPython.display import Latex
Latex(r"""\begin{eqnarray}
\frac{m_p^4}{m_p\cdot m_H\cdot\ m_e} = M  \\
\R_U = \frac{2\cdot G\cdot M_U}{c^2} \\
\M_U = \frac{({ħc}{G})^2}{m_em_pm_h} \\
\frac{r_H}{ƛ_e} = \frac{\sum\frac{1}{n}}{\sum\frac{1}{n^2}}\\
\end{eqnarray}""")
_____no_output_____
</code>

:star:

# Docs

This project aims at providing a repository dedicated to Dr Francis Michel Sanchez' work in holography and cosmology; it is just a mere continuation of what was initiated and published on a couple of other blogs.

## Blog
* [cosmhology blog in english](https://cosmhology.blogspot.com/)
* [cosmhologie blog in french](https://cosmhologie.blogspot.com/)
* [Concorde Physiques in russian ](http://concorde-physiques.blogspot.com/)

## Publications
* Please, cite this paper (http://vixra.org/pdf/1811.0146v8.pdf) if you employ this package in your research work.
* [Vixra Publications](https://vixra.org/author/francis_m_sanchez)
* [A coherent resonant cosmology approach and its implications in microphysics and Biophysics Pages 375-407 of Quantum Systems in Physics, Chemistry](https://www.springer.com/fr/book/9783319502540?token=gbgen&wt_mc=GoogleBooks.GoogleBooks.3.EN#)
* [From Coherent Cosmic Oscillations to the Steady-State Cyclic Ultrafast](https://rxiv.org/abs/1401.0228)
* [Evidence for a Steady-State, Holographic, Tachyonic and Super-Symmetric Cosmology](https://www.researchgate.net/publication/267430152_Evidence_for_a_Steady-State_Holographic_Tachyonic_and_Super-Symmetric_Cosmology)
* [academia.edu Correcting Five Historical Faults in Modern Cosmology](https://www.academia.edu/30843365/Correcting_Five_Historical_Faults_in_Modern_Cosmology)

## Videos

* [L'enigme des grands nombres Part1](https://www.youtube.com/watch?v=ev_eIqLtK8s)
* [L'enigme des grands nombres Part2](https://www.youtube.com/watch?v=i4w392nYqns)
* [L'enigme des grands nombres Part3](https://www.youtube.com/watch?v=b4WvI0yVXnE)
* [Exposés de Francis Sanchez](https://www.youtube.com/playlist?list=PLQXUNrzoAeFbmfHo3jgWzzExlVoBn2JUw)
* [Khelif/Torgoz/Sanchez' Debate on LE FORMALISME EN PHYSIQUE ET EN SCIENCE](https://www.youtube.com/watch?v=TEK2hCI2we0)

* https://www.youtube.com/watch?v=dyUlUahgHGg
* https://www.youtube.com/watch?v=NmvvIQjxBw8
* https://www.youtube.com/watch?v=dyUlUahgHGg
* https://www.youtube.com/watch?v=zUt1hxTdiuA
* https://www.youtube.com/watch?v=05g416VWjnw

## Books

* [Quantum Systems in Physics, Chemistry, Springer Ed](https://www.amazon.com/gp/search?index=books&linkCode=qs&keywords=9783319502557)
* [The Worldwide List of Alternative Theories and Critics](https://books.google.com/books?isbn=2902425171)
* [The Dirac Electron: From Quantum Chemistry to Holistic Cosmology by J.Maruani](https://onlinelibrary.wiley.com/doi/abs/10.1002/jccs.201500374#)
* [solar 22 years cycle](https://link.springer.com/article/10.1007%2Fs10509-016-2985-8)
* [Current Issues in Cosmology](https://www.cambridge.org/gb/academic/subjects/physics/cosmology-relativity-and-gravitation/current-issues-cosmology?format=HB&isbn=9780521858984)
* ```@inproceedings{Sanchez2015RemarkablePO,
  title=Remarkable Properties of the Eddington Number 137 and Electric Parameter 137 . 036 excluding the Multiverse Hypothesis},
  author={Francis M. Sanchez and Valery A. Kotov,
  year=2015```

## Papers

* [Discussions sur les oscillations cosmiques, les nombres sans dimension et les periodicites en microphysique et cosmologie - College de France 2004](https://lempel.pagesperso-orange.fr/occ_cdf.pdf)

More to come soon stay tuned.../.:stars:._____no_output_____

<code>

    #Write your equation in a dedicated cell with latex

from IPython.display import Latex
Latex(r"""\begin{eqnarray}
\frac{m_p^4}{m_p\cdot m_H\cdot\ m_e} = M \\
\frac{\pi_Pt}{\pi_Bb} = 6 \cdot \frac{\pi^5}{p_H^9} \\
\end{eqnarray}""")

_____no_output_____
</code>


# Notebook from danielbdias/mdp-sir-modelling
Path: sir_mdp_example.ipynb

# Modelling epidemics evolution SIR model through a factored MDP

brief descrition about this work

[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/danielbdias/mdp-sir-modelling/blob/master/sir_mdp_example.ipynb)_____no_output_____## Dependencies setup_____no_output_____

# Uncomment this line to run this code on Google Colab
#!sh -c "$(curl -fsSL https://raw.githubusercontent.com/danielbdias/mdp-sir-modelling/master/google_colab_setup.sh)"

# enable rich graphs instead of images on notebook
#import mpld3
#mpld3.enable_notebook()

# code dependencies
import time

from sir_modelling.simulation import plot_sir
from sir_modelling.enumerative_model import create_representation
from sir_modelling.enumerative_model_simulation import simulate_policy_with_mdp_model

from mdp.algorithms.value_iteration import enumerative_finite_horizon_value_iteration_____no_output_____

## Creating a model

Model parameters_____no_output_____

# approximate values by 2 decimal digits
approximation_threshold = 1e-2

# beta (infection rate)
# assumption
# beta < 1.0 - social distancing
# beta >= 1.0 - no social distancing
betas = [0.5, 1.0, 2.5, 4]

# recovery rate (1 person takes 15 days to recover)
gamma = 1.0 / 15.0

horizon = 10 # in weeks
discount_factor = 0.9

steps_per_transition = 7 # each chosen action affects the virus evolution by 7 days (1 week)
initial_state = "s_99_i_01_r_00"_____no_output_____

Creating MDP representation_____no_output_____

def run_model(approximation_threshold, gamma, betas, steps_per_transition, reward_function, initial_state):
    start_time = time.perf_counter()

    # create MDP representation using SIR model
    mdp = create_representation(approximation_threshold, gamma, betas, steps_per_transition, reward_function)
    # run VI on MDP
    policy, value_function, statistics = enumerative_finite_horizon_value_iteration(mdp, discount_factor, horizon)
    # simulate policy
    chosen_betas, S, I, R = simulate_policy_with_mdp_model(policy, initial_state, mdp, horizon, approximation_threshold)

    elapsed_time = time.perf_counter() - start_time

    # plot results
    t = list(range(horizon + 1))
    plot_sir(t, S, I, R, time_label="weeks")

    # print chosen betas
    print(chosen_betas)

    # print elapsed time
    print(f"Elapsed time: {elapsed_time:0.4f} seconds")_____no_output_____# weighted reward function (S_weight + R_weight = I_weight)
reward_function = lambda susceptibles, infective, recovered, beta: 10 * susceptibles + 5 * recovered - 15 * infective
run_model(approximation_threshold, gamma, betas, steps_per_transition, reward_function, initial_state)_____no_output_____# weighted reward function (S_weight + R_weight > I_weight)
reward_function = lambda susceptibles, infective, recovered, beta: 10 * susceptibles + 5 * recovered - 1 * infective
run_model(approximation_threshold, gamma, betas, steps_per_transition, reward_function, initial_state)_____no_output_____# weighted reward function (S_weight + R_weight < I_weight)
reward_function = lambda susceptibles, infective, recovered, beta: 10 * susceptibles + 5 * recovered - 30 * infective
run_model(approximation_threshold, gamma, betas, steps_per_transition, reward_function, initial_state)_____no_output_____# complex reward function
def reward_function(susceptibles, infective, recovered, beta):
    if infective > 0.75:
        return -10.0

    productivity = beta if beta >= 1.0 else -(susceptibles * beta)

    return productivity * (susceptibles + recovered) - 2 * infective

run_model(approximation_threshold, gamma, betas, steps_per_transition, reward_function, initial_state)_____no_output_____