11/24/2018
# Learning and Exploring Artificial Neural Networks
I've always been interested in technology and exploring how the body works from since I can remember.  Simulating the entire human body in a computer is the ultimate goal of mine, but the practicallity of that is years off in the future.  I'm mostly interested in simulating the human mind, not because it would be a great scientific feat, but for the very fact that it can be maniuplated.  This interest of mine landed me on the front door step of artificial intelligence (AI).  Before I sat down and started studying about AI, I wanted to get an appreciation for the subject matter.  I've started to look at a lot of video's that were informative about AI at the 10,000ft level over the course of a couple months.  This appreciation period made me realize that I wanted to study deep learning.  Deep learning is part of machine learning and machine learning is part of AI. I also wanted to figure out where to start my study, top down or bottom up.  Even though a top down approach would of allowed me to get started doing real practical work, I'm the type of person who enjoys starting from the bottom up and learning the fundamentals.  Therefore, I decided to start as far back as practically possible, the history of the perceptron.

Artificial Intelligence, Machine Learning, and Deep Learning Relationship

# Brief History Overview
Artificial Neural Networks (ANN's) are inspired by the brain, a biological neural network.  Neural networks are made up of connected neurons to carry out specific functions/computations when activated.  [Warren Sturgis McCulloch](https://en.wikipedia.org/wiki/Warren_Sturgis_McCulloch), a neurophysiologist, and [Walter Pitts](https://en.wikipedia.org/wiki/Walter_Pitts), a logician, worked in the field of computational neuroscience and created computational models based on mathematical algorithms called threshold logic.  [Frank Rosenblatt](https://en.wikipedia.org/wiki/Frank_Rosenblatt) introduced the perceptron in 1958 inspired by the earlier work of Warren McCulloch.  There was an optimism about the power of the perceptron. In the 1960's the optimism came to a halt after [Marvin Minksy's](https://en.wikipedia.org/wiki/Marvin_Minsky) and [Seymour Papert's](https://en.wikipedia.org/wiki/Seymour_Papert) 1969 book, [Perceptrons](https://www.amazon.com/Perceptrons-Introduction-Computational-Geometry-Expanded/dp/0262631113), was published.  The book showed there're severe limitations to what the perceptron can do and that Frank's claims were exaggerated.  One of the limitations of the perceptron is its inability to solve a simple function called, the [exclusive disjunction (XOR)](https://en.wikipedia.org/wiki/Exclusive_or).  This had a negative effect of the research community and research in connectionism stopped for 10 years.  To learn more about the building blocks the perceptron is built on, read the book, [Neural Networks. A Systematic Introduction](https://www.semanticscholar.org/paper/Neural-Networks-A-Systematic-Introduction-Rojas/0ab5e90d14264b7faf406c15615a4f08cc945f27).

# What is a Neuron?
A neuron is a cell which is the basic working unit of the brain.  It is a specialized cell designed to transmit information to other nerve, muscle, or gland cells.  It was estimated that a human brain has approximately 100 billion neurons, but no one knew where this number came from.  Brazilian researcher, [Dr. Suzana Herculano-Houzel](https://en.wikipedia.org/wiki/Suzana_Herculano-Houzel), decided to find out where this number originated, but didn't have much luck.  She decided to count them herself and discovered a method to do just that.  She discovered the human brain has approximately 86 billion neurons in total.  This may seem like a small difference from 100 billion, so to provide more context around this number, the following is a list of other animals and the number of neurons they have:

- Fruit fly: 100 thousand neurons
- Cockroach: One million neurons
- Mouse: 75 million neurons
- Cat: One billion neurons
- Chimpanzee: 7 billion neurons
- Elephant: 23 billion neurons

Cells typcially have a body containing a liquid called cytoplasm, a nucleus containing DNA, and other organelles. Think of a cells organelles like the organs in your body that perform specific tasks to keep the body alive. Most neurons have a cell body, an axon, and dendrites.

Graphical Neuron

The axon extends from the cell body and often gives rise to many smaller branches before ending at nerve terminals. Dendrites extend from the neuron cell body and receive messages from other neurons. Synapses are the contact points where one neuron communicates with another. The dendrites are covered with synapses formed by the ends of axons from other neurons.

Biological Neuron (credit: Blake Richards)

# Perceptron
A perceptron is inspired by a single biological neuron. There has been a lot of research over the years inventing the building blocks of the perceptron. Read [Threshold Logic](ThresholdLogic.ipynb) to get a perspective on what it took to get here. The perceptron is more formally called a binary classifier, a type of linear classifier. A classifier separates data into categories, and in this case, the perceptron is designed to predict the inputs category.  Our brains do this without much effort, such as classifying mammales and reptiles, fruit and vegtables, and ect.  The perceptron simply takes some inputs and performs a mathematical process and spits out an output/classification/prediction.  In order to look into this mathematical process in more detail, let's look at a more detailed graph.

Formal Perceptron Structure - Details

The perceptrons' algorithm computes the output in two steps and they're described in the blue circle.  The first step is to compute the weighted sum of the inputs, called $z$.  The second step computes the activation function with the input $z$.  This two step process, is called the Feed Forward process. In the machine learning field, the output of a perceptron is called a prediction.

Weighted Sum of the Inputs

      This is the sum of all inputs multiplied by their weighted connections.

Activation Function

      There're many different types of activation functions and they translate the output to be within a desired range. This is an important concept in neural network based machine learning.

Let's look at a perceptron with 2 inputs.  The inputs and outputs of a perceptron are just numbers. The activation function of a binary classifier takes some inputs and produces a binary output (i.e. {-1, 1}). What mathematical function can turn any number (i.e. weighted sum) into a binary output?  The Sign function is one such function, and it will be used as the activation function in the implementation.  Simply, the Sign function takes any number as an input and then outputs a binary value, i.e {-1, 1}.  The formal Sign function is defined in the equation below.

\begin{equation}
  sgn(x)=\begin{cases}
    -1, & \text{if $x<0$},\\
    0, & \text{if $x=0$},\\
    1, & \text{if $x>0$}
  \end{cases}
\end{equation}

I said we need a activation function that outputs a binary value, but the above Sign equation outputs three values {-1, 0, 1}.  We have to simplify it for implementation purposes, and the modified definition is shown below.  Take notice that it now outputs two values {-1, 1}.

\begin{equation}
  sgn(x)=\begin{cases}
    -1, & \text{if $x<0$},\\
    1, & \text{if $x\geq0$}
  \end{cases}
\end{equation}

Now that we have an appropriate activation function, the perceptron looks like the following computational graph below.  You can see the two computational steps, weighted sum and activation function, described in the blue circle.  The computation is also written out in mathematical notation.

Formal Perceptron Structure - Details

$$prediction = sgn(x_1 \cdot w_1+x_2 \cdot w_2)$$

Let's take a minute to look at what we are trying to do again.  We want our perceptron to predict what category our set of inputs, $x_1$ and $x_2$, belong to.  The categories are not known at this point, but we will cover that later.  $x_1$ and $x_2$ are the inputs that will be fed into an equation to compute a prediction, but what are the weights, $w_1$ and $w_2$?  These weights will need to be **learned** using a supervised learning algorithm.  The learning process will find the optimum weights to correctly categorize the inputs.  Before the learning process starts, these weights will be set to random values. Initializing the weights of a perceptron can be more sophisticated than randomly assigning values, but it's not in the scope of this paper.  If you want to read more about it, check out this notebook, [Neural Network Initialization](NeuralNetworkInitialization.ipynb).

A perceptron learns by training it with training data.  Training data is a set of inputs and outputs.  The outputs are typically called labels in a training set.  I know this sounds very vauge, so let's walk-thru an analogy.  Let's imagine a kid is trying to learn the difference between dog and cat pictures.  The kid might have a set of dog and cat pictures with the answers on the back.  The kid would look at each picture, guess whether it's a picture of a dog or cat, and then look at the answers on the back to determine if the guess was correct.  If the kid guessed correctly, he does nothing.  If the kid guessed incorrectly, he'll make the appropriate adjustments in his mind.  For instance, maybe the kid didn't know cats can be bigger than dogs and might classify a large cat as a dog.  The kid will remember that some cats are bigger than dogs so next time he doesn't make the same mistake.  With enough time, the kid will be able to classify all the pictures correctly.  In this analogy, the set of pictures is considered the training data where the pictures would be the inputs and the answers on the back would be the lables/outputs.  Once a perceptron is trained, it should be able to correctly classify dog and cat pictures that it hasn't seen before.

Using the same analogy, we can imagine that if the kid did classify a picture incorrectly, he'll be able to tell you how much he was off.  For instance, classifying a Pitbull as a cat is much more incorrect than classifying a Chihuahua as a cat.  This is very important for perceptrons because if the error can be computed then the algorithm can be adjusted accordingly.  With a prediction and a label, an error can be computed.  The error is computed by subtracting the prediction from the label.

$$error = label - prediction$$

The above $error$ function computes the cost for one training example, but we need to compute the cost/error for all training examples.  Before we do this, let's use standard mathematical notation.  For each value $i$ in the training example, we can say that $t^{(i)}$ is the target value for the $i^{th}$ training example and $y^{(i)}$ is the guess produced by the current set of weights in the perceptron.  Let's look at the above $error$ function again using this new notation.

$$E = t^{(i)} - y^{(i)}$$

Therefore, the cumulative error/cost for the training data is defined below.  If you're familiar with statistics, you'll recognize the formula as the [Mean Squared Error (MSE)](https://en.wikipedia.org/wiki/Mean_squared_error).

$$E=\dfrac{1}{2}\sum_i(t^{(i)}-y^{(i)})^{2}$$

All possible error values for a perceptron are  in the table below.

| label | prediction | error |
| :---: | :---: | :---: |
| -1 | -1 | 0 |
| -1 | 1 | -2 |
| 1 | -1 | 2 |
|  1 | 1 | 0 |

Once the error can be computed, then how is the algorithm adjusted so that it learns not to make the same mistake in the future.  As stated above, the weights, $w_1$ and $w_2$, need to be adjusted up or down relative to the magnitude of the error.  The amount $w_1$ and $w_2$ change is defined by the mathemtatical terms $\Delta w_1$ and $\Delta w_2$.  These terms can be read as "the change in $w_1$ and the change in $w_2$".  For instance, if it's determined that the weights need to be changed by 2, then $w_1$ and $w_2$ will decrease by 2. $\Delta w_1$ and $\Delta w_2$ do not have to be the same value, but they can be different.  Below are the equations that represent how to change the weights.

$$w_1 = w_1 - \Delta w_1$$
$$w_2 = w_2 - \Delta w_2$$

$$\text{Generically, } w_n = w_n - \Delta w_n$$

Now that we have the equations to adjust the weights, how are the delta's, $\Delta w_n$, computed? The $\Delta w_n$ will be computed using a process called gradient descent.  Gradient descent is not in the scope of this article, because there's already so much information on the internet about it, for instance, [Gradient Decent](https://en.wikipedia.org/wiki/Gradient_descent).

Discuss exactly how and why the equations are the way they are.  Why multiply the error to the input?  What does it mean to multiply an error to an input?

The learningRate is a configurable parameter to adjust how fast to move down the gradient.  The $\Delta w_n$ can be computed using gradient descent as follows,

$$\Delta w_1 = error \cdot x_1 \cdot learningRate$$
$$\Delta w_2 = error \cdot x_2 \cdot learningRate$$

$$\text{Generically, }\Delta w_n = error \cdot x_n \cdot learningRate$$

Now that the feed forward algorithm and gradient descent can be computed, lets look at the entire supervised learning algorithm and then implement it in code.

1. Initialize a perceptron with random weights
2. Provide the perceptron with training data and the labels
3. Ask the perceptron to compute a prediction with a $sgn(x)$ function (a function that return's -1 or 1)
4. Compute the error (Did the perceptron get the answer right or wrong?)
5. Adjust weights with respect to the error
6. Return to step 2 and repeat

There is a problem with the above perceptron.  Imagine if $x_1$ and $x_2$ are both 0, then it wouldn't matter what $w_1$ and $w_2$ are because they would always end up being 0 too.  To fix this problem, another input is added called the bias and the bias is always 1.  The algorithm will work the same, but instead of 2 inputs, it will work with three inputs with bias always being 1.  The bias will ensure that at least one input is not 0.  The figure below illustrates this concept.

Therefore, if a perceptron represents a liner line $y=mx+b$, then the line can be described as, ($x_3$ is the bias)

$$w_1 \cdot x_1 + w_2 \cdot x_2 + w_3 \cdot x_3 = 0$$
$$w_2 \cdot x_2 = -w_1 \cdot x_1 - w_3 \cdot x_3$$
$$x_2 = \dfrac{-w_1}{w_2} \cdot x_1 - \dfrac{w_3}{w_2} \cdot x_3$$

Let's jump right into the implementation. First let's add some library references that we'll need

```python
%matplotlib inline
import plotly.offline as plt
import plotly.graph_objs as go
import numpy as np
```

To start, object-oriented programming will be used for better understanding.  The object that needs to be implemented is Perceptron. The Perceptron class is implemented as follows...

```python
# ---------------------------
# class: Perceptron
# This perceptron has three inputs (including bias) and one output
# ---------------------------
class Perceptron:
    def __init__(self, numberOfFeatures, learningRate, bias, epochs):
        # create (numberOfFeatures) random weights (float) between -1 and 1
        # create an additional weight for the bias
        self.weights = np.random.uniform(-1, 1, numberOfFeatures + 1)
        self.learningRate = learningRate
        self.error = []
        self.bias = bias
        self.epochs = epochs

    # ---------------------------
    # Feed forward algorithm that takes inputs
    # and produces and outputs
    # ---------------------------
    def predict(self, inputs):
        weightedSum = 0

        # compute the weighted sum
        # first handle the bias
        weightedSum += self.bias * self.weights[0]
        # second, use dot product to calculated a weighted sum of the inputs and weights
        weightedSum = np.dot(inputs, self.weights[1:])

        # pass the weighted sum to the sign function (activation function)
        output = self.sign(weightedSum)

        #return output
        return output

    # ---------------------------
    # Activation Function - Basic step function
    # Return 1 if n is 1 or greater, else -1
    # ---------------------------
    def sign(self, n):
        if n >= 0:
            return 1
        else:
            return -1

    # ---------------------------
    # Trains the perceptron by determining the outputs
    # error and progating it backwards through the perceptron
    # ---------------------------
    def train(self, inputs, labels):

        for epoch in range(self.epochs):
            e = 0
            for inpt, label in zip(inputs, labels):
                guess = self.predict(inpt)
                error = label - guess

                #Adjust all the weights
                self.weights[0] += self.learningRate * error * self.bias
                self.weights[1:] += self.learningRate * error * inpt

                e += int((error * self.learningRate) != 0.0)

            self.error.append(e)

```

Below is an arbitrary data set plotted on a graph with a line through them.  The goal of this perceptron is to classify what points are below and above a given line.  What it means for a perceptron to learn to classify is that it learns a mathematical function to separate the two groups.  The mathematical function it will learn is represented by the line below.  Therefore, a perceptron or neural network is call a "Univesral Function Approximater".  The data points below represent training data for the perceptron.  Once the perceptron is trained it will be able to take in data points not listed in the graph below and predict if the point is below or above the line.  The perceptron that will be implemented will be trained using supervised learning and takes in inputs and outputs a 1 above the line and -1 for below the line.  The supervised learning approach means the training data below will be fed into the perceptron one-by-one and if it predicts the wrong category the perceptron will be penalized by tweaking it's algorithm programmatically.  Tweaking of the algorithm is known as gradient decent.  If the perceptron predicts the correct category, no penalty.  After training, the perceptron should be able to predict the correct category for data points not in the training set.

The formula for a line is represented like, $y=mx+b$.  The line that our perceptron will try to approximate below is $y=0.5x+20$.  As indicated in the graph below, the line passes through the y-intercept at 20 and has a slope of 0.5.

Let's create some random points

```python
# Set number of points
numberOfPoints = 500

# Initialize the points and labels array
points = np.zeros(shape=(numberOfPoints, 2))
labels = []

# Initialize random points between 1 and 100
for i in range(numberOfPoints):
    x = np.random.randint(-250, 250)
    y = np.random.randint(-250, 250)
    label = 1 if (x>y) else -1
    points[i] = [x,y]
    labels.append(label)
```

Now let's plot the random points on a graph

```python
# Create scatter plot points using the random points created above
# All x-coordinates go into one array and all y-coordinates into another
graphPoints = go.Scatter(
    x = points[:, [0]].flatten(),
    y = points[:, [1]].flatten(),
    mode = 'markers',
    marker = dict(
        symbol = 'circle'
    ),
    name = 'Random Data'

)

layout = go.Layout(
    title='Random Data Points',
    xaxis=dict(
        title='$x-axis\\text{ }(x_1)$'
    ),
    yaxis=dict(
        title='$y-axis\\text{ }(x_2)$'
    )
)

fig = go.Figure(data=[graphPoints], layout=layout)
plt.iplot(fig)

```

Let's graph a line through the points in which we want to perdict

```python
# Plot a line y=mx+b
x = np.linspace(-250,250,2)
m = 0.5
b = 20
y = m*x+b

line = go.Scatter(
    x = x,
    y = y,
    name = 'Seperate Categories'
)

layout.title = 'Line to Seperate Categories'
fig = go.Figure(data=[graphPoints, line], layout=layout)
plt.iplot(fig)
```

Now let's create a Perceptron and use it to guess if the points are above the line or below the line

```python
#
# 1. Using the untrained perceptron, guess if the data points are above or below the line
# 2. Train the perceptron
# 3. Using the trained perceptron, guess if the data points are above or below the line
#
# Note: Green points mean a correct prediction and red points mean an incorrect prediction.
# After the perceptron is trained, all points should be green
#

# Create a Perceptron
numberOfFeatures = 2
learningRate = 0.001
bias = 1
epochs = 100
perceptron = Perceptron(numberOfFeatures, learningRate, bias, epochs)

# Display the Perceptrons' predictions
# The predictions are incorrect because the perceptron hasn't been trained
#
c = np.empty(shape=(0, 2))
w = np.empty(shape=(0, 2))

for point, label in zip(points, labels):
    guess = perceptron.predict(point)

    if guess == label:
        c = np.append(c, np.array([point]), axis=0)
    else:
        w = np.append(w, np.array([point]), axis=0)

correct = go.Scatter(
    x = c[:, [0]].flatten(),
    y = c[:, [1]].flatten(),
    mode = 'markers',
    marker = dict(
        symbol = 'circle',
        color = 'rgb(0, 255, 0)'
    ),
    name = 'Perceptron Guessed Right'
)

wrong = go.Scatter(
    x = w[:, [0]].flatten(),
    y = w[:, [1]].flatten(),
    mode = 'markers',
    marker = dict(
        symbol = 'circle',
        color = 'rgb(255, 0, 0)'
    ),
    name = 'Perceptron Guessed Wrong'
)

layout.title = 'Untrained'
fig = go.Figure(data=[correct, wrong, line], layout=layout)
plt.iplot(fig)
```

The perceptron does poorly at guessing which points are above and below the line.  The green points indicate a correct guess by the perceptron and red otherwise.

Next, let's train the perceptron.

```python
# Train the Perceptron
perceptron.train(points, labels)
```

Now let's see how the perceptron performs now

```python
c = np.empty(shape=(0, 2))
w = np.empty(shape=(0, 2))

# Display the Perceptrons' predictions
# The predictions are correct because the perceptrons has been trained
for point, label in zip(points, labels):
    guess = perceptron.predict(point)

    if guess == label:
        c = np.append(c, np.array([point]), axis=0)
    else:
        w = np.append(w, np.array([point]), axis=0)

correct = go.Scatter(
    x = c[:, [0]].flatten(),
    y = c[:, [1]].flatten(),
    mode = 'markers',
    marker = dict(
        symbol = 'circle',
        color = 'rgb(0, 255, 0)'
    ),
    name = 'Perceptron Guessed Right'
)

wrong = go.Scatter(
    x = w[:, [0]].flatten(),
    y = w[:, [1]].flatten(),
    mode = 'markers',
    marker = dict(
        symbol = 'circle',
        color = 'rgb(255, 0, 0)'
    ),
    name = 'Perceptron Guessed Wrong'
)

layout.title = 'Trained'
fig = go.Figure(data=[correct, wrong, line], layout=layout)
plt.iplot(fig)
```

It's good to always view how well your algorithm does over time.  Looking at the loss/error can give us a good indication that the algorithm is working and getting better with each iteration.  This will be very important when implementing multi-layer neural networks. The error should be minimized to zero for perfect solution.  These parameters

    numberOfFeatures = 2
    learningRate = 0.001
    bias = 1
    epochs = 100

can be experimented with to see how it affects the algorithms performance.  For instance, with a small learning rate and a large feature set, the algorithm error function should converge to zero relatively slowly.

```python
print(perceptron.error)
```

    [21, 16, 10, 11, 10, 8, 8, 14, 11, 14, 11, 14, 7, 11, 9, 7, 6, 11, 8, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

```python
errorGraphPoints = go.Scatter(
    x = np.linspace(0, len(perceptron.error), len(perceptron.error)),
    y = perceptron.error,
    mode = 'lines',
    marker = dict(
        symbol = 'circle',
        color = 'rgb(0, 255, 0)'
    ),
    name = 'Loss'
)

layout = go.Layout(
    title='Loss',
    xaxis=dict(
        title='Epochs'
    ),
    yaxis=dict(
        title='Error'
    )
)

fig = go.Figure(data=[errorGraphPoints], layout=layout)
plt.iplot(fig)
```

```python

```


```python
from IPython.core.display import HTML
from IPython.display import Image
HTML("""
<style>
.output_png {
    display: table-cell;
    text-align: center;
    vertical-align: middle;
}
</style>
""")
```

<style>
.output_png {
    display: table-cell;
    text-align: center;
    vertical-align: middle;
}
</style>

# *Circuitos Elétricos I - Semana 8*

# Circuitos de segunda ordem

## Circuito RLC em paralelo

Simulação: https://tinyurl.com/ycnkjnot

### Aplicação das Leis de Kirchhoff

$$\begin{align}
   i_R + i_L + i_C &= i_s\\
   \frac{v}{R} + i_L + C\frac{d v}{d t} &= i_s\nonumber\\
    \frac{1}{R}\left[L\frac{d i_L}{d t}\right] + i_L + C \frac{d}{d t}\left[L\frac{d i_L}{d t}\right]  &= i_s\nonumber\\
    \frac{d^{2} i_L}{d t^{2}}+\frac{1}{RC} \frac{d i_L}{d t}+\frac{1}{L C} i_L &=\frac{i_s}{LC}
   \end{align}
$$

com $\alpha = \frac{1}{2RC}$ e $\omega_0 = \frac{1}{\sqrt{LC}}$, temos

$$
\begin{equation}\label{EDO1}
\frac{d^{2} i_L}{d t^{2}}+2\alpha \frac{d i_L}{d t}+\omega_0^2 i_L=i_{\mathrm{s}}\omega_0^2
\end{equation}
$$

A equação \ref{EDO1} é uma EDO de segunda ordem, linear e de coeficientes constantes.

Equação característica:

$$\begin{equation} s^2+2\alpha s + \omega_0^2=0 \end{equation} $$

Raízes da equação característica:

$$
\begin{align}
s_1 &=-\alpha +\sqrt{\alpha^2-\omega_0^2}\\
s_2 &=-\alpha -\sqrt{\alpha^2-\omega_0^2}
\end{align}
$$

### Resposta total

A resposta transiente de circuitos RLC em paralelo, seja ela natural ($i_L(\infty)=0$) ou ao degrau ($i_L(\infty)=i_s$), pode ser classificada nas três casos seguintes. Em todos os casos, assume-se que o transiente teve início no instante $t_0$ (instante do chaveamento).

#### Caso superamortecido ($\alpha>\omega_0\Rightarrow \text{raízes reais e distintas}$):

Solução geral da EDO:

$$ \begin{equation} i_L(t) =A_{1} e^{s_{1} (t-t_0)}+A_{2} e^{s_{2} (t-t_0)}+i_L(\infty) \end{equation}$$

Condições iniciais para $i_L(t)$:

$$
\begin{align}
i_L(t_0^+) &=A_1 + A_2 + i_L(\infty)\\
\frac{di_L(t_0^+)}{dt} &=s_1A_1 + s_2A_2 = \frac{1}{L} v_C(t_0^+)
\end{align}
$$

Solução do sistema em $A_1$ e $A_2$:

$$
\begin{align}
A_{1} &=\frac{\frac{1}{L} v_C(t_0^+)-s_{2}\left[i_L(t_0^+)-i_L(\infty)\right]}{s_{1}-s_{2}} \\
A_{2} &=\frac{-\frac{1}{L} v_C(t_0^+)+s_{1}\left[i_L(t_0^+)-i_L(\infty)\right]}{s_{1}-s_{2}}
\end{align}
$$

#### Caso subamortecido ($ \alpha<\omega_0\Rightarrow\text{raízes complexas e conjugadas} $):

Fazendo $\omega_d = \sqrt{\omega_0^2-\alpha^2}$ temos $s_1 = -\alpha +j\omega_d$ e $s_2 = -\alpha -j\omega_d$.

Solução geral da EDO:

$$\begin{equation} i_L(t) = e^{-\alpha(t-t_0)}\left\lbrace B_{1}\cos[\omega_d (t-t_0)] + B_{2}\sin[\omega_d (t-t_0)]  \right\rbrace + i_L(\infty) \end{equation}$$

Condições iniciais para $i_L(t)$:

$$
\begin{align}
i_L(t_0^+) &=B_1 + i_L(\infty)\\
\frac{di_L(t_0^+)}{dt} &= -\alpha B_1 +\omega_d B_2 = \frac{1}{L} v_C(t_0^+)
\end{align}
$$

Solução do sistema em $B_1$ e $B_2$:

$$
\begin{align}
B_{1} &=i_L(t_0^+)-i_L(\infty) \\
B_{2} &=\frac{\frac{1}{L} v_C(t_0^+)+\alpha\left[i_L(t_0^+)-i_L(\infty)\right]}{\omega_d}
\end{align}
$$

#### Caso criticamente amortecido ($\alpha=\omega_0\Rightarrow \text{raízes reais e idênticas } s_1=s_2=-\alpha$):

Solução geral da EDO:

$$ \begin{equation} i_L(t) =D_{1} e^{-\alpha(t-t_0)}+D_{2}(t-t_0) e^{-\alpha(t-t_0)}+i_L(\infty) \end{equation}$$

Condições iniciais para $i_L(t)$:

$$
\begin{align}
i_L(t_0^+) &=D_1 + i_L(\infty)\\
\frac{di_L(t_0^+)}{dt} &=-\alpha D_1 + D_2 = \frac{1}{L} v_C(t_0^+)
\end{align}
$$

Solução do sistema em $D_1$ e $D_2$:

$$
\begin{align}
D_{1} &=i_L(t_0^+)-i_L(\infty) \\
D_{2} &= \frac{1}{L} v_C(t_0^+)+\alpha\left[i_L(t_0^+)-i_L(\infty)\right]
\end{align}
$$

### Problema 1

Para o circuito a seguir, as chaves $ch1$ e $ch2$ estiveram abertas por um longo intervalo de tempo.

a. Considerando que apenas $ch1$ fecha em $t_0=0$s, determine a tensão sobre o capacitor $v_C(t)$ para $t\geq 0^+$.\
b. Qual o máximo valor (absoluto) atingido pela tensão aplicada aos terminais do capacitor para $t\geq 0^+$? Em que instante ele ocorre?\
c. Considerando que apenas $ch2$ fecha em $t_0=0$s, determine a tensão sobre o capacitor $v_C(t)$ para $t\geq 0^+$.

Link para a simulação do circuito: https://tinyurl.com/yf8pj57t

```python
import numpy as np
import sympy as sp
from utils import round_expr, symdisp, symplot
```

### Resolvendo o problema com o Python

**Resolução item (a):**

**Calcula $\alpha$ e $\omega_0$**

```python
# Parâmetros do circuito:
R = 1.64
L = 6e-3
C = 0.5e-3

# calculando α e ω0
α  = 1/(2*R*C)       # Frequência de Neper do circuito RLC em paralelo
ω0 = 1/np.sqrt(L*C)  # Frequência de ressonância do circuito RLC

symdisp('α =', round(α, 2), ' rad/s')
symdisp('ω_0 =', round(ω0, 2), ' rad/s')
```

$\displaystyle α =609.76\; rad/s$

$\displaystyle ω_0 =577.35\; rad/s$

**Define os valores iniciais $v_c(t_0^+)$ e $i_L(t_0^+)$ e o valor final $i_L(\infty)$ (obtidos da análise do circuito)**

```python
# informações obtidas pela análise do circuito

t0 = 0 # instante do chaveamento

iL_inf = -4 # valor final da corrente no indutor
iL_t0 = 2   # valor inicial da corrente no indutor
vc_t0 = 0   # valor inicial da tensão no capcitor
```

**Calcula as raízes da equação característica $s_1$ e $s_2$**

```python
# raízes da equação característica
s1 = -α + np.sqrt(α**2-ω0**2)
s2 = -α - np.sqrt(α**2-ω0**2)

print('Raízes da equação característica:')
symdisp('s_1 =', round(s1, 2), ' rad/s')
symdisp('s_2 =', round(s2, 2), ' rad/s')

# define variáveis da solução geral
t, A1, A2 = sp.symbols('t, A1, A2')

# expressão geral da resposta superamortecida
iL = A1*sp.exp(s1*(t-t0)) + A2*sp.exp(s2*(t-t0)) + iL_inf

print('Forma geral da solução:')
symdisp('i_L(t) = ', round_expr(iL,2), 'A')
```

    Raízes da equação característica:

$\displaystyle s_1 =-413.62\; rad/s$

$\displaystyle s_2 =-805.89\; rad/s$

    Forma geral da solução:

$\displaystyle i_L(t) = A_{1} e^{- 413.62 t} + A_{2} e^{- 805.89 t} - 4\;A$

**Resolve o sistema de equações para determinação das constantes $A_1$ e $A_2$**

```python
# define os sistema de equações com as condições iniciais
eq1 = sp.Eq(   A1 +    A2 + iL_inf, iL_t0)
eq2 = sp.Eq(s1*A1 + s2*A2,        vc_t0/L)

print('Sistema de equações:')
symdisp('(I): ', round_expr(eq1, 2),' ')
symdisp('(II): ', round_expr(eq2, 2),' ')
```

    Sistema de equações:

$\displaystyle (I): A_{1} + A_{2} - 4 = 2\; $

$\displaystyle (II): - 413.62 A_{1} - 805.89 A_{2} = 0.0\; $

```python
# resolve o sistema
soluc = sp.solve((eq1, eq2), dict=True)
A1 = np.array([sol[A1] for sol in soluc])
A2 = np.array([sol[A2] for sol in soluc])

A1 = A1[0]
A2 = A2[0]

print('Solução do sistema:')
symdisp('A_1 =', round(A1, 2), ' A')
symdisp('A_2 =', round(A2, 2), ' A')
```

    Solução do sistema:

$\displaystyle A_1 =12.33\; A$

$\displaystyle A_2 =-6.33\; A$

**Determina a expressão final para $i_L(t)$**

```python
# expressão geral da resposta subamortecida
iL = A1*sp.exp(s1*(t-t0)) + A2*sp.exp(s2*(t-t0)) + iL_inf

symdisp('i_L(t) = ', round_expr(iL,2), 'A')
```

$\displaystyle i_L(t) = -4 - 6.33 e^{- 805.89 t} + 12.33 e^{- 413.62 t}\;A$

**Plota gráfico de $i_L(t)$**

```python
# plota gráfico da função
intervalo = np.linspace(t0,t0+0.02,100)
symplot(t,iL, intervalo, 'iL(t)')
```

**Determina $v_C(t)$ a partir de $i_L(t)$**

```python
# tensão aplicada sobre o capacitor (= tensão sobre o indutor)
vC = L*sp.diff(iL, t)

symdisp('v_C(t) = ', round_expr(vC, 2), ' V')
```

$\displaystyle v_C(t) = 30.59 e^{- 805.89 t} - 30.59 e^{- 413.62 t}\; V$

**Plota gráfico de $v_C(t)$**

```python
# plota gráfico da função
intervalo = np.linspace(t0,t0+0.02,100)
symplot(t, vC, intervalo, 'vC(t)')
```

**Resolução item (b):**

$$\frac{dv_C}{dt} = 0$$

```python
# define a equação dvC/dt = 0
eq1 = sp.Eq(sp.diff(vC, t),0)

round_expr(eq1, 2)
```

$\displaystyle - 24653.1 e^{- 805.89 t} + 12653.1 e^{- 413.62 t} = 0$

```python
# resolve a equação em t
ts = sp.solveset(eq1, t).args[0]

# calcula o valor de vC(t) para t = ts
vC_ts = vC.evalf(subs={t:ts})

symdisp('t_s = ', round(ts*1000,2), 'ms')
symdisp('v_C(t_s) = ', round(vC_ts,2), 'V')
```

$\displaystyle t_s = 1.7\;ms$

$\displaystyle v_C(t_s) = -7.37\;V$

**Resolução item (c):**

```python
# Parâmetros do circuito:
R = 6
L = 6e-3
C = 0.5e-3

# calculando α e ω0
α  = 1/(2*R*C)       # Frequência de Neper do circuito RLC em paralelo
ω0 = 1/np.sqrt(L*C)  # Frequência de ressonância do circuito RLC

symdisp('α =', round(α, 2), ' rad/s')
symdisp('ω_0 =', round(ω0, 2), ' rad/s')
```

$\displaystyle α =166.67\; rad/s$

$\displaystyle ω_0 =577.35\; rad/s$

```python
# informações obtidas pela análise do circuito
iL_inf = 0  # valor final da corrente no indutor
iL_t0 = 2   # valor inicial da corrente no indutor
vc_t0 = 0   # valor inicial da tensão no capcitor

t0 = 0 # instante do chaveamento
```

```python
# define a frequência de oscilação amortecida
ωd = np.sqrt(ω0**2 - α**2)

print('Frequência de oscilação amortercida:')
symdisp('ω_d =', round(ωd, 2), ' rad/s')

# define variáveis da solução geral
t, B1, B2 = sp.symbols('t, B1, B2')

# expressão geral da resposta subamortecida
iL = sp.exp(-α*(t-t0))*(B1*sp.cos(ωd*(t-t0)) + B2*sp.sin(ωd*(t-t0))) + iL_inf

print('Forma geral da solução:')
symdisp('i_L(t) = ', round_expr(iL,2), ' A')
```

    Frequência de oscilação amortercida:

$\displaystyle ω_d =552.77\; rad/s$

    Forma geral da solução:

$\displaystyle i_L(t) = \left(B_{1} \cos{\left(552.77 t \right)} + B_{2} \sin{\left(552.77 t \right)}\right) e^{- 166.67 t}\; A$

```python
# define os sistema de equações com as condições iniciais
eq1 = sp.Eq(   B1 + iL_inf, iL_t0)
eq2 = sp.Eq(-α*B1 + ωd*B2,  vc_t0/L)

print('Sistema de equações:')
symdisp('(I): ', round_expr(eq1, 2),' ')
symdisp('(II): ', round_expr(eq2, 2),' ')
```

    Sistema de equações:

$\displaystyle (I): B_{1} = 2\; $

$\displaystyle (II): - 166.67 B_{1} + 552.77 B_{2} = 0.0\; $

```python
# resolve o sistema
soluc = sp.solve((eq1, eq2), dict=True)
B1 = np.array([sol[B1] for sol in soluc])
B2 = np.array([sol[B2] for sol in soluc])

B1 = B1[0]
B2 = B2[0]

print('Solução do sistema:')
symdisp('B_1 =', round(B1, 2), ' A')
symdisp('B_2 =', round(B2, 2), ' A')
```

    Solução do sistema:

$\displaystyle B_1 =2.0\; A$

$\displaystyle B_2 =0.6\; A$

```python
# expressão geral da resposta subamortecida
iL = sp.exp(-α*(t-t0))*(B1*sp.cos(ωd*(t-t0))+B2*sp.sin(ωd*(t-t0))) + iL_inf

symdisp('i_L(t) = ', round_expr(iL,2), 'A')
```

$\displaystyle i_L(t) = \left(0.6 \sin{\left(552.77 t \right)} + 2.0 \cos{\left(552.77 t \right)}\right) e^{- 166.67 t}\;A$

```python
# plota gráfico da função
intervalo = np.linspace(t0,t0+0.05,100)
symplot(t, iL, intervalo, 'iL(t)')
```

```python
# tensão aplicada sobre o capacitor (= tensão sobre o indutor)
vC = L*sp.diff(iL, t)
vC = sp.simplify(vC)

symdisp('v_C(t) = ', round_expr(vC, 2), ' V')
```

$\displaystyle v_C(t) = - 7.24 e^{- 166.67 t} \sin{\left(552.77 t \right)}\; V$

```python
# plota gráfico da função
intervalo = np.linspace(t0,t0+0.05,100)
symplot(t, vC, intervalo, 'vC(t)')
symplot(t, [iL, vC], intervalo, ['iL(t)', 'vC(t)'])
```

## Circuito RLC em série

### Aplicação das Leis de Kirchhoff

$$\begin{align}
   v_R + v_L + v_C &= v_s\\
   R i + L\frac{d i}{dt}+v_C &= v_s\nonumber\\
   R\left[C\frac{dv_C}{dt}\right] + L\frac{d}{dt}\left[C\frac{dv_C}{dt} \right] + v_C &= v_s\nonumber\\
    \frac{d^{2} v_{\mathrm{C}}}{d t^{2}}+\frac{R}{L} \frac{d v_{\mathrm{C}}}{d t}+\frac{1}{L C}   v_{\mathrm{C}} &=\frac{v_{\mathrm{s}}}{LC}
   \end{align}
$$

com $ \alpha = \frac{R}{2L}$ e $\omega_0 = \frac{1}{\sqrt{LC}}$, temos

$$ \begin{equation}\label{EDO2} \frac{d^{2} v_{\mathrm{C}}}{d t^{2}}+2\alpha \frac{d v_{\mathrm{C}}}{d t}+\omega_0^2 v_{\mathrm{C}}=v_{\mathrm{s}}\omega_0^2 \end{equation} $$

A equação \ref{EDO2} é uma EDO de segunda ordem, linear e de coeficientes constantes.

Equação característica:

$$\begin{equation} s^2+2\alpha s + \omega_0^2=0 \end{equation}$$

Raízes da equação característica:

$$
\begin{align}
s_1 &=-\alpha +\sqrt{\alpha^2-\omega_0^2}\\
s_2 &=-\alpha -\sqrt{\alpha^2-\omega_0^2}
\end{align}
$$

### Resposta total

A resposta transiente de circuitos RLC em série, seja ela natural ($v_C(\infty)=0$) ou ao degrau ($v_C(\infty)=v_s$), pode ser classificada nas três casos seguintes. Em todos os casos, assume-se que o transiente teve início no instante $t_0$ (instante do chaveamento).

#### Caso superamortecido ($\alpha>\omega_0\Rightarrow \text{raízes reais e distintas}$):

Solução geral da EDO:

$$\begin{equation} v_{\mathrm{C}}(t) =A_{1} e^{s_{1} (t-t_0)}+A_{2} e^{s_{2} (t-t_0)}+v_{\mathrm{C}}(\infty) \end{equation}$$

Condições iniciais para $v_{\mathrm{C}}(t)$:

$$
\begin{align}
v_{\mathrm{C}}(t_0^+) &=A_1 + A_2 + v_{\mathrm{C}}(\infty)\\
\frac{dv_{\mathrm{C}}(t_0^+)}{dt} &=s_1A_1 + s_2A_2 = \frac{1}{C} i_{\mathrm{L}}(t_0^+)
\end{align}
$$

Solução do sistema em $A_1$ e $A_2$:

$$
\begin{align}
A_{1} &=\frac{\frac{1}{C} i_{\mathrm{L}}(t_0^+)-s_{2}\left[v_{\mathrm{C}}(t_0^+)-v_{\mathrm{C}}(\infty)\right]}{s_{1}-s_{2}} \\
A_{2} &=\frac{-\frac{1}{C} i_{\mathrm{L}}(t_0^+)+s_{1}\left[v_{\mathrm{C}}(t_0^+)-v_{\mathrm{C}}(\infty)\right]}{s_{1}-s_{2}}
\end{align}
$$

#### Caso subamortecido ($\alpha<\omega_0\Rightarrow \text{raízes complexas e conjugadas}$):

Fazendo $\omega_d = \sqrt{\omega_0^2-\alpha^2}$ temos $s_1 = -\alpha +j\omega_d$ e $s_2 = -\alpha -j\omega_d$.

Solução geral da EDO:

$$\begin{equation} v_{\mathrm{C}}(t) = e^{-\alpha(t-t_0)}\left\lbrace B_{1}\cos[\omega_d (t-t_0)] + B_{2}\sin[\omega_d (t-t_0)]  \right\rbrace + v_{\mathrm{C}}(\infty) \end{equation}$$

Condições iniciais para $v_{\mathrm{C}}(t)$:

$$
\begin{align}
v_{\mathrm{C}}(t_0^+) &=B_1 + v_{\mathrm{C}}(\infty)\\
\frac{dv_{\mathrm{C}}(t_0^+)}{dt} &= -\alpha B_1 +\omega_d B_2 = \frac{1}{C} i_{\mathrm{L}}(t_0^+)
\end{align}
$$

Solução do sistema em $B_1$ e $B_2$:

$$
\begin{align}
B_{1} &=v_{\mathrm{C}}(t_0^+)-v_{\mathrm{C}}(\infty) \\
B_{2} &=\frac{\frac{1}{C} i_{\mathrm{L}}(t_0^+)+\alpha\left[v_{\mathrm{C}}(t_0^+)-v_{\mathrm{C}}(\infty)\right]}{\omega_d}
\end{align}
$$

#### Caso criticamente amortecido ($\alpha=\omega_0\Rightarrow \text{raízes reais e idênticas } s_1=s_2=-\alpha$):

Solução geral da EDO:

$$ \begin{equation} v_{\mathrm{C}}(t) =D_{1} e^{-\alpha(t-t_0)}+D_{2}(t-t_0) e^{-\alpha(t-t_0)}+v_{\mathrm{C}}(\infty) \end{equation}$$

Condições iniciais para $v_{\mathrm{C}}(t)$:

$$
\begin{align}
v_{\mathrm{C}}(t_0^+) &=D_1 + v_{\mathrm{C}}(\infty)\\
\frac{dv_{\mathrm{C}}(t_0^+)}{dt} &=-\alpha D_1 + D_2 = \frac{1}{C} i_{\mathrm{L}}(t_0^+)
\end{align}
$$

Solução do sistema em $D_1$ e $D_2$:

$$
\begin{align}
D_{1} &=v_{\mathrm{C}}(t_0^+)-v_{\mathrm{C}}(\infty) \\
D_{2} &= \frac{1}{C} i_{\mathrm{L}}(t_0^+)+\alpha\left[v_{\mathrm{C}}(t_0^+)-v_{\mathrm{C}}(\infty)\right]
\end{align}
$$

### Problema 2

No circuito da figura a seguir, a chave $ch1$ estive aberta por um longo intervalo de tempo antes de fechar, no instante $t_0=0$s.

a. Para qual valor de $R_x$ o circuito exibe uma resposta criticamente amortecida para $t\geq 0^+$?\
b. Considerando que $R_x$ vale exatamente o valor calculado em (a), determine a tensão sobre o capacitor $v_C(t)$ para $t\geq 0^+$.\
c. Considerando que $R_x$ vale um décimo do valor calculado em (a), determine a tensão sobre o capacitor $v_C(t)$ para $t\geq 0^+$.

Link para a simulação deste circuito: https://tinyurl.com/yjgkkzhu

Resolução item (b):

```python
# Parâmetros do circuito:
R =
L =
C =

# calculando α e ω0
α  = R/(2*L)
ω0 = 1/np.sqrt(L*C)

symdisp('α =', round(α, 2), ' rad/s')
symdisp('ω_0 =', round(ω0, 2), ' rad/s')
```

```python
# informações obtidas pela análise do circuito
vC_inf =    # valor final da tensão sobre o capacitor
iL_t0 =   # valor inicial da corrente no indutor
vC_t0 =     # valor inicial da tensão sobre o capacitor

t0 = 0 # instante do chaveamento
```

```python
# raízes da equação característica
s1 = -α
s2 = -α

# define variáveis da solução geral
t, D1, D2 = sp.symbols('t, D1, D2')

# expressão geral da resposta
vC = D1*sp.exp(s1*(t-t0)) + D2*(t-t0)*sp.exp(s2*(t-t0)) + vC_inf

symdisp('v_C(t) = ', round_expr(vC, 2), ' V')
```

```python
# define os sistema de equações com as condições iniciais
eq1 = sp.Eq()
eq2 = sp.Eq()

print('Sistema de equações:')
symdisp('(I): ', round_expr(eq1, 2),' ')
symdisp('(II): ', round_expr(eq2, 2),' ')
```

```python
# resolve o sistema
soluc = sp.solve((eq1, eq2), dict=True)
D1 = np.array([sol[D1] for sol in soluc])
D2 = np.array([sol[D2] for sol in soluc])

D1 = D1[0]
D2 = D2[0]

print('Solução do sistema:')
symdisp('D_1 =', round(D1, 2), ' V')
symdisp('D_2 =', round(D2, 2), ' V')
```

```python
# expressão geral da resposta
vC = D1*sp.exp(s1*(t-t0)) + D2*(t-t0)*sp.exp(s2*(t-t0)) + vC_inf

symdisp('v_C(t) = ', round_expr(vC, 2), ' V')
```

```python
# corrente passando pelo indutor
iL = C*sp.diff(vC, t)

symdisp('i_L(t) = ', round_expr(iL,2), 'A')
```

```python
# plota gráfico da função
intervalo = np.linspace(t0,t0+0.05,100)
symplot(t, vC, intervalo, 'vC(t)')
```

```python
# plota gráfico da função
intervalo = np.linspace(t0,t0+0.05,100)
symplot(t, iL, intervalo, 'iL(t)')
```

Resolução item (c):

```python
# Parâmetros do circuito:
R = 65.02
L = 1
C = 100e-6

# calculando α e ω0
α  = R/(2*L)
ω0 = 1/np.sqrt(L*C)

symdisp('α =', round(α, 2), ' rad/s')
symdisp('ω_0 =', round(ω0, 2), ' rad/s')
```

```python
# informações obtidas pela análise do circuito
vC_inf = 75.84   # valor final da tensão sobre o capacitor
iL_t0 = 0        #  valor inicial da corrente no indutor
vC_t0 = 21.3     # valor inicial da tensão sobre o capacitor

t0 = 0 # instante do chaveamento
```

```python
# define a frequência de oscilação amortecida
ωd =np.sqrt(ω0**2-α**2)

symdisp('ω_d =', round(ωd, 2), ' rad/s')

# define variáveis da solução geral
t, B1, B2 = sp.symbols('t, B1, B2')

# expressão geral da resposta subamortecida
vC = sp.exp(-α*(t-t0))*(B1*sp.cos(ωd*(t-t0)) + B2*sp.sin(ωd*(t-t0))) + vC_inf

symdisp('v_C(t) = ', round_expr(vC, 2), ' V')
```

```python
# define os sistema de equações com as condições iniciais
eq1 = sp.Eq(   B1 + vC_inf, vC_t0)
eq2 = sp.Eq(-α*B1 + ωd*B2,  iL_t0/C)

print('Sistema de equações:')
symdisp('(I): ', round_expr(eq1, 2),' ')
symdisp('(II): ', round_expr(eq2, 2),' ')
```

```python
# resolve o sistema
soluc = sp.solve((eq1, eq2), dict=True)
B1 = np.array([sol[B1] for sol in soluc])
B2 = np.array([sol[B2] for sol in soluc])

B1 = B1[0]
B2 = B2[0]

print('Solução do sistema:')
symdisp('B_1 =', round(B1, 2), ' V')
symdisp('B_2 =', round(B2, 2), ' V')
```

```python
# expressão geral da resposta subamortecida
vC = sp.exp(-α*(t-t0))*(B1*sp.cos(ωd*(t-t0)) + B2*sp.sin(ωd*(t-t0))) + vC_inf

symdisp('v_C(t) = ', round_expr(vC, 2), ' V')
```

```python
# corrente passando pelo indutor
iL = C*sp.diff(vC, t)

symdisp('i_L(t) = ', round_expr(iL.simplify(),5), 'A')
```

```python
# plota gráfico da função
intervalo = np.linspace(t0,t0+0.25,100)
symplot(t, vC, intervalo, 'vC(t)')
```

```python
# plota gráfico da função
intervalo = np.linspace(t0,t0+0.25,100)
symplot(t, iL, intervalo, 'iL(t)')
```

Jupyter notebooks/Circuitos Eletricos I - Semana 8.2.ipynb


# COVID-19 - Análise dos dados
##  Para esta análise estão sendo usadas as bases de dados do COVID-19 fornecidas pela universidade [Johns Hopkins], serão usadas as bases de dados por categoria ( [Confirmed], [Recovered], [Deaths]) assim como a base de [reporte diario].

## As bases de dados  por categoria são composta por:
- Province/State - Província/estado que foi registrado a ocorrência
- Country/Region - Continente/região que foi registrado a ocorrência
- Lat - latitude da região
- Long - longitude da região
- data - colunas que representam a data diário das ocorrências

## A base de dados  de reporte diário é composta por:
- Province/State - Provincia/estado que foi registrado a ocorrência
- Country/Region - Continente/região que foi registrado a ocorrência
- Last Update - Data da última atualização
- Confirmed - Quantidade de casos confirmados
- Deaths - Quantidade de mortes
- Recovered - quantidade de recuperações
- Lat - latitude da região
- Long - longitude da região
- data - colunas que representam a data diário das ocorrências

### obs: todas as bases de dados usadas são atualizadas diariamente.

[Johns Hopkins]: <https://github.com/CSSEGISandData>
[Confirmed]: <https://github.com/CSSEGISandData/COVID-19/blob/master/csse_covid_19_data/csse_covid_19_time_series/time_series_19-covid-Confirmed.csv>
[Recovered]: <https://github.com/CSSEGISandData/COVID-19/blob/master/csse_covid_19_data/csse_covid_19_time_series/time_series_19-covid-Recovered.csv>
[Deaths]: <https://github.com/CSSEGISandData/COVID-19/blob/master/csse_covid_19_data/csse_covid_19_time_series/time_series_19-covid-Deaths.csv>
[reporte diario]: <https://github.com/CSSEGISandData/COVID-19/tree/master/csse_covid_19_data/csse_covid_19_daily_reports>

```python
# importes necessários
import pandas as pd
import altair as alt
import numpy as np
from datetime import date
from vega_datasets import data
```

```python
# Constantes
prefixo_taxa = 'taxa_aumento_'
data_formato = '%m-%d-%Y'
data_atual = date.today()
data_atual_formatada = data_atual.strftime(data_formato)
data_ultima_checagem_confirmada = '03-21-2020'
URL_covid_confirmed_for_day = 'https://raw.githubusercontent.com/CSSEGISandData/COVID-19/master/csse_covid_19_data/csse_covid_19_daily_reports/{0}.csv'
```

### Importe das báses de dados por categoria

```python
covid_confirmed = pd.read_csv('https://raw.githubusercontent.com/CSSEGISandData/COVID-19/master/csse_covid_19_data/csse_covid_19_time_series/time_series_covid19_confirmed_global.csv')
covid_recovered = pd.read_csv('https://raw.githubusercontent.com/CSSEGISandData/COVID-19/master/csse_covid_19_data/csse_covid_19_time_series/time_series_covid19_recovered_global.csv')
covid_deaths = pd.read_csv('https://raw.githubusercontent.com/CSSEGISandData/COVID-19/master/csse_covid_19_data/csse_covid_19_time_series/time_series_covid19_deaths_global.csv')
covid_deaths.head()
```

      Province/State
      Country/Region
      Lat
      Long
      1/22/20
      1/23/20
      1/24/20
      1/25/20
      1/26/20
      1/27/20
      ...
      3/19/20
      3/20/20
      3/21/20
      3/22/20
      3/23/20
      3/24/20
      3/25/20
      3/26/20
      3/27/20
      3/28/20

      0
      NaN
      Afghanistan
      33.0000
      65.0000
      0
      0
      0
      0
      0
      0
      ...
      0
      0
      0
      1
      1
      1
      2
      4
      4
      4

      1
      NaN
      Albania
      41.1533
      20.1683
      0
      0
      0
      0
      0
      0
      ...
      2
      2
      2
      2
      4
      5
      5
      6
      8
      10

      2
      NaN
      Algeria
      28.0339
      1.6596
      0
      0
      0
      0
      0
      0
      ...
      9
      11
      15
      17
      17
      19
      21
      25
      26
      29

      3
      NaN
      Andorra
      42.5063
      1.5218
      0
      0
      0
      0
      0
      0
      ...
      0
      0
      0
      1
      1
      1
      1
      3
      3
      3

      4
      NaN
      Angola
      -11.2027
      17.8739
      0
      0
      0
      0
      0
      0
      ...
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0

5 rows × 71 columns

## Importe da báse de dados de reporte diário, caso não seja encontrado a base relacionada ao dia atual será carregado uma base de data pré definida

```python
covid_reports_for_day = []

try:
    covid_reports_for_day = pd.read_csv(URL_covid_confirmed_for_day.format(data_atual_formatada))
except:
    covid_reports_for_day = pd.read_csv(URL_covid_confirmed_for_day.format(data_ultima_checagem_confirmada))

covid_reports_for_day.tail()
```

      Province/State
      Country/Region
      Last Update
      Confirmed
      Deaths
      Recovered
      Latitude
      Longitude

      299
      NaN
      Jersey
      2020-03-17T18:33:03
      0
      0
      0
      49.1900
      -2.110

      300
      NaN
      Puerto Rico
      2020-03-17T16:13:14
      0
      0
      0
      18.2000
      -66.500

      301
      NaN
      Republic of the Congo
      2020-03-17T21:33:03
      0
      0
      0
      -1.4400
      15.556

      302
      NaN
      The Bahamas
      2020-03-19T12:13:38
      0
      0
      0
      24.2500
      -76.000

      303
      NaN
      The Gambia
      2020-03-18T14:13:56
      0
      0
      0
      13.4667
      -16.600

### É criado uma base de dados para análise da série temporal a partir da união das bases de dados por categoria.

```python
todal_casos_morte_por_dia = np.sum(covid_deaths.iloc[ : , 4 : covid_deaths.shape[1]])
todal_casos_confirmacao_por_dia = np.sum(covid_confirmed.iloc[ : , 4 : covid_confirmed.shape[1]])
todal_casos_recuperacao_por_dia = np.sum(covid_recovered.iloc[ : , 4 : covid_recovered.shape[1]])

covid_serie_temporal = pd.DataFrame([todal_casos_confirmacao_por_dia, todal_casos_recuperacao_por_dia, todal_casos_morte_por_dia]).T
covid_serie_temporal.reset_index(inplace = True)
covid_serie_temporal.columns = ['data','confirmacao', 'recuperacao', 'mortes']
covid_serie_temporal['data'] = pd.to_datetime(covid_serie_temporal['data'])

covid_serie_temporal.tail()
```

      data
      confirmacao
      recuperacao
      mortes

      62
      2020-03-24
      418045
      108000
      18625

      63
      2020-03-25
      467653
      113787
      21181

      64
      2020-03-26
      529591
      122150
      23970

      65
      2020-03-27
      593291
      130915
      27198

      66
      2020-03-28
      660706
      139415
      30652

### Método ira criar uma nova coluna do dataset a partir de um prefixo e o nome da coluna, essa nova coluna representa a taxa de créscimento diário da coluna. A taxa de crescimento é cálculada pela seguinte fórmula:

\begin{align}
\text{taxa_crescimento} ={\frac  {({\text{valor_presente}})-({\text{valor_passado}})}{({\text{valor_passado}})}}
\end{align}

```python
def taxa_de_crescimento(data, colum):
    dimensao = data[colum].shape[0]
    resultados =[]
    for i in range (dimensao - 1):
        resultados.append(data[colum][i+1] - data[colum][i])
        if(i == dimensao - 2):
            resultados.append(np.nan)

    data[prefixo_taxa + colum] = resultados
```

### aplica o método `taxa_de_crescimento` aos campos da análise

```python
taxa_de_crescimento(covid_serie_temporal, 'confirmacao')
taxa_de_crescimento(covid_serie_temporal, 'recuperacao')
taxa_de_crescimento(covid_serie_temporal, 'mortes')
covid_serie_temporal.tail()
```

      data
      confirmacao
      recuperacao
      mortes
      taxa_aumento_confirmacao
      taxa_aumento_recuperacao
      taxa_aumento_mortes

      62
      2020-03-24
      418045
      108000
      18625
      49608.0
      5787.0
      2556.0

      63
      2020-03-25
      467653
      113787
      21181
      61938.0
      8363.0
      2789.0

      64
      2020-03-26
      529591
      122150
      23970
      63700.0
      8765.0
      3228.0

      65
      2020-03-27
      593291
      130915
      27198
      67415.0
      8500.0
      3454.0

      66
      2020-03-28
      660706
      139415
      30652
      NaN
      NaN
      NaN

### O gráfico demonstra a evolução dos casos de infecção pelo COVID-19, o gráfico inferior demonstra o comparativo de crescimento no decorrer do tempo para os casos de confirmação, recuperação e morte. Já os gráficos superiores demonstram a evolução de acordo com o respectivos status: confirmação, recuperação e morte. Sendo possível selecionar um período de tempo ao clicar e arrastar o mouse.

```python
brush = alt.selection_interval(encodings=['x'])

card_principal = alt.Chart(covid_serie_temporal).mark_area(
    color="lightyellow",
    line=True
).encode(
    alt.X("data:T",),
    alt.Y('confirmacao:Q'),
    tooltip = ["data:T", 'confirmacao:Q']
).properties(width=800,height=200, title='Ocorrências do COVID-19 ao decorrer do tempo.').add_selection(brush)

confirmacao = alt.Chart(covid_serie_temporal).mark_area(
    color="lightyellow",
    line=True
).encode(
    alt.X("data:T", scale=alt.Scale(domain=brush)),
    alt.Y('confirmacao:Q', title= 'Quantidade de pessoas')
).properties(width=200,height=200, title=['Casos confirmados de infecção', 'pelo COVID-19']).transform_filter(brush)

recuperacao = alt.Chart(covid_serie_temporal).mark_area(
    color="lightgreen",
    line=True
).encode(
    alt.X("data:T"),
    alt.Y('recuperacao:Q', title= 'Quantidade de pessoas')
).properties(width=200,height=200, title=['Casos confirmados de recuperação', 'do COVID-19']).transform_filter(brush)

morte = alt.Chart(covid_serie_temporal).mark_area(
    color="red",
    line=True
).encode(
    alt.X("data:T"),
    alt.Y('mortes:Q', title= 'Quantidade de pessoas')
).properties(width=200,height=200, title=['Casos confirmados de morte' ,'pelo COVID-19']).transform_filter(brush)

evolucao_COVID = (confirmacao | recuperacao | morte) & (card_principal.encode(alt.Y('confirmacao', title= 'Quantidade de pessoas')) + recuperacao + morte)

evolucao_COVID.save('plots/evolucao_COVID_19.html')

evolucao_COVID
```

### O gráfico demonstra a taxa de crescimento dos casos de infecção pelo COVID-19, o gráfico inferior demonstra o comparativo de crescimento no decorrer do tempo para os casos de confirmação, recuperação e morte. Já os gráficos superiores demonstram a taxa de evolução de acordo com o respectivos status: confirmação, recuperação e morte. Sendo possível selecionar um período de tempo ao clicar e arrastar o mouse.

```python
brush = alt.selection_interval(encodings=['x'])

card_principal = alt.Chart(covid_serie_temporal.dropna()).mark_area(
    color="lightyellow",
    line=True
).encode(
    alt.X("data:T",),
    alt.Y('taxa_aumento_confirmacao:Q'),
    tooltip = ["data:T", 'taxa_aumento_confirmacao:Q']
).properties(width=800,height=300, title='Taxa de crescimento do contágio pelo COVID-19 ao decorrer do tempo.').add_selection(brush)

confirmacao = alt.Chart(covid_serie_temporal.dropna()).mark_area(
    color="lightyellow",
    line=True
).encode(
    alt.X("data:T", scale=alt.Scale(domain=brush)),
    alt.Y('taxa_aumento_confirmacao:Q', title= 'Quantidade de pessoas')
).properties(width=200,height=200, title=['Taxa de crescimento de casos', 'confirmados de infecção pelo COVID-19']).transform_filter(brush)

recuperacao = alt.Chart(covid_serie_temporal.dropna()).mark_area(
    color="lightgreen",
    line=True
).encode(
    alt.X("data:T"),
    alt.Y('taxa_aumento_recuperacao:Q', title= 'Quantidade de pessoas')
).properties(width=200,height=200, title=['Taxa de crescimento de casos','confirmados de recuperação do COVID-19']).transform_filter(brush)

morte = alt.Chart(covid_serie_temporal.dropna()).mark_area(
    color="red",
    line=True
).encode(
    alt.X("data:T"),
    alt.Y('taxa_aumento_mortes:Q', title= 'Quantidade de pessoas')
).properties(width=200,height=200, title=['Taxa de crescimento de casos','confirmados de morte pelo COVID-19']).transform_filter(brush)

taxa_aumento_contagio = (confirmacao | recuperacao | morte) & (card_principal.encode(alt.Y('taxa_aumento_confirmacao', title= 'Quantidade de pessoas')) + recuperacao + morte)

taxa_aumento_contagio.save('plots/taxa_de_aumento_no_contagio_COVID_19.html')

taxa_aumento_contagio
```

```python
covid_reports_for_day.head()
```

      Province/State
      Country/Region
      Last Update
      Confirmed
      Deaths
      Recovered
      Latitude
      Longitude

      0
      Hubei
      China
      2020-03-21T10:13:08
      67800
      3139
      58946
      30.9756
      112.2707

      1
      NaN
      Italy
      2020-03-21T17:43:03
      53578
      4825
      6072
      41.8719
      12.5674

      2
      NaN
      Spain
      2020-03-21T13:13:30
      25374
      1375
      2125
      40.4637
      -3.7492

      3
      NaN
      Germany
      2020-03-21T20:43:02
      22213
      84
      233
      51.1657
      10.4515

      4
      NaN
      Iran
      2020-03-21T11:13:12
      20610
      1556
      7635
      32.4279
      53.6880

### O mapa demonstra a situação de contágio pelo COVID-19 dos Países/Estados, podendo-se interagir com o mapa ao passar o mouse nas regiões em vermelho, onde irá ser demonstrado respectivamente:
- Quantidade de confirmações;
- Quantidade de mortes;
- Quantidade de recuperações;
- Província/Estado selecionado;
- País selecionado;
- Data da última atualização.

```python
source = alt.topo_feature(data.world_110m.url, 'countries')
sphere = alt.sphere()
graticule = alt.graticule()

projections = 'equirectangular'

size = alt.Size('Confirmed:N', legend=None)

circles = alt.Chart(covid_reports_for_day).mark_circle(color='#FE2E2E', opacity = 0.4).encode(
    longitude="Longitude:Q",
    latitude="Latitude:Q",
    size = size,
    tooltip = ['Confirmed:Q', 'Deaths:Q', 'Recovered:Q', 'Province/State:N', 'Country/Region:N', 'Last Update:T']
)

geo_shape = alt.layer(
    alt.Chart(sphere).mark_geoshape(fill='lightblue', stroke = "invert ? '#777' : '#bbb'"),
    alt.Chart(graticule).mark_geoshape(stroke='white', strokeWidth=0),
    alt.Chart(source).mark_geoshape(fill='#F5F3A6', stroke='black')
).properties(
    width=800,
    height=400
).project(projections).properties(title='Mapa mundi')

fig = alt.layer(
    geo_shape,
    circles,
)

fig.save('plots/situação_COVID_19.html')

fig
```


### Generating & training hybrid models

I hand you a quantum circuit with some free parameters and then I hand you some samples from that quantum circuit at specific parameter values, but I don't tell you what the parameter values are and then your goal is to try and determine what the parameter values are via some optimization or ML type algorithm that takes the samples I gave you as input.

```python
# must have tensorflow, tensorflow-gpu, tensorflow_quantum in the list
# tensorflow==2.4.1
# tensorflow-gpu==2.4.1
!pip list
```

    Package                       Version
    ----------------------------- --------------
    absl-py                       0.12.0
    alabaster                     0.7.12
    albumentations                0.1.12
    altair                        4.1.0
    appdirs                       1.4.4
    argcomplete                   1.12.3
    argon2-cffi                   21.1.0
    arviz                         0.11.4
    astor                         0.8.1
    astropy                       4.3.1
    astunparse                    1.6.3
    atari-py                      0.2.9
    atomicwrites                  1.4.0
    attrs                         20.3.0
    audioread                     2.1.9
    autograd                      1.3
    Babel                         2.9.1
    backcall                      0.2.0
    beautifulsoup4                4.6.3
    bleach                        4.1.0
    blis                          0.4.1
    bokeh                         2.3.3
    Bottleneck                    1.3.2
    branca                        0.4.2
    bs4                           0.0.1
    CacheControl                  0.12.6
    cached-property               1.5.2
    cachetools                    4.2.4
    catalogue                     1.0.0
    certifi                       2021.5.30
    cffi                          1.14.6
    cftime                        1.5.1
    chardet                       3.0.4
    charset-normalizer            2.0.6
    cirq                          0.11.0
    cirq-aqt                      0.13.0
    cirq-core                     0.11.0
    cirq-google                   0.11.0
    cirq-ionq                     0.13.0
    cirq-pasqal                   0.13.0
    cirq-rigetti                  0.13.0
    cirq-web                      0.13.0
    clang                         5.0
    click                         7.1.2
    cloudpickle                   1.3.0
    cmake                         3.12.0
    cmdstanpy                     0.9.5
    colorcet                      2.0.6
    colorlover                    0.3.0
    community                     1.0.0b1
    contextlib2                   0.5.5
    convertdate                   2.3.2
    coverage                      3.7.1
    coveralls                     0.5
    crcmod                        1.7
    cufflinks                     0.17.3
    cupy-cuda111                  9.4.0
    cvxopt                        1.2.7
    cvxpy                         1.0.31
    cycler                        0.10.0
    cymem                         2.0.5
    Cython                        0.29.24
    daft                          0.0.4
    dask                          2.12.0
    datascience                   0.10.6
    debugpy                       1.0.0
    decorator                     4.4.2
    defusedxml                    0.7.1
    descartes                     1.1.0
    dill                          0.3.4
    distributed                   1.25.3
    dlib                          19.18.0
    dm-tree                       0.1.6
    docopt                        0.6.2
    docutils                      0.17.1
    dopamine-rl                   1.0.5
    duet                          0.2.2
    earthengine-api               0.1.284
    easydict                      1.9
    ecos                          2.0.7.post1
    editdistance                  0.5.3
    en-core-web-sm                2.2.5
    entrypoints                   0.3
    ephem                         4.1
    et-xmlfile                    1.1.0
    fa2                           0.3.5
    fastai                        1.0.61
    fastdtw                       0.3.4
    fastprogress                  1.0.0
    fastrlock                     0.6
    fbprophet                     0.7.1
    feather-format                0.4.1
    filelock                      3.3.0
    firebase-admin                4.4.0
    fix-yahoo-finance             0.0.22
    Flask                         1.1.4
    flatbuffers                   1.12
    folium                        0.8.3
    future                        0.16.0
    gast                          0.3.3
    GDAL                          2.2.2
    gdown                         3.6.4
    gensim                        3.6.0
    geographiclib                 1.52
    geopy                         1.17.0
    gin-config                    0.4.0
    glob2                         0.7
    google                        2.0.3
    google-api-core               1.21.0
    google-api-python-client      1.12.8
    google-auth                   1.18.0
    google-auth-httplib2          0.0.4
    google-auth-oauthlib          0.4.6
    google-cloud-bigquery         1.21.0
    google-cloud-bigquery-storage 1.1.0
    google-cloud-core             1.0.3
    google-cloud-datastore        1.8.0
    google-cloud-firestore        1.7.0
    google-cloud-language         1.2.0
    google-cloud-storage          1.18.1
    google-cloud-translate        1.5.0
    google-colab                  1.0.0
    google-pasta                  0.2.0
    google-resumable-media        0.4.1
    googleapis-common-protos      1.52.0
    googledrivedownloader         0.4
    graphviz                      0.10.1
    greenlet                      1.1.2
    grpcio                        1.32.0
    gspread                       3.0.1
    gspread-dataframe             3.0.8
    gym                           0.17.3
    h11                           0.9.0
    h5py                          2.10.0
    HeapDict                      1.0.1
    hijri-converter               2.2.2
    holidays                      0.10.5.2
    holoviews                     1.14.6
    html5lib                      1.0.1
    httpcore                      0.11.1
    httpimport                    0.5.18
    httplib2                      0.17.4
    httplib2shim                  0.0.3
    httpx                         0.15.5
    humanize                      0.5.1
    hyperopt                      0.1.2
    ideep4py                      2.0.0.post3
    idna                          2.10
    imageio                       2.4.1
    imagesize                     1.2.0
    imbalanced-learn              0.4.3
    imblearn                      0.0
    imgaug                        0.2.9
    importlib-metadata            3.10.1
    importlib-resources           5.2.2
    imutils                       0.5.4
    inflect                       2.1.0
    iniconfig                     1.1.1
    intel-openmp                  2021.4.0
    intervaltree                  2.1.0
    ipykernel                     4.10.1
    ipython                       5.5.0
    ipython-genutils              0.2.0
    ipython-sql                   0.3.9
    ipywidgets                    7.6.5
    iso8601                       0.1.16
    itsdangerous                  1.1.0
    jax                           0.2.21
    jaxlib                        0.1.71+cuda111
    jdcal                         1.4.1
    jedi                          0.18.0
    jieba                         0.42.1
    Jinja2                        2.11.3
    joblib                        1.0.1
    jpeg4py                       0.1.4
    jsonschema                    2.6.0
    jupyter                       1.0.0
    jupyter-client                5.3.5
    jupyter-console               5.2.0
    jupyter-core                  4.8.1
    jupyterlab-pygments           0.1.2
    jupyterlab-widgets            1.0.2
    kaggle                        1.5.12
    kapre                         0.3.5
    keras                         2.6.0
    Keras-Preprocessing           1.1.2
    keras-vis                     0.4.1
    kiwisolver                    1.3.2
    korean-lunar-calendar         0.2.1
    lark                          0.11.3
    librosa                       0.8.1
    lightgbm                      2.2.3
    llvmlite                      0.34.0
    lmdb                          0.99
    LunarCalendar                 0.0.9
    lxml                          4.2.6
    Markdown                      3.3.4
    MarkupSafe                    2.0.1
    matplotlib                    3.2.2
    matplotlib-inline             0.1.3
    matplotlib-venn               0.11.6
    missingno                     0.5.0
    mistune                       0.8.4
    mizani                        0.6.0
    mkl                           2019.0
    mlxtend                       0.14.0
    more-itertools                8.10.0
    moviepy                       0.2.3.5
    mpmath                        1.2.1
    msgpack                       0.6.2
    multiprocess                  0.70.12.2
    multitasking                  0.0.9
    murmurhash                    1.0.5
    music21                       5.5.0
    natsort                       5.5.0
    nbclient                      0.5.4
    nbconvert                     5.6.1
    nbformat                      5.1.3
    nest-asyncio                  1.5.1
    netCDF4                       1.5.7
    networkx                      2.6.3
    nibabel                       3.0.2
    nltk                          3.2.5
    notebook                      5.3.1
    numba                         0.51.2
    numexpr                       2.7.3
    numpy                         1.19.5
    nvidia-ml-py3                 7.352.0
    oauth2client                  4.1.3
    oauthlib                      3.1.1
    okgrade                       0.4.3
    opencv-contrib-python         4.1.2.30
    opencv-python                 4.1.2.30
    openpyxl                      2.5.9
    opt-einsum                    3.3.0
    osqp                          0.6.2.post0
    packaging                     21.0
    palettable                    3.3.0
    pandas                        1.1.5
    pandas-datareader             0.9.0
    pandas-gbq                    0.13.3
    pandas-profiling              1.4.1
    pandocfilters                 1.5.0
    panel                         0.12.1
    param                         1.11.1
    parso                         0.8.2
    pathlib                       1.0.1
    patsy                         0.5.2
    pep517                        0.11.0
    pexpect                       4.8.0
    pickleshare                   0.7.5
    Pillow                        7.1.2
    pip                           21.1.3
    pip-tools                     6.2.0
    plac                          1.1.3
    plotly                        4.4.1
    plotnine                      0.6.0
    pluggy                        0.7.1
    pooch                         1.5.1
    portpicker                    1.3.9
    prefetch-generator            1.0.1
    preshed                       3.0.5
    prettytable                   2.2.1
    progressbar2                  3.38.0
    prometheus-client             0.11.0
    promise                       2.3
    prompt-toolkit                1.0.18
    protobuf                      3.13.0
    psutil                        5.4.8
    psycopg2                      2.7.6.1
    ptyprocess                    0.7.0
    py                            1.10.0
    pyarrow                       3.0.0
    pyasn1                        0.4.8
    pyasn1-modules                0.2.8
    pycocotools                   2.0.2
    pycparser                     2.20
    pyct                          0.4.8
    pydantic                      1.8.2
    pydata-google-auth            1.2.0
    pydot                         1.3.0
    pydot-ng                      2.0.0
    pydotplus                     2.0.2
    PyDrive                       1.3.1
    pyemd                         0.5.1
    pyerfa                        2.0.0
    pyglet                        1.5.0
    Pygments                      2.6.1
    pygobject                     3.26.1
    PyJWT                         1.7.1
    pymc3                         3.11.4
    PyMeeus                       0.5.11
    pymongo                       3.12.0
    pymystem3                     0.2.0
    PyOpenGL                      3.1.5
    pyparsing                     2.4.7
    pyquil                        3.0.1
    pyrsistent                    0.18.0
    pysndfile                     1.3.8
    PySocks                       1.7.1
    pystan                        2.19.1.1
    pytest                        3.6.4
    python-apt                    0.0.0
    python-chess                  0.23.11
    python-dateutil               2.8.2
    python-louvain                0.15
    python-rapidjson              1.5
    python-slugify                5.0.2
    python-utils                  2.5.6
    pytz                          2018.9
    pyviz-comms                   2.1.0
    PyWavelets                    1.1.1
    PyYAML                        3.13
    pyzmq                         22.3.0
    qcs-api-client                0.8.0
    qdldl                         0.1.5.post0
    qtconsole                     5.1.1
    QtPy                          1.11.2
    regex                         2019.12.20
    requests                      2.23.0
    requests-oauthlib             1.3.0
    resampy                       0.2.2
    retry                         0.9.2
    retrying                      1.3.3
    rfc3339                       6.2
    rfc3986                       1.5.0
    rpcq                          3.9.2
    rpy2                          3.4.5
    rsa                           4.7.2
    ruamel.yaml                   0.17.16
    ruamel.yaml.clib              0.2.6
    scikit-image                  0.16.2
    scikit-learn                  0.22.2.post1
    scipy                         1.7.1
    screen-resolution-extra       0.0.0
    scs                           2.1.4
    seaborn                       0.11.2
    semver                        2.13.0
    Send2Trash                    1.8.0
    setuptools                    57.4.0
    setuptools-git                1.2
    Shapely                       1.7.1
    simplegeneric                 0.8.1
    six                           1.15.0
    sklearn                       0.0
    sklearn-pandas                1.8.0
    smart-open                    5.2.1
    sniffio                       1.2.0
    snowballstemmer               2.1.0
    sortedcontainers              2.4.0
    SoundFile                     0.10.3.post1
    spacy                         2.2.4
    Sphinx                        1.8.5
    sphinxcontrib-serializinghtml 1.1.5
    sphinxcontrib-websupport      1.2.4
    SQLAlchemy                    1.4.25
    sqlparse                      0.4.2
    srsly                         1.0.5
    statsmodels                   0.10.2
    sympy                         1.5
    tables                        3.4.4
    tabulate                      0.8.9
    tblib                         1.7.0
    tensorboard                   2.6.0
    tensorboard-data-server       0.6.1
    tensorboard-plugin-wit        1.8.0
    tensorflow                    2.4.1
    tensorflow-datasets           4.0.1
    tensorflow-estimator          2.4.0
    tensorflow-gcs-config         2.6.0
    tensorflow-gpu                2.4.1
    tensorflow-hub                0.12.0
    tensorflow-metadata           1.2.0
    tensorflow-probability        0.14.1
    tensorflow-quantum            0.5.1
    termcolor                     1.1.0
    terminado                     0.12.1
    testpath                      0.5.0
    text-unidecode                1.3
    textblob                      0.15.3
    Theano-PyMC                   1.1.2
    thinc                         7.4.0
    tifffile                      2021.8.30
    toml                          0.10.2
    tomli                         1.2.1
    toolz                         0.11.1
    torch                         1.9.0+cu111
    torchsummary                  1.5.1
    torchtext                     0.10.0
    torchvision                   0.10.0+cu111
    tornado                       5.1.1
    tqdm                          4.62.3
    traitlets                     5.1.0
    tweepy                        3.10.0
    typeguard                     2.7.1
    typing-extensions             3.7.4.3
    tzlocal                       1.5.1
    uritemplate                   3.0.1
    urllib3                       1.24.3
    vega-datasets                 0.9.0
    wasabi                        0.8.2
    wcwidth                       0.2.5
    webencodings                  0.5.1
    Werkzeug                      1.0.1
    wheel                         0.37.0
    widgetsnbextension            3.5.1
    wordcloud                     1.5.0
    wrapt                         1.12.1
    xarray                        0.18.2
    xgboost                       0.90
    xkit                          0.0.0
    xlrd                          1.1.0
    xlwt                          1.3.0
    yellowbrick                   0.9.1
    zict                          2.0.0
    zipp                          3.6.0

```python
try:
    import cirq
except ImportError:
    print("installing cirq...")
    !pip install --quiet cirq
    print("installed cirq.")
```

```python
import cirq

cirq.__version__
```

    '0.11.0'

```python
try:
    import tensorflow as tf
    if tf.__version__ != '2.4.1':
        !pip uninstall --quiet tensorflow
        !pip uninstall --quiet tensorflow-gpu
        !pip install --quiet tensorflow==2.4.1
        !pip install --quiet tensorflow-gpu==2.4.1
except ImportError:
    !pip uninstall --quiet tensorflow-gpu
    !pip uninstall --quiet tensorflow
    !pip install --quiet tensorflow==2.4.1
    !pip install --quiet tensorflow-gpu==2.4.1
```

```python
import tensorflow as tf

tf.__version__
```

    '2.4.1'

```python
# !pip uninstall tensorflow-gpu
# !pip install --quiet tensorflow-gpu==2.4.1
```

```python
# !pip install --quiet tensorflow-quantum=='0.5.1'
```

```python
try:
    import tensorflow_quantum as tfq
    if tfq.__version__ != '0.5.1':
        !pip uninstall --quiet tensorflow-quantum
        !pip install --quiet tensorflow-quantum=='0.5.1'
except ImportError:
    !pip install --quiet tensorflow-quantum=='0.5.1'
```

```python
import tensorflow_quantum as tfq

tfq.__version__
```

    '0.5.1'

```python
# !pip install tensorflow-quantum
```

```python
# hopefully not required
# !pip uninstall tensorflow
# !pip install tensorflow
# !pip install --upgrade pip
# !pip install --upgrade tensorflow
# !pip install tensorflow-gpu
```

```python
# Update package resources to account for version changes.
import importlib, pkg_resources
importlib.reload(pkg_resources)
```

    <module 'pkg_resources' from '/usr/local/lib/python3.7/dist-packages/pkg_resources/__init__.py'>

```python
import cirq
import cirq_google
import numpy as np
import sympy as sp
import tensorflow as tf
import matplotlib.pyplot as plt
import tensorflow_quantum as tfq
from math import sqrt, pi
```

```python
# set random seed
tf.random.set_seed(42)
```

```python
# define two qubits
q0 = cirq.GridQubit(0, 0)
q1 = cirq.GridQubit(0, 1)

# make a Pauli Sum gate that will return [1] if the state is a bell state b00 (|00⟩)
z0 = cirq.Z(q0)
z1 = cirq.Z(q1)
z00 = (((1+z0)/2)*((1+z1)/2))

# define three parameters, phi will be varied, alpha & beta are fixed
phi = sp.Symbol('phi')
alpha = sp.Symbol('alpha')
beta = sp.Symbol('beta')

# create a circuit with the following gates RX, RY, H, CNOT etc.
# the circuit has three parameters: phi, alpha & beta
circuit = cirq.Circuit([
                cirq.rx(phi)(q0),
                cirq.ry(-2*phi)(q1),
                cirq.H(q0),
                cirq.CNOT(q0,q1),
                cirq.rx(alpha)(q0),
                cirq.ry(beta)(q1),
                ])
circuit
```

(0, 0): ───Rx(phi)──────H───@───Rx(alpha)───
                            │
(0, 1): ───Ry(-2*phi)───────X───Ry(beta)────

```python
# get the qasm code
circuit.to_qasm
```

    <bound method AbstractCircuit.to_qasm of cirq.Circuit([
        cirq.Moment(
            cirq.Rx(rads=sympy.Symbol('phi')).on(cirq.GridQubit(0, 0)),
            cirq.Ry(rads=sympy.Mul(sympy.Integer(-1), sympy.Integer(2), sympy.Symbol('phi'))).on(cirq.GridQubit(0, 1)),
        ),
        cirq.Moment(
            cirq.H(cirq.GridQubit(0, 0)),
        ),
        cirq.Moment(
            cirq.CNOT(cirq.GridQubit(0, 0), cirq.GridQubit(0, 1)),
        ),
        cirq.Moment(
            cirq.Rx(rads=sympy.Symbol('alpha')).on(cirq.GridQubit(0, 0)),
            cirq.Ry(rads=sympy.Symbol('beta')).on(cirq.GridQubit(0, 1)),
        ),
    ])>

### Expectation

```python
# If we fix the values of alpha and beta, and perform a scan over phi
# from [0, 2π], we can generate a distribution of the number of times
# the system is measured in state |00⟩

# fix two values of alpha and beta
a = 1/sqrt(2)
b = pi/3

# loop over phi from -5 to 5
x_values = np.arange(-5, 5, 0.05)
y_expectations = []

for j in x_values:
    # resolve the circuit for different values of phi
    resolver = cirq.ParamResolver({'alpha': a, 'beta': b, 'phi': j})

    # calcuclate the expectation value
    result = cirq.Simulator().simulate(cirq.resolve_parameters(circuit, resolver))
    e = z00.expectation_from_state_vector(result.final_state_vector, {q0:0, q1:1})
    y_expectations.append(e)

print(result)
print(e)

plt.scatter(x_values, y_expectations, label=f'Expected, $\\alpha$={round(a,3)}, $\\beta$={round(b,3)}')
plt.xlabel('$\phi$')
plt.ylabel('Expectation')
plt.legend()
plt.show()
```

```python
z00
```

    cirq.PauliSum(cirq.LinearDict({frozenset({(cirq.GridQubit(0, 1), cirq.Z), (cirq.GridQubit(0, 0), cirq.Z)}): (0.25+0j), frozenset({(cirq.GridQubit(0, 0), cirq.Z)}): (0.25+0j), frozenset({(cirq.GridQubit(0, 1), cirq.Z)}): (0.25+0j), frozenset(): (0.25+0j)}))

```python
z00.expectation_from_state_vector(result.final_state_vector, {q0:0, q1:1})
```

    (0.130918700248003+0j)

```python
result.final_state_vector
```

    array([ 0.25667757+0.25502023j, -0.39576927-0.2607826j ,
           -0.33476508+0.24268252j, -0.50486577+0.4675345j ], dtype=complex64)

### Decomposing the circuit

```python
# Input qubits with rotations by the angle \phi
input_circuit = cirq.Circuit([
                cirq.rx(phi)(q0),
                cirq.ry(-2*phi)(q1)
                ])
input_circuit
```

(0, 0): ───Rx(phi)──────

(0, 1): ───Ry(-2*phi)───

```python
# The circuit that is going to be my model
# a.k.a given some inputs (rotated qubits) these will be entangled and
# rotated by fixed values that we want to learn
model_circuit = cirq.Circuit([
                cirq.H(q0),
                cirq.CNOT(q0,q1),
                cirq.rx(alpha)(q0),
                cirq.ry(beta)(q1),
              ])
model_circuit
```

(0, 0): ───H───@───Rx(alpha)───
               │
(0, 1): ───────X───Ry(beta)────

```python
# building a keras model
# the input is the input_circuit
# Parameterized Quantum Circuit - PQC
# the PQC outputs the expectation to be in state |00> from the model circuit, given the input circuit
model = tf.keras.Sequential([
    tf.keras.layers.Input(shape=(), dtype=tf.string),
    tfq.layers.PQC(model_circuit, z00),
])
```

```python
model.summary()
```

    Model: "sequential"
    _________________________________________________________________
    Layer (type)                 Output Shape              Param #
    =================================================================
    pqc (PQC)                    (None, 1)                 2
    =================================================================
    Total params: 2
    Trainable params: 2
    Non-trainable params: 0
    _________________________________________________________________

```python
model.trainable_variables
```

    [<tf.Variable 'parameters:0' shape=(2,) dtype=float32, numpy=array([4.175567 , 2.7709274], dtype=float32)>]

```python
# convert the input circuits (one circuit per value of phi) into tensors
# generate a series of input circuits, with different x values (values of phi)
input_circuits =  tfq.convert_to_tensor([cirq.resolve_parameters(input_circuit, cirq.ParamResolver({'phi': x}))] for x in x_values)
print(input_circuits[:3])
```

    tf.Tensor(
    [[b'\n\x0e\n\x0ctfq_gate_set\x12\x99\x02\x08\x01\x12\x94\x02\n\x87\x01\n\x04\n\x02XP\x12\x16\n\x0econtrol_qubits\x12\x04\n\x02\x1a\x00\x12\x13\n\x08exponent\x12\x07\n\x05\r\xe1\xb7\xcb\xbf\x12\x17\n\x0cglobal_shift\x12\x07\n\x05\r\x00\x00\x00\xbf\x12\x16\n\x0econtrol_values\x12\x04\n\x02\x1a\x00\x12\x1a\n\x0fexponent_scalar\x12\x07\n\x05\r\x00\x00\x80?\x1a\x05\x12\x030_0\n\x87\x01\n\x04\n\x02YP\x12\x16\n\x0econtrol_qubits\x12\x04\n\x02\x1a\x00\x12\x17\n\x0cglobal_shift\x12\x07\n\x05\r\x00\x00\x00\xbf\x12\x16\n\x0econtrol_values\x12\x04\n\x02\x1a\x00\x12\x13\n\x08exponent\x12\x07\n\x05\r\xe5\xb7K@\x12\x1a\n\x0fexponent_scalar\x12\x07\n\x05\r\x00\x00\x80?\x1a\x05\x12\x030_1']
     [b'\n\x0e\n\x0ctfq_gate_set\x12\x99\x02\x08\x01\x12\x94\x02\n\x87\x01\n\x04\n\x02XP\x12\x1a\n\x0fexponent_scalar\x12\x07\n\x05\r\x00\x00\x80?\x12\x16\n\x0econtrol_values\x12\x04\n\x02\x1a\x00\x12\x13\n\x08exponent\x12\x07\n\x05\r`\xae\xc9\xbf\x12\x17\n\x0cglobal_shift\x12\x07\n\x05\r\x00\x00\x00\xbf\x12\x16\n\x0econtrol_qubits\x12\x04\n\x02\x1a\x00\x1a\x05\x12\x030_0\n\x87\x01\n\x04\n\x02YP\x12\x13\n\x08exponent\x12\x07\n\x05\r`\xaeI@\x12\x17\n\x0cglobal_shift\x12\x07\n\x05\r\x00\x00\x00\xbf\x12\x1a\n\x0fexponent_scalar\x12\x07\n\x05\r\x00\x00\x80?\x12\x16\n\x0econtrol_qubits\x12\x04\n\x02\x1a\x00\x12\x16\n\x0econtrol_values\x12\x04\n\x02\x1a\x00\x1a\x05\x12\x030_1']
     [b'\n\x0e\n\x0ctfq_gate_set\x12\x99\x02\x08\x01\x12\x94\x02\n\x87\x01\n\x04\n\x02XP\x12\x13\n\x08exponent\x12\x07\n\x05\r\xd7\xa4\xc7\xbf\x12\x17\n\x0cglobal_shift\x12\x07\n\x05\r\x00\x00\x00\xbf\x12\x16\n\x0econtrol_qubits\x12\x04\n\x02\x1a\x00\x12\x16\n\x0econtrol_values\x12\x04\n\x02\x1a\x00\x12\x1a\n\x0fexponent_scalar\x12\x07\n\x05\r\x00\x00\x80?\x1a\x05\x12\x030_0\n\x87\x01\n\x04\n\x02YP\x12\x13\n\x08exponent\x12\x07\n\x05\r\xdb\xa4G@\x12\x17\n\x0cglobal_shift\x12\x07\n\x05\r\x00\x00\x00\xbf\x12\x16\n\x0econtrol_values\x12\x04\n\x02\x1a\x00\x12\x16\n\x0econtrol_qubits\x12\x04\n\x02\x1a\x00\x12\x1a\n\x0fexponent_scalar\x12\x07\n\x05\r\x00\x00\x80?\x1a\x05\x12\x030_1']], shape=(3, 1), dtype=string)

```python
x_values[:5]
```

    array([-5.  , -4.95, -4.9 , -4.85, -4.8 ])

```python
len(x_values)
```

    200

```python
input_circuits.shape
```

    TensorShape([200, 1])

```python
# test the model with randomly initialised values of alpha and beta given the input circuits
y_test = model([input_circuits]).numpy()

# plot how a randomly initiated model compares with the expected
plt.plot(x_values, y_expectations, lw=4, label=f'Expected, $\\alpha$={round(a,3)}, $\\beta$={round(b,3)}')
plt.plot(x_values, y_test,'ro', label='NN output')
plt.xlabel('$\phi$')
plt.ylabel('Expectation')
plt.legend()
plt.show()
```

```python
# compile the model, using a basic Adam optimizer and MSE loss
optimizer = tf.keras.optimizers.Adam(learning_rate=0.01)
loss = tf.keras.losses.MeanSquaredError()
model.compile(optimizer=optimizer, loss=loss)
model.summary()
```

    Model: "sequential"
    _________________________________________________________________
    Layer (type)                 Output Shape              Param #
    =================================================================
    pqc (PQC)                    (None, 1)                 2
    =================================================================
    Total params: 2
    Trainable params: 2
    Non-trainable params: 0
    _________________________________________________________________

```python
# model can be fitted given now the y_expected from the original expectations
# (this could easily be the original 'measurements' distribution instead)
y_expected = np.array(y_expectations)
history = model.fit(input_circuits, y_expected,
                    epochs=25, verbose=1)
type(history)
```

    Epoch 1/25
    7/7 [==============================] - 1s 8ms/step - loss: 0.0769
    Epoch 2/25
    7/7 [==============================] - 0s 11ms/step - loss: 0.0806
    Epoch 3/25
    7/7 [==============================] - 0s 10ms/step - loss: 0.0692
    Epoch 4/25
    7/7 [==============================] - 0s 9ms/step - loss: 0.0669
    Epoch 5/25
    7/7 [==============================] - 0s 9ms/step - loss: 0.0639
    Epoch 6/25
    7/7 [==============================] - 0s 9ms/step - loss: 0.0704
    Epoch 7/25
    7/7 [==============================] - 0s 11ms/step - loss: 0.0614
    Epoch 8/25
    7/7 [==============================] - 0s 9ms/step - loss: 0.0598
    Epoch 9/25
    7/7 [==============================] - 0s 9ms/step - loss: 0.0514
    Epoch 10/25
    7/7 [==============================] - 0s 10ms/step - loss: 0.0474
    Epoch 11/25
    7/7 [==============================] - 0s 10ms/step - loss: 0.0453
    Epoch 12/25
    7/7 [==============================] - 0s 10ms/step - loss: 0.0400
    Epoch 13/25
    7/7 [==============================] - 0s 9ms/step - loss: 0.0328
    Epoch 14/25
    7/7 [==============================] - 0s 9ms/step - loss: 0.0248
    Epoch 15/25
    7/7 [==============================] - 0s 11ms/step - loss: 0.0200
    Epoch 16/25
    7/7 [==============================] - 0s 9ms/step - loss: 0.0142
    Epoch 17/25
    7/7 [==============================] - 0s 9ms/step - loss: 0.0107
    Epoch 18/25
    7/7 [==============================] - 0s 11ms/step - loss: 0.0065
    Epoch 19/25
    7/7 [==============================] - 0s 11ms/step - loss: 0.0049
    Epoch 20/25
    7/7 [==============================] - 0s 9ms/step - loss: 0.0032
    Epoch 21/25
    7/7 [==============================] - 0s 9ms/step - loss: 0.0023
    Epoch 22/25
    7/7 [==============================] - 0s 9ms/step - loss: 0.0017
    Epoch 23/25
    7/7 [==============================] - 0s 9ms/step - loss: 0.0013
    Epoch 24/25
    7/7 [==============================] - 0s 10ms/step - loss: 0.0010
    Epoch 25/25
    7/7 [==============================] - 0s 9ms/step - loss: 7.4552e-04

    tensorflow.python.keras.callbacks.History

```python
history.params
```

    {'epochs': 25, 'steps': 7, 'verbose': 1}

```python
# test the model
y_test = model([input_circuits]).numpy()

# plot how a randomly initiated model compares with the expected
plt.plot(x_values, y_expected, lw=4, label=f'Expected, $\\alpha$={round(a,3)}, $\\beta$={round(b,3)}')
plt.plot(x_values, y_test,'ro', label='NN output')
plt.xlabel('$\phi$')
plt.ylabel('Expectation')
plt.legend()
plt.show()
```

```python

```