# Notebook from DianaMosquera/course-content
Path: tutorials/W1D4_MachineLearning/W1D4_Tutorial2.ipynb

_____no_output_____# Neuromatch Academy: Week 1, Day 4, Tutorial 2
# Machine Learning: Classifiers and regularizers

__Content creators:__ Pierre-Etienne H. Fiquet, Ari Benjamin, Jakob Macke

__Content reviewers:__ Davide Valeriani, Alish Dipani, Michael Waskom
_____no_output_____This is part 2 of a 2-part series about Generalized Linear Models (GLMs), which are a fundamental framework for supervised learning. In part 1, we learned about and implemented GLMs. In this tutorial, we’ll implement logistic regression, a special case of GLMs used to model binary outcomes.
Oftentimes the variable you would like to predict takes only one of two possible values. Left or right? Awake or asleep? Car or bus? In this tutorial, we will decode a mouse's left/right decisions from spike train data. Our objectives are to:
1.	Learn about logistic regression, how it is derived within the GLM theory, and how it is implemented in scikit-learn
2.	Apply logistic regression to decode choies from neural responses
3.	Learn about regularization, including the different approaches and the influence of hyperparameters

---
We would like to acknowledge [Steinmetz _et al._ (2019)](https://www.nature.com/articles/s41586-019-1787-x) for sharing their data, a subset of which is used here.
_____no_output_____# Setup
_____no_output_____

import numpy as np
import matplotlib.pyplot as plt

from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import cross_val_score_____no_output_____#@title Figure settings
import ipywidgets as widgets

%matplotlib inline
%config InlineBackend.figure_format = 'retina'
plt.style.use("https://raw.githubusercontent.com/NeuromatchAcademy/course-content/master/nma.mplstyle")_____no_output_____#@title Helper functions

def plot_weights(models, sharey=True):
  """Draw a stem plot of weights for each model in models dict."""
  n = len(models)
  f = plt.figure(figsize=(10, 2.5 * n))
  axs = f.subplots(n, sharex=True, sharey=sharey)
  axs = np.atleast_1d(axs)

  for ax, (title, model) in zip(axs, models.items()):

    ax.margins(x=.02)
    stem = ax.stem(model.coef_.squeeze(), use_line_collection=True)
    stem[0].set_marker(".")
    stem[0].set_color(".2")
    stem[1].set_linewidths(.5)
    stem[1].set_color(".2")
    stem[2].set_visible(False)
    ax.axhline(0, color="C3", lw=3)
    ax.set(ylabel="Weight", title=title)
  ax.set(xlabel="Neuron (a.k.a. feature)")
  f.tight_layout()

def plot_function(f, name, var, points=(-10, 10)):
    """Evaluate f() on linear space between points and plot.

    Args:
      f (callable): function that maps scalar -> scalar
      name (string): Function name for axis labels
      var (string): Variable name for axis labels.
      points (tuple): Args for np.linspace to create eval grid.
    """
    x = np.linspace(*points)
    ax = plt.figure().subplots()
    ax.plot(x, f(x))
    ax.set(
      xlabel=f'${var}$',
      ylabel=f'${name}({var})$'
    )

def plot_model_selection(C_values, accuracies):
  """Plot the accuracy curve over log-spaced C values."""
  ax = plt.figure().subplots()
  ax.set_xscale("log")
  ax.plot(C_values, accuracies, marker="o")
  best_C = C_values[np.argmax(accuracies)]
  ax.set(
      xticks=C_values,
      xlabel="$C$",
      ylabel="Cross-validated accuracy",
      title=f"Best C: {best_C:1g} ({np.max(accuracies):.2%})",
  )

def plot_non_zero_coefs(C_values, non_zero_l1, n_voxels):
  """Plot the accuracy curve over log-spaced C values."""
  ax = plt.figure().subplots()
  ax.set_xscale("log")
  ax.plot(C_values, non_zero_l1, marker="o")
  ax.set(
    xticks=C_values,
    xlabel="$C$",
    ylabel="Number of non-zero coefficients",
  )
  ax.axhline(n_voxels, color=".1", linestyle=":")
  ax.annotate("Total\n# Neurons", (C_values[0], n_voxels * .98), va="top")_____no_output_____#@title Data retrieval and loading
import os
import requests
import hashlib

url = "https://osf.io/r9gh8/download"
fname = "W1D4_steinmetz_data.npz"
expected_md5 = "d19716354fed0981267456b80db07ea8"

if not os.path.isfile(fname):
  try:
    r = requests.get(url)
  except requests.ConnectionError:
    print("!!! Failed to download data !!!")
  else:
    if r.status_code != requests.codes.ok:
      print("!!! Failed to download data !!!")
    elif hashlib.md5(r.content).hexdigest() != expected_md5:
      print("!!! Data download appears corrupted !!!")
    else:
      with open(fname, "wb") as fid:
        fid.write(r.content)

def load_steinmetz_data(data_fname=fname):

  with np.load(data_fname) as dobj:
    data = dict(**dobj)

  return data_____no_output_____

---

#Section 1: Logistic regression_____no_output_____

#@title Video 1: Logistic regression
from IPython.display import YouTubeVideo
video = YouTubeVideo(id="qfXFrUnLU0o", width=854, height=480, fs=1)
print("Video available at https://youtube.com/watch?v=" + video.id)
video_____no_output_____

Logistic Regression is a binary classification model. It is a GLM with a *logistic* link function and a *Bernoulli* (i.e. coinflip) noise model.

Like in the last notebook, logistic regression invokes a standard procedure:

1.   Define a *model* of how inputs relate to outputs.
2.   Adjust the parameters to maximize (log) probability of your data given your model

## Section 1.1: The logistic regression model

The fundamental input/output equation of logistic regression is:

$$ \hat{y} \equiv p(y=1|x,\theta) = \sigma(\theta^Tx)$$

Note that we interpret the output of logistic regression, $\hat{y}$, as the **probability that y = 1** given inputs $x$ and parameters $\theta$.

Here $\sigma()$ is a "squashing" function called the **sigmoid function** or **logistic function**. Its output is in the range $0 \leq y \leq 1$. It looks like this:

$$\sigma(z) = \frac{1}{1 + \textrm{exp}(-z)}$$

Recall that $z = \theta^T x$. The parameters decide whether $\theta^T x$ will be very negative, in which case $\sigma(\theta^T x)\approx 0$, or very positive, meaning  $\sigma(\theta^T x)\approx 1$.
_____no_output_____### Exercise 1: implement the sigmoid function
_____no_output_____

def sigmoid(z):
  """Return the logistic transform of z."""
  ##############################################################################
  # TODO for students: Fill in the missing code (...) and remove the error
  raise NotImplementedError("Student excercise: implement the sigmoid function")
  ##############################################################################
  return ...

# Uncomment to test your sigmoid function
# plot_function(sigmoid, "\sigma", "z", (-10, 10))_____no_output_____# to_remove solution
def sigmoid(z):
  """Return the logistic transform of z."""
  return 1 / (1 + np.exp(-z))
with plt.xkcd():
  plot_function(sigmoid, "\sigma", "z", (-10, 10))_____no_output_____

## Section 1.2: Using scikit-learn

Unlike the previous notebook, we're not going to write the code that implements all of the Logistic Regression model itself. Instead, we're going to use the implementation in [scikit-learn](https://scikit-learn.org/stable/), a very popular library for Machine Learning.

The goal of this next section is to introduce `scikit-learn` classifiers and understand how to apply it to real neural data._____no_output_____#Section 2: Decoding neural data with logistic regression_____no_output_____## Section 2.1: Setting up the data

In this notebook we'll use the Steinmetz dataset that you have seen previously. Recall that this dataset includes recordings of neurons as mice perform a decision task.

Mice had the task of turning a wheel to indicate whether they perceived a Gabor stimulus to the left, to the right, or not at all. Neuropixel probes measured spikes across the cortex. Check out the following task schematic from the BiorXiv preprint:

Today we're going to **decode the decision from neural data** using Logistic Regression. We will only consider trials where the mouse chose "Left" or "Right" and ignore NoGo trials.

### Data format

In the hidden `Data retrieval and loading` cell, there is a function that loads the data:

- `spikes`: an array of normalized spike rates with shape `(n_trials, n_neurons)`
- `choices`: a vector of 0s and 1s, indicating the animal's behavioral response, with length `n_trials`._____no_output_____

data = load_steinmetz_data()
for key, val in data.items():
  print(key, val.shape)_____no_output_____

As with the GLMs you've seen in the previous tutorial (Linear and Poisson Regression), we will need two data structures:

- an `X` matrix with shape `(n_samples, n_features)`
- a `y` vector with length `n_samples`.

In the previous notebook, `y` corresponded to the neural data, and `X` corresponded to something about the experiment. Here, we are going to invert those relationships. That's what makes this a *decoding* model: we are going to predict behavior (`y`) from the neural responses (`X`):_____no_output_____

y = data["choices"]
X = data["spikes"]_____no_output_____

## Section 2.2: Fitting the model

Using a Logistic Regression model within `scikit-learn` is very simple. _____no_output_____

# First define the model
log_reg = LogisticRegression(penalty="none")

#Then fit it to data
log_reg.fit(X, y)_____no_output_____

There's two steps here:

- We *initialized* the model with a hyperparameter, telling it what penalty to use (we'll focus on this in the second part of the notebook)
- We *fit* the model by passing it the `X` and `y` objects.

_____no_output_____## Section 2.3: Classifying the training data

Fitting the model performs maximum likelihood optimization, learning a set of *feature weights*. We can use those learned weights to *classify* new data, or predict the labels for each sample:_____no_output_____

y_pred = log_reg.predict(X)_____no_output_____

## Section 2.4: Evaluating the model

Now we need to evaluate the model's predictions. We'll do that with an *accuracy* score. The accuracy of the classifier is the proportion of trials where the predicted label matches the true label.
_____no_output_____### Exercise 2: classifier accuracy

For the first exercise, implement a function to evaluate a classifier using the accuracy score. Use it to get the accuracy of the classifier on the *training* data._____no_output_____

def compute_accuracy(X, y, model):
  """Compute accuracy of classifier predictions.

  Args:
    X (2D array): Data matrix
    y (1D array): Label vector
    model (sklearn estimator): Classifier with trained weights.

  Returns:
    accuracy (float): Proportion of correct predictions.
  """
  #############################################################################
  # TODO Complete the function, then remove the next line to test it
  raise NotImplementedError("Implement the compute_accuracy function")
  #############################################################################

  y_pred = model.predict(X)
  accuracy = ...

  return accuracy

# Uncomment and run to test your function:
# train_accuracy = compute_accuracy(X, y, log_reg)
# print(f"Accuracy on the training data: {train_accuracy:.2%}")_____no_output_____# to_remove solution
def compute_accuracy(X, y, model):
  """Compute accuracy of classifier predictions.

  Args:
    X (2D array): Data matrix
    y (1D array): Label vector
    model (sklearn estimator): Classifier with trained weights.

  Returns:
    accuracy (float): Proportion of correct predictions.
  """
  y_pred = model.predict(X)
  accuracy = (y == y_pred).mean()

  return accuracy

train_accuracy = compute_accuracy(X, y, log_reg)
print(f"Accuracy on the training data: {train_accuracy:.2%}")_____no_output_____

## Section 2.5: Cross-validating the classifer

Classification accuracy on the training data is 100%! That might sound impressive, but you should recall from yesterday the concept of *overfitting*: the classifier may have learned something idiosyncratic about the training data. If that's the case, it won't have really learned the underlying data->decision function, and thus won't generalize well to new data.

To check this, we can evaluate the *cross-validated* accuracy.

_____no_output_____### Cross-validating using `scikit-learn` helper functions

Yesterday, we asked you to write your own functions for implementing cross-validation. In practice, this won't be necessary, because `scikit-learn` offers a number of [helpful functions](https://scikit-learn.org/stable/model_selection.html) that will do this for you. For example, you can cross-validate a classifer using `cross_val_score`.

`cross_val_score` takes a `sklearn` model like `LogisticRegression`, as well as your `X` and `y` data. It then retrains your model on test/train splits of `X` and `y`, and returns the test accuracy on each of the test sets._____no_output_____

accuracies = cross_val_score(LogisticRegression(penalty='none'), X, y, cv=8) # k=8 crossvalidation_____no_output_____#@title
#@markdown Run to plot out these `k=8` accuracy scores.
f, ax = plt.subplots(figsize=(8, 3))
ax.boxplot(accuracies, vert=False, widths=.7)
ax.scatter(accuracies, np.ones(8))
ax.set(
  xlabel="Accuracy",
  yticks=[],
  title=f"Average test accuracy: {accuracies.mean():.2%}"
)
ax.spines["left"].set_visible(False)_____no_output_____

The lower cross-validated accuracy compared to the training accuracy (100%) suggests that the model is being *overfit*. Is this surprising? Think about the shape of the $X$ matrix:_____no_output_____

X.shape_____no_output_____

The model has almost three times as many features as samples. This is a situation where overfitting is very likely (almost guaranteed).

**Link to neuroscience**: Neuro data commonly has more features than samples. Having more neurons than independent trials is one example. In fMRI data, there are commonly more measured voxels than independent trials.
_____no_output_____
### Why more features than samples leads to overfitting

In brief, the variance of model estimation increases when there are more features than samples. That is, you would get a very different model every time you get new data and run `.fit()`. This is very related to the *bias/variance tradeoff* you learned about on day 1.

Why does this happen? Here's a tiny example to get your intuition going. Imagine trying to find a best-fit line in 2D when you only have 1 datapoint. There are simply a infinite number of lines that pass through that point. This is the situation we find ourselves in with more features than samples.

### What we can do about it
As you learned on day 1, you can decrease model variance if you don't mind increasing its bias. Here, we will increase bias by assuming that the correct parameters are all small. In our 2D example, this is like prefering the horizontal line to all others. This is one example of *regularization*. _____no_output_____-----

#Section 3: Regularization
_____no_output_____

#@title Video 2: Regularization
from IPython.display import YouTubeVideo
video = YouTubeVideo(id="b2IaUCZ91bo", width=854, height=480, fs=1)
print("Video available at https://youtube.com/watch?v=" + video.id)
video_____no_output_____

Regularization forces a model to learn a set solutions you *a priori* believe to be more correct, which reduces overfitting because it doesn't have as much flexibility to fit idiosyncracies in the training data. This adds model bias, but it's a good bias because you know (maybe) that parameters should be small or mostly 0.

In a GLM, a common form of regularization is to *shrink* the classifier weights. In a linear model, you can see its effect by plotting the weights. We've defined a helper function, `plot_weights`, that we'll use extensively in this section.

Here is what the weights look like for a Logistic Regression model with no regularization:_____no_output_____

log_reg = LogisticRegression(penalty="none").fit(X, y)
plot_weights({"No regularization": log_reg})_____no_output_____

It's important to understand this plot. Each dot visualizes a value in our parameter vector $\theta$. (It's the same style of plot as the one showing $\theta$ in the video). Since each feature is the time-averaged response of a neuron, each dot shows how the model uses each neuron to estimate a decision.

Note the scale of the y-axis. Some neurons have values of about $20$, whereas others scale to $-20$._____no_output_____## Section 3.1: $L_2$ regularization

Regularization comes in different flavors. A very common one uses an $L_2$ or "ridge" penalty. This changes the objective function to

$$-\log\mathcal{L}'(\theta | X, y)=
-\log\mathcal{L}(\theta | X, y) +\frac\beta2\sum_i\theta_i^2,
$$

where $\beta$ is a *hyperparameter* that sets the *strength* of the regularization.

You can use regularization in `scikit-learn` by changing the `penalty`, and you can set the strength of the regularization with the `C` hyperparameter ($C = \frac{1}{\beta}$, so this sets the *inverse* regularization).

Let's compare the unregularized classifier weights with the classifier weights when we use the default `C = 1`:_____no_output_____

log_reg_l2 = LogisticRegression(penalty="l2", C=1).fit(X, y)

# now show the two models
models = {
  "No regularization": log_reg,
  "$L_2$ (C = 1)": log_reg_l2,
}
plot_weights(models)_____no_output_____

Using the same scale for the two y axes, it's almost impossible to see the $L_2$ weights. Let's allow the y axis scales to adjust to each set of weights:_____no_output_____

plot_weights(models, sharey=False)_____no_output_____

Now you can see that the weights have the same basic pattern, but the regularized weights are an order-of-magnitude smaller.

### Interactive Demo: The effect of varying C on parameter size

We can use this same approach to see how the weights depend on the *strength* of the regularization:_____no_output_____

#@title

#@markdown Execute this cell to enable the widget!

# Precompute the models so the widget is responsive
log_C_steps = 1, 10, 1
penalized_models = {}
for log_C in np.arange(*log_C_steps, dtype=int):
  m = LogisticRegression("l2", C=10 ** log_C, max_iter=5000)
  penalized_models[log_C] = m.fit(X, y)

@widgets.interact
def plot_observed(log_C=log_C_steps):
  models = {
    "No regularization": log_reg,
    f"$L_2$ (C = $10^{log_C}$)": penalized_models[log_C]
  }
  plot_weights(models)_____no_output_____

Recall from above that $C=\frac1\beta$ so larger `C` is less regularization. The top panel corresponds to $C=\infty$._____no_output_____## Section 3.2: $L_1$ regularization

$L_2$ is not the only option for regularization. There is also the $L_1$, or "Lasso" penalty. This changes the objective function to

$$
-\log\mathcal{L}'(\theta | X, y)=
-\log\mathcal{L}(\theta | X, y) +\frac\beta2\sum_i|\theta_i|
$$

In practice, using the summed absolute values of the weights causes *sparsity*: instead of just getting smaller, some of the weights will get forced to $0$:_____no_output_____

log_reg_l1 = LogisticRegression(penalty="l1", C=1, solver="saga", max_iter=5000)
log_reg_l1.fit(X, y)
models = {
  "$L_2$ (C = 1)": log_reg_l2,
  "$L_1$ (C = 1)": log_reg_l1,
}
plot_weights(models)_____no_output_____

Note: You'll notice that we added two additional parameters: `solver="saga"` and `max_iter=5000`. The `LogisticRegression` class can use several different optimization algorithms ("solvers"), and not all of them support the $L_1$ penalty. At a certain point, the solver will give up if it hasn't found a minimum value. The `max_iter` parameter tells it to make more attempts; otherwise, we'd see an ugly warning about "convergence"._____no_output_____# Section 4: The key difference between $L_1$ and $L_2$ regularization: sparsity

When should you use $L_1$ vs. $L_2$ regularization? Both penalties shrink parameters, and both will help reduce overfitting. However, the models they lead to are different.

In particular, the $L_1$ penalty encourages *sparse* solutions in which most parameters are 0. Let's unpack the notion of sparsity.

A "dense" vector has mostly nonzero elements:
$\begin{bmatrix}
  0.1 \\ -0.6\\-9.1\\0.07
\end{bmatrix}$.
A "sparse" vector has mostly zero elements:
$\begin{bmatrix}
  0 \\ -0.7\\ 0\\0
\end{bmatrix}$.

The same is true of matrices:_____no_output_____

#@title
#@markdown Execute to plot a dense and a sparse matrix
np.random.seed(50)
n = 5
M = np.random.random((n, n))
M_sparse = np.random.choice([0,1], size=(n, n), p=[0.8, 0.2])

fig, axs = plt.subplots(1, 2, sharey=True, figsize=(10,5))

axs[0].imshow(M)
axs[1].imshow(M_sparse)
axs[0].axis('off')
axs[1].axis('off')
axs[0].set_title("A dense matrix", fontsize=15)
axs[1].set_title("A sparse matrix", fontsize=15)
text_kws = dict(ha="center", va="center")
for i in range(n):
  for j in range(n):
    iter_parts = axs, [M, M_sparse], ["{:.1f}", "{:d}"]
    for ax, mat, fmt in zip(*iter_parts):
      val = mat[i, j]
      color = ".1" if val > .7 else "w"
      ax.text(j, i, fmt.format(val), c=color, **text_kws)_____no_output_____

## Exercise 3: The effect of $L_1$ regularization on parameter sparsity

Please complete the following function to fit a regularized `LogisticRegression` model and return **the number of coefficients in the parameter vector that are equal to 0**.

Don't forget to check out the [scikit-learn documentation](https://scikit-learn.org/stable/modules/generated/sklearn.linear_model.LogisticRegression.html).
_____no_output_____

def count_non_zero_coefs(X, y, C_values):
  """Fit models with different L1 penalty values and count non-zero coefficients.

  Args:
    X (2D array): Data matrix
    y (1D array): Label vector
    C_values (1D array): List of hyperparameter values

  Returns:
    non_zero_coefs (list): number of coefficients in each model that are nonzero

  """
  #############################################################################
  # TODO Complete the function and remove the error
  raise NotImplementedError("Implement the count_non_zero_coefs function")
  #############################################################################

  non_zero_coefs = []
  for C in C_values:

    # Initialize and fit the model
    # (Hint, you may need to set max_iter)
    model = ...
    ...

    # Get the coefs of the fit model
    coefs = ...

    # Count the number of non-zero elements in coefs
    non_zero = ...
    non_zero_coefs.append(non_zero)

  return non_zero_coefs

# Use log-spaced values for C
C_values = np.logspace(-4, 4, 5)

# Uncomment and run when the function is ready for testing
# non_zero_l1 = count_non_zero_coefs(X, y, C_values)
# plot_non_zero_coefs(C_values, non_zero_l1, n_voxels=X.shape[1])_____no_output_____# to_remove solution
def count_non_zero_coefs(X, y, C_values):
  """Fit models with different L1 penalty values and count non-zero coefficients.

  Args:
    X (2D array): Data matrix
    y (1D array): Label vector
    C_values (1D array): List of hyperparameter values

  Returns:
    non_zero_coefs (list): number of coefficients in each model that are nonzero

  """
  non_zero_coefs = []
  for C in C_values:

    # Initialize and fit the model
    # (Hint, you may need to set max_iter)
    model = LogisticRegression(penalty="l1", C=C, solver="saga", max_iter=5000)
    model.fit(X,y)

    # Get the coefs of the fit model
    coefs = model.coef_

    # Count the number of non-zero elements in coefs
    non_zero = np.sum(coefs != 0)
    non_zero_coefs.append(non_zero)

  return non_zero_coefs

# Use log-spaced values for C
C_values = np.logspace(-4, 4, 5)

non_zero_l1 = count_non_zero_coefs(X, y, C_values)

with plt.xkcd():
  plot_non_zero_coefs(C_values, non_zero_l1, n_voxels=X.shape[1])_____no_output_____

Smaller `C` (bigger $\beta$) leads to sparser solutions.

**Link to neuroscience**: When is it OK to assume that the parameter vector is sparse? Whenever it is true that most features don't affect the outcome. One use-case might be decoding low-level visual features from whole-brain fMRI: we may expect only voxels in V1 and thalamus should be used in the prediction.

**WARNING**: be careful when interpreting $\theta$. Never interpret the nonzero coefficients as *evidence* that only those voxels/neurons/features carry information about the outcome. This is a product of our regularization scheme, and thus *our prior assumption that the solution is sparse*. Other regularization types or models may find very distributed relationships across the brain. Never use a model as evidence for a phenomena when that phenomena is encoded in the assumptions of the model. _____no_output_____---

## Section 4.1: Choosing the regularization penalty

In the examples above, we just picked arbitrary numbers for the strength of regularization. How do you know what value of the hyperparameter to use?

The answer is the same as when you want to know whether you have learned good parameter values: use cross-validation. The best hyperparameter will be the one that allows the model to generalize best to unseen data._____no_output_____### Exercise 4: Model selection

In the final exercise, we will use cross-validation to evaluate a set of models, each with a different $L_2$ penalty. Your `model_selection` function should have a for-loop that gets the mean cross-validated accuracy for each penalty value (use the `cross_val_score` function that we introduced above)._____no_output_____

def model_selection(X, y, C_values):
  """Compute CV accuracy for each C value.

  Args:
    X (2D array): Data matrix
    y (1D array): Label vector
    C_values (1D array): Array of hyperparameter values

  Returns:
    accuracies (1D array): CV accuracy with each value of C

  """
  #############################################################################
  # TODO Complete the function and remove the error
  raise NotImplementedError("Implement the model_selection function")
  #############################################################################

  accuracies = []
  for C in C_values:

    # Initialize and fit the model
    # (Hint, you may need to set max_iter)
    model = ...

    # Get the accuracy for each test split
    accs = ...

    # Store the average test accuracy for this value of C
    accuracies.append(...)

  return accuracies

# Use log-spaced values for C
C_values = np.logspace(-4, 4, 9)

# Uncomment and run when the function is ready to test
# accuracies = model_selection(X, y, C_values)
# plot_model_selection(C_values, accuracies)_____no_output_____# to_remove solution
def model_selection(X, y, C_values):
  """Compute CV accuracy for each C value.

  Args:
    X (2D array): Data matrix
    y (1D array): Label vector
    C_values (1D array): Array of hyperparameter values.

  Returns:
    accuracies (1D array): CV accuracy with each value of C.

  """
  accuracies = []
  for C in C_values:

    # Initialize and fit the model
    # (Hint, you may need to set max_iter)
    model = LogisticRegression(penalty="l2", C=C, max_iter=5000)

    # Get the accuracy for each test split
    accs = cross_val_score(model, X, y, cv=8)

    # Store the average test accuracy for this value of C
    accuracies.append(accs.mean())

  return accuracies

# Use log-spaced values for C
C_values = np.logspace(-4, 4, 9)

accuracies = model_selection(X, y, C_values)
with plt.xkcd():
  plot_model_selection(C_values, accuracies)_____no_output_____

This plot suggests that the right value of $C$ does matter — up to a point. Remember that C is the *inverse* regularization. The plot shows that models where the regularization was too strong (small C values) performed very poorly. For $C > 10^{-2}$, the differences are marginal, but the best performance was obtained with an intermediate value ($C \approx 10^1$)._____no_output_____# Summary

In this notebook, we learned about Logistic Regression, a fundamental algorithm for *classification*. We applied the algorithm to a *neural decoding* problem: we tried to predict an animal's behavioral choice from its neural activity. We saw again how important it is to use *cross-validation* to evaluate complex models that are at risk for *overfitting*, and we learned how *regularization* can be used to fit models that generalize better. Finally, we learned about some of the different options for regularization, and we saw how cross-validation can be useful for *model selection*._____no_output_____-----_____no_output_____# Appendix: The Logistic Regression model in full

The fundamental input/output equation of logistic regression is:

$$p(y_i = 1 |x_i, \theta) = \sigma(\theta^Tx_i)$$

## The logistic link function

You've seen $\theta^T x_i$ before, but the $\sigma$ is new. It's the *sigmoidal* or *logistic* link function that "squashes" $\theta^T x_i$ to keep it between $0$ and $1$:

$$\sigma(z) = \frac{1}{1 + \textrm{exp}(-z)}$$

## The Bernoulli likelihood

You might have noticed that the output of the sigmoid, $\hat{y}$ is not a binary value (0 or 1), even though the true data $y$ is! Instead, we interpret the value of $\hat{y}$ as the *probability that y = 1*:

$$ \hat{y_i} \equiv p(y_i=1|x_i,\theta) = \frac{1}{{1 + \textrm{exp}(-\theta^Tx_i)}}$$

To get the likelihood of the parameters, we need to define *the probability of seeing $y$ given $\hat{y}$*. In logistic regression, we do this using the Bernoulli distribution:

$$P(y_i\ |\ \hat{y}_i) = \hat{y}_i^{y_i}(1 - \hat{y}_i)^{(1 - y_i)}$$

So plugging in the regression model:

$$P(y_i\ |\ \theta, x_i) = \sigma(\theta^Tx_i)^{y_i}(1 - \sigma(\theta^Tx_i))^{(1 - y_i)}.$$

This expression effectively measures how good our parameters $\theta$ are. We can also write it as the likelihood of the parameters given the data:

$$\mathcal{L}(\theta\ |\ y_i, x_i) = P(y_i\ |\ \theta, x_i),$$

and then use this as a target of optimization, considering all of the trials independently:

$$\textrm{log}\mathcal{L}(\theta | X, y) = \sum_{i=1}^Ny_i\textrm{log}(\sigma(\theta^Tx_i))\ +\ (1-y_i)\textrm{log}(1 - \sigma(\theta^Tx_i)).$$_____no_output_____# Appendix: More detail about model selection

In the final exercise, we used all of the data to choose the hyperparameters. That means we don't have any fresh data left over to evaluate the performance of the selected model. In practice, you would want to have two *nested* layers of cross-validation, where the final evaluation is performed on data that played no role in selecting or training the model.

Indeed, the proper method for splitting your data to choose hyperparameters can get confusing. Here's a guide that the authors of this notebook developed while writing a tutorial on using machine learning for neural decoding (https://arxiv.org/abs/1708.00909).

_____no_output_____


# Notebook from kazewong/nrpytutorial
Path: notebook/Tutorial-ETK_thorn-GiRaFFEfood_HO_Exact_Wald.ipynb

# $\texttt{GiRaFFEfood}$: An Einstein Toolkit Initial Data Thorn for $\texttt{GiRaFFE}$

## Authors: Zach Etienne & Patrick Nelson
### Formatting improvements courtesy Brandon Clark

[comment]: <> (Abstract: TODO)

[comment]: <> (Module Status and Validation Notes: TODO)

### NRPy+ Source Code for this module: [GiRaFFEfood_HO_Exact_Wald.py](../edit/GiRaFFEfood_HO_Exact_Wald.py) [\[tutorial\]](Tutorial-GiRaFFEfood_HO_Exact_Wald.ipynb) Constructs SymPy expressions for Exact Wald data

## Introduction:
In this part of the tutorial, we will construct an Einstein Toolkit (ETK) thorn (module) that will set up *initial data* for $\texttt{GiRaFFE}$. In the [Tutorial-GiRaFFEfood_HO_Exact_Wald](Tutorial-GiRaFFEfood_HO_Exact_Wald.ipynb) tutorial notebook, we used NRPy+ to contruct the SymPy expressions for Exact Wald initial data.

We will construct this thorn in two steps.

1. Call on NRPy+ to convert the SymPy expressions for the initial data into one C-code kernel.
1. Write the C code and linkages to the Einstein Toolkit infrastructure (i.e., the .ccl files) to complete this Einstein Toolkit module.

This thorn requires Initial data to be set up in several stages.

1. Run the shifted Kerr-Schild thorn to set up the four-metric we wish to use.
1. (**This module**) Set up the four-vector potential $A_\mu$ (which includes $\Phi$ and $A_i$) and the Valencia 3-velocity.
1. (From thorn `GiRaFFE_HO`) Run the A-to-B driver from $\texttt{GiRaFFE}$ to fill in the initial $B^i$ data.
1. (**This module**) Run the Primitive-to-conservative solver to calculate $\tilde{S}_i$.
_____no_output_____

# Table of Contents
$$\label{toc}$$

This notebook is organized as follows

1. [Step 1](#initializenrpy): Call on NRPy+ to convert the SymPy expression for the Eact Wald initial data into a C-code kernel
    1. [Step 1.a](#aligned_rotator): Repeat for the Aligned Rotator case
1. [Step 2](#etk): Interfacing with the Einstein Toolkit
    1. [Step 2.a](#einstein_c): Constructing the Einstein Toolkit C-code calling functions that include the C code kernels
        1. [Step 2.a.i](#aligned_rotator_file): Similar file for Aligned Rotator data
    1. [Step 2.b](#primitive2conservative): The Primitive-to-Conservative Solver
        1. [2.b.i](#ccode_p2c): C Code to use the Primitive-to-Conservative Solver
    1. [Step 2.c](#cclfiles): CCL files - Define how this module interacts and interfaces with the larger Einstein Toolkit infrastructure
        1. [Step2.c.i](#interface): `interface.ccl`
        1. [Step2.c.ii](#param): `param.ccl`
        1. [Step2.c.iiii](#schedule): `schedule.ccl`
    1. [Step 2.d](#einstein_list): Add the C code to the Einstein Toolkit compilation list
1. [Step 3](#data_agreement): Current Initial Data Agreement
1. [Step 4](#latex_pdf_output): Output this notebook to $\LaTeX$-formatted PDF file_____no_output_____

# Step 1: Call on NRPy+ to convert the SymPy expressions for the Eact Wald initial data into a C-code kernel  \[Back to [top](#toc)\]
$$\label{initializenrpy}$$

We start by importing the core NRPy+ modules we need and setting commonly used parameters. Since we are writing an ETK thorn, we'll need to set `"grid::GridFuncMemAccess"` to `"ETK"`. SymPy expressions for plane wave initial data are written inside [GiRaFFEfood_HO.py](../edit/GiRaFFEfood_HO/GiRaFFEfood_HO.py), and we simply import them for use here._____no_output_____

# Step 1a: Import needed NRPy+ core modules:
import NRPy_param_funcs as par
import indexedexp as ixp
import grid as gri
import finite_difference as fin
from outputC import *
import loop

# Step 1b: This is an Einstein Toolkit (ETK) thorn. Here we
#          tell NRPy+ that gridfunction memory access will
#          therefore be in the "ETK" style.
par.set_parval_from_str("grid::GridFuncMemAccess","ETK")
#Set the spatial dimension parameter to 3.
par.set_parval_from_str("grid::DIM", 3)
DIM = par.parval_from_str("grid::DIM")

# Step 1c: Within the ETK, the 3D gridfunctions x, y, and z store the
#          Cartesian grid coordinates. Setting the gri.xx[] arrays
#          to point to these gridfunctions forces NRPy+ to treat
#          the Cartesian coordinate gridfunctions properly --
#          reading them from memory as needed.
x,y,z = gri.register_gridfunctions("AUX",["x","y","z"])
gri.xx[0] = x
gri.xx[1] = y
gri.xx[2] = z

import GiRaFFEfood_HO.GiRaFFEfood_HO_Exact_Wald as gfho
gfho.GiRaFFEfood_HO_Exact_Wald()

# Step 2: Create the C code output kernel.
# To best format this for the ETK, we'll need to register this gridfunction.
ValenciavU = ixp.register_gridfunctions_for_single_rank1("AUX","ValenciavU")
#BU = ixp.register_gridfunctions_for_single_rank1("AUX","BU")
GiRaFFEfood_A_v_to_print = [\
                            lhrh(lhs=gri.gfaccess("out_gfs","AD0"),rhs=gfho.AD[0]),\
                            lhrh(lhs=gri.gfaccess("out_gfs","AD1"),rhs=gfho.AD[1]),\
                            lhrh(lhs=gri.gfaccess("out_gfs","AD2"),rhs=gfho.AD[2]),\
                            lhrh(lhs=gri.gfaccess("out_gfs","ValenciavU0"),rhs=gfho.ValenciavU[0]),\
                            lhrh(lhs=gri.gfaccess("out_gfs","ValenciavU1"),rhs=gfho.ValenciavU[1]),\
                            lhrh(lhs=gri.gfaccess("out_gfs","ValenciavU2"),rhs=gfho.ValenciavU[2]),\
                            ]

GiRaFFEfood_A_v_CKernel = fin.FD_outputC("returnstring",GiRaFFEfood_A_v_to_print,params="outCverbose=False")

# Format the code within a C loop over cctkGH
GiRaFFEfood_A_v_looped = loop.loop(["i2","i1","i0"],["0","0","0"],["cctk_lsh[2]","cctk_lsh[1]","cctk_lsh[0]"],\
                                   ["1","1","1"],["#pragma omp parallel for","",""],"",\
                                   GiRaFFEfood_A_v_CKernel.replace("time","cctk_time"))
# Step 3: Create directories for the thorn if they don't exist.
!mkdir GiRaFFEfood_HO     2>/dev/null # 2>/dev/null: Don't throw an error if the directory already exists.
!mkdir GiRaFFEfood_HO/src 2>/dev/null # 2>/dev/null: Don't throw an error if the directory already exists.

# Step 4: Write the C code kernel to file.
with open("GiRaFFEfood_HO/src/GiRaFFEfood_A_v_ExactWald.h", "w") as file:
    file.write(str(GiRaFFEfood_A_v_looped))
_____no_output_____

## Step 1.a: Repeat for Aligned Rotator case \[Back to [top](#toc)\]
$$\label{aligned_rotator}$$

We'll repeat this process for the Aligned Rotator case.
_____no_output_____

gri.glb_gridfcs_list = []
x,y,z = gri.register_gridfunctions("AUX",["x","y","z"])
gri.xx[0] = x
gri.xx[1] = y
gri.xx[2] = z

import GiRaFFEfood_HO.GiRaFFEfood_HO_Aligned_Rotator as gfAR
gfAR.GiRaFFEfood_HO_Aligned_Rotator()

# Step 2: Create the C code output kernel.
# To best format this for the ETK, we'll need to register this gridfunction.
ValenciavU = ixp.register_gridfunctions_for_single_rank1("AUX","ValenciavU")
#BU = ixp.register_gridfunctions_for_single_rank1("AUX","BU")
GiRaFFEfood_A_v_to_print = [\
                            lhrh(lhs=gri.gfaccess("out_gfs","AD0"),rhs=gfAR.AD[0]),\
                            lhrh(lhs=gri.gfaccess("out_gfs","AD1"),rhs=gfAR.AD[1]),\
                            lhrh(lhs=gri.gfaccess("out_gfs","AD2"),rhs=gfAR.AD[2]),\
                            lhrh(lhs=gri.gfaccess("out_gfs","ValenciavU0"),rhs=gfAR.ValenciavU[0]),\
                            lhrh(lhs=gri.gfaccess("out_gfs","ValenciavU1"),rhs=gfAR.ValenciavU[1]),\
                            lhrh(lhs=gri.gfaccess("out_gfs","ValenciavU2"),rhs=gfAR.ValenciavU[2]),\
                            ]

GiRaFFEfood_A_v_CKernel = fin.FD_outputC("returnstring",GiRaFFEfood_A_v_to_print,params="outCverbose=False")

# Format the code within a C loop over cctkGH
#GiRaFFEfood_A_v_looped = loop.loop(["i2","i1","i0"],["0","0","0"],["cctk_lsh[2]","cctk_lsh[1]","cctk_lsh[0]"],\
#                                   ["1","1","1"],["#pragma omp parallel for","",""],"",\
#                                   GiRaFFEfood_A_v_CKernel.replace("time","cctk_time"))

# Step 4: Write the C code kernel to file.
with open("GiRaFFEfood_HO/src/GiRaFFEfood_A_v_AlignedRotator.h", "w") as file:
    file.write(str(GiRaFFEfood_A_v_CKernel))_____no_output_____

# Step 2: Interfacing with the Einstein Toolkit \[Back to [top](#toc)\]
$$\label{etk}$$_____no_output_____

## Step 2.a: Constructing the Einstein Toolkit C-code calling functions that include the C code kernels. \[Back to [top](#toc)\]
$$\label{einstein_c}$$

We will write another C file with the functions we need here._____no_output_____

%%writefile GiRaFFEfood_HO/src/InitialData_ExactWald.c
#include <math.h>
#include <stdio.h>

#include "cctk.h"
#include "cctk_Parameters.h"
#include "cctk_Arguments.h"
void GiRaFFE_set_A_v_EW(const cGH* restrict const cctkGH,const int *cctk_lsh,const int *cctk_nghostzones,
                     const CCTK_REAL *xGF,const CCTK_REAL *yGF,const CCTK_REAL *zGF,const CCTK_REAL *u4upperZeroGF,
                     const CCTK_REAL *gammaDD00GF,const CCTK_REAL *gammaDD01GF,const CCTK_REAL *gammaDD02GF,const CCTK_REAL *gammaDD11GF,const CCTK_REAL *gammaDD12GF,const CCTK_REAL *gammaDD22GF,
                     CCTK_REAL *AD0GF,CCTK_REAL *AD1GF,CCTK_REAL *AD2GF,
                     //CCTK_REAL *BU0GF,CCTK_REAL *BU1GF,CCTK_REAL *BU2GF,
                     CCTK_REAL *ValenciavU0GF,CCTK_REAL *ValenciavU1GF,CCTK_REAL *ValenciavU2GF) {

  DECLARE_CCTK_PARAMETERS;

#include "GiRaFFEfood_A_v_ExactWald.h"

}

void Write_to_HydroBase_EW(const cGH* restrict const cctkGH,const int *cctk_lsh,const int *cctk_nghostzones,
                        const CCTK_REAL *ValenciavU0,const CCTK_REAL *ValenciavU1,const CCTK_REAL *ValenciavU2,
                        const CCTK_REAL *AD0,const CCTK_REAL *AD1,const CCTK_REAL *AD2,
                        CCTK_REAL *vel, CCTK_REAL *Avec) {
  /* Bvec[i] <- BUi
   * Avec[i] <- ADi
   * vel[i]  <- ValenciavUi
   */
  DECLARE_CCTK_PARAMETERS;

#pragma omp parallel for
  for(int i2=0; i2<cctk_lsh[2]; i2++) {
      for(int i1=0; i1<cctk_lsh[1]; i1++) {
          for(int i0=0; i0<cctk_lsh[0]; i0++) {
              CCTK_INT idx3;
              CCTK_INT idx4[3];
              idx3 = CCTK_GFINDEX3D(cctkGH, i0,i1,i2);
              idx4[0] = CCTK_GFINDEX4D(cctkGH, i0,i1,i2,0);
              idx4[1] = CCTK_GFINDEX4D(cctkGH, i0,i1,i2,1);
              idx4[2] = CCTK_GFINDEX4D(cctkGH, i0,i1,i2,2);
              Avec[idx4[0]] = AD0[idx3];
              Avec[idx4[1]] = AD1[idx3];
              Avec[idx4[2]] = AD2[idx3];
              vel[idx4[0]] = ValenciavU0[idx3];
              vel[idx4[1]] = ValenciavU1[idx3];
              vel[idx4[2]] = ValenciavU2[idx3];
              /* Our Scalar potential is defined differently from Hydrobase's,
               * so we will not copy that data.
               */
          }
      }
  }
}

void GiRaFFE_HO_ExactWaldID(CCTK_ARGUMENTS)
{
  DECLARE_CCTK_ARGUMENTS;
  DECLARE_CCTK_PARAMETERS;

  GiRaFFE_set_A_v_EW(cctkGH,cctk_lsh,cctk_nghostzones,
                  x,y,z,u4upperZero,
                  gxx,gxy,gxz,gyy,gyz,gzz,
                  AD0,AD1,AD2,
                  //BU0_init,BU1_init,BU2_init,
                  ValenciavU0,ValenciavU1,ValenciavU2);

#pragma omp parallel for
  for(int i2=0; i2<cctk_lsh[2]; i2++) {
      for(int i1=0; i1<cctk_lsh[1]; i1++) {
          for(int i0=0; i0<cctk_lsh[0]; i0++) {
              // We also need to set psi6Phi.
              psi6Phi[CCTK_GFINDEX3D(cctkGH, i0,i1,i2)] = 0; // Phi is always set to zero in GiRaFFE ID thorns.
          }
      }
  }

  Write_to_HydroBase_EW(cctkGH,cctk_lsh,cctk_nghostzones,
                     ValenciavU0,ValenciavU1,ValenciavU2,
                     AD0,AD1,AD2,
                     vel,Avec);
}Writing GiRaFFEfood_HO/src/InitialData_ExactWald.c

### Step 2.a.i:  Similar file for Aligned Rotator data \[Back to [top](#toc)\]
$$\label{aligned_rotator_file}$$

We will also need another, similar file for aligned rotator data._____no_output_____

%%writefile GiRaFFEfood_HO/src/InitialData_AlignedRotator.c
#include <math.h>
#include <stdio.h>

#include "cctk.h"
#include "cctk_Parameters.h"
#include "cctk_Arguments.h"
void GiRaFFE_set_A_v_AR(const cGH* restrict const cctkGH,const int *cctk_lsh,const int *cctk_nghostzones,const CCTK_REAL *r,
                     const CCTK_REAL *xGF,const CCTK_REAL *yGF,const CCTK_REAL *zGF,const CCTK_REAL *u4upperZeroGF,
                     const CCTK_REAL *gammaDD00GF,const CCTK_REAL *gammaDD01GF,const CCTK_REAL *gammaDD02GF,const CCTK_REAL *gammaDD11GF,const CCTK_REAL *gammaDD12GF,const CCTK_REAL *gammaDD22GF,
                     CCTK_REAL *AD0GF,CCTK_REAL *AD1GF,CCTK_REAL *AD2GF,
                     //CCTK_REAL *BU0GF,CCTK_REAL *BU1GF,CCTK_REAL *BU2GF,
                     CCTK_REAL *ValenciavU0GF,CCTK_REAL *ValenciavU1GF,CCTK_REAL *ValenciavU2GF) {

  DECLARE_CCTK_PARAMETERS;
#pragma omp parallel for
    for(int i2=0; i2<cctk_lsh[2]; i2++) {
        for(int i1=0; i1<cctk_lsh[1]; i1++) {
            for(int i0=0; i0<cctk_lsh[0]; i0++) {
#include "GiRaFFEfood_A_v_AlignedRotator.h"
                if(r[CCTK_GFINDEX3D(cctkGH,i0,i1,i2)] > R_NS_aligned_rotator) {
                    ValenciavU0GF[CCTK_GFINDEX3D(cctkGH, i0, i1, i2)] = 0;
                    ValenciavU1GF[CCTK_GFINDEX3D(cctkGH, i0, i1, i2)] = 0;
                    ValenciavU2GF[CCTK_GFINDEX3D(cctkGH, i0, i1, i2)] = 0;
                }
            } // END LOOP: for(int i0=0; i0<cctk_lsh[0]; i0++)
        } // END LOOP: for(int i1=0; i1<cctk_lsh[1]; i1++)
    } // END LOOP: for(int i2=0; i2<cctk_lsh[2]; i2++)
}

void Write_to_HydroBase_AR(const cGH* restrict const cctkGH,const int *cctk_lsh,const int *cctk_nghostzones,
                        const CCTK_REAL *ValenciavU0,const CCTK_REAL *ValenciavU1,const CCTK_REAL *ValenciavU2,
                        const CCTK_REAL *AD0,const CCTK_REAL *AD1,const CCTK_REAL *AD2,
                        CCTK_REAL *vel, CCTK_REAL *Avec) {
  /* Bvec[i] <- BUi
   * Avec[i] <- ADi
   * vel[i]  <- ValenciavUi
   */
  DECLARE_CCTK_PARAMETERS;

#pragma omp parallel for
  for(int i2=0; i2<cctk_lsh[2]; i2++) {
      for(int i1=0; i1<cctk_lsh[1]; i1++) {
          for(int i0=0; i0<cctk_lsh[0]; i0++) {
              CCTK_INT idx3;
              CCTK_INT idx4[3];
              idx3 = CCTK_GFINDEX3D(cctkGH, i0,i1,i2);
              idx4[0] = CCTK_GFINDEX4D(cctkGH, i0,i1,i2,0);
              idx4[1] = CCTK_GFINDEX4D(cctkGH, i0,i1,i2,1);
              idx4[2] = CCTK_GFINDEX4D(cctkGH, i0,i1,i2,2);
              Avec[idx4[0]] = AD0[idx3];
              Avec[idx4[1]] = AD1[idx3];
              Avec[idx4[2]] = AD2[idx3];
              vel[idx4[0]] = ValenciavU0[idx3];
              vel[idx4[1]] = ValenciavU1[idx3];
              vel[idx4[2]] = ValenciavU2[idx3];
              /* Our Scalar potential is defined differently from Hydrobase's,
               * so we will not copy that data.
               */
          }
      }
  }
}

void GiRaFFE_HO_AlignedRotatorID(CCTK_ARGUMENTS)
{
  DECLARE_CCTK_ARGUMENTS;
  DECLARE_CCTK_PARAMETERS;

  GiRaFFE_set_A_v_AR(cctkGH,cctk_lsh,cctk_nghostzones,r,
                  x,y,z,u4upperZero,
                  gxx,gxy,gxz,gyy,gyz,gzz,
                  AD0,AD1,AD2,
                  //BU0_init,BU1_init,BU2_init,
                  ValenciavU0,ValenciavU1,ValenciavU2);

#pragma omp parallel for
  for(int i2=0; i2<cctk_lsh[2]; i2++) {
      for(int i1=0; i1<cctk_lsh[1]; i1++) {
          for(int i0=0; i0<cctk_lsh[0]; i0++) {
              // We also need to set psi6Phi.
              psi6Phi[CCTK_GFINDEX3D(cctkGH, i0,i1,i2)] = 0; // Phi is always set to zero in GiRaFFE ID thorns.
          }
      }
  }

  Write_to_HydroBase_AR(cctkGH,cctk_lsh,cctk_nghostzones,
                     ValenciavU0,ValenciavU1,ValenciavU2,
                     AD0,AD1,AD2,
                     vel,Avec);
}Writing GiRaFFEfood_HO/src/InitialData_AlignedRotator.c

## Step 2.b: Primitive-to-Conservative Solver \[Back to [top](#toc)\]
$$\label{primitive2conservative}$$

The initial data for this module is set up in several steps.
1. Run the shifted Kerr-Schild thorn to set up the four-metric we wish to use.
1. Set up the four-vector potential $A_\mu$ (which includes $\Phi$ and $A_i$) and the Valencia 3-velocity.
1. (See the [next tutorial]((Tutorial-GiRaFFE_Higher_Order.ipynb))) Run the A-to-B driver from $\texttt{GiRaFFE}$ to fill in the initial $B^i$ data.
1. Run the Primitive-to-Conservative solver to calculate $\tilde{S}_i$.

We will now write a Primitive-to-Conservative solver to calculate initial data for $\tilde{S}_i$. _____no_output_____

# Step 2: Create the C code output kernel.
gri.glb_gridfcs_list = []
x,y,z = gri.register_gridfunctions("AUX",["x","y","z"])
gri.xx[0] = x
gri.xx[1] = y
gri.xx[2] = z
gfho.GiRaFFEfood_HO_ID_converter()
# To best format this for the ETK, we'll need to register this gridfunction.
StildeD = ixp.register_gridfunctions_for_single_rank1("EVOL","StildeD")
GiRaFFE_S_to_print = [\
                      lhrh(lhs=gri.gfaccess("out_gfs","StildeD0"),rhs=gfho.StildeD[0]),\
                      lhrh(lhs=gri.gfaccess("out_gfs","StildeD1"),rhs=gfho.StildeD[1]),\
                      lhrh(lhs=gri.gfaccess("out_gfs","StildeD2"),rhs=gfho.StildeD[2]),\
                     ]

GiRaFFE_S_CKernel = fin.FD_outputC("returnstring",GiRaFFE_S_to_print,params="outCverbose=False")

# Format the code within a C loop over cctkGH
GiRaFFE_S_looped = loop.loop(["i2","i1","i0"],["0","0","0"],["cctk_lsh[2]","cctk_lsh[1]","cctk_lsh[0]"],\
                                   ["1","1","1"],["#pragma omp parallel for","",""],"",\
                                   GiRaFFE_S_CKernel.replace("time","cctk_time"))

# Step 4: Write the C code kernel to file.
with open("GiRaFFEfood_HO/src/GiRaFFEfood_HO_Stilde.h", "w") as file:
    file.write(str(GiRaFFE_S_looped))
_____no_output_____

### Step 2.b.i: C Code to use the Primitive-to-Conservative Solver \[Back to [top](#toc)\]
$$\label{ccode_p2c}$$

And now, we will write the C code to use this, which is based in large part upon `ID_converter_GiRaFFE` from the old $\texttt{GiRaFFE}$._____no_output_____

%%writefile GiRaFFEfood_HO/src/StildeD_from_initial_data.c
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <sys/time.h>
#include "cctk.h"
#include "cctk_Parameters.h"
#include "cctk_Arguments.h"
#include "GiRaFFE_headers.h"

void calc_StildeD(const cGH* restrict const cctkGH,const int *cctk_lsh,const int *cctk_nghostzones,
                  const CCTK_REAL *xGF,const CCTK_REAL *yGF,const CCTK_REAL *zGF,
                  const CCTK_REAL *alphaGF, const CCTK_REAL *betaU0GF, const CCTK_REAL *betaU1GF, const CCTK_REAL *betaU2GF,
                  const CCTK_REAL *gammaDD00GF,const CCTK_REAL *gammaDD01GF,const CCTK_REAL *gammaDD02GF,const CCTK_REAL *gammaDD11GF,const CCTK_REAL *gammaDD12GF,const CCTK_REAL *gammaDD22GF,
                  const CCTK_REAL *BU0GF,const CCTK_REAL *BU1GF,const CCTK_REAL *BU2GF,
                  const CCTK_REAL *ValenciavU0GF,const CCTK_REAL *ValenciavU1GF,const CCTK_REAL *ValenciavU2GF,
                  CCTK_REAL *StildeD0GF,CCTK_REAL *StildeD1GF,CCTK_REAL *StildeD2GF){
  DECLARE_CCTK_PARAMETERS;

#include "GiRaFFEfood_HO_Stilde.h"

}

void StildeD_from_initial_data(CCTK_ARGUMENTS){
  DECLARE_CCTK_PARAMETERS;
  DECLARE_CCTK_ARGUMENTS;

  calc_StildeD(cctkGH,cctk_lsh,cctk_nghostzones,
               x,y,z,
               alp,betax,betay,betaz,
               gxx,gxy,gxz,gyy,gyz,gzz,
               BU0,BU1,BU2,
               ValenciavU0,ValenciavU1,ValenciavU2,
               StildeD0,StildeD1,StildeD2);
}Writing GiRaFFEfood_HO/src/StildeD_from_initial_data.c

## Step 2.c: CCL files - Define how this module interacts and interfaces with the larger Einstein Toolkit infrastructure \[Back to [top](#toc)\]
$$\label{cclfiles}$$

Writing a module ("thorn") within the Einstein Toolkit requires that three "ccl" files be constructed, all in the root directory of the thorn:_____no_output_____

### Step 2.c.i: `interface.ccl` \[Back to [top](#toc)\]
$$\label{interface}$$

1. `interface.ccl`: defines the gridfunction groups needed, and provides keywords denoting what this thorn provides and what it should inherit from other thorns. Specifically, this file governs the interaction between this thorn and others; more information can be found in the [official Einstein Toolkit documentation](http://cactuscode.org/documentation/referencemanual/ReferenceManualch8.html#x12-260000C2.2).
With "implements", we give our thorn its unique name. By "inheriting" other thorns, we tell the Toolkit that we will rely on variables and functions that exist and are declared "public" within those thorns._____no_output_____

%%writefile GiRaFFEfood_HO/interface.ccl
implements: GiRaFFEfood_HO
inherits: admbase GiRaFFE_HO grid HydroBase ShiftedKerrSchild
Writing GiRaFFEfood_HO/interface.ccl

### Step 2.c.ii: `param.ccl` \[Back to [top](#toc)\]
$$\label{param}$$

2. `param.ccl`: specifies free parameters within the thorn, enabling them to be set at runtime. It is required to provide allowed ranges and default values for each parameter. More information on this file's syntax can be found in the [official Einstein Toolkit documentation](http://cactuscode.org/documentation/referencemanual/ReferenceManualch8.html#x12-265000C2.3)._____no_output_____

%%writefile GiRaFFEfood_HO/param.ccl
shares: grid

USES KEYWORD type

shares: GiRaFFE_HO
USES CCTK_REAL GAMMA_SPEED_LIMIT

shares: ShiftedKerrSchild
USES KEYWORD KerrSchild_radial_shift

restricted:
CCTK_KEYWORD initial_data "Type of initial data"
{
  "ExactWald"       :: "Exact Wald Electrovacuum Solution"
  "AlignedRotator"  :: "Aligned Rotator Solution"
} "ExactWald"

restricted:
CCTK_REAL M "Kerr-Schild BH mass. Probably should always set M=1."
{
  0.0:* :: "Must be positive"
} 1.0

REAL B_p_aligned_rotator "The magnitude of the poloidal magnetic field in the aligned rotator test." STEERABLE=ALWAYS
{
  *:* :: "any real"
} 1e-5

REAL Omega_aligned_rotator "The angular velocity for the aligned rotator solution test." STEERABLE=ALWAYS
{
  *:0) :: "any negative value"
  (0:* :: "any positive value"
} 1e3  # the default = an arbitrary but crazy value that can be set to any value other than zero, as R_NS_aligned_rotator/Omega_aligned_rotator cannot result in a division by zero.

REAL R_NS_aligned_rotator "The radius of the so-called neutron star (NS) set by hand for the aligned rotator solution test." STEERABLE=ALWAYS
{
  -1.  :: "disable the conservative-to-primitive solver modification"
  (0:* :: "any positive value"
}  -1.
Writing GiRaFFEfood_HO/param.ccl

### Step 2.c.iii: `schedule.ccl` \[Back to [top](#toc)\]
$$\label{schedule}$$

3. `schedule.ccl`: allocates storage for gridfunctions, defines how the thorn's functions should be scheduled in a broader simulation, and specifies the regions of memory written to or read from gridfunctions. `schedule.ccl`'s official documentation may be found [here](http://cactuscode.org/documentation/referencemanual/ReferenceManualch8.html#x12-268000C2.4).

We specify here the standardized ETK "scheduling bins" in which we want each of our thorn's functions to run._____no_output_____

%%writefile GiRaFFEfood_HO/schedule.ccl
STORAGE: GiRaFFE_HO::GiRaFFE_vars[3]
STORAGE: GiRaFFE_HO::GiRaFFE_Vs[1]

STORAGE: HydroBase::rho[1],HydroBase::press[1],HydroBase::eps[1],HydroBase::vel[1],HydroBase::Avec[1],HydroBase::Aphi[1]

schedule GROUP GiRaFFE_Initial IN CCTK_INITIAL after HydroBase_Initial before GiRaFFE_ID_Converter
{
} "Schedule GiRaFFE functions in HydroBase_Initial"

if (CCTK_Equals(initial_data,"ExactWald")) {
  schedule GiRaFFE_HO_ExactWaldID in GiRaFFE_Initial as GiRaFFE_Food
  {
    LANG: C
  #  READS: admbase::alp(Everywhere)
  #  READS: admbase::betax(Everywhere)
  #  READS: admbase::betay(Everywhere)
  #  READS: admbase::betaz(Everywhere)
  #  READS: admbase::gxx(Everywhere)
  #  READS: admbase::gxy(Everywhere)
    READS: admbase::gxz(Everywhere)
  #  READS: admbase::gyy(Everywhere)
    READS: admbase::gyz(Everywhere)
    READS: admbase::gzz(Everywhere)
    READS: grid::x(Everywhere)
    READS: grid::y(Everywhere)
    READS: grid::y(Everywhere)
    WRITES: GiRaFFE_HO::AD0(Everywhere)
    WRITES: GiRaFFE_HO::AD1(Everywhere)
    WRITES: GiRaFFE_HO::AD2(Everywhere)
    WRITES: GiRaFFE_HO::ValenciavU0(Everywhere)
    WRITES: GiRaFFE_HO::ValenciavU1(Everywhere)
    WRITES: GiRaFFE_HO::ValenciavU2(Everywhere)
  } "Initial data for GiRaFFE"
}

if (CCTK_Equals(initial_data,"AlignedRotator")) {
  schedule GiRaFFE_HO_AlignedRotatorID in GiRaFFE_Initial as GiRaFFE_Food
  {
    LANG: C
    READS: grid::x(Everywhere)
    READS: grid::y(Everywhere)
    READS: grid::y(Everywhere)
    WRITES: GiRaFFE_HO::AD0(Everywhere)
    WRITES: GiRaFFE_HO::AD1(Everywhere)
    WRITES: GiRaFFE_HO::AD2(Everywhere)
    WRITES: GiRaFFE_HO::ValenciavU0(Everywhere)
    WRITES: GiRaFFE_HO::ValenciavU1(Everywhere)
    WRITES: GiRaFFE_HO::ValenciavU2(Everywhere)
  } "Initial data for GiRaFFE"
}

schedule group GiRaFFE_ID_Converter at CCTK_INITIAL after HydroBase_Initial before Convert_to_HydroBase
{
} "Translate ET-generated, HydroBase-compatible initial data and convert into variables used by GiRaFFE"

schedule StildeD_from_initial_data IN GiRaFFE_ID_Converter as first_initialdata before TOV_Initial_Data
{
  LANG:    C
  OPTIONS: LOCAL
  # What the heck, let's synchronize everything!
  SYNC: GiRaFFE_HO::GiRaFFE_vars,GiRaFFE_HO::GiRaFFE_Bs,GiRaFFE_HO::GiRaFFE_Vs,ADMBase::metric,ADMBase::lapse,ADMBase::shift,ADMBase::curv
  READS: admbase::alp(Everywhere)
  READS: admbase::betax(Everywhere)
  READS: admbase::betay(Everywhere)
  READS: admbase::betaz(Everywhere)
  READS: admbase::gxx(Everywhere)
  READS: admbase::gxy(Everywhere)
  READS: admbase::gxz(Everywhere)
  READS: admbase::gyy(Everywhere)
  READS: admbase::gyz(Everywhere)
  READS: admbase::gzz(Everywhere)
  READS: GiRaFFE_HO::ValenciavU0(Everywhere)
  READS: GiRaFFE_HO::ValenciavU1(Everywhere)
  READS: GiRaFFE_HO::ValenciavU2(Everywhere)
  READS: GiRaFFE_HO::BU0(Everywhere)
  READS: GiRaFFE_HO::BU1(Everywhere)
  READS: GiRaFFE_HO::BU2(Everywhere)
  WRITES: GiRaFFE_HO::StildeD0(Interior)
  WRITES: GiRaFFE_HO::StildeD1(Interior)
  WRITES: GiRaFFE_HO::StildeD2(Interior)
} "Convert HydroBase initial data (ID) to ID that GiRaFFE can read."
Writing GiRaFFEfood_HO/schedule.ccl

## Step 2.d: Add the C file to Einstein Toolkit compilation list. \[Back to [top](#top)\]
$$\label{einstein_list}$$

We will also need `make.code.defn`, which indicates the list of files that need to be compiled._____no_output_____

%%writefile GiRaFFEfood_HO/src/make.code.defn
SRCS = InitialData_ExactWald.c InitialData_AlignedRotator.c \
       StildeD_from_initial_data.cWriting GiRaFFEfood_HO/src/make.code.defn

# Step 3: Current Initial Data Agreement
$$\label{data_agreement}$$

Quantity | 1-D Slice | Worst Case Agreement
--- | --- | ---
$\tilde{S}_x$ | x | 1.1
 | y | 1.1
 | z | 1.1
$\tilde{S}_y$ | x | 1.1
 | y | 1.1
 | z | 1.1
$\tilde{S}_z$ | x | 0.7
 | y | 0.7
 | z | 0.7
--- | --- | ---
$A_x$ | x | 0.8
 | y | 0.8
 | z | 0.8
$A_y$ | x | 0.8
 | y | 0.8
 | z | 0.8
$A_z$ | x | $\infty$
 | y | $\infty$
 | z | $\infty$
--- | --- | ---
$B^x$ | x | 0.7 (zeros)
 | y | 0.7 (zeros)
 | z | 0.7 (zeros)
$B^y$ | x | 0.8 (zeros)
 | y | 0.8 (zeros)
 | z | 0.8 (zeros)
$B^z$ | x | 1.2
 | y | 1.2
 | z | 1.2

#### Notes:
* Still significant issues to resolve
* $A_z$ is trivially $0$
* We don't compare velocities, because we use Valencia instead of drift velocity.
* $B^i$ still has some NaNs, possibly inherited from $A_i$ (Need to copy timelevels?)
* $A_i$ is about an order of magnitude smaller than in original $\texttt{GiRaFFE}$ in some directions.
     * Could disagreement here be related to neglecting the Kerr-Schild radial shift?
          * No, using the default $r_0 = 0$_____no_output_____

# Step 3: Output this notebook to $\LaTeX$-formatted PDF file \[Back to [top](#toc)\]
$$\label{latex_pdf_output}$$

The following code cell converts this Jupyter notebook into a proper, clickable $\LaTeX$-formatted PDF file. After the cell is successfully run, the generated PDF may be found in the root NRPy+ tutorial directory, with filename
[Tutorial-ETK_thorn-GiRaFFEfood_HO.pdf](Tutorial-ETK_thorn-GiRaFFEfood_HO.pdf) (Note that clicking on this link may not work; you may need to open the PDF file through another means.)_____no_output_____

!jupyter nbconvert --to latex --template latex_nrpy_style.tplx Tutorial-ETK_thorn-GiRaFFEfood_HO_Exact_Wald.ipynb
!pdflatex -interaction=batchmode Tutorial-ETK_thorn-GiRaFFEfood_HO_Exact_Wald.tex
!pdflatex -interaction=batchmode Tutorial-ETK_thorn-GiRaFFEfood_HO_Exact_Wald.tex
!pdflatex -interaction=batchmode Tutorial-ETK_thorn-GiRaFFEfood_HO_Exact_Wald.tex
!rm -f Tut*.out Tut*.aux Tut*.log[NbConvertApp] Converting notebook Tutorial-ETK_thorn-GiRaFFEfood_HO_Exact_Wald.ipynb to latex
[pandoc warning] Duplicate link reference `[comment]' "source" (line 8, column 1)
[NbConvertApp] Writing 76890 bytes to Tutorial-ETK_thorn-GiRaFFEfood_HO_Exact_Wald.tex
This is pdfTeX, Version 3.14159265-2.6-1.40.18 (TeX Live 2017/Debian) (preloaded format=pdflatex)
 restricted \write18 enabled.
entering extended mode
This is pdfTeX, Version 3.14159265-2.6-1.40.18 (TeX Live 2017/Debian) (preloaded format=pdflatex)
 restricted \write18 enabled.
entering extended mode
This is pdfTeX, Version 3.14159265-2.6-1.40.18 (TeX Live 2017/Debian) (preloaded format=pdflatex)
 restricted \write18 enabled.
entering extended mode


# Notebook from SkyrookieYu/info8010-deep-learning
Path: homeworks/homework2.ipynb

# INFO8010: Homework 2

Last week you learned how to program your first neural network starting from the very first principles of deep learning. If you managed to solve last week's assignment without any problems **congratulations!**, if that was not the case **don't worry**, here's a second assignment for you which you can use to get better at deep learning.

In this homework we will see some slighly more complicated deep learning concepts: we will start by taking a look at some of PyTorch's functionalities that are necessary for training deep networks efficiently. We will then train our first neural networks for tackling different image classification tasks, learn to build custom datasets and explore how to train a CNN.

The strucutre of the notebook is identical to the one of the previous assignment. Similarly to last time you will have to handle in the notebook with your solutions to the exercises. When you will encounter the following instruction  Your code comes below  you will again have to write some code yourself, while when you will see the instruction:  Your discussion comes here  you will just have to discuss the results you obtained.

Without further ado let's start by importing the libraries we will need throughout this assignment!_____no_output_____

import numpy as np
import torch, torchvision
import torch.nn as nn
from torchvision import datasets
import torchvision.transforms as transforms
import matplotlib.pyplot as plt
from PIL import Image_____no_output_____

## 1. Dataloaders

Today's first concept are PyTorch's dataloaders. As you have seen during the theoretical lectures, one of the main ingredients for successfully training deep learning models is data: **lots of data!**.

As you can easily imagine, it is computationally not suitable to load datasets of millions of images into the memory of your machine, furthermore these images do also come in a form that does not make it possible to exploit the tensor operations we have seen in the previous assignment.

To deal with these issues (and many more of them) we can use [dataloaders](https://pytorch.org/docs/stable/data.html), a data loading utility that allows us to deal with large datasets very efficiently. In what follows you are given your first example of dataloader which will use the popular [CIFAR10](https://www.cs.toronto.edu/~kriz/cifar.html) dataset. _____no_output_____

transform = transforms.Compose([transforms.ToTensor()])
trainset = datasets.CIFAR10(root="./data", train=True, download=True, transform=transform)
testset = datasets.CIFAR10(root="./data", train=False, download=True, transform=transform)_____no_output_____

Let's explain what we just did. Thanks to the PyTorch [Torchvision](https://pytorch.org/vision/stable/index.html) package we just downloaded the CIFAR10 dataset on our machine. The dataset was stored in the `./data` folder and comes in two different forms thanks to the use of the `train` flag: a version that can be used as training set, and a version that can be used as testing set. These two datasets are abstracted through Torchvision `Dataset` sub-classes, we will see later in what this `Dataset` class consists exactly. Torchvision also allows us to define a set of image transformations which we have defined at the beginning of this cell: in this case we would like to convert our images to tensors, see the [doc](https://pytorch.org/vision/stable/transforms.html#torchvision.transforms.ToTensor) for an exact description of this transformation.

Now that we have defined which dataset we would like to use, and the form in which we would like to have our images we can create our very first dataloader that will load, transform and return us mini-batches of 4 images at the time that we can later on use for training. The advantage of dataloaders is that they can perform pre-processing of the data in parallel, e.g. while a batch is being processed in another thread of by your GPU._____no_output_____

trainloader = torch.utils.data.DataLoader(trainset, batch_size=4, shuffle=True, num_workers=2)
testloader = torch.utils.data.DataLoader(testset, batch_size=4, shuffle=False, num_workers=2)_____no_output_____

Before training anything however, let's take a look at the images we just downloaded!_____no_output_____

classes = ('plane', 'car', 'bird', 'cat', 'deer',
           'dog', 'frog', 'horse', 'ship', 'truck')

def show_images(img):
    img = img
    npimg = img.numpy()
    plt.imshow(np.transpose(npimg, (1, 2, 0)))
    plt.show()

dataiter = iter(trainloader)
images, labels = dataiter.next()
show_images(torchvision.utils.make_grid(images))
print(' '.join('%5s' % classes[labels[j]] for j in range(4)))_____no_output_____

There we go: here's your first four images of the CIFAR10 dataset!_____no_output_____**Some additional data transformations**

The `transforms` module comes also in as very handy for performing other type of data transformations: here's an example which transforms the CIFAR10 images into gray scaled images._____no_output_____

transform = transforms.Compose([transforms.Grayscale(), transforms.ToTensor()])
gray_scaled_trainset = datasets.CIFAR10(root="./data", train=True, download=False, transform=transform)
gray_scaled_trainloader = torch.utils.data.DataLoader(gray_scaled_trainset, batch_size=4,
                                                      shuffle=True, num_workers=2)

dataiter = iter(gray_scaled_trainloader)
images, labels = dataiter.next()
show_images(torchvision.utils.make_grid(images))
print(' '.join('%5s' % classes[labels[j]] for j in range(4)))_____no_output_____

### Find the bug

Al remembered from the theoretical lectures that one way to make neural networks more robust to overfitting can be based on data augmentation. Therefore he programmed this code snippet for performing random horizontal flips on his training set. However he encountered the following bug:_____no_output_____

transform = transforms.Compose([transforms.ToTensor(), transforms.RandomHorizontalFlip()])
bugged_trainset = datasets.CIFAR10(root="./data", train=True, download=False, transform=transform)
bugged_trainloader = torch.utils.data.DataLoader(bugged_trainset, batch_size=4,
                                          shuffle=True, num_workers=2)

dataiter = iter(bugged_trainloader)
images, labels = dataiter.next()
show_images(torchvision.utils.make_grid(images))
print(' '.join('%5s' % classes[labels[j]] for j in range(4)))_____no_output_____

Fix his mistake and discuss what he did wrong: put
your code below together  with your explanation:_____no_output_____

# your code_____no_output_____

However these images are not completely ready for a neural network, in fact Al forgot to perform a **very important** pre-processing step which could lead to vanishing gradient problems!

What did Al not do? Fix his code snippet and candidate yourself as the next deep learning teaching assistant!

 Your code comes below _____no_output_____

transform = transforms.Compose() # write the sequence of appropriate transformations
trainset = datasets.CIFAR10(root="./data", train=True, download=True, transform=transform)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=4, shuffle=True, num_workers=2)_____no_output_____

### Running operations on a GPU
As you may know, one important aspect of deep learning is that large models can be trained efficiently on specialized hardwares such as Graphical Processing Units (GPUs) or Tensorial Processing Units (TPUs). Pytorch allows you to perform operations on GPUs very easily by simply transfering the concerned models and/or tensors to the GPU, see the following examples._____no_output_____First you need to check that you have access to a GPU._____no_output_____

print(torch.cuda.is_available())_____no_output_____

If you see `True`, everything is ready to run on the GPU and you can go to the next cell. Otherwise it means you do not have any GPU that is compatible with the torch version that is installed on your machine. We invite you to use [Google Colab](https://colab.research.google.com/) to do the rest of this homework. Do not forget to ask Colab for a GPU._____no_output_____Let's compare the speed of tensor operations on GPU and CPU. _____no_output_____

%%timeit
# On CPU:
A = torch.randn(1000, 100000)
B = torch.randn(100000, 1)
x = A @ B_____no_output_____%%timeit
# On GPU:
device = 'cuda:0'
for i in range(10):
    # We directly create random tensors on the GPU
    A = torch.randn((1000, 100000), device=device)
    B = torch.randn((100000, 1), device=device)
    x = A @ B_____no_output_____

Instead of directly creating a tensor on the GPU you may also transfer a model or a tensor on the GPU, for example we can transfer a simple MLP on the GPU and then back to the CPU as follows._____no_output_____

# Create MLP on CPU
mlp = nn.Sequential(nn.Linear(2, 200), nn.ReLU(),
                    nn.Linear(200, 200), nn.ReLU(),
                    nn.Linear(200, 200), nn.ReLU(),
                    nn.Linear(200, 200), nn.ReLU(),
                    nn.Linear(200, 200), nn.ReLU(),
                    nn.Linear(200, 1), nn.Sigmoid())_____no_output_____%%timeit
# Forward pass on CPU
y_pred = mlp(torch.randn(100, 2))_____no_output_____mlp = mlp.to('cuda:0')_____no_output_____%%timeit
# Forward pass on GPU, be careful the input data must be on the GPU as well
y_pred = mlp(torch.randn((100, 2), device='cuda:0'))_____no_output_____mlp = mlp.to('cpu')_____no_output_____%%timeit
# Forward pass back on CPU, be careful the input data must be on the CPU as well
y_pred = mlp(torch.randn((100, 2), device='cpu')_____no_output_____

As you may notice computations are more efficient on the GPU, however data transfer between GPU and CPU (and vice-versa) may be very slow, we therefore recommend you to reduce the transfer of data between GPU and CPU as much as possible. For example when you want to save your loss after each iteration, in order to avoid a memory leak you would prefer doing `.detach` (which will run on GPU without data transfer) instead of `.cpu()`, `.item()` or `.numpy()` (which would transfer data back to the CPU)._____no_output_____## 2.  Classifying the CIFAR10 dataset with an MLP

Now that you have all the necessary information about how to handle datasets, we are ready to properly train today's first deep learning model on the CIFAR10 dataset. Before we dive into it **do not underestimate** the importance of properly pre-processing the data before training neural networks. This step is as important as defining the neural architectures themeselves which however gets very often overlooked. Al Dente forgets to do it every year, and each year he complains about the bad grade he receives for the course.

In this exercise you will be provided with an already defined multi-layer perceptron that you can train to classify CIFAR10 images. The structure of the network is already defined, yet some crucial hyperparameters for training are missing. It is your job to fill them in and successfully train the network. As part of the exercise you are also required to monitor the evolution of training: this usually consists in keeping track of the accuracy that the model obtains on the training and testing sets, and in checking how the training and testing losses evolve while optimizing the network. Report these statistics with some plots. In addition, transfer the model and the data on GPU in order to fasten up the training.

 Fill in the code below  and  describe  why you chose some parameters over others. Also discuss your results, are you satisifed with the final accuracy of the model?_____no_output_____

input_dim =              # fill in with appropriate values
hidden_dim =
output_dim =
learning_rate =
num_epochs =

class net(nn.Module):
    def __init__(self, input_dim, hidden_dim, output_dim):
        super(net, self).__init__()
        self.input_dim = input_dim
        self.net = nn.Sequential(nn.Linear(input_dim, hidden_dim), nn.ReLU(),
                                 nn.Linear(hidden_dim, hidden_dim), nn.ReLU(),
                                 nn.Linear(hidden_dim, hidden_dim), nn.ReLU(),
                                 nn.Linear(hidden_dim, output_dim))

    def forward(self, x):
        return self.net(x.view(x.size(0), self.input_dim))

device = 'cuda:0'
model = net(input_dim, hidden_dim, output_dim).to(device)
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)
transform = transforms.Compose([transforms.Resize((32, 32)),
                                transforms.ToTensor(),
                                transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])

trainset = datasets.CIFAR10(root = "./data", train=True, download=True, transform=transform)
testset = datasets.CIFAR10(root = "./data", train=False, download=True, transform=transform)

trainloader = torch.utils.data.DataLoader(trainset, batch_size=100, shuffle=True, num_workers=2)
testloader = torch.utils.data.DataLoader(testset, batch_size=100, shuffle=True, num_workers=2)

def train(num_epochs):
    epochs_train_loss = []
    epochs_test_loss = []
    for i in range(num_epochs):
        if i % 1 == 0:
            with torch.no_grad():
                correct = 0
                total = 0
                for inputs, targets in testloader:
                    outputs = model(inputs.to(device))
                    loss = criterion(outputs, targets.to(device))
                    _, predicted = outputs.max(1)
                    total += targets.size(0)
                    correct += predicted.eq(targets.to(device)).sum().item()

            print('Accuracy of the model on the testing images: %d %%' % (100 * correct / total))
        tmp_loss = []
        for (x, y) in trainloader:
            outputs = model(x.to(device))
            loss = criterion(outputs, y.to(device))
            tmp_loss.append(loss.detach())
            optimizer.zero_grad()
            loss.backward()
            optimizer.step()

    return epochs_train_loss, epochs_test_loss_____no_output_____epochs_train_loss, epochs_test_loss = train(num_epochs)_____no_output_____

Plot the train and test loss here below:

 Your code comes below  _____no_output_____

# your code_____no_output_____

## 3.  Create a custom dataset!

Sometimes you would like to train a model on your own dataset, which will very likely not be part of the Torchvision package. To overcome this you can create a custom dataset class which will handle the data for you. This can be done by inheriting from the `torch.utils.data.TensorDataset` class.

In this exercise your goal is to program a custom dataset class which we will later use for training two different models. We will use the Kaggle Cats and Dogs Dataset which you can download from [here](https://www.microsoft.com/en-us/download/details.aspx?id=54765).

When programming a custom dataset class you have to start by defining the constructor, which will get as input the location of your dataset, whether the images that will be returned will serve for training or testing, and some other potential attributes. For this exercise we will be using 20000 images for training, while 5000 ones for testing.  For the `__getitem__` function you may find the `PIL.Image.open` useful, do not forget to return the item class as well ($0$ or $1$).

 Your code comes below:_____no_output_____

from PIL import Image
from numpy import asarray
import os

class CatAndDogsDataset(torch.utils.data.Dataset):
    def __init__(self, root_dir, train=True):
        """Initializes a dataset containing images and labels."""
        super().__init__()
        # Your code

    def __len__(self):
        """Returns the size of the dataset."""
        # Your code

    def __getitem__(self, index):
        """Returns the index-th data item of the dataset."""
        # Your code

transform = transforms.Compose([transforms.Resize((32, 32)),
                                transforms.ToTensor(),
                                transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])_____no_output_____

Let us have a quick look at these samples._____no_output_____

def show_images(img):
    img = img
    npimg = img.numpy() * .5 + .5
    plt.imshow(np.transpose(npimg, (1, 2, 0)))
    plt.show()

my_dataset = CatAndDogsDataset('kagglecatsanddogs_3367a/PetImages/', transform=transform) # training directory
my_loader = torch.utils.data.DataLoader(my_dataset, batch_size=4, shuffle=True, num_workers=0)

dataiter = iter(my_loader)
images, labels = dataiter.next()
show_images(torchvision.utils.make_grid(images))_____no_output_____

## 4. Classifying the Cats and Dogs dataset with a CNN!

As we have seen in class, classifying images with a multi-layer perceptron isn't really a good idea. Convolutional Neural Networks (CNN) are in fact a much better option for this task. It is now your job to create your custom CNN and train it on the Cats and Dogs Dataset.

Similarly to what you have done when classifying the CIFAR10 dataset you are again required to report and discuss the performance of your model.

 Your code comes below  together  with an explanation  about what has motivated you some of your design choices._____no_output_____

# your code_____no_output_____

## Feedback

Now that you are done with this final deep-learning assignment here are some final questions about the exercises you were required to solve:_____no_output_____How much time did you spend on this homework?_____no_output_____Do you feel confortable with what it means to define a neural network and train it?_____no_output_____Do you think you now have enough preliminary knowledge for successfully starting to work on your course final project?_____no_output_____If you had to go through the two homeworks again, is there something you would have liked to explore more or explained more into detail?_____no_output_____


# Notebook from jtcanty/Linear-Modelling-and-Network-Analysis-of-the-Human-Brain-Proteome
Path: Python/WGCNA - Frontal Cortex.ipynb

<code>
suppressMessages(library("limma"))
suppressMessages(library("ggplot2"))
suppressMessages(library("WGCNA"))
options(stringsAsFactors = FALSE)==========================================================================
*
*  Package WGCNA 1.64.1 loaded.
*
*    Important note: It appears that your system supports multi-threading,
*    but it is not enabled within WGCNA in R.
*    To allow multi-threading within WGCNA with all available cores, use
*
*          allowWGCNAThreads()
*
*    within R. Use disableWGCNAThreads() to disable threading if necessary.
*    Alternatively, set the following environment variable on your system:
*
*          ALLOW_WGCNA_THREADS=<number_of_processors>
*
*    for example
*
*          ALLOW_WGCNA_THREADS=8
*
*    To set the environment variable in linux bash shell, type
*
*           export ALLOW_WGCNA_THREADS=8
*
*     before running R. Other operating systems or shells will
*     have a similar command to achieve the same aim.
*
==========================================================================

load("../data/filtered_TMT_Summary_Data_FC.RData")
load("../data/Quantile_TMT_Summary_Data_FC.RData")_____no_output_____nCols <- ncol(Quantile_TMT_Summary_Data)
nGenes <- nrow(Quantile_TMT_Summary_Data)

# additive linear model
addBatchFactor <- c(2,2,1,1,3,3,4,4)
addDiseaseGroup <- factor(rep(addBatchFactor,5))
addDesign <- model.matrix(~addDiseaseGroup + 0)
colnames(addDesign) <- c("Ctl", "AD", "PD","ADPD")

addFit <- lmFit(Quantile_TMT_Summary_Data, addDesign)

contrast.matrix <- makeContrasts(AD-Ctl, PD-Ctl, ADPD-Ctl, levels=addDesign)
addFit2 <- contrasts.fit(addFit, contrast.matrix)
addFit2 <- eBayes(addFit2)
topGenesAdd <- topTable(addFit2, coef=1, adjust="BH")

# interaction linear model
Ctl <-factor(rep(c(0,0,1,1,0,0,0,0),5))
AD <- factor(rep(c(1,1,0,0,0,0,1,1),5))
PD <- factor(rep(c(0,0,0,0,1,1,1,1),5))
intDesign <- model.matrix(~Ctl + AD + PD + AD:PD + 0)
intDesign <- intDesign[,2:5]
colnames(intDesign) <- c("Ctl", "AD", "PD","ADPD")

intFit <- lmFit(Quantile_TMT_Summary_Data, intDesign)

contrast.matrix <- makeContrasts(AD-Ctl, PD-Ctl, ADPD-Ctl, levels=intDesign)
intFit2 <- contrasts.fit(intFit, contrast.matrix)
intFit2 <- eBayes(intFit2)
topGenesInt <- topTable(intFit2, coef=1, adjust="BH")_____no_output_____# Generate Log-fold changes
adAddGenes <- topTable(addFit2, coef=1, number=1000000, sort.by="logFC")
pdAddGenes <- topTable(addFit2, coef=2, number=1000000, sort.by="logFC")
adpdAddGenes <- topTable(addFit2, coef=3, number=1000000, sort.by="logFC")

adIntGenes <- topTable(intFit2, coef=1, number=1000000, sort.by="logFC")
pdIntGenes <- topTable(intFit2, coef=2, number=1000000, sort.by="logFC")
adpdIntGenes <- topTable(intFit2, coef=3, number=1000000, sort.by="logFC")

# Add compute expressed genes threshold (TRUE is differentially expressed)
adAddGenes$threshold = as.factor(abs(adAddGenes$logFC) > 0.25 & adAddGenes$P.Value < 0.05/nGenes)
pdAddGenes$threshold = as.factor(abs(pdAddGenes$logFC) > 0.25 & pdAddGenes$P.Value < 0.05/nGenes)
adpdAddGenes$threshold = as.factor(abs(adpdAddGenes$logFC) > 0.25 & adpdAddGenes$P.Value < 0.05/nGenes)

adIntGenes$threshold = as.factor(abs(adIntGenes$logFC) > 0.25 & adIntGenes$P.Value < 0.05/nGenes)
pdIntGenes$threshold = as.factor(abs(pdIntGenes$logFC) > 0.25 & pdIntGenes$P.Value < 0.05/nGenes)
adpdIntGenes$threshold = as.factor(abs(adpdIntGenes$logFC) > 0.25 & adpdIntGenes$P.Value < 0.05/nGenes)

# Generate volcano plots using the additive model.
g1 = ggplot(data=adAddGenes, aes(x=logFC, y=-log10(P.Value), colour=threshold)) +
  geom_point(alpha=0.4, size=1.75) +
  theme(legend.position = "none") +
  xlim(c(-2.5, 2.5)) + ylim(c(0, 15)) +
  xlab("log2 fold change") + ylab("-log10 p-value") + ggtitle('Additive model: Ctrl vs. AD')
g1

g2 = ggplot(data=pdAddGenes, aes(x=logFC, y=-log10(P.Value), colour=threshold)) +
  geom_point(alpha=0.4, size=1.75) +
  theme(legend.position = "none") +
  xlim(c(-2.5, 2.5)) + ylim(c(0, 15)) +
  xlab("log2 fold change") + ylab("-log10 p-value") + ggtitle('Additive model: Ctrl vs. PD')
g2

g3 = ggplot(data=adpdAddGenes, aes(x=logFC, y=-log10(P.Value), colour=threshold)) +
  geom_point(alpha=0.4, size=1.75) +
  theme(legend.position = "none") +
  xlim(c(-2.5, 2.5)) + ylim(c(0, 15)) +
  xlab("log2 fold change") + ylab("-log10 p-value") + ggtitle('Additive model: Ctrl vs. ADPD')
g3

# Generate volcano plots using the interaction model.
g4 = ggplot(data=adIntGenes, aes(x=logFC, y=-log10(P.Value), colour=threshold)) +
  geom_point(alpha=0.4, size=1.75) +
  theme(legend.position = "none") +
  xlim(c(-2.5, 2.5)) + ylim(c(0, 15)) +
  xlab("log2 fold change") + ylab("-log10 p-value") + ggtitle('Interaction model: Ctrl vs. AD')
g4

g5 = ggplot(data=pdIntGenes, aes(x=logFC, y=-log10(P.Value), colour=threshold)) +
  geom_point(alpha=0.4, size=1.75) +
  theme(legend.position = "none") +
  xlim(c(-2.5, 2.5)) + ylim(c(0, 15)) +
  xlab("log2 fold change") + ylab("-log10 p-value") + ggtitle('Interaction model: Ctrl vs. PD')
g5

g6 = ggplot(data=adpdIntGenes, aes(x=logFC, y=-log10(P.Value), colour=threshold)) +
  geom_point(alpha=0.4, size=1.75) +
  theme(legend.position = "none") +
  xlim(c(-2.5, 2.5)) + ylim(c(0, 15)) +
  xlab("log2 fold change") + ylab("-log10 p-value") + ggtitle('Interaction model: Ctrl vs. ADPD')
g6_____no_output_____# Might have to do manual construction of blocks to get the maximally connected genes

get_max_conn_genes <- function(data, data_mat, ad_ctl_len, pd_ctl_len, adpd_ctl_len, softPower, min_mod_size){
  softPower = softPower;
  adjacency = adjacency(data, power = softPower);

  # Turn adjacency into topological overlap
  TOM = TOMsimilarity(adjacency);
  dissTOM = 1-TOM

  # Call the hierarchical clustering function
  geneTree = hclust(as.dist(dissTOM), method = "average");
  # Plot the resulting clustering tree (dendrogram)
  sizeGrWindow(12,9)
  plot(geneTree, xlab="", sub="", main = "Gene clustering on TOM-based dissimilarity",
  labels = FALSE, hang = 0.04);

  minModuleSize = 1;
  # Module identification using dynamic tree cut:
  dynamicMods = cutreeDynamic(dendro = geneTree, distM = dissTOM,
  deepSplit = 2, pamRespectsDendro = FALSE,
  minClusterSize = min_mod_size);
  table(dynamicMods)

  # Convert numeric lables into colors
  dynamicColors = labels2colors(dynamicMods)
  table(dynamicColors)
  # Plot the dendrogram and colors underneath
  sizeGrWindow(8,6)
  plotDendroAndColors(geneTree, dynamicColors, "Dynamic Tree Cut",
  dendroLabels = FALSE, hang = 0.03,
  addGuide = TRUE, guideHang = 0.05,
  main = "Gene dendrogram and module colors")

  "
  # Maximally connected gene, let's call it mc_gene
  # For each cluster, find elements in that cluster, sum up the corresponding row from the     adjacency matrix.
  mc_genes = list()
  for(i in c(1:max(dynamicMods))){
    all_row_indices = which(dynamicMods == i)
    max_val = -1
    max_idx = -1
    for(id in all_row_indices){
      curr_sum = sum(adjacency[id,all_row_indices])
      if(curr_sum>max_val){
        max_val = curr_sum
        max_idx = id
      }
    }
    mc_genes[[i]] <- rownames(data_mat[max_idx,])
  }
  "

  # Calculate eigengenes
  MEList = moduleEigengenes(data, colors = dynamicColors)
  MEs = MEList$eigengenes
  # Calculate dissimilarity of module eigengenes
  MEDiss = 1-cor(MEs);
  # Cluster module eigengenes
  METree = hclust(as.dist(MEDiss), method = "average");
  # Plot the result
  sizeGrWindow(7, 6)
  plot(METree, main = "Clustering of module eigengenes",
  xlab = "", sub = "")

  MEDissThres = 0.2
  # Plot the cut line into the dendrogram
  abline(h=MEDissThres, col = "red")
  # Call an automatic merging function
  merge = mergeCloseModules(data, dynamicColors, cutHeight = MEDissThres, verbose = 3)
  # The merged module colors
  mergedColors = merge$colors;
  # Eigengenes of the new merged modules:
  mergedMEs = merge$newMEs;

  sizeGrWindow(12, 9)
  #pdf(file = "Plots/geneDendro-3.pdf", wi = 9, he = 6)
  adpd_colors = c(rep("blue",ad_ctl_len), rep("green", pd_ctl_length), rep("red",adpd_ctl_length))
  plotDendroAndColors(geneTree, cbind(dynamicColors, mergedColors, adpd_colors),
  c("Dynamic Tree Cut", "Merged dynamic", "R:AD, B:ADPD, G:PD"),
  dendroLabels = FALSE, hang = 0.03,
  addGuide = TRUE, guideHang = 0.05)

  # Rename to moduleColors
  moduleColors = mergedColors
  # Construct numerical labels corresponding to the colors
  colorOrder = c("grey", standardColors(50));
  moduleLabels = match(moduleColors, colorOrder)-1;
  MEs = mergedMEs;
  return(moduleLabels)
}_____no_output_____# Get all differentially expressed genes into a dataframe

ad_ctl <-  as.data.frame((Quantile_TMT_Summary_Data[as.integer(rownames(adAddGenes[which(adAddGenes$threshold == TRUE),])), c(1,2,7:nCols)]))
rownames(ad_ctl) <- ad_ctl[,1]
ad_ctl_length = length(rownames(ad_ctl))
ad_ctl[,1] <- NULL

pd_ctl <-  as.data.frame((Quantile_TMT_Summary_Data[as.integer(rownames(pdAddGenes[which(pdAddGenes$threshold == TRUE),])), c(1,2,7:nCols)]))
rownames(pd_ctl) <- pd_ctl[,1]
pd_ctl_length = length(rownames(pd_ctl))
pd_ctl[,1] <- NULL

adpd_ctl <-  as.data.frame((Quantile_TMT_Summary_Data[as.integer(rownames(adpdAddGenes[which(adpdAddGenes$threshold == TRUE),])), c(1,2,7:nCols)]))
rownames(adpd_ctl) <- adpd_ctl[,1]
adpd_ctl_length = length(rownames(adpd_ctl))
adpd_ctl[,1] <- NULL

all_diff <- rbind(ad_ctl,pd_ctl,adpd_ctl)_____no_output_____all_diff_data <- all_diff[,-1]
all_diff_data <- t(all_diff_data)

gsg = goodSamplesGenes(all_diff_data, verbose = 3);
gsg$allOK

# Choose a set of soft-thresholding powers
powers = c(c(1:10), seq(from = 12, to=20, by=2))
# Call the network topology analysis function
sft = pickSoftThreshold(all_diff_data, powerVector = powers, verbose = 5)
# Plot the results:
sizeGrWindow(9, 5)
par(mfrow = c(1,2));
cex1 = 0.9;
# Scale-free topology fit index as a function of the soft-thresholding power
plot(sft$fitIndices[,1], -sign(sft$fitIndices[,3])*sft$fitIndices[,2],
xlab="Soft Threshold (power)",ylab="Scale Free Topology Model Fit,signed R^2",type="n",
main = paste("Scale independence"));
text(sft$fitIndices[,1], -sign(sft$fitIndices[,3])*sft$fitIndices[,2],
labels=powers,cex=cex1,col="red");
# this line corresponds to using an R^2 cut-off of h
abline(h=0.70,col="red")
# Mean connectivity as a function of the soft-thresholding power
plot(sft$fitIndices[,1], sft$fitIndices[,5],
xlab="Soft Threshold (power)",ylab="Mean Connectivity", type="n",
main = paste("Mean connectivity"))
text(sft$fitIndices[,1], sft$fitIndices[,5], labels=powers, cex=cex1,col="red") Flagging genes and samples with too many missing values...
  ..step 1
all_diff_____no_output_____module_labels = get_max_conn_genes(all_diff_data, all_diff, ad_ctl_length, pd_ctl_length, adpd_ctl_length, 18, 2)
# For each cluster
for(i in unique(module_labels)){
  if(i!=0){
    all_row_indices = which(colors_merge == i)

    from_ad = sum(all_row_indices<=27)
    from_adpd = length(all_row_indices) - from_ad
    print(c(from_ad, from_adpd))
}
}..connectivity..
..matrix multiplication (system BLAS)..
..normalization..
..done.
 ..cutHeight not given, setting it to 0.99  ===>  99% of the (truncated) height range in dendro.
 ..done.
 mergeCloseModules: Merging modules whose distance is less than 0.2
   multiSetMEs: Calculating module MEs.
     Working on set 1 ...
     moduleEigengenes: Calculating 11 module eigengenes in given set.
   multiSetMEs: Calculating module MEs.
     Working on set 1 ...
     moduleEigengenes: Calculating 7 module eigengenes in given set.
   multiSetMEs: Calculating module MEs.
     Working on set 1 ...
     moduleEigengenes: Calculating 6 module eigengenes in given set.
   multiSetMEs: Calculating module MEs.
     Working on set 1 ...
     moduleEigengenes: Calculating 4 module eigengenes in given set.
   Calculating new MEs...
   multiSetMEs: Calculating module MEs.
     Working on set 1 ...
     moduleEigengenes: Calculating 4 module eigengenes in given set.

</code>


# Notebook from Vega95/Deep-Learning-with-fastai-Cookbook
Path: ch4/text_model_training.ipynb

# Training a language model with fastai
- train a language model with curated dataset IMDB using pre-trained model AWD_LSTM
_____no_output_____

#hide
!pip install -Uqq fastbook
import fastbook
fastbook.setup_book()_____no_output_____#hide
from fastbook import *
from fastai.text.all import *_____no_output_____# define timestamp string for saving models
modifier = "aug13_2021"_____no_output_____

# Training a language model
- take a pretrained model and train it some more using the IMDB dataset_____no_output_____

%%time
# create path object
path = untar_data(URLs.IMDB)
path.ls()_____no_output_____%%time
# create TextDataLoaders object
get_imdb = partial(get_text_files, folders=['train', 'test', 'unsup'])
dls = TextDataLoaders.from_folder(path, valid = 'test', is_lm=True, bs=16)_____no_output_____dls.show_batch(max_n=2)_____no_output_____%%time
# define and train model
learn = language_model_learner(dls,AWD_LSTM,metrics=accuracy).to_fp16()
learn.fine_tune(1, 1e-2)_____no_output_____# get prediction
# preds = learn.predict('The star is', n_words=20)
learn.predict("what comes next", n_words=20)_____no_output_____keep_path = learn.path_____no_output_____# workaround to make path writeable
learn.path = Path('/notebooks/temp')_____no_output_____learn.path_____no_output_____learn.model_dir_____no_output_____learn.save('lm_'+modifier)_____no_output_____# workaround to save encoder - need to do this to later load encoder for classifier
learn.save_encoder('ft_'+modifier)_____no_output_____learn.export('lm_model_'+modifier+'.pkl')_____no_output_____learn.summary()_____no_output_____learn.model_____no_output_____


# Notebook from Potla1995/Blueqat-tutorials
Path: tutorial/318_portfolio.ipynb

#Portfolio Optimization
Portfolio optimization is a simple optimization problem using the existing financial data. We find out the best combination of assets by calculating the return and risk.
_____no_output_____##Cost Function
To solve portfolio optimization we have the cost function like below.

The 1st term shows the return of the assets and the 2nd as risk we estimate. The 3rd term is the constaint for the number of assets how many assets we select.

##Example
Now we have an example. Choose 2 assets from 6 possibility.
Here we use dummy data on return and risk.

As a return data we use,

As a risk data we use,

First we prepare blueqat and numpy_____no_output_____

!pip install -U blueqat_____no_output_____import blueqat.wq as wq
import numpy as np
a = wq.Opt()_____no_output_____

First let's think about return. Put return data into the diagonal of the matrix._____no_output_____

ret = wq.diag([-0.026,-0.031,-0.007,-0.022,-0.010,-0.055])
print(ret)[[-0.026  0.     0.     0.     0.     0.   ]
 [ 0.    -0.031  0.     0.     0.     0.   ]
 [ 0.     0.    -0.007  0.     0.     0.   ]
 [ 0.     0.     0.    -0.022  0.     0.   ]
 [ 0.     0.     0.     0.    -0.01   0.   ]
 [ 0.     0.     0.     0.     0.    -0.055]]

Next we think about risk._____no_output_____

ris = [[0,0.0015,0.0012,0.0018,0.0022,0.0012],[0,0,0.0017,0.0022,0.0005,0.0019],[0,0,0,0.0040,0.0032,0.0024],[0,0,0,0,0.0012,0.0076],[0,0,0,0,0,0.0021],[0,0,0,0,0,0]]_____no_output_____

By adding these 2 matrix, we get the final QUBO to calculate._____no_output_____

a.qubo = ret + np.asarray(ris)*0.5
print(a.qubo)[[-0.026    0.00075  0.0006   0.0009   0.0011   0.0006 ]
 [ 0.      -0.031    0.00085  0.0011   0.00025  0.00095]
 [ 0.       0.      -0.007    0.002    0.0016   0.0012 ]
 [ 0.       0.       0.      -0.022    0.0006   0.0038 ]
 [ 0.       0.       0.       0.      -0.01     0.00105]
 [ 0.       0.       0.       0.       0.      -0.055  ]]

#Constraint for K assets.
Finally we need one more term to make a constraint on the number of assets we choose.
Here we try a simple function which blueqat provide, sel() function to choose 2 from 6._____no_output_____

a.qubo += wq.sel(6,2)
print(a.qubo)[[-3.026    2.00075  2.0006   2.0009   2.0011   2.0006 ]
 [ 0.      -3.031    2.00085  2.0011   2.00025  2.00095]
 [ 0.       0.      -3.007    2.002    2.0016   2.0012 ]
 [ 0.       0.       0.      -3.022    2.0006   2.0038 ]
 [ 0.       0.       0.       0.      -3.01     2.00105]
 [ 0.       0.       0.       0.       0.      -3.055  ]]
print(a.sa())
print(a.E[-1])1.5345256328582764
[0, 0, 1, 0, 0, 1]
-4.060799999999999

Finally we get result just 2 assets have value 1 and others has 0.
The assets which has value 1 were selected.

We can also check the time evolution of cost function._____no_output_____

a.plot()_____no_output__________no_output_____