# Notebook from yupinghuang/LSSTC-DSFP-Sessions
Path: Session10/Day2/PGMs/generative_models.ipynb

This notebook is adapted from a lesson from the 2019 [KIPAC/StatisticalMethods course](https://github.com/KIPAC/StatisticalMethods), (c) 2019 Adam Mantz and Phil Marshall, licensed under the [GPLv2](LICENSE)._____no_output_____# Generative Models and Probabilistic Graphical Models

Goals:
* Introduce generative models in the context of mocking data and inference
* Introduce probabilistic graphical models as a tool for model visualization
* Practice drawing some PGMs_____no_output_____You've already seen Bayes Theorem and the role of the prior, sampling and posterior distributions._____no_output_____Fully specifying these requires us to write down (or at least approximate) _how the data set comes to exist,_ e.g.
* physical processes happening out there in the Universe
* instrumental effects and the measurement process
* any computations done prior to calling the result a "data set"_____no_output_____A **generative model** is formally the joint distribution of all our data and model parameters,

$P(\mathrm{data},\mathrm{params}) = P(\mathrm{params}) P(\mathrm{data}|\mathrm{params})$_____no_output_____It encodes the modelling information needed for both inference and creating mock data._____no_output_____What are generative models useful for?

* Performing inference: constructing the *sampling distribution* or *likelihood function*_____no_output_____* Testing inference: does our analysis, run on mock data, recover the input model?_____no_output_____* Checking inferences: do mock data generated from a fitted model resemble the real data?_____no_output_____$P(\mathrm{data},\mathrm{params})$ is an abstract beast, but usually it factorizes in helpful ways._____no_output_____This is where **probabilistic graphical models** (PGMs) come in. A PGM is a sketch that encodes the conditional dependences within a generative model._____no_output_____PGMs are to inference what free-body diagrams are to kinematics. Everyone hates having to draw them, yet everyone makes fewer mistakes when they do._____no_output_____Let's do a simple example, first specifying a problem in words and then building the PGM._____no_output_____Here's an image (and a zoom-in):

_____no_output_____Our measurement is the number of counts in each pixel. Here is a generative model in words:_____no_output_____* There's an object emitting light, whose properties are parametrized by $\theta$._____no_output_____* From $\theta$, we can determine the average flux falling on a given pixel $k$, $F_k$._____no_output_____* Given the exposure time of our observation, $T$ (and some conversion factors), $F_k$ determines the average number of counts expected, $\mu_k$._____no_output_____* The number of counts measured, $N_k$, is a Poisson draw, given the average $\mu_k$._____no_output_____Notice that the model was described in terms of conditional relationships.
* $\theta \sim$ some prior

For every pixel, $k$
* $F_k \Leftarrow \theta,k$
* $\mu_k \Leftarrow F_k,T$
* $N_k \sim \mathrm{Poisson}(\mu_k)$

NB: $\Leftarrow$ indicates a deterministic dependence, $\sim$ means "is distributed as"._____no_output_____The PGM shows most of the same information, visually:
_____no_output_____Ingredients of a PGM (sometimes also called a *directed acyclic graph*):
* **Nodes** (dots and circles) represent PDFs for parameters
* **Edges** (arrows) represent conditional relationships
* **Plates** (rectangles) represent repeated model components whose contents are conditionally independent_____no_output_____Types of nodes:
* **Circles** represent a PDF. This parameter is a *stochastic* ($\sim$) function of the parameters feeding into it.
* **Points** represent a delta-function PDF. This parameter is a *deterministic* ($\Leftarrow$) function of the parameters feeding into it.
* **Double circles** (or shading) indicate measured data. They are stochastic ($\sim$) in the context of generating mock data, but fixed in the context of parameter inference._____no_output_____Q: According to this PGM, how can we factorize $p(\theta,T,\{F_k, \mu_k, N_k\})$?

_____no_output_____What does all this imply in the context of
1. parameter inference?
2. mock data generation?_____no_output_____The key here is that the PGM shows _conditional dependences_ - therefore, it also shows (by omission) where parameters are _conditionally independent_.

That feature, plus the directional aspect, mean that the PGM is a map to the most logical sequence of steps (lines in code) for either generating mock data or evaluating the posterior density of real data._____no_output_____Q: How are these PGMs different, and what does the difference mean?

_____no_output_____In this case, some PDFs are delta functions, so we can straightforwardly marginalize over such _deterministic_ variables:

$p(\theta,\{N_k\}) = $

$\quad \int dF_k\,d\mu_k\,dT\; p(\theta)p(T) \prod_k P(N_k|\mu_k)p(\mu_k|F_k,T)p(F_k|\theta)$_____no_output_____$= \underbrace{p(\theta)} ~ \underbrace{\prod_k P\left(N_k|\mu_k(\theta,T)\right)}$_____no_output_____$= \mathrm{prior}(\theta) ~\times~ (\mathrm{sampling~distribution~of~}\vec{N})$_____no_output_____We *could* have written/drawn everything without explicitly mentioning $F_k$ (or even $\mu_k$). Like all simplifications, this is sometimes helpful and sometimes a pitfall._____no_output_____### Exercise

* On your own, write down the probability expressions (factorization of the generative model) illustrated by these two graphs.
* Then, discuss their meaning with your neighbor, and prepare to report back to the class._____no_output_____### Take-home messages

* Both simulation of mock data and inference from data require a model for how the Universe (or our computer) generates data.
* PGMs are a helpful way of visualizing the conditional dependences of a model (how the probability expressions factorize).

Note: the `daft` Python package can be useful for making PGMs programatically, though it's no substitute for paper._____no_output_____### Exercise: linear regression

Your data is a list of $\{x_k,y_k,\sigma_k\}$ triplets, where $\sigma_k$ is some estimate of the "error" on $y_k$. You think a linear model, $y(x)=a+bx$, might explain these data. To start exploring this idea, you decide to generate some simulated data, to compare with your real dataset.

In the absence of any better information, assume that $\vec{x}$ and $\vec{\sigma}$ are (somehow) known precisely, and that the "error" on $y_k$ is Gaussian (mean of $a+bx_k$ and standard deviation $\sigma_k$)._____no_output_____1. Draw the PGM, and write down the corresponding probability expressions, for this problem.

2. What (unspecified) assumptions, if any, would you have to make to actually generate data? Which assumptions do you think are unlikely to hold in practice? Choose one (or more) of these assumptions and work out how to generalize the PGM/generative model to avoid making it._____no_output_____## Bonus numerical exercise:

Extending the linear regression exercise, simulate a few data sets, given some values (your choice) for the input parameters. The commented code below is a (crummy) starting point._____no_output_____

'''
import numpy as np
import scipy.stats as st
import matplotlib
matplotlib.use('TkAgg')
import matplotlib.pyplot as plt
plt.rcParams['xtick.labelsize'] = 'x-large'
plt.rcParams['ytick.labelsize'] = 'x-large'
%matplotlib inline
''';_____no_output_____"""
# Choose some linear model parameters, somehow
a =
b =

# Choose some x and sigma values... somehow
n = 10 # Number of data points. Feel free to change.
x = np.array([
sigma = np.array([

# Work out the values for any intermediate nodes in your PGM

# generate the "observed" y values
y = st.norm.rvs(
""";_____no_output_____"""
# plot x, y and sigma in the usual way
plt.rcParams['figure.figsize'] = (12.0, 5.0)
plt.errorbar(x, y, yerr=sigma, fmt='none');
plt.plt(x, y, 'bo');
plt.xlabel('x', fontsize=14);
plt.ylabel('y', fontsize=14);
""";_____no_output_____

## Bonus exercise:  Exoplanet transit photometry

You've taken several images of a particular field, in order to record the transit of an exoplanet in front of a star (resulting in a temporary decrease in its brightness). Some kind of model, parametrized by $\theta$, describes the time series of the resulting flux. Before we get to measure a number of counts, however, each image is affected by time-specific variables, e.g. related to changing weather. To account for these, you've also measured a second star in the same field in every exposure. The assumption is that the average intrinsic flux of this second star is constant in time, so that it can be used to correct for photometric variations, putting the multiple measurements of the target star on the same scale.

Draw a PGM and write down the corresponding probability expressions for this problem._____no_output_____Thanks to Anja von der Linden for inspiring (and then correcting) the above problem._____no_output_____Note: Sketchy solutions for the PGM-drawing exercises can be found with the corresponding material from DSFP Session 4. _____no_output_____


# Notebook from idekerlab/cyrest-examples
Path: notebooks/Realistic workflow/The workflow of Anne/The R workflow of Anne v2.ipynb

# Basic Workflow for R
---------------------------------------

This jupyter-notebook contains basic R workflow for network visualizatin.

You can understand the basic workflow by reading this, but the RCy3's documentation is really useful and you can know much more about this.

The documentation : https://www.bioconductor.org/packages/release/bioc/html/RCy3.html_____no_output_____# Setup
---------------------------------------
To execute this, please sutisfy following items.

- Java SE 8
- Cytoscape version 3.3+
- CyREST
- Use docker file to do this. The docker file provide you the environment below.
    - RCy3
    - igraph
    - ..._____no_output_____# Start a new session
---------------------------------------
To start a new session, you should do the following step.

### Steps
1. Please execute cytoscape
1. Import libraries
1. Load Data
1. Shaping data

# IMPORTANT NOTE

If you are running this notebook from a Docker container, you need to specify ***IP address of your machine running Cytoscape***.  Usually, it is the IP address of your laptop/workstation.  Please edit the following line before you run this notebook!

```
ip_address <- '192.168.1.1'
```_____no_output_____

# !!!!!!!!!!!! EDIT  THIS VALUE FIRST!!!!!!!!!!!!!!
ip_address <- 'IP_OF_YOUR_MACHINE'
ip_address <- '137.110.137.158'

library(RCy3)
library(igraph)
library(RJSONIO)
library(httr)
library(data.table)

# Test Connection to Cytoscape
conn <- CytoscapeConnection(host = ip_address)
print(conn)

# Start from scratch: Delete all networks
deleteAllWindows(conn)_____no_output_____

## Import network table from Data Frame object

In this example, we'll use [HumanNet v1]() data sets as the base network.  Also, for convenience, we'll use [data.table](https://github.com/Rdatatable/data.table/wiki) library to import data._____no_output_____

# Read as an igraph object
humannet.url <- "http://www.functionalnet.org/humannet/HumanNet.v1.benchmark.txt"
humannet.ig <- read.graph(humannet.url)
summary(humannet.ig)_____no_output_____cw <- CytoscapeWindow('vignette', host = ip_address , graph=g, overwrite=TRUE)_____no_output_____# Load Data
gal.table <- read.table('sampleData/galFiltered.sif',stringsAsFactors=FALSE)
head(gal.table)_____no_output_____# create graph class
g <- new ('graphNEL', edgemode='directed')

# Get NodesVec
gal.table.nodevec <- unique(c(gal.table[[1]], gal.table[[3]]))

# add nodes to graph
for(node in gal.table.nodevec){
    g <- graph::addNode(node, g)
}

# get EdgeList
gal.table.fromvec = gal.table[[1]]
gal.table.tovec = gal.table[[3]]

for (index in 1:length(gal.table.fromvec)){
    g <- graph::addEdge (gal.table.fromvec[[index]] ,gal.table.tovec[[index]], g)
}_____no_output_____# show it in cytescape
cw <- CytoscapeWindow('vignette', host = ip_address , graph=g, overwrite=TRUE)
displayGraph (cw)
layoutNetwork (cw, layout.name='degree-circle')_____no_output_____

### Option 2: Load from list of URLs

Instead of passing actual graph data, you can pass list of file locations to load files into Cytoscape.

#### Note:
This feature is not implemented in RCy3, so you need to use the following code snippet to do this..._____no_output_____

port.number = 1234
base.url = paste("http://", ip_address, ":", toString(port.number), "/v1/", sep="")
res <-GET(base.url)
rawToChar(res$content)_____no_output_____network.url = paste(base.url, "networks?source=url&collection=FromR", sep="")

urls <-c(
    'file:///Users/kono/git/cyrest-examples/notebooks/cookbook/sampleData/galFiltered.sif',
    'file:///Users/kono/git/cyrest-examples/notebooks/cookbook/sampleData/galFiltered.gml'
)_____no_output_____res <- POST(url=network.url, body=toJSON(urls), encode="json")
network.suid = unname(fromJSON(rawToChar(res$content)))
print(network.suid)_____no_output_____

Then, you can get this image in cytoscape.

![cytoscape image](graph_image.jpeg)_____no_output_____# Upload  a node attribute table.

---------------------------------------

## Import Annotation Data from bioconductor and merge it into Network data
---------------------------------------

In this example of workflow, by executing the following steps, you can load annotation data and merge it.

1. First, import network data from local file/URL to Cytoscape.
1. Second, import annotation data from database(in R, we can use bioconductor as database).
  - Human database in bioconductor:
  - https://www.bioconductor.org/packages/release/data/annotation/html/org.Hs.eg.db.html
  - Yeast database in bioconductor :
  - https://www.bioconductor.org/packages/release/data/annotation/html/org.Sc.sgd.db.html
1. Finally, merge the above two data table and push it to Cytoscape.

Now, we use the data file 'galFiltered.sif' that is the yeast data. So, we will try to add annotation data to this.
_____no_output_____

# Second, import annotation data from database(in R, we can use bioconductor as database).
# import library to access database
library(org.Sc.sgd.db)

# import Data
# DESCRIPTION data
descriptions <- select(org.Sc.sgd.db, keys=getAllNodes(cw), columns="DESCRIPTION")
df.descriptions <- data.frame(descriptions)

# GENENAME data
gene.names <- select(org.Sc.sgd.db, keys=getAllNodes(cw), columns="GENENAME")
df.gene.names <- data.frame(gene.names)

# GO data
go <- select(org.Sc.sgd.db, keys=getAllNodes(cw), columns="GO")
df.go <- data.frame(go)

# Finally, merge the above two data table and push it to Cytoscape.
# set DESCRIPTION as attribute data
nodeDataDefaults(g, attr = "DESCRIPTION") <- "undefined"
attr(nodeDataDefaults(g, attr = "DESCRIPTION"), "class") <- "STRING"

# set GENENAME as attribute data
nodeDataDefaults(g, attr = "name") <- "undefined"
attr(nodeDataDefaults(g, attr = "name"), "class") <- "STRING"

# set GO as attribute data
#nodeDataDefaults(g, attr = "go") <- "undefined"
#attr(nodeDataDefaults(g, attr = "go"), "class") <- "LIST"

# marge DESCRIPTION attribute.
for (index in 1:length(df.descriptions$DESCRIPTION)){
    nodeData(g, df.descriptions$ORF[[index]], "DESCRIPTION") <- df.descriptions$DESCRIPTION[[index]]
}

# marge GENENAME attribute.
for (index in 1:length(df.gene.names$GENENAME)){
    nodeData(g, df.gene.names$ORF[[index]], "name") <- df.gene.names$GENENAME[[index]]
}

# marge GO attribute.
#for (index in 1:length(df.gene.names$GENENAME)){
#    nodeData(g, df.gene.names$ORF[[index]], "name") <- df.gene.names$GENENAME[[index]]
#}

# show it in cytescape
cw <- CytoscapeWindow('vignette', graph=g, overwrite=TRUE)
displayGraph (cw)
layoutNetwork (cw, layout.name='degree-circle')_____no_output_____

# Upload an edge attribute table_____no_output_____

# add attribute:"edgeType" and the type:"STRING"
edgeDataDefaults(g, attr = "edgeType") <- "undefined"
attr(edgeDataDefaults(g, attr = "edgeType"), "class") <- "STRING"

# get EdgeList and type
gal.table.fromvec <- gal.table[,1]
gal.table.type <- gal.table[,2]
gal.table.tovec <-  gal.table[,3]

# add attribute value to the network
for (index in 1:length(gal.table.fromvec)){
    edgeData(g, gal.table.fromvec[[index]] ,gal.table.tovec[[index]], "edgeType") <- gal.table.type[[index]]
}
cw <- CytoscapeWindow('vignette', graph=g, overwrite=TRUE)
displayGraph (cw)
layoutNetwork (cw, layout.name='degree-circle')_____no_output_____

# Select edges based on some edge attributes
---------------------------------------

### How to select edges based on their attributes?

First, we have to set attributes to edges. Then, we can select edges based on attributes.

### Method : selectEdges

#### Usage
selectEdges(obj, edge.names, preserve.current.selection=TRUE)

#### Arguments

- **obj** : a CytoscapeWindowClass object.
- **edge.names** : a list of strings, the names of edges to select.
- **preserve.current.selection** : a logical object.

_____no_output_____

# clear selection
clearSelection (cw)_____no_output_____# selection Edges
vec = c()
for (index in 1:length(gal.table.type)){
    if(gal.table.type[[index]] == 'pp'){
        vec = c(vec, paste(gal.table.fromvec[[index]], '(pp)', gal.table.tovec[[index]]))
    }
}
selectEdges (cw, vec)_____no_output_____

Then, you can get the output,like the follwoing image, in cytoscape.

![cytoscape image](select_edge_image.jpeg)_____no_output_____# Hide these edges
---------------------------------------

If you want to hide selected edges, you can use this method.

### Method : hideSelectedEdges

#### Usage

hideSelectedEdges(obj)

#### Arguments

obj a CytoscapeWindowClass object._____no_output_____

# The code is only this!!
# TODO : this methods have some error. I can't do anything with this method.
hideSelectedEdges (cw)_____no_output_____

# Select nodes based on some node attributes
---------------------------------------

### method : selectNodes

#### Usage
selectNodes(obj, node.names, preserve.current.selection=TRUE)

#### Arguments

- **obj** : a CytoscapeWindowClass object.
- **node.names** : a list of strings, the names of nodes to select.
- **preserve.current.selection** : a logical object.
_____no_output_____

# get attributes data
gal.node.attrs <- read.table('sampleData/galFiltered_node.attrs',stringsAsFactors=FALSE, skip=1)
head(gal.node.attrs)_____no_output_____g <- initNodeAttribute (graph=g, attribute.name='moleculeType', attribute.type='char', default.value='undefined')

# get EdgeAttrs
gal.node = gal.node.attrs[[1]]
gal.attrs = gal.node.attrs[[3]]

for (index in 1:length(gal.node)){
    nodeData (g, gal.node, 'moleculeType') <- as.character(gal.attrs[[index]])
}

cw <- setGraph (cw, g)
displayGraph (cw)_____no_output_____# clear selection
clearSelection (cw)_____no_output_____vac = c()
for (index in 1:length(gal.node)){
    if(gal.attrs[[index]] == '1'){
        vec = c(vec, gal.node[[index]])
    }
}

selectNodes(cw, vec)_____no_output_____

Then, you can get the output,like the follwoing image, in cytoscape.

![cytoscape image](select_nodes_image.jpeg)_____no_output_____# Hide these nodes
---------------------------------------
### Method : hideSelectedNodes

#### Usage

hideSelectedNodes(obj)

#### Arguments

obj : a CytoscapeWindowClass object.
_____no_output_____

# TODO : There are some error in this method. I can't do anything by this.
hideSelectedNodes(cw)_____no_output_____

# Apply a layout the the network that is not hidden
---------------------------------------
### Method : getLayoutNames, layoutNetwork

We use two method in this section. 'getLayoutNames' let us know available layoutNames and we can apply one of them by 'layoutNetwork' method.

#### 1. Usage
getLayoutNames(obj)

#### 1. Arguments
obj : a CytoscapeConnectionClass object.

#### 2. Usage
layoutNetwork(obj, layout.name= grid )

#### 2. Arguments
obj a CytoscapeWindowClass object.
layout.name a string, one of the values returned by getLayoutNames, ’grid’ by default.
_____no_output_____

# get available layout names
getLayoutNames(cw)_____no_output_____# execute layout that you want
layoutNetwork(cw, layout.name= 'isom')_____no_output_____

Then, you can get the output,like the follwoing image, in cytoscape.

![cytoscape image](isom_graph_image.jpeg)_____no_output_____

# execute layout that you want
layoutNetwork(cw, layout.name= 'kamada-kawai')_____no_output_____

Then, you can get the output,like the follwoing image, in cytoscape.

![cytoscape image](kamada_kawai_image.jpeg)_____no_output_____# Apply a visual style to this network
---------------------------------------

By default, Cytoscape displays nodes as pale red circles circles, edges as blue undecorated lines, selected nodes as yellow and selected edges as red. RCy3 provides an easy way to change these defaults. More interestingly, RCy3 provides easy programmatic access to the vizmapper, by means of which the size, shape and color of nodes and edges is determined by the data attributes you have attached to those nodes and edges.

## The methods
---------------------------------------

There are so many methods to do this.

#TODO : I have to remove following message when I push. I think this section will be similar to cookbook, so I will copy and modify with it.

At this point I do every bit of the visual style manually, but it is always the same.

That is because I do not know how to transfer a visual style from one session to the next yet.

It would be great if that was automated (I do it manually for one network and then it is automatic for the following ones that have the same exact attributes)'_____no_output_____


# Notebook from trevorgokey/drug-computing
Path: uci-pharmsci/assignments/3D_structure_shape/3D_structure_shape_assignment.ipynb

[![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/MobleyLab/drug-computing/blob/master/uci-pharmsci/assignments/3D_structure_shape/3D_structure_shape_assignment.ipynb)_____no_output_____# 3D Structure/Shape Assignment

## Overview

This assignment involves two major parts. It first introduces the OEChem toolkits by having you perform some simple tasks with molecules in two and three dimensions, including generating 3D structures of molecules (much of which you will have already seen in the 3D structure/shape lecture and Jupyter notebook). You will also convert between common file formats and do simple shape overlays as well as visualization of your molecules. Some software installation will be required.

In the second major part of this assignment, you will use shape overlays to try and recognize HIV integrase inhibitors from a small library of potential HIV integrase inhibitors.

## More detailed overview

This assignment works through common tasks for working with small molecules. Specifically, you want to model a molecule, and you need to build 2D and 3D structures of the molecule, save to a particular file format for visualization, and compare it to other molecules. Here, you get to do these for the first time. Many different tool sets do these tasks, but here I’ll introduce you to one particular tool set (from OpenEye software) that works particularly well with Python and is free to academics. You have already seen and used this set of tools in class in the 3D structure/shape Jupyter notebook. This assignment will simply build on that notebook. Especially, Step 0 through Step 3 replicate tasks done in that notebook.

After completing these initial tasks, you will then take a small library of potential HIV integrase inhibitors, and use a shape-based search to try and identify active compounds on the basis of shape similarity to known inhibitors._____no_output_____# Getting started/warm-up

Before getting started, make a copy of this Jupyter notebook to work in and name it something else, such as `Mobley_3D_structure_shape.ipynb` if your last name is Mobley.

***If you are running this on Google Colab, please add the installation blocks from the [getting started notebook](https://github.com/MobleyLab/drug-computing/blob/master/uci-pharmsci/Getting_Started.ipynb) or [condacolab](https://github.com/aakankschit/drug-computing/blob/master/uci-pharmsci/Getting_Started_condacolab.ipynb) here and then execute the code below***

## Step 0: Install PyMol for visualization

We've seen `nglview` already for interactive visualization, but it is not as useful for visualizing things which are neither (a) MD simulations, nor (b) single structures. Specifically, here we will generate multiple structures/conformations, so we need a bit better visualization.

In this exercise, we’ll begin looking at and working with molecules in 3D, which means we need a viewer. PyMol is a popular general purpose one we will use extensively in this class. You should obtain the free academic version of PyMol online, but if you have any trouble with that please contact me. Alternate viewers like VMD or Chimera could also be used, but here I'll explain PyMol's usage.

## Step 1: Build 2D structures of some molecules

The first step is to build some molecules to work with. You might need to do this because your boss or research adviser asks you to model certain molecules, or you might come across some 2D structures of molecules in a research paper you would like to model. In any case, for whatever reason, you have a list of molecules to model, and you need to start by building their structures.

Let’s start by building 2D structures. If you already have commercial or favorite software for this, like ChemDraw, feel free to use that. If not, I recommend Marvin, which is free, as noted in the 3D structure/shape lecture. You can use it [within your browser](https://marvinjs-demo.chemaxon.com/latest/); you may need to install a free Java plugin to do so) or [download it](https://www.chemaxon.com/products/marvin/marvinsketch/) and install it on your computer (http://www.chemaxon.com/download/marvin/for-end-users/). I normally use the desktop version.

In Marvin, draw some structures of molecules (if you have some molecules you’re interested in because of another class or research, you should do them, otherwise just pick some) and save them to your computer (from the file->save as menu) as MDL SDF files, one at a time.

Drawing them should produce something like this:
![image](https://raw.githubusercontent.com/MobleyLab/drug-computing/master/uci-pharmsci/lectures/3D_structure_shape/Marvin_molecule.png)

Use the buttons at the bottom to create pre-formed rings; the tools at right can be used to select specific bond types or stereochemistry, and the buttons at right can be used to introduce elements other than carbon.

You should select at least 5-10 molecules to build, of your choice. Try to include some which are relatively similar to one another (i.e. differing only by a functional group, for example) and some which are not.
	Once your molecules are built, I suggest loading them (or at least one) in PyMol (open PyMol, then go to File->Open, then select your file) and having a quick look. You should see something like the below, though obviously details will depend on your platform (OS X, Linux, Windows) and what you’ve named your molecules. I suggest clicking the “show” button (S, as indicated by the red arrow) and choosing the “sticks” representation so you can see your molecule more clearly. Try clicking it and dragging to rotate -- notice how the molecule is completely planar (two dimensional) right now, even though it shouldn’t be. If you would like, you can choose Display -> Show Valences to show bond types. Experiment with using S to show other representations (such as surface) if you like, and use H to hide representations you’re done with.

![pymol](https://github.com/MobleyLab/drug-computing/blob/master/uci-pharmsci/assignments/3D_structure_shape/pymol.png?raw=1)_____no_output_____## Step 2: Building 3D structures of molecules

In the next step, you will want to build 3D structures of your molecules. I have provided some example Python code for this (and for other tasks) in the [`3D_Structure_shape.ipynb`](https://github.com/MobleyLab/drug-computing/blob/master/uci-pharmsci/lectures/3D_structure_shape/3D_Structure_Shape.ipynb) notebook we used in the relevant lecture.

### First, do this for just one molecule

**Put some code in the box just below to read in one of the molecules you drew and saved from the SDF file, generate a 3D conformation, and write it out to a mol2 file**, drawing on the examples from the lecture._____no_output_____### But we're going to want to do this for many molecules, so write some functions

Here let's write functions to read molecules, write molecules, and generate conformations; we'll call the last one `expandConformations` because it could be used on a molecule which already has one conformation in order to generate many conformations, as well.

We'll make these functions so you can easily reuse them on many molecules.

I've written the documentation strings for you in the code below; your job is to fill in the inner workings and then make sure they work

#### Write your functions here_____no_output_____

def readMolecule( inputfile ):
    """Take specified input molecule file with an OEChem supported molecular file format. Read a molecule from the file and return it as an OEChem OEMol molecule."""

    #Insert your code (as seen in the example) here to read the input file 'inputfile'

    return oemol #Replace oemol with the name of your molecule object

def writeMolecule( oemol, outputfile):
    """Write a provided oemol to specified output file using an OEChem supported file format, as determined by the file extension (such as .mol2 or .sdf).
Arguments:
- oemol: OEMol molecule to write
- outputfile: File to write to.
Returns: Nothing"""

    #Insert your code (as seen in the example) here to write an output file

def expandConformations( oemol, maxconfs = 100, strictStereo = True ):
    """Take a provided OEMol molecule, and use OpenEye's Omega to generate multiple conformations of the molecule, which are then returned in a new OEMol molecule.
Arguments:
- oemol: Provided molecule to generate conformations for
- maxconfs: Optional argument (default 100) specifying the maximum number of conformations to generate. Specify 1 to get just a single conformation.
- strictStereo: Optional argument specifying whether (True) or not (False) to use strict stereochemistry checking for Omega. Default: True.
Returns:
- expanded_oemol: OEMol containing the generated conformations."""

    #Write your code here (as seen in the example)
    return expanded_oemol_____no_output_____

#### You probably want to test your functions now
You should probably use the box below to make sure you can read in one of the molecules you created, generate conformations, and write it back out again. If you've written your functions properly, this code should just work!_____no_output_____

inputfile = 'test.sdf' # A test molecule I provided

mol = readMolecule(inputfile)
expandedmol = expandConformations( mol )
writeMolecule('test.mol2') #Write out to a mol2 file_____no_output_____

Now you should probably check with PyMol that `test.mol2` has a molecule with a 3D conformer in it, or add visualization in this notebook using `nglview` to check._____no_output_____## Step 3: Shape overlay your molecules

### Get ready
Next, we want to do a shape overlay of your molecules so we can see how structurally similar they are. This is a common task in drug discovery, as we noted in class, and often can be used in several ways, such as finding out about the likely shape of a binding site based on the molecules which bind there, or finding new molecules which may fit a binding site while being chemically distinct from existing molecules based on their shape.

Here, our goal will be to take your entire set of molecules and overlay them onto a common reference structure. Here's an example which does a shape (+color, where color basically measures chemical similarity) overlay for a pair of molecules; this is rather likewhat you saw in the 3D structure/shape lecture: _____no_output_____

from openeye.oechem import *
from openeye.oeomega import *
from openeye.oeiupac import *
from openeye.oeshape import *

omega = OEOmega() #Initialize class
omega.SetMaxConfs(100) #Here we want to use more conformers if needed
omega.SetStrictStereo(False) #Set to false to pick random stereoisomer if stereochemistry is not specified

# Make some molecules
ibuprofen = OEMol()
naproxen = OEMol()
OEParseIUPACName(ibuprofen, 'ibuprofen')
OEParseIUPACName(naproxen, 'naproxen')

#Set up reference molecule
refmol = ibuprofen
omega(refmol)

#Fit molecule
fitmol = naproxen
omega(fitmol)
fitmol.SetTitle('naproxen')

# Open output stream for output molecule
outfs = oemolostream('fitted_output.mol2')
# How many conformers will we generate for fitting?
nconfs = 10

# Setup ROCS to provide specified number of conformers per hit
options = OEROCSOptions()
options.SetNumBestHits(nconfs)
options.SetConfsPerHit(nconfs)
rocs = OEROCS(options)
rocs.AddMolecule(fitmol) #Add our molecule as the one we are fitting

# Loop over results and output
for res in rocs.Overlay(refmol):
    outmol = res.GetOverlayConfs() #Use GetOverlayConf to get just the best; GetOverlayConfs for all
    OERemoveColorAtoms(outmol)
    OEAddExplicitHydrogens(outmol)
    OEWriteMolecule(outfs, outmol)
    print("title: %s  tanimoto combo = %.2f" % (outmol.GetTitle(), res.GetTanimotoCombo()))
outfs.close()

# If you wanted, you could also print just the "color" similarity:
#print(res.GetColorTanimoto())
# Or just the shape similarity:
#print(res.GetShapeTanimoto())title: naproxen_13  tanimoto combo = 1.47
title: naproxen_0  tanimoto combo = 1.47
title: naproxen_16  tanimoto combo = 1.47
title: naproxen_12  tanimoto combo = 1.47
title: naproxen_1  tanimoto combo = 1.47
title: naproxen_8  tanimoto combo = 1.47
title: naproxen_4  tanimoto combo = 1.47
title: naproxen_5  tanimoto combo = 1.47
title: naproxen_9  tanimoto combo = 1.46
title: naproxen_17  tanimoto combo = 1.46

### Make a function to do this

Again, we'd be better off with a function to do this (and we'll use this function also below in the ligand-based design portion of the assignment, below). So, edit the code below and complete the `fitMoleculeToReference` function. The idea of this function is to take two molecules and compare them based on shape. The code here is a little complicated (which is why I’ve written similar code for you) and the only things you should have to change are the variable names and the indentation. What this ultimately will do is take two molecules, overlay them in a number of different ways, and then return (give back to the user) the actual overlays of the molecules, sorted from best to worst, and the “Tanimoto” scores of the overlays in the same order. Tanimoto scores are scores running from 0 to 1 and essentially measure similarity, where 0 means totally dissimilar and 1 means totally identical. Here, we are actually combining TWO Tanimoto scores (a ‘shape’ Tanimoto score and a ‘color’ (Chemistry) tanimoto score) so our overall Tanimoto score runs from 0 to 2.

Once you think you’ve got your `fitMoleculeToReference` function working, test it on a specific pair of molecules, then use `writeMolecule` (as above) to write out the reference molecule and the fitted molecule (to .mol2 or .sdf format) and view them in PyMol (after downloading to your computer) to ensure it’s working.

When you have the function working, then set up a loop over all of your molecules except your chosen reference molecule, ‘molecule1’, to overlay all of the molecules onto it. Before the loop, set up a list to store the best score for each pair of molecules. Inside the loop, fit each molecule onto the reference and then store the resulting fits to files (being careful not to overwrite any of your existing files). Also store the best score to the list of scores. Also have your program print out the scores. Visualize in PyMol the resulting overlays, and note which ones seem good, which ones seem bad, and which ones seem OK. Note whether these observations seem consistent with the scores (Tanimoto scores) you’re getting or not.

For this portion of the assignment, you should submit your reference molecule’s structure (.mol2 or .sdf) as well as the structure of the best overlay you found, with its score. You should also include a sentence or more explaining what you found in this step in your e-mail._____no_output_____

def fitMoleculeToReference( fitmol, refmol, ShapeColor = True):
    """Take a multi-conformation molecule to be fitted, and fit it onto a reference molecule using shape overlays.
Arguments:
- fitmol: Molecule to fit onto the reference molecule; should be multiple-conformation
- refmol: Reference molecule to fit onto
- ShapeColor: Optional argument specifying whether to score overlays based on shape alone (False) or to score based on both shape and color fit (True); in the latter case scores will run from 0 to 2 while in the former they will run from 0 to 1. Default: True.
Returns:
- tanimotos: Tanimoto scores of the fitted conformations.
- fitted: The fitted conformations, ordered by score from best to worst
"""

    #Write your code here, as seen in the example

    return (tanimotos, fitted)_____no_output_____

## Step 4: Ligand-based design

Your next step involves a simple application of this shape overlay technique to a real problem. You will take an existing ligand -- an HIV-1 integrase inhibitor -- from a structure the protein data bank (PDB) and compare this to a set of molecules which were tested experimentally for possible HIV integrase inhibition. The idea is to see whether the molecules which actually are HIV integrase inhibitors are among those which are most shape similar to the known inhibitor.

Here, our exact plan is to take an existing list of 600-700 compounds which were tested for their ability to inhibit HIV-1 integrase. The 3NF8 structure in the PDB gives the crystal structure of one integrase inhibitor from this series bound to HIV-1 integrase. We will use the ligand from that structure as a query, or reference molecule, to see if it easily allows us to recognize other molecules which are likely to bind from our larger set of 600-700 compounds. This somewhat echoes the SAMPL4 HIV integrase challenge reported in [this paper]( http://link.springer.com/article/10.1007/s10822-014-9723-5), though the set of inactive compounds has been expanded somewhat by addition of a portion of the Maybridge fragment library which was also tested experimentally.

You will find several supporting files in this directory to help with this part of the assignment:
- `actual_w_maybridge.txt`: List of compounds actually tested
- `3NF8_ligand.pdb`: To simplify your life, I downloaded the 3NF8 structure and extracted the ligand of interest from it to serve as your reference molecule.
- `final_clean_binders.txt`: “Answers” — a text file which can be parsed (by code provided below) into a list of names of binders, which can be used to check your results.
Here is what you should do to complete the assignment, starting from the code provided below and the functions you wrote for in Steps 1-3 above:

- Set up a loop over the molecules in the set
- For every molecule, generate a 3D conformation with your `expandConformations` function, using `strictStereo` set to False (a few molecules have unspecified stereochemistry, and setting this to false here will cause OEChem to pick a random stereoisomer in such cases, which is acceptable in this test)
- Use your `fitMolToReference` function to overlay your molecule/conformations onto the reference molecule. Do this twice (getting back two different sets of output) — once with ShapeColor = False and once with it True, so we can compare and find out which works better here.
- Store your scores (I store them to a dictionary keyed by molecule name) in a way which allows you to track which molecules they correspond to.
- Sort your molecule names by score (for each of the two sets of scores you have). I give an example of how to sort below.
- Compare your list of molecule names sorted by score with the list of actual binders (see code below) and identify where you find the actual binders. What you want to track at this stage is how many binders you find at or before each *rank* (entry number) in the list of sorted scores. For example, if there are 60 binders distributed across 650 molecules, you might find the first binder at the top of the list, the second binder at entry number 5, and the third binder at entry number 10. For every entry in the sorted list, store the number of binders found — so in this example, your first 10 entries would be `[ 1, 1, 1, 1, 2, 2, 2, 2, 2, 3]`. If this is still confusing, [see here](ranks.md) for a little more about this process.
- For each list of number of compounds found by rank (do this twice, once for each of your two sets of scores), convert it to a list of fraction of actual binders found by rank, by dividing each entry by the total number of binders in the set. For example, if you have found one binder and there are 60 in total, the fracion actives found is 1/60.
- Plot the fraction of binders found at each rank, versus the rank, for each of your two scoring schemes. This is an "enrichment plot".
- Also plot what would be expected if you picked compounds to call "active" at random. (Hint, in this approach you would expect to find no actives at rank 0, and all of the actives at rank N, with the fraction of actives found increasing linearly in between). This will allow you to determine whether your approach is doing better than random at recognizing likely active compounds._____no_output_____### Some code to get you started

This shows how to read in the potential ligands, the reference molecule, and the actual binders._____no_output_____

from openeye.oechem import *
from openeye.oeiupac import *
from openeye.oequacpac import *
import pickle

# Load the text file containing all the potential ligands
file = open('actual_w_maybridge.txt', 'r')
text = file.readlines()
file.close()

#Generate OEMols for all of the potential ligands in our test set; store them to a dictionary by their compound name.
mol_by_name = {}
for line in text:
    mol = OEMol()
    tmp = line.split()
    name=tmp[0]
    smiles=tmp[1]
    parsed = OEParseSmiles(mol, smiles)
    if not parsed:
        print("Warning, could not parse %s, pausing." % name)
        raw_input()
    mol_by_name[name] = mol

#Load reference molecule which we will ultimately overlay onto
istream = oemolistream('3NF8_ligand.pdb')
refmol = OEMol()
OEReadMolecule( istream, refmol)
istream.close()

#Standardize protonation state and make sure there are explicit hydrogens
OE3DToInternalStereo(refmol)
OESetNeutralpHModel(refmol)
OEAddExplicitHydrogens(refmol)

#For the purposes of telling how we did, load a list of the names of actual binders
#Load actual binders
file = open('final_clean_binders.txt', 'r')
text = file.readlines()
file.close()
binders = [ line.split()[0].split('_')[0] for line in text]_____no_output_____

### Sorting lists_____no_output_____I mentioned I would give an example of a sorting a list. If I have scores stored in the dictionary `scores_by_name` and I wanted to make a list of names sorted by scores, I could do it this way: _____no_output_____

# Make up a dummy dictionary to sort
scores_by_name = {'a':32, 'b':11, 'c':43}

# Make a list we want to sort
names = list(scores_by_name.keys())

# Define function for sorting
def f(x):
    return scores_by_name[x]

# Sort, print sorted list
sorted_names = sorted( names, key=f )
print(sorted_names)['b', 'a', 'c']

In reality you will have molecule names, and be sorting them by Tanimoto score. _____no_output_____### Write your code here

Here, complete your assignment as described above:_____no_output_____## What to submit

For this assignment, please submit via the course website:
- From step 3, structure files for your reference molecule as well as for the best shape overlay onto that structure, with scores. Also submit a sentence or more describing what you found in this section.
- From step 4, Your resulting graph of “enrichment” of actual ligands from the set, with axes and curves labeled, along with this Jupyter notebook which produced it
- In a box below, or a separate document, a brief explanation of how you see the performance of the shape overlay approach, in view of the linked paper on the SAMPL4 challenge. Why does it do well, or poorly, in this case? Does ‘color’ scoring in addition to shape help? Why or why not? How does this compare to the “null” models tested in the paper? _____no_output_____


# Notebook from navicester/deeplearning.ai-exercise
Path: 05.Sequence_Models/Week1/Dinosaur Island -- Character-level language model/Dinosaurus_Island_Character_level_language_model_final_v3b.ipynb

# Character level language model - Dinosaurus Island

Welcome to Dinosaurus Island! 65 million years ago, dinosaurs existed, and in this assignment they are back. You are in charge of a special task. Leading biology researchers are creating new breeds of dinosaurs and bringing them to life on earth, and your job is to give names to these dinosaurs. If a dinosaur does not like its name, it might go berserk, so choose wisely!

Luckily you have learned some deep learning and you will use it to save the day. Your assistant has collected a list of all the dinosaur names they could find, and compiled them into this [dataset](dinos.txt). (Feel free to take a look by clicking the previous link.) To create new dinosaur names, you will build a character level language model to generate new names. Your algorithm will learn the different name patterns, and randomly generate new names. Hopefully this algorithm will keep you and your team safe from the dinosaurs' wrath!

By completing this assignment you will learn:

- How to store text data for processing using an RNN
- How to synthesize data, by sampling predictions at each time step and passing it to the next RNN-cell unit
- How to build a character-level text generation recurrent neural network
- Why clipping the gradients is important

We will begin by loading in some functions that we have provided for you in `rnn_utils`. Specifically, you have access to functions such as `rnn_forward` and `rnn_backward` which are equivalent to those you've implemented in the previous assignment. _____no_output_____## Updates

#### If you were working on the notebook before this update...
* The current notebook is version "3b".
* You can find your original work saved in the notebook with the previous version name ("v3a")
* To view the file directory, go to the menu "File->Open", and this will open a new tab that shows the file directory.

#### List of updates 3b
- removed redundant numpy import
* `clip`
    - change test code to use variable name 'mvalue' rather than 'maxvalue' and deleted it from namespace to avoid confusion.
* `optimize`
    - removed redundant description of clip function to discourage use of using 'maxvalue' which is not an argument to optimize
* `model`
    - added 'verbose mode to print X,Y to aid in creating that code.
    - wordsmith instructions to prevent confusion
        - 2000 examples vs 100, 7 displayed vs 10
        - no randomization of order
* `sample`
    - removed comments regarding potential different sample outputs to reduce confusion._____no_output_____

import numpy as np
from utils import *
import random
import pprint_____no_output_____

## 1 - Problem Statement

### 1.1 - Dataset and Preprocessing

Run the following cell to read the dataset of dinosaur names, create a list of unique characters (such as a-z), and compute the dataset and vocabulary size. _____no_output_____

data = open('dinos.txt', 'r').read()
data= data.lower()
chars = list(set(data))
data_size, vocab_size = len(data), len(chars)
print('There are %d total characters and %d unique characters in your data.' % (data_size, vocab_size))_____no_output_____

* The characters are a-z (26 characters) plus the "\n" (or newline character).
* In this assignment, the newline character "\n" plays a role similar to the `<EOS>` (or "End of sentence") token we had discussed in lecture.
    - Here, "\n" indicates the end of the dinosaur name rather than the end of a sentence.
* `char_to_ix`: In the cell below, we create a python dictionary (i.e., a hash table) to map each character to an index from 0-26.
* `ix_to_char`: We also create a second python dictionary that maps each index back to the corresponding character.
    -  This will help you figure out what index corresponds to what character in the probability distribution output of the softmax layer. _____no_output_____

chars = sorted(chars)
print(chars)_____no_output_____char_to_ix = { ch:i for i,ch in enumerate(chars) }
ix_to_char = { i:ch for i,ch in enumerate(chars) }
pp = pprint.PrettyPrinter(indent=4)
pp.pprint(ix_to_char)_____no_output_____

### 1.2 - Overview of the model

Your model will have the following structure:

- Initialize parameters
- Run the optimization loop
    - Forward propagation to compute the loss function
    - Backward propagation to compute the gradients with respect to the loss function
    - Clip the gradients to avoid exploding gradients
    - Using the gradients, update your parameters with the gradient descent update rule.
- Return the learned parameters

 **Figure 1**: Recurrent Neural Network, similar to what you had built in the previous notebook "Building a Recurrent Neural Network - Step by Step".

* At each time-step, the RNN tries to predict what is the next character given the previous characters.
* The dataset $\mathbf{X} = (x^{\langle 1 \rangle}, x^{\langle 2 \rangle}, ..., x^{\langle T_x \rangle})$ is a list of characters in the training set.
* $\mathbf{Y} = (y^{\langle 1 \rangle}, y^{\langle 2 \rangle}, ..., y^{\langle T_x \rangle})$ is the same list of characters but shifted one character forward.
* At every time-step $t$, $y^{\langle t \rangle} = x^{\langle t+1 \rangle}$.  The prediction at time $t$ is the same as the input at time $t + 1$._____no_output_____## 2 - Building blocks of the model

In this part, you will build two important blocks of the overall model:
- Gradient clipping: to avoid exploding gradients
- Sampling: a technique used to generate characters

You will then apply these two functions to build the model._____no_output_____### 2.1 - Clipping the gradients in the optimization loop

In this section you will implement the `clip` function that you will call inside of your optimization loop.

#### Exploding gradients
* When gradients are very large, they're called "exploding gradients."
* Exploding gradients make the training process more difficult, because the updates may be so large that they "overshoot" the optimal values during back propagation.

Recall that your overall loop structure usually consists of:
* forward pass,
* cost computation,
* backward pass,
* parameter update.

Before updating the parameters, you will perform gradient clipping to make sure that your gradients are not "exploding."

#### gradient clipping
In the exercise below, you will implement a function `clip` that takes in a dictionary of gradients and returns a clipped version of gradients if needed.
* There are different ways to clip gradients.
* We will use a simple element-wise clipping procedure, in which every element of the gradient vector is clipped to lie between some range [-N, N].
* For example, if the N=10
    - The range is [-10, 10]
    - If any component of the gradient vector is greater than 10, it is set to 10.
    - If any component of the gradient vector is less than -10, it is set to -10.
    - If any components are between -10 and 10, they keep their original values.

 **Figure 2**: Visualization of gradient descent with and without gradient clipping, in a case where the network is running into "exploding gradient" problems.

**Exercise**:
Implement the function below to return the clipped gradients of your dictionary `gradients`.
* Your function takes in a maximum threshold and returns the clipped versions of the gradients.
* You can check out [numpy.clip](https://docs.scipy.org/doc/numpy-1.13.0/reference/generated/numpy.clip.html).
    - You will need to use the argument "`out = ...`".
    - Using the "`out`" parameter allows you to update a variable "in-place".
    - If you don't use "`out`" argument, the clipped variable is stored in the variable "gradient" but does not update the gradient variables `dWax`, `dWaa`, `dWya`, `db`, `dby`._____no_output_____

### GRADED FUNCTION: clip

def clip(gradients, maxValue):
    '''
    Clips the gradients' values between minimum and maximum.

    Arguments:
    gradients -- a dictionary containing the gradients "dWaa", "dWax", "dWya", "db", "dby"
    maxValue -- everything above this number is set to this number, and everything less than -maxValue is set to -maxValue

    Returns:
    gradients -- a dictionary with the clipped gradients.
    '''

    dWaa, dWax, dWya, db, dby = gradients['dWaa'], gradients['dWax'], gradients['dWya'], gradients['db'], gradients['dby']

    ### START CODE HERE ###
    # clip to mitigate exploding gradients, loop over [dWax, dWaa, dWya, db, dby]. (≈2 lines)
    for gradient in [None]:
        None
    ### END CODE HERE ###

    gradients = {"dWaa": dWaa, "dWax": dWax, "dWya": dWya, "db": db, "dby": dby}

    return gradients_____no_output_____# Test with a maxvalue of 10
mValue = 10
np.random.seed(3)
dWax = np.random.randn(5,3)*10
dWaa = np.random.randn(5,5)*10
dWya = np.random.randn(2,5)*10
db = np.random.randn(5,1)*10
dby = np.random.randn(2,1)*10
gradients = {"dWax": dWax, "dWaa": dWaa, "dWya": dWya, "db": db, "dby": dby}
gradients = clip(gradients, mValue)
print("gradients[\"dWaa\"][1][2] =", gradients["dWaa"][1][2])
print("gradients[\"dWax\"][3][1] =", gradients["dWax"][3][1])
print("gradients[\"dWya\"][1][2] =", gradients["dWya"][1][2])
print("gradients[\"db\"][4] =", gradients["db"][4])
print("gradients[\"dby\"][1] =", gradients["dby"][1])_____no_output_____

** Expected output:**

```Python
gradients["dWaa"][1][2] = 10.0
gradients["dWax"][3][1] = -10.0
gradients["dWya"][1][2] = 0.29713815361
gradients["db"][4] = [ 10.]
gradients["dby"][1] = [ 8.45833407]
```_____no_output_____

# Test with a maxValue of 5
mValue = 5
np.random.seed(3)
dWax = np.random.randn(5,3)*10
dWaa = np.random.randn(5,5)*10
dWya = np.random.randn(2,5)*10
db = np.random.randn(5,1)*10
dby = np.random.randn(2,1)*10
gradients = {"dWax": dWax, "dWaa": dWaa, "dWya": dWya, "db": db, "dby": dby}
gradients = clip(gradients, mValue)
print("gradients[\"dWaa\"][1][2] =", gradients["dWaa"][1][2])
print("gradients[\"dWax\"][3][1] =", gradients["dWax"][3][1])
print("gradients[\"dWya\"][1][2] =", gradients["dWya"][1][2])
print("gradients[\"db\"][4] =", gradients["db"][4])
print("gradients[\"dby\"][1] =", gradients["dby"][1])
del mValue # avoid common issue_____no_output_____

** Expected Output: **
```Python
gradients["dWaa"][1][2] = 5.0
gradients["dWax"][3][1] = -5.0
gradients["dWya"][1][2] = 0.29713815361
gradients["db"][4] = [ 5.]
gradients["dby"][1] = [ 5.]
```_____no_output_____### 2.2 - Sampling

Now assume that your model is trained. You would like to generate new text (characters). The process of generation is explained in the picture below:

 **Figure 3**: In this picture, we assume the model is already trained. We pass in $x^{\langle 1\rangle} = \vec{0}$ at the first time step, and have the network sample one character at a time.
_____no_output_____**Exercise**: Implement the `sample` function below to sample characters. You need to carry out 4 steps:

- **Step 1**: Input the "dummy" vector of zeros $x^{\langle 1 \rangle} = \vec{0}$.
    - This is the default input before we've generated any characters.
    We also set $a^{\langle 0 \rangle} = \vec{0}$_____no_output_____- **Step 2**: Run one step of forward propagation to get $a^{\langle 1 \rangle}$ and $\hat{y}^{\langle 1 \rangle}$. Here are the equations:

hidden state:
$$ a^{\langle t+1 \rangle} = \tanh(W_{ax}  x^{\langle t+1 \rangle } + W_{aa} a^{\langle t \rangle } + b)\tag{1}$$

activation:
$$ z^{\langle t + 1 \rangle } = W_{ya}  a^{\langle t + 1 \rangle } + b_y \tag{2}$$

prediction:
$$ \hat{y}^{\langle t+1 \rangle } = softmax(z^{\langle t + 1 \rangle })\tag{3}$$

- Details about $\hat{y}^{\langle t+1 \rangle }$:
   - Note that $\hat{y}^{\langle t+1 \rangle }$ is a (softmax) probability vector (its entries are between 0 and 1 and sum to 1).
   - $\hat{y}^{\langle t+1 \rangle}_i$ represents the probability that the character indexed by "i" is the next character.
   - We have provided a `softmax()` function that you can use._____no_output_____#### Additional Hints

- $x^{\langle 1 \rangle}$ is `x` in the code. When creating the one-hot vector, make a numpy array of zeros, with the number of rows equal to the number of unique characters, and the number of columns equal to one.  It's a 2D and not a 1D array.
- $a^{\langle 0 \rangle}$ is `a_prev` in the code.  It is a numpy array of zeros, where the number of rows is $n_{a}$, and number of columns is 1.  It is a 2D array as well.  $n_{a}$ is retrieved by getting the number of columns in $W_{aa}$ (the numbers need to match in order for the matrix multiplication $W_{aa}a^{\langle t \rangle}$ to work.
- [numpy.dot](https://docs.scipy.org/doc/numpy/reference/generated/numpy.dot.html)
- [numpy.tanh](https://docs.scipy.org/doc/numpy/reference/generated/numpy.tanh.html)_____no_output_____#### Using 2D arrays instead of 1D arrays
* You may be wondering why we emphasize that $x^{\langle 1 \rangle}$ and $a^{\langle 0 \rangle}$ are 2D arrays and not 1D vectors.
* For matrix multiplication in numpy, if we multiply a 2D matrix with a 1D vector, we end up with with a 1D array.
* This becomes a problem when we add two arrays where we expected them to have the same shape.
* When two arrays with  a different number of dimensions are added together, Python "broadcasts" one across the other.
* Here is some sample code that shows the difference between using a 1D and 2D array._____no_output_____

matrix1 = np.array([[1,1],[2,2],[3,3]]) # (3,2)
matrix2 = np.array([[0],[0],[0]]) # (3,1)
vector1D = np.array([1,1]) # (2,)
vector2D = np.array([[1],[1]]) # (2,1)
print("matrix1 \n", matrix1,"\n")
print("matrix2 \n", matrix2,"\n")
print("vector1D \n", vector1D,"\n")
print("vector2D \n", vector2D)_____no_output_____print("Multiply 2D and 1D arrays: result is a 1D array\n",
      np.dot(matrix1,vector1D))
print("Multiply 2D and 2D arrays: result is a 2D array\n",
      np.dot(matrix1,vector2D))_____no_output_____print("Adding (3 x 1) vector to a (3 x 1) vector is a (3 x 1) vector\n",
      "This is what we want here!\n",
      np.dot(matrix1,vector2D) + matrix2)_____no_output_____print("Adding a (3,) vector to a (3 x 1) vector\n",
      "broadcasts the 1D array across the second dimension\n",
      "Not what we want here!\n",
      np.dot(matrix1,vector1D) + matrix2
     )_____no_output_____

- **Step 3**: Sampling:
    - Now that we have $y^{\langle t+1 \rangle}$, we want to select the next letter in the dinosaur name. If we select the most probable, the model will always generate the same result given a starting letter. To make the results more interesting, we will use np.random.choice to select a next letter that is *likely*, but not always the same.
    - Pick the next character's **index** according to the probability distribution specified by $\hat{y}^{\langle t+1 \rangle }$.
    - This means that if $\hat{y}^{\langle t+1 \rangle }_i = 0.16$, you will pick the index "i" with 16% probability.
    - Use [np.random.choice](https://docs.scipy.org/doc/numpy-1.13.0/reference/generated/numpy.random.choice.html).

    Example of how to use `np.random.choice()`:
    ```python
    np.random.seed(0)
    probs = np.array([0.1, 0.0, 0.7, 0.2])
    idx = np.random.choice(range(len((probs)), p = probs)
    ```

    - This means that you will pick the index (`idx`) according to the distribution:

    $P(index = 0) = 0.1, P(index = 1) = 0.0, P(index = 2) = 0.7, P(index = 3) = 0.2$.

    - Note that the value that's set to `p` should be set to a 1D vector.
    - Also notice that $\hat{y}^{\langle t+1 \rangle}$, which is `y` in the code, is a 2D array.
    - Also notice, while in your implementation, the first argument to np.random.choice is just an ordered list [0,1,.., vocab_len-1], it is *Not* appropriate to use char_to_ix.values(). The *order* of values returned by a python dictionary .values() call will be the same order as they are added to the dictionary. The grader may have a different order when it runs your routine than when you run it in your notebook._____no_output_____##### Additional Hints
- [range](https://docs.python.org/3/library/functions.html#func-range)
- [numpy.ravel](https://docs.scipy.org/doc/numpy/reference/generated/numpy.ravel.html) takes a multi-dimensional array and returns its contents inside of a 1D vector.
```Python
arr = np.array([[1,2],[3,4]])
print("arr")
print(arr)
print("arr.ravel()")
print(arr.ravel())
```
Output:
```Python
arr
[[1 2]
 [3 4]]
arr.ravel()
[1 2 3 4]
```

- Note that `append` is an "in-place" operation.  In other words, don't do this:
```Python
fun_hobbies = fun_hobbies.append('learning')  ## Doesn't give you what you want
```_____no_output_____- **Step 4**: Update to $x^{\langle t \rangle }$
    - The last step to implement in `sample()` is to update the variable `x`, which currently stores $x^{\langle t \rangle }$, with the value of $x^{\langle t + 1 \rangle }$.
    - You will represent $x^{\langle t + 1 \rangle }$ by creating a one-hot vector corresponding to the character that you have chosen as your prediction.
    - You will then forward propagate $x^{\langle t + 1 \rangle }$ in Step 1 and keep repeating the process until you get a "\n" character, indicating that you have reached the end of the dinosaur name. _____no_output_____##### Additional Hints
- In order to reset `x` before setting it to the new one-hot vector, you'll want to set all the values to zero.
    - You can either create a new numpy array: [numpy.zeros](https://docs.scipy.org/doc/numpy/reference/generated/numpy.zeros.html)
    - Or fill all values with a single number: [numpy.ndarray.fill](https://docs.scipy.org/doc/numpy/reference/generated/numpy.ndarray.fill.html)_____no_output_____

# GRADED FUNCTION: sample

def sample(parameters, char_to_ix, seed):
    """
    Sample a sequence of characters according to a sequence of probability distributions output of the RNN

    Arguments:
    parameters -- python dictionary containing the parameters Waa, Wax, Wya, by, and b.
    char_to_ix -- python dictionary mapping each character to an index.
    seed -- used for grading purposes. Do not worry about it.

    Returns:
    indices -- a list of length n containing the indices of the sampled characters.
    """

    # Retrieve parameters and relevant shapes from "parameters" dictionary
    Waa, Wax, Wya, by, b = parameters['Waa'], parameters['Wax'], parameters['Wya'], parameters['by'], parameters['b']
    vocab_size = by.shape[0]
    n_a = Waa.shape[1]

    ### START CODE HERE ###
    # Step 1: Create the a zero vector x that can be used as the one-hot vector
    # representing the first character (initializing the sequence generation). (≈1 line)
    x = None
    # Step 1': Initialize a_prev as zeros (≈1 line)
    a_prev = None

    # Create an empty list of indices, this is the list which will contain the list of indices of the characters to generate (≈1 line)
    indices = []

    # idx is the index of the one-hot vector x that is set to 1
    # All other positions in x are zero.
    # We will initialize idx to -1
    idx = -1

    # Loop over time-steps t. At each time-step:
    # sample a character from a probability distribution
    # and append its index (`idx`) to the list "indices".
    # We'll stop if we reach 50 characters
    # (which should be very unlikely with a well trained model).
    # Setting the maximum number of characters helps with debugging and prevents infinite loops.
    counter = 0
    newline_character = char_to_ix['\n']

    while (idx != newline_character and counter != 50):

        # Step 2: Forward propagate x using the equations (1), (2) and (3)
        a = None
        z = None
        y = None

        # for grading purposes
        np.random.seed(counter+seed)

        # Step 3: Sample the index of a character within the vocabulary from the probability distribution y
        # (see additional hints above)
        idx = None

        # Append the index to "indices"
        None

        # Step 4: Overwrite the input x with one that corresponds to the sampled index `idx`.
        # (see additional hints above)
        x = None
        x[None] = None

        # Update "a_prev" to be "a"
        a_prev = None

        # for grading purposes
        seed += 1
        counter +=1

    ### END CODE HERE ###

    if (counter == 50):
        indices.append(char_to_ix['\n'])

    return indices_____no_output_____np.random.seed(2)
_, n_a = 20, 100
Wax, Waa, Wya = np.random.randn(n_a, vocab_size), np.random.randn(n_a, n_a), np.random.randn(vocab_size, n_a)
b, by = np.random.randn(n_a, 1), np.random.randn(vocab_size, 1)
parameters = {"Wax": Wax, "Waa": Waa, "Wya": Wya, "b": b, "by": by}

indices = sample(parameters, char_to_ix, 0)
print("Sampling:")
print("list of sampled indices:\n", indices)
print("list of sampled characters:\n", [ix_to_char[i] for i in indices])_____no_output_____

** Expected output:**

```Python
Sampling:
list of sampled indices:
 [12, 17, 24, 14, 13, 9, 10, 22, 24, 6, 13, 11, 12, 6, 21, 15, 21, 14, 3, 2, 1, 21, 18, 24, 7, 25, 6, 25, 18, 10, 16, 2, 3, 8, 15, 12, 11, 7, 1, 12, 10, 2, 7, 7, 11, 17, 24, 12, 13, 24, 0]
list of sampled characters:
 ['l', 'q', 'x', 'n', 'm', 'i', 'j', 'v', 'x', 'f', 'm', 'k', 'l', 'f', 'u', 'o', 'u', 'n', 'c', 'b', 'a', 'u', 'r', 'x', 'g', 'y', 'f', 'y', 'r', 'j', 'p', 'b', 'c', 'h', 'o', 'l', 'k', 'g', 'a', 'l', 'j', 'b', 'g', 'g', 'k', 'q', 'x', 'l', 'm', 'x', '\n']
```
_____no_output_____## 3 - Building the language model

It is time to build the character-level language model for text generation.

### 3.1 - Gradient descent

* In this section you will implement a function performing one step of stochastic gradient descent (with clipped gradients).
* You will go through the training examples one at a time, so the optimization algorithm will be stochastic gradient descent.

As a reminder, here are the steps of a common optimization loop for an RNN:

- Forward propagate through the RNN to compute the loss
- Backward propagate through time to compute the gradients of the loss with respect to the parameters
- Clip the gradients
- Update the parameters using gradient descent

**Exercise**: Implement the optimization process (one step of stochastic gradient descent).

The following functions are provided:

```python
def rnn_forward(X, Y, a_prev, parameters):
    """ Performs the forward propagation through the RNN and computes the cross-entropy loss.
    It returns the loss' value as well as a "cache" storing values to be used in backpropagation."""
    ....
    return loss, cache

def rnn_backward(X, Y, parameters, cache):
    """ Performs the backward propagation through time to compute the gradients of the loss with respect
    to the parameters. It returns also all the hidden states."""
    ...
    return gradients, a

def update_parameters(parameters, gradients, learning_rate):
    """ Updates parameters using the Gradient Descent Update Rule."""
    ...
    return parameters
```

Recall that you previously implemented the `clip` function:

_____no_output_____#### parameters

* Note that the weights and biases inside the `parameters` dictionary are being updated by the optimization, even though `parameters` is not one of the returned values of the `optimize` function. The `parameters` dictionary is passed by reference into the function, so changes to this dictionary are making changes to the `parameters` dictionary even when accessed outside of the function.
* Python dictionaries and lists are "pass by reference", which means that if you pass a dictionary into a function and modify the dictionary within the function, this changes that same dictionary (it's not a copy of the dictionary)._____no_output_____

# GRADED FUNCTION: optimize

def optimize(X, Y, a_prev, parameters, learning_rate = 0.01):
    """
    Execute one step of the optimization to train the model.

    Arguments:
    X -- list of integers, where each integer is a number that maps to a character in the vocabulary.
    Y -- list of integers, exactly the same as X but shifted one index to the left.
    a_prev -- previous hidden state.
    parameters -- python dictionary containing:
                        Wax -- Weight matrix multiplying the input, numpy array of shape (n_a, n_x)
                        Waa -- Weight matrix multiplying the hidden state, numpy array of shape (n_a, n_a)
                        Wya -- Weight matrix relating the hidden-state to the output, numpy array of shape (n_y, n_a)
                        b --  Bias, numpy array of shape (n_a, 1)
                        by -- Bias relating the hidden-state to the output, numpy array of shape (n_y, 1)
    learning_rate -- learning rate for the model.

    Returns:
    loss -- value of the loss function (cross-entropy)
    gradients -- python dictionary containing:
                        dWax -- Gradients of input-to-hidden weights, of shape (n_a, n_x)
                        dWaa -- Gradients of hidden-to-hidden weights, of shape (n_a, n_a)
                        dWya -- Gradients of hidden-to-output weights, of shape (n_y, n_a)
                        db -- Gradients of bias vector, of shape (n_a, 1)
                        dby -- Gradients of output bias vector, of shape (n_y, 1)
    a[len(X)-1] -- the last hidden state, of shape (n_a, 1)
    """

    ### START CODE HERE ###

    # Forward propagate through time (≈1 line)
    loss, cache = None

    # Backpropagate through time (≈1 line)
    gradients, a = None

    # Clip your gradients between -5 (min) and 5 (max) (≈1 line)
    gradients = None

    # Update parameters (≈1 line)
    parameters = None

    ### END CODE HERE ###

    return loss, gradients, a[len(X)-1]_____no_output_____np.random.seed(1)
vocab_size, n_a = 27, 100
a_prev = np.random.randn(n_a, 1)
Wax, Waa, Wya = np.random.randn(n_a, vocab_size), np.random.randn(n_a, n_a), np.random.randn(vocab_size, n_a)
b, by = np.random.randn(n_a, 1), np.random.randn(vocab_size, 1)
parameters = {"Wax": Wax, "Waa": Waa, "Wya": Wya, "b": b, "by": by}
X = [12,3,5,11,22,3]
Y = [4,14,11,22,25, 26]

loss, gradients, a_last = optimize(X, Y, a_prev, parameters, learning_rate = 0.01)
print("Loss =", loss)
print("gradients[\"dWaa\"][1][2] =", gradients["dWaa"][1][2])
print("np.argmax(gradients[\"dWax\"]) =", np.argmax(gradients["dWax"]))
print("gradients[\"dWya\"][1][2] =", gradients["dWya"][1][2])
print("gradients[\"db\"][4] =", gradients["db"][4])
print("gradients[\"dby\"][1] =", gradients["dby"][1])
print("a_last[4] =", a_last[4])_____no_output_____

** Expected output:**

```Python
Loss = 126.503975722
gradients["dWaa"][1][2] = 0.194709315347
np.argmax(gradients["dWax"]) = 93
gradients["dWya"][1][2] = -0.007773876032
gradients["db"][4] = [-0.06809825]
gradients["dby"][1] = [ 0.01538192]
a_last[4] = [-1.]
```_____no_output_____### 3.2 - Training the model _____no_output_____* Given the dataset of dinosaur names, we use each line of the dataset (one name) as one training example.
* Every 2000 steps of stochastic gradient descent, you will sample several randomly chosen names to see how the algorithm is doing.

**Exercise**: Follow the instructions and implement `model()`. When `examples[index]` contains one dinosaur name (string), to create an example (X, Y), you can use this:

##### Set the index `idx` into the list of examples
* Using the for-loop, walk through the shuffled list of dinosaur names in the list "examples".
* For example, if there are n_e examples, and the for-loop increments the index to n_e onwards, think of how you would make the index cycle back to 0, so that we can continue feeding the examples into the model when j is n_e, n_e + 1, etc.
* Hint: n_e + 1 divided by n_e is zero with a remainder of 1.
* `%` is the modulus operator in python.

##### Extract a single example from the list of examples
* `single_example`: use the `idx` index that you set previously to get one word from the list of examples._____no_output_____##### Convert a string into a list of characters: `single_example_chars`
* `single_example_chars`: A string is a list of characters.
* You can use a list comprehension (recommended over for-loops) to generate a list of characters.
```Python
str = 'I love learning'
list_of_chars = [c for c in str]
print(list_of_chars)
```

```
['I', ' ', 'l', 'o', 'v', 'e', ' ', 'l', 'e', 'a', 'r', 'n', 'i', 'n', 'g']
```_____no_output_____##### Convert list of characters to a list of integers: `single_example_ix`
* Create a list that contains the index numbers associated with each character.
* Use the dictionary `char_to_ix`
* You can combine this with the list comprehension that is used to get a list of characters from a string._____no_output_____##### Create the list of input characters: `X`
* `rnn_forward` uses the **`None`** value as a flag to set the input vector as a zero-vector.
* Prepend the list [**`None`**] in front of the list of input characters.
* There is more than one way to prepend a value to a list.  One way is to add two lists together: `['a'] + ['b']`_____no_output_____##### Get the integer representation of the newline character `ix_newline`
* `ix_newline`: The newline character signals the end of the dinosaur name.
    - get the integer representation of the newline character `'\n'`.
    - Use `char_to_ix`_____no_output_____##### Set the list of labels (integer representation of the characters): `Y`
* The goal is to train the RNN to predict the next letter in the name, so the labels are the list of characters that are one time step ahead of the characters in the input `X`.
    - For example, `Y[0]` contains the same value as `X[1]`
* The RNN should predict a newline at the last letter so add ix_newline to the end of the labels.
    - Append the integer representation of the newline character to the end of `Y`.
    - Note that `append` is an in-place operation.
    - It might be easier for you to add two lists together._____no_output_____

# GRADED FUNCTION: model

def model(data, ix_to_char, char_to_ix, num_iterations = 35000, n_a = 50, dino_names = 7, vocab_size = 27, verbose = False):
    """
    Trains the model and generates dinosaur names.

    Arguments:
    data -- text corpus
    ix_to_char -- dictionary that maps the index to a character
    char_to_ix -- dictionary that maps a character to an index
    num_iterations -- number of iterations to train the model for
    n_a -- number of units of the RNN cell
    dino_names -- number of dinosaur names you want to sample at each iteration.
    vocab_size -- number of unique characters found in the text (size of the vocabulary)

    Returns:
    parameters -- learned parameters
    """

    # Retrieve n_x and n_y from vocab_size
    n_x, n_y = vocab_size, vocab_size

    # Initialize parameters
    parameters = initialize_parameters(n_a, n_x, n_y)

    # Initialize loss (this is required because we want to smooth our loss)
    loss = get_initial_loss(vocab_size, dino_names)

    # Build list of all dinosaur names (training examples).
    with open("dinos.txt") as f:
        examples = f.readlines()
    examples = [x.lower().strip() for x in examples]

    # Shuffle list of all dinosaur names
    np.random.seed(0)
    np.random.shuffle(examples)

    # Initialize the hidden state of your LSTM
    a_prev = np.zeros((n_a, 1))

    # Optimization loop
    for j in range(num_iterations):

        ### START CODE HERE ###

        # Set the index `idx` (see instructions above)
        idx = None

        # Set the input X (see instructions above)
        single_example = None
        single_example_chars = None
        single_example_ix = None
        X = None

        # Set the labels Y (see instructions above)
        ix_newline = None
        Y = None

        # Perform one optimization step: Forward-prop -> Backward-prop -> Clip -> Update parameters
        # Choose a learning rate of 0.01
        curr_loss, gradients, a_prev = None

        ### END CODE HERE ###

        # debug statements to aid in correctly forming X, Y
        if verbose and j in [0, len(examples) -1, len(examples)]:
            print("j = " , j, "idx = ", idx,)
        if verbose and j in [0]:
            print("single_example =", single_example)
            print("single_example_chars", single_example_chars)
            print("single_example_ix", single_example_ix)
            print(" X = ", X, "\n", "Y =       ", Y, "\n")

        # Use a latency trick to keep the loss smooth. It happens here to accelerate the training.
        loss = smooth(loss, curr_loss)

        # Every 2000 Iteration, generate "n" characters thanks to sample() to check if the model is learning properly
        if j % 2000 == 0:

            print('Iteration: %d, Loss: %f' % (j, loss) + '\n')

            # The number of dinosaur names to print
            seed = 0
            for name in range(dino_names):

                # Sample indices and print them
                sampled_indices = sample(parameters, char_to_ix, seed)
                print_sample(sampled_indices, ix_to_char)

                seed += 1  # To get the same result (for grading purposes), increment the seed by one.

            print('\n')

    return parameters_____no_output_____

Run the following cell, you should observe your model outputting random-looking characters at the first iteration. After a few thousand iterations, your model should learn to generate reasonable-looking names. _____no_output_____

parameters = model(data, ix_to_char, char_to_ix, verbose = True)_____no_output_____

** Expected Output**

```Python
j =  0 idx =  0
single_example = turiasaurus
single_example_chars ['t', 'u', 'r', 'i', 'a', 's', 'a', 'u', 'r', 'u', 's']
single_example_ix [20, 21, 18, 9, 1, 19, 1, 21, 18, 21, 19]
 X =  [None, 20, 21, 18, 9, 1, 19, 1, 21, 18, 21, 19]
 Y =        [20, 21, 18, 9, 1, 19, 1, 21, 18, 21, 19, 0]

Iteration: 0, Loss: 23.087336

Nkzxwtdmfqoeyhsqwasjkjvu
Kneb
Kzxwtdmfqoeyhsqwasjkjvu
Neb
Zxwtdmfqoeyhsqwasjkjvu
Eb
Xwtdmfqoeyhsqwasjkjvu

j =  1535 idx =  1535
j =  1536 idx =  0
Iteration: 2000, Loss: 27.884160

...

Iteration: 34000, Loss: 22.447230

Onyxipaledisons
Kiabaeropa
Lussiamang
Pacaeptabalsaurus
Xosalong
Eiacoteg
Troia
```_____no_output_____## Conclusion

You can see that your algorithm has started to generate plausible dinosaur names towards the end of the training. At first, it was generating random characters, but towards the end you could see dinosaur names with cool endings. Feel free to run the algorithm even longer and play with hyperparameters to see if you can get even better results. Our implementation generated some really cool names like `maconucon`, `marloralus` and `macingsersaurus`. Your model hopefully also learned that dinosaur names tend to end in `saurus`, `don`, `aura`, `tor`, etc.

If your model generates some non-cool names, don't blame the model entirely--not all actual dinosaur names sound cool. (For example, `dromaeosauroides` is an actual dinosaur name and is in the training set.) But this model should give you a set of candidates from which you can pick the coolest!

This assignment had used a relatively small dataset, so that you could train an RNN quickly on a CPU. Training a model of the english language requires a much bigger dataset, and usually needs much more computation, and could run for many hours on GPUs. We ran our dinosaur name for quite some time, and so far our favorite name is the great, undefeatable, and fierce: Mangosaurus!

_____no_output_____## 4 - Writing like Shakespeare

The rest of this notebook is optional and is not graded, but we hope you'll do it anyway since it's quite fun and informative.

A similar (but more complicated) task is to generate Shakespeare poems. Instead of learning from a dataset of Dinosaur names you can use a collection of Shakespearian poems. Using LSTM cells, you can learn longer term dependencies that span many characters in the text--e.g., where a character appearing somewhere a sequence can influence what should be a different character much much later in the sequence. These long term dependencies were less important with dinosaur names, since the names were quite short.

 Let's become poets!

We have implemented a Shakespeare poem generator with Keras. Run the following cell to load the required packages and models. This may take a few minutes. _____no_output_____

from __future__ import print_function
from keras.callbacks import LambdaCallback
from keras.models import Model, load_model, Sequential
from keras.layers import Dense, Activation, Dropout, Input, Masking
from keras.layers import LSTM
from keras.utils.data_utils import get_file
from keras.preprocessing.sequence import pad_sequences
from shakespeare_utils import *
import sys
import io_____no_output_____

To save you some time, we have already trained a model for ~1000 epochs on a collection of Shakespearian poems called [*"The Sonnets"*](shakespeare.txt). _____no_output_____Let's train the model for one more epoch. When it finishes training for an epoch---this will also take a few minutes---you can run `generate_output`, which will prompt asking you for an input (`<`40 characters). The poem will start with your sentence, and our RNN-Shakespeare will complete the rest of the poem for you! For example, try "Forsooth this maketh no sense " (don't enter the quotation marks). Depending on whether you include the space at the end, your results might also differ--try it both ways, and try other inputs as well.
_____no_output_____

print_callback = LambdaCallback(on_epoch_end=on_epoch_end)

model.fit(x, y, batch_size=128, epochs=1, callbacks=[print_callback])_____no_output_____# Run this cell to try with different inputs without having to re-train the model
generate_output()_____no_output_____

The RNN-Shakespeare model is very similar to the one you have built for dinosaur names. The only major differences are:
- LSTMs instead of the basic RNN to capture longer-range dependencies
- The model is a deeper, stacked LSTM model (2 layer)
- Using Keras instead of python to simplify the code

If you want to learn more, you can also check out the Keras Team's text generation implementation on GitHub: https://github.com/keras-team/keras/blob/master/examples/lstm_text_generation.py.

Congratulations on finishing this notebook! _____no_output_____**References**:
- This exercise took inspiration from Andrej Karpathy's implementation: https://gist.github.com/karpathy/d4dee566867f8291f086. To learn more about text generation, also check out Karpathy's [blog post](http://karpathy.github.io/2015/05/21/rnn-effectiveness/).
- For the Shakespearian poem generator, our implementation was based on the implementation of an LSTM text generator by the Keras team: https://github.com/keras-team/keras/blob/master/examples/lstm_text_generation.py _____no_output_____


# Notebook from blablabananarama/thesis
Path: data_science/code/data_conversion/count2conc.ipynb

<code>
import os
from dotenv import load_dotenv, find_dotenv
from os.path import join, dirname, basename, exists, isdir

### Load environmental variables from the project root directory ###
# find .env automagically by walking up directories until it's found
dotenv_path = find_dotenv()

# load up the entries as environment variables
load_dotenv(dotenv_path)

# now you can get the variables using their names

# Check whether a network drive has been specified
DATABASE = os.environ.get("NETWORK_URL")
if DATABASE == 'None':
    pass
else:
    pass
    #mount network drive here

# set up directory paths
CURRENT_DIR = os.getcwd()
PROJ = dirname(dotenv_path) # project root directory

DATA = join(PROJ, 'data') #data directory
RAW_EXTERNAL = join(DATA, 'raw_external') # external data raw directory
RAW_INTERNAL = join(DATA, 'raw_internal') # internal data raw directory
INTERMEDIATE = join(DATA, 'intermediate') # intermediate data directory
FINAL = join(DATA, 'final') # final data directory

RESULTS = join(PROJ, 'results') # output directory
FIGURES = join(RESULTS, 'figures') # figure output directory
PICTURES = join(RESULTS, 'pictures') # picture output directory

# make folders specific for certain data
folder_name = ''
if folder_name != '':
    #make folders if they don't exist
    if not exists(join(RAW_EXTERNAL, folder_name)):
        os.makedirs(join(RAW_EXTERNAL, folder_name))

    if not exists(join(INTERMEDIATE, folder_name)):
        os.makedirs(join(INTERMEDIATE, folder_name))

    if not exists(join(FINAL, folder_name)):
        os.makedirs(join(FINAL, folder_name))

print('Standard variables loaded, you are good to go!')Standard variables loaded, you are good to go!
import pandas as pd
import re

# Extract of supplementary table 5:
data = pd.read_csv(f"{RAW_INTERNAL}/proteomics/protein_values.csv", index_col=0)
data_counts = pd.read_csv(f"{RAW_EXTERNAL}/raw_proteomics_all.csv", index_col=0)
weight_data = pd.read_csv(f"{RAW_INTERNAL}/proteomics/protein_values.csv", index_col=0)

data_counts = data_counts.drop(data_counts.index[0])

# remove original index and duplicates
data_counts.index = data_counts['UP']

# # Molecular weights:
MW = data["Molecular weight (Da)"] # Da = g/mol
MW = MW / 1000 # kDa = g/mmol
print(MW)

Uniprot Accession
P0A8T7    155.045008
P0A8V2    150.520276
P36683     93.420946
P15254    141.295898
P09831    163.176315
             ...
P36667     31.022762
P0AC78     40.912094
P76164      8.702816
P38506     28.130364
Q46810     21.481914
Name: Molecular weight (Da), Length: 2359, dtype: float64
data_counts.head()_____no_output_____# start and end columns
start_column = data_counts.columns.get_loc("LB")
end_column = data_counts.columns.get_loc("fructose.2")

# rename columns from .2 to uncertainty
# rename_dict = {col_name:re.sub(r".2$", "_uncertainty", col_name) for col_name in \
#  data.columns[start_column_uncertainty:end_column_uncertainty+1]}
# data = data.rename(columns=rename_dict)
# list of indices of data needed
list_of_indices = list(range(start_column,end_column+1))
data_counts = data_counts.iloc[:, list_of_indices]

# get cell volumes
cell_volumes = pd.read_csv(f"{RAW_INTERNAL}/proteomics/growth_conditions.csv", index_col=0)
cell_volumes = cell_volumes["Single cell volume [fl]1"]
# remove the first two rows of LB
cell_volumes = cell_volumes.loc[~cell_volumes.index.duplicated(keep='first')]
# rename the number 3 in there
cell_volumes = cell_volumes.rename({'Osmotic-stress glucose3':'Osmotic-stress glucose_uncertainty'}, axis='index')
_____no_output_____
# get Molecular weights
weights = data_counts
data_counts['Molecular weights'] = weight_data['Molecular weight (Da)'].drop_duplicates()[weights.index]
_____no_output_____# Convert values to mmol/cell:
data_counts = data_counts.apply(lambda x: pd.to_numeric(x, errors='coerce'))
data_counts = data_counts.astype(float) / 6.022e+23 * 1000

# convert names
cell_volumes.index = [re.sub(r'\W+', '', i).lower() for i in cell_volumes.index]
data_counts.columns = [re.sub(r'\W+', '', i).lower() for i in data_counts.columns]

# Iterate through the dataset and divide by the corresponding cell volume, to get mmol/fL:
for (col_name, d) in data_counts.iteritems():
    chemo_name = re.sub(r'2$', '', re.sub(r'1$', '', col_name))
    try:
        data_counts[col_name] = data_counts[col_name] / cell_volumes.loc[chemo_name]#["cell_volume"]
    except:
        print(chemo_name)

# Finally, convert to mmol/gDW:
water_content = 0.3
cell_density = 1.105e-12
data_counts = data_counts / cell_density / water_content

chemostatµ01
chemostatµ01
stationary1day
stationary1day
stationary1day
stationary3days
stationary3days
stationary3days
glucose3
glucose4
glucose5
50mmnacl
50mmnacl
50mmnacl
42c
42c
42c
ph6
ph6
ph6
data_counts.to_csv(f"{INTERMEDIATE}/proteomics/proteomics_concentrations.csv")_____no_output_____for i in range(0,len(weights.columns)-1):
    try:
        weights.iloc[:,i] = pd.to_numeric(weights.iloc[:,i], errors = 'coerce') * weights.iloc[:,-1]
    except:
        print(i)

weights.to_csv(f"{INTERMEDIATE}/proteomics/proteomics_masses.csv")_____no_output_____
</code>