# Notebook from ajlee21/generic-expression-patterns
Path: human_cancer_analysis/2_identify_generic_genes_pathways.ipynb

# Identify generic genes and pathways

Studies have found that some genes are more likely to be differentially expressed even across a wide range of experimental designs. These generic genes and subsequent pathways are not necessarily specific to the biological process being studied but instead represent a more systematic change.

This notebook identifies generic genes and pathways and then evaluates if those identified are consistent with published findings.

**Steps to identify generic genes:**
1. Simulates N gene expression experiments using [ponyo](https://github.com/ajlee21/ponyo)
2. Perform DE analysis to get association statistics for each gene
3. For each gene, aggregate statsitics across all simulated experiments
4. Rank genes based on this aggregated statistic (i.e. log fold change, or p-value)

**Steps to identify generic gene sets (pathways):**
1. Using the same simulated experiments from above, perform GSEA analysis. This analysis will determine whether the genes contained in a gene set are clustered towards the beginning or the end of the ranked list of genes, where genes are ranked by log fold change, indicating a correlation with change in expression.
2. For each gene set (pathway), aggregate statistics across all simulated experiments
3. Rank gene sets based on this aggregated statistic

**Evaluation:**
* We want to compare the ranking of genes identified using the above method with the ranking found from [Crow et. al.](https://www.pnas.org/content/pnas/116/13/6491.full.pdf), which identified a set of genes as generic based on how frequently they were found to be DE across 600 experiments
* We want to compare the ranking of pathways identified using the above method with the ranking based on the [Powers et. al.](https://www.biorxiv.org/content/10.1101/259440v1.full.pdf) data, where ranking was determined based on the fraction of 442 experiments a pathway was found to be enriched
* This comparison will validate our method being used as a way to automatically identify generic genes and pathways._____no_output_____

%load_ext autoreload
%load_ext rpy2.ipython
%autoreload 2
%matplotlib inline

import os
import sys
import pandas as pd
import numpy as np
import pickle
import glob
import scipy.stats as ss
from keras.models import load_model
from rpy2.robjects import pandas2ri
from ponyo import utils, simulate_expression_data
from generic_expression_patterns_modules import process, stats, ranking

pandas2ri.activate()

np.random.seed(123)Using TensorFlow backend.
/home/alexandra/anaconda3/envs/generic_expression/lib/python3.7/site-packages/matplotlib/__init__.py:886: MatplotlibDeprecationWarning:
examples.directory is deprecated; in the future, examples will be found relative to the 'datapath' directory.
  "found relative to the 'datapath' directory.".format(key))
# Read in config variables
base_dir = os.path.abspath(os.path.join(os.getcwd(), "../"))

config_filename = os.path.abspath(
    os.path.join(base_dir, "configs", "config_human_cancer.tsv")
)

params = utils.read_config(config_filename)_____no_output_____# Load params
local_dir = params["local_dir"]
dataset_name = params["dataset_name"]
NN_architecture = params["NN_architecture"]
latent_dim = params["latent_dim"]
num_runs = params["num_simulated"]
project_id = params["project_id"]
metadata_col_id = params["metadata_colname"]
mapped_template_filename = params["mapped_template_filename"]
processed_template_filename = params["processed_template_filename"]
normalized_compendium_filename = params["normalized_compendium_filename"]
scaler_filename = params["scaler_filename"]
col_to_rank_genes = params["rank_genes_by"]
col_to_rank_pathways = params["rank_pathways_by"]
statistic = params["gsea_statistic"]
logFC_name = params["DE_logFC_name"]
pvalue_name = params["DE_pvalue_name"]

# Load metadata file with grouping assignments for samples
sample_id_metadata_filename = os.path.join(
    base_dir, dataset_name, "data", "metadata", f"{project_id}_process_samples.tsv"
)

# Load metadata file with grouping assignments for samples
grp_metadata_filename = os.path.join(
    base_dir, dataset_name, "data", "metadata", f"{project_id}_groups.tsv"
)

# Load metadata file with mapping between experiments and associated samples
metadata_simulate_filename = os.path.join(
    base_dir, dataset_name, "data", "metadata", "all_experiments_sample_annotations.csv"
)
metadata_delimiter = ","
experiment_id_colname = "gse"

# Load pickled file
with open(scaler_filename, "rb") as scaler_fh:
    scaler = pickle.load(scaler_fh)

# Percentile threshold to identify generic genes
percentile_threshold = 80.0/home/alexandra/anaconda3/envs/generic_expression/lib/python3.7/site-packages/sklearn/base.py:315: UserWarning: Trying to unpickle estimator MinMaxScaler from version 0.23.2 when using version 0.24.1. This might lead to breaking code or invalid results. Use at your own risk.
  UserWarning)
# Output files
gene_summary_filename = os.path.join(
    base_dir, dataset_name, f"generic_gene_summary_{project_id}.tsv"
)

pathway_summary_filename = os.path.join(
    base_dir, dataset_name, f"generic_pathway_summary_{project_id}.tsv"
)_____no_output_____

### Simulate experiments using selected template experiment

Workflow:
1. Get the gene expression data for the selected template experiment
2. Encode this experiment into a latent space using the trained VAE model
3. Linearly shift the encoded template experiment in the latent space
4. Decode the samples. This results in a new experiment
5. Repeat steps 1-4 to get multiple simulated experiments_____no_output_____

# Simulate multiple experiments
# This step creates the following files in "<local_dir>/pseudo_experiment/" directory:
#   - selected_simulated_data_SRP012656_<n>.txt
#   - selected_simulated_encoded_data_SRP012656_<n>.txt
#   - template_normalized_data_SRP012656_test.txt
# in which "<n>" is an integer in the range of [0, num_runs-1]
os.makedirs(os.path.join(local_dir, "pseudo_experiment"), exist_ok=True)
simulate_expression_data.shift_template_experiment(
    normalized_compendium_filename,
    NN_architecture,
    latent_dim,
    dataset_name,
    scaler,
    metadata_simulate_filename,
    metadata_delimiter,
    experiment_id_colname,
    metadata_col_id,
    project_id,
    local_dir,
    base_dir,
    num_runs,
)_____no_output_____

### Process template and simulated experiments

* Remove samples not required for comparison. Since this experiment contains multiple conditions (i.e. estradiol vs EtOH at 12, 24, and 48 hrs are each considered a different comparison) being tested, we will only include those samples within the same condition.
* Make sure ordering of samples matches metadata for proper comparison_____no_output_____

if not os.path.exists(sample_id_metadata_filename):
    sample_id_metadata_filename = None

stats.process_samples_for_limma(
    mapped_template_filename,
    grp_metadata_filename,
    processed_template_filename,
    sample_id_metadata_filename,
)

for i in range(num_runs):
    simulated_filename = os.path.join(
        local_dir, "pseudo_experiment", f"selected_simulated_data_{project_id}_{i}.txt"
    )
    out_simulated_filename = os.path.join(
        local_dir,
        "pseudo_experiment",
        f"selected_simulated_data_{project_id}_{i}_processed.txt",
    )
    stats.process_samples_for_limma(
        simulated_filename,
        grp_metadata_filename,
        out_simulated_filename,
        sample_id_metadata_filename,
    )_____no_output_____

### Differential expression analysis

The gene expression dataset is array-based so we will use Limma in this case_____no_output_____

# Create subdirectory: "<local_dir>/DE_stats/"
os.makedirs(os.path.join(local_dir, "DE_stats"), exist_ok=True)_____no_output_____%%R -i grp_metadata_filename -i project_id -i processed_template_filename -i local_dir -i base_dir

source(paste0(base_dir, '/generic_expression_patterns_modules/DE_analysis.R'))

# File created: "<local_dir>/DE_stats/DE_stats_template_data_SRP012656_real.txt"
get_DE_stats_limma(grp_metadata_filename,
                   project_id,
                   processed_template_filename,
                   "template",
                   local_dir,
                   "real")R[write to console]: Loading required package: S4Vectors

R[write to console]: Loading required package: stats4

R[write to console]: Loading required package: BiocGenerics

R[write to console]: Loading required package: parallel

R[write to console]:
Attaching package: ‘BiocGenerics’

R[write to console]: The following objects are masked from ‘package:parallel’:

    clusterApply, clusterApplyLB, clusterCall, clusterEvalQ,
    clusterExport, clusterMap, parApply, parCapply, parLapply,
    parLapplyLB, parRapply, parSapply, parSapplyLB

R[write to console]: The following object is masked from ‘package:limma’:

    plotMA

R[write to console]: The following objects are masked from ‘package:stats’:

    IQR, mad, sd, var, xtabs

R[write to console]: The following objects are masked from ‘package:base’:

    anyDuplicated, append, as.data.frame, basename, cbind, colnames,
    dirname, do.call, duplicated, eval, evalq, Filter, Find, get, grep,
    grepl, intersect, is.unsorted, lapply, Map, mapply, match, mget,
    order, paste, pmax, pmax.int, pmin, pmin.int, Position, rank,
    rbind, Reduce, rownames, sapply, setdiff, sort, table, tapply,
    union, unique, unsplit, which, which.max, which.min

R[write to console]:
Attaching package: ‘S4Vectors’

R[write to console]: The following object is masked from ‘package:base’:

    expand.grid

R[write to console]: Loading required package: IRanges

R[write to console]: Loading required package: GenomicRanges

R[write to console]: Loading required package: GenomeInfoDb

R[write to console]: Loading required package: SummarizedExperiment

R[write to console]: Loading required package: Biobase

R[write to console]: Welcome to Bioconductor

    Vignettes contain introductory material; view with
    'browseVignettes()'. To cite Bioconductor, see
    'citation("Biobase")', and for packages 'citation("pkgname")'.

R[write to console]: Loading required package: DelayedArray

R[write to console]: Loading required package: matrixStats

R[write to console]:
Attaching package: ‘matrixStats’

R[write to console]: The following objects are masked from ‘package:Biobase’:

    anyMissing, rowMedians

R[write to console]: Loading required package: BiocParallel

R[write to console]:
Attaching package: ‘DelayedArray’

R[write to console]: The following objects are masked from ‘package:matrixStats’:

    colMaxs, colMins, colRanges, rowMaxs, rowMins, rowRanges

R[write to console]: The following objects are masked from ‘package:base’:

    aperm, apply, rowsum

R[write to console]: Error in read.table(file = file, header = header, sep = sep, quote = quote,  :
  duplicate 'row.names' are not allowed

R[write to console]: In addition:
R[write to console]: There were 12 warnings (use warnings() to see them)
R[write to console]:

# Check number of DEGs
template_DE_stats_filename = os.path.join(
    local_dir, "DE_stats", f"DE_stats_template_data_{project_id}_real.txt"
)

template_DE_stats = pd.read_csv(
    template_DE_stats_filename, sep="\t", header=0, index_col=0
)

selected = template_DE_stats[
    (template_DE_stats["adj.P.Val"] < 0.05) & (abs(template_DE_stats["logFC"]) > 1)
]
print(selected.shape)(33, 6)
%%R -i grp_metadata_filename -i project_id -i base_dir -i local_dir -i num_runs

source(paste0(base_dir, '/generic_expression_patterns_modules/DE_analysis.R'))

# Files created: "<local_dir>/DE_stats/DE_stats_simulated_data_SRP012656_<n>.txt"
for (i in 0:(num_runs-1)){
    simulated_data_filename <- paste(local_dir,
                                     "pseudo_experiment/selected_simulated_data_",
                                     project_id,
                                     "_",
                                     i,
                                     "_processed.txt",
                                     sep = "")

    get_DE_stats_limma(grp_metadata_filename,
                       project_id,
                       simulated_data_filename,
                       "simulated",
                       local_dir,
                       i)
}R[write to console]: Error in read.table(file = file, header = header, sep = sep, quote = quote,  :
  duplicate 'row.names' are not allowed

### Rank genes_____no_output_____

analysis_type = "DE"
template_DE_stats, simulated_DE_summary_stats = ranking.process_and_rank_genes_pathways(
    template_DE_stats_filename,
    local_dir,
    num_runs,
    project_id,
    analysis_type,
    col_to_rank_genes,
    logFC_name,
    pvalue_name,
)/home/alexandra/anaconda3/envs/generic_expression/lib/python3.7/site-packages/pandas/core/reshape/merge.py:544: UserWarning: merging between different levels can give an unintended result (2 levels on the left, 1 on the right)
  warnings.warn(msg, UserWarning)

### Gene summary table_____no_output_____

summary_gene_ranks = ranking.generate_summary_table(
    template_DE_stats_filename,
    template_DE_stats,
    simulated_DE_summary_stats,
    col_to_rank_genes,
    local_dir,
    "gene",
    params,
)

summary_gene_ranks.head()_____no_output_____# Check if there is an NaN values, there should not be
summary_gene_ranks.isna().any()_____no_output_____# Create `gene_summary_fielname`
summary_gene_ranks.to_csv(gene_summary_filename, sep="\t")_____no_output_____

### Compare gene ranking
Studies have found that some genes are more likely to be differentially expressed even across a wide range of experimental designs. These *generic genes* are not necessarily specific to the biological process being studied but instead represent a more systematic change.

We want to compare the ability to detect these generic genes using our method vs those found by [Crow et. al. publication](https://www.pnas.org/content/pnas/116/13/6491.full.pdf). Their genes are ranked 0 = not commonly DE; 1 = commonly DE. Genes were ranked by the number differentially expressed gene sets a gene appeared in across 600 experiments._____no_output_____

# Get generic genes identified by Crow et. al.
DE_prior_filename = params["reference_gene_filename"]
ref_gene_col = params["reference_gene_name_col"]
ref_rank_col = params["reference_rank_col"]

figure_filename = f"gene_ranking_{col_to_rank_genes}.svg"

corr, shared_ranking = ranking.compare_gene_ranking(
    summary_gene_ranks, DE_prior_filename, ref_gene_col, ref_rank_col, figure_filename
)r = 0.32918582536172053
p = 7.633076397458203e-162
ci_low = 0.30692420900631356
ci_high = 0.35204350564866504

Hypergeometric test:

Given $N$ number of genes with $K$ common genes in Crow et al. SOPHIE identifies $n$ genes as being common. What is the probability that $k$ of the genes identified by SOPHIE are also common in Crow et al.? What is the probability of drawing $k$ or more concordant genes?

This was a way for us to quantify the correlation between SOPHIE and Crow et al common findings, since the correlation coefficient wasn't very convincing since we're considering all genes in addition to the common ones_____no_output_____

num_Crow_genes = shared_ranking.shape[0]
num_generic_Crow_genes = shared_ranking.query(f"{ref_rank_col}>=80.0").shape[0]
num_generic_SOPHIE_genes = shared_ranking[
    shared_ranking["Percentile (simulated)"] >= percentile_threshold
].shape[0]
num_concordant_generic_genes = shared_ranking[
    (shared_ranking[ref_rank_col] >= percentile_threshold)
    & (shared_ranking["Percentile (simulated)"] >= percentile_threshold)
].shape[0]_____no_output_____print(num_Crow_genes)
print(num_generic_Crow_genes)
print(num_generic_SOPHIE_genes)
print(num_concordant_generic_genes)6410
1441
1332
508
p = ss.hypergeom.sf(
    num_concordant_generic_genes,
    num_Crow_genes,
    num_generic_Crow_genes,
    num_generic_SOPHIE_genes,
)
print(p)1.3579186964397293e-49

**Takeaway:**
* Here we are comparing gene percentiles obtained from a SOPHIE trained on Powers et al. vs gene percentiles obtained from manual curation using Crow et al. The plot shows that there is a high correlation between those very high and low ranked genes -- high correlation at the extremes but there is a lot of noise in the middle.
* While the two datasets used the same array platform to generate data, the datasets have different compositions – Crow et al. is a heterogenous mixture of different types of experiments while Power et al. is specifically cancer cell lines treated with small molecules. The consistency we observe in the common DEGs despite the differences in context demonstrates that many common DEGs are differentially expressed regardless of the context._____no_output_____### GSEA
**Goal:** To detect modest but coordinated changes in prespecified sets of related genes (i.e. those genes in the same pathway or share the same GO term).

1. Rank all genes using DE association statistics.
2. An enrichment score (ES) is defined as the maximum distance from the middle of the ranked list. Thus, the enrichment score indicates whether the genes contained in a gene set are clustered towards the beginning or the end of the ranked list (indicating a correlation with change in expression).
3. Estimate the statistical significance of the ES by a phenotypic-based permutation test in order to produce a null distribution for the ES (i.e. scores based on permuted phenotype)_____no_output_____

# Create "<local_dir>/GSEA_stats/" subdirectory
os.makedirs(os.path.join(local_dir, "GSA_stats"), exist_ok=True)_____no_output_____# Load pathway data
hallmark_DB_filename = params["pathway_DB_filename"]_____no_output_____%%R -i base_dir -i template_DE_stats_filename -i hallmark_DB_filename -i statistic -i local_dir -o template_enriched_pathways

source(paste0(base_dir, '/generic_expression_patterns_modules/GSEA_analysis.R'))

out_filename <- paste(local_dir,
                     "GSA_stats/GSEA_stats_template_data_",
                     project_id,
                     "_real.txt",
                     sep = "")

template_enriched_pathways <- find_enriched_pathways(template_DE_stats_filename, hallmark_DB_filename, statistic)
template_enriched_pathways <- as.data.frame(template_enriched_pathways[1:7])

write.table(template_enriched_pathways, file = out_filename, row.names = F, sep = "\t")R[write to console]: Loading required package: Rcpp

print(template_enriched_pathways.shape)
template_enriched_pathways[template_enriched_pathways["padj"] < 0.05].sort_values(
    by="padj"
)(50, 7)

**Quick check:** Looks like enriched pathways are consistent with estradiol being estrogen hormone treatment._____no_output_____

%%R -i project_id -i local_dir -i hallmark_DB_filename -i num_runs -i statistic -i base_dir

source(paste0(base_dir, '/generic_expression_patterns_modules/GSEA_analysis.R'))

# New files created: "<local_dir>/GSEA_stats/GSEA_stats_simulated_data_<project_id>_<n>.txt"
for (i in 0:(num_runs-1)) {
    simulated_DE_stats_filename <- paste(local_dir,
                                     "DE_stats/DE_stats_simulated_data_",
                                     project_id,
                                     "_",
                                     i,
                                     ".txt",
                                     sep = "")

    out_filename <- paste(local_dir,
                     "GSA_stats/GSEA_stats_simulated_data_",
                     project_id,
                     "_",
                     i,
                     ".txt",
                     sep = "")

    enriched_pathways <- find_enriched_pathways(simulated_DE_stats_filename, hallmark_DB_filename, statistic)

    write.table(as.data.frame(enriched_pathways[1:7]), file = out_filename, row.names = F, sep = "\t")
}_____no_output_____

### Rank pathways_____no_output_____

analysis_type = "GSA"
template_GSEA_stats_filename = os.path.join(
    local_dir, "GSA_stats", f"GSEA_stats_template_data_{project_id}_real.txt"
)
(
    template_GSEA_stats,
    simulated_GSEA_summary_stats,
) = ranking.process_and_rank_genes_pathways(
    template_GSEA_stats_filename,
    local_dir,
    num_runs,
    project_id,
    analysis_type,
    col_to_rank_pathways,
    logFC_name,
    pvalue_name,
    "GSEA",
)_____no_output_____

### Pathway summary table_____no_output_____

# Create intermediate file: "<local_dir>/gene_summary_table_<col_to_rank_pathways>.tsv"
summary_pathway_ranks = ranking.generate_summary_table(
    template_GSEA_stats_filename,
    template_GSEA_stats,
    simulated_GSEA_summary_stats,
    col_to_rank_pathways,
    local_dir,
    "pathway",
    params,
)

summary_pathway_ranks.sort_values(by="Rank (simulated)", ascending=False).head(10)/home/alexandra/anaconda3/envs/generic_expression/lib/python3.7/site-packages/pandas/core/reshape/merge.py:544: UserWarning: merging between different levels can give an unintended result (1 levels on the left, 2 on the right)
  warnings.warn(msg, UserWarning)
# Create `pathway_summary_filename`
summary_pathway_ranks.to_csv(pathway_summary_filename, sep="\t")_____no_output_____

### Compare pathway ranking_____no_output_____Studies have found that there are some pathways (gene sets) that are more likely to be significantly enriched in DEGs across a wide range of experimental designs. These generic pathways are not necessarily specific to the biological process being studied but instead represents a more systematic change.

We want to compare the ability to detect these generic pathways using our method vs those found by [Powers et. al.](https://www.biorxiv.org/content/10.1101/259440v1.full.pdf) publication.  We will use the `Hallmarks_qvalues_GSEAPreranked.csv` file from https://www.synapse.org/#!Synapse:syn11806255 as a reference. The file contains the q-value (adjusted p-value) for the test: given the enrichment score (ES) of the experiment is significant compared to the null distribution of enrichment scores, where the null set is generated from permuted gene sets. For each gene set (pathway) they calculate the q-value using this test.

To get a `reference ranking`, we calculate the fraction of experiments that a given pathway was significant (q-value <0.05) and use this rank pathways. `Our ranking` is to rank pathways based on the median q-value across the simulated experiments. We can then compare `our ranking` versus the `reference ranking.`_____no_output_____

# Load Powers et. al. results file
powers_rank_filename = os.path.join(
    base_dir, dataset_name, "data", "metadata", "Hallmarks_qvalues_GSEAPreranked.csv"
)_____no_output_____# Read Powers et. al. data
# This file contains qvalue results for hallmark pathways across ~400 experiments
powers_rank_df = pd.read_csv(powers_rank_filename, header=0, index_col=0)
powers_rank_df.drop(["Category"], axis=1, inplace=True)
print(powers_rank_df.shape)
powers_rank_df.head()(50, 443)
# Count the number of experiments where a given pathway was found to be enriched (qvalue < 0.05)
total_num_experiments = powers_rank_df.shape[1]
frac_enriched_pathways = (powers_rank_df < 0.05).sum(axis=1) / total_num_experiments

# Rank pathways from 0-50, 50 indicating that the pathways was frequently enriched
pathway_ranks = frac_enriched_pathways.rank()

powers_rank_stats_df = pd.DataFrame(
    data={
        "Fraction enriched": frac_enriched_pathways.values,
        "Powers Rank": pathway_ranks.values,
    },
    index=powers_rank_df.index,
)
powers_rank_stats_df.sort_values(by="Powers Rank", ascending=False).head()_____no_output_____# Save reference file for input into comparison
powers_rank_processed_filename = os.path.join(
    base_dir,
    dataset_name,
    "data",
    "metadata",
    "Hallmarks_qvalues_GSEAPreranked_processed.tsv",
)

powers_rank_stats_df.to_csv(
    powers_rank_processed_filename,
    sep="\t",
)_____no_output_____figure_filename = f"pathway_ranking_{col_to_rank_pathways}.svg"

ranking.compare_pathway_ranking(
    summary_pathway_ranks, powers_rank_processed_filename, figure_filename
)r = 0.664977303386151
p = 1.385061712795202e-07
ci_low = 0.4260096158769527
ci_high = 0.8167144638148575
{'r': 0.664977303386151, 'p': 1.385061712795202e-07, 'ci_low': 0.4260096158769527, 'ci_high': 0.8167144638148575}

**Takeaway:**
* Our method ranked pathways using median adjusted p-value score across simulated experiments.
* Powers et. al. ranked pathways based on the fraction of experiments they had adjusted p-value < 0.05.
* Here we validated that our analysis pipeline is working correctly by comparing pathway ranks obtained from a (Powers et. al.)-trained VAE model vs pathway ranking based on manual curation using Powers et. al datasets. We expect to see a high correlation between pathway ranks given that we are using the same training dataset. Indeed that is what we find._____no_output_____


# Notebook from tobias-fyi/vela
Path: ds/practice/daily_practice/20-08/20-08-21-234-fri.ipynb

# 20-08-21: Daily Practice_____no_output_____---
---

## Practices_____no_output_____### Daily

* [x] [Job Search](#2-Hour-Job-Search)
  * [x] 2HJS progress
  * [x] Network
  * [x] Social media
* [ ] [Meta-data: read & write](#Meta-data:-reading-&-writing)
* [x] [Practice & learn](#Practice-&-learn)
  * [x] Data science and machine learning
  * [x] Algorithms, data structures, and brain teasers
  * [ ] Interviewing, STAR storytelling_____no_output_____### Weekly

Vim command(s) I'll get familiar with this week:

_____no_output_____---
---

## 2-Hour Job Search_____no_output_____### Networking and application

Sent a resume through Twitter to an opportunity that arose somewhat randomly. Figured I should send it and see what happens._____no_output_____---
---

## Meta-data: read & write_____no_output_____---
---

## Practice & learn_____no_output_____---

### Data science and machine learning_____no_output_____#### Grokking Deep Learning, Ch 2

Making a prediction with multiple inputs

* Combining intelligence from multiple datapoints
  * Multiply each input by its own weight
  * Sum the predictions (weighted sum)
    * Dot product_____no_output_____

def w_sum(a, b):
    assert(len(a) == len(b))
    output = 0
    for i in range(len(a)):
        output += (a[i] * b[i])
    return output

def neural_network(input, weights):
    pred = w_sum(input, weights)
    return pred_____no_output_____weights = [0.1, 0.2, 0]

toes = [8.5, 9.5, 9.9, 9.0]
wlrec = [0.65, 0.8, 0.8, 0.9]
nfans = [1.2, 1.3, 0.5, 1.0]

input = [toes[0], wlrec[0], nfans[0]]

pred = neural_network(input, weights)
pred_____no_output_____

##### Vector math

* Elementwise operation
  * Mathematical operation between two vectors of equal length
  * Pair up values according to their positions in their vectors

##### Dot product intuition

The intuition behind how and why a dot product (weighted sum) works is one of the most important parts of understanding how NNs make predictions.

> A dot product gives you a notion of similarity between two vectors

The highest dot product is between two identical vectors._____no_output_____#### Left off on page 32_____no_output_____---

### Algorithms, data structures, and brain teasers_____no_output_____#### [Alternating Characters](https://www.hackerrank.com/challenges/alternating-characters/problem)

You are given a string containing characters `A` and `B` only. Your task is to change it into a string such that there are no matching adjacent characters. To do this, you are allowed to delete zero or more characters in the string.

Your task is to find the minimum number of required deletions.

For example, given the string `s = AABAAB`, remove an A at positions 0 and 3 to make `s = ABAB` in 2 deletions._____no_output_____

# Complete the alternatingCharacters function below.
def alternatingCharacters(s):
    deletions = 0  # Keep track of number of deletions
    prev = None    # Keep track of previous letter
    counter = 0    # Use counter + while loop
    while counter < len(s):  # Iterate through letters of string
        if prev is not None:
            # If there was a previous letter, then compare to current
            if s[counter] == prev:
                # If they are the same, increment deletions.
                deletions += 1
        prev = s[counter]    # Set prev to current
        counter += 1    # Iterate to next letter
    # Return deletions
    return deletions_____no_output_____st1 = "AABAAB"

alternatingCharacters(st1)_____no_output_____

#### [Sherlock and the Valid String](https://www.hackerrank.com/challenges/sherlock-and-valid-string/problem)

Sherlock considers a string to be valid if all characters of the string appear the same number of times. It is also valid if he can remove just 1 character at 1 index in the string, and the remaining characters will occur the same number of times. Given a string `s`, determine if it is valid. If so, return YES, otherwise return NO.

For example, if `s = abc`, it is a valid string because frequencies are `{a:1, b:1, c:1}`. So is because we can remove one `c` and have of `1` each character in the remaining string. If `s = abccc` however, the string is not valid as we can only remove `1` occurrence of `c`. That would leave character frequencies of `{a:1, b:1, c:2}`.

Complete the isValid function in the editor below. It should return either the string `YES` or the string `NO`._____no_output_____

# Complete the isValid function below.
def isValid(s):
    pass_____no_output_____s1 = "aabbccddeefghi"  # NO
s2 = "abcdefghhgfedecba" # YES_____no_output_____

---

### Interviewing and STAR storytelling_____no_output_____This question was asked by: Amazon

Let's say you have a table with a billion rows.

How would you add a column inserting data from the original source without affecting the user experience?_____no_output_____


# Notebook from adithyaan-creator/Python
Path: A_Primer_on_Deep_Learning_in_Genomics_Public.ipynb

# Deep Learning in Genomics Primer (*Tutorial*)_____no_output_____This tutorial is a supplement to the manuscript, **A Primer on Deep Learning in Genomics** (*Nature Genetics, 2018*) by James Zou, Mikael Huss, Abubakar Abid, Pejman Mohammadi, Ali Torkamani & Amalio Telentil. [Read the accompanying paper here](https://www.nature.com/articles/s41588-018-0295-5).

If you have any questions or feedback regarding this tutorial, please contact Abubakar Abid <<a12d@stanford.edu>> or James Zou <<jamesz@stanford.edu>>._____no_output_____## Outline _____no_output_____* **How to Use This Tutorial**
* ** 0. Background **
* **1. Curate the Data**
* **2. Select the Architecture and Train**
* **3. Evaluate**
* **4. Interpret**_____no_output_____## How to Use This Tutorial _____no_output_____This tutorial utilizes a Colab notebook , which is an interactive computational enviroment that combines live code, visualizations, and explanatory text. To run this notebook, you may first need to make a copy by choosing **File > Save a Copy in Drive** from the menu bar (may take a few moments to save).

The notebook is organized into a series of cells. You can modify the Python command and execute each cell as you would a Jupyter notebook. To run all of the cells at once, choose **Runtime > Run all** from the menu bar._____no_output_____## 0. Background_____no_output_____In this tutorial, we will show how to use deep learning to approach an important problem in functional genomics: **the discovery of transcription-factor binding sites in DNA**.

As we go through this notebook, we will  design a neural network that can discover binding motifs in DNA based on the results of an assay that determines whether a longer DNA sequence binds to the protein or not. Here, the longer DNA sequences are our *independent variables* (or *predictors*), while the positive or negative response of the assay is the *dependent variable* (or *response*).

We will use simulated data that consists of DNA sequences of length 50 bases (chosen to be artificially short so that the data is easy to play around with), and is labeled with 0 or 1 depending on the result of the assay. Our goal is to build a classifier that can predict whether a particular sequence will bind to the protein and discover the short motif that is the binding site in the sequences that are bound to the protein.

(Spoiler alert: the true regulatory motif is *`CGACCGAACTCC`*. Of course, the neural network doesn't know this.)
_____no_output_____## 1. Curate the Data_____no_output_____![alt text](https://github.com/abidlabs/deep-learning-genomics-primer/blob/master/Screenshot%20from%202018-08-01%2020-31-01.png?raw=true)_____no_output_____In order to train the neural network, we must load and preprocess the data, which consists of DNA sequences and their corresponding labels.By processing this data, the network will learn to distinguish sequences that bind to the transcription factor from those that do not. We will split the data into three different sub-datasets:

(1) Training dataset: a dataset used to fit the parameters of a model or to define the weights of connections between neurons of a neural network.

(2) Validation dataset: a second dataset used to minimize overfitting. The weights of the network are not adjusted with this data set. After each training cycle, if the accuracy over the training data set increases, but the accuracy over the validation data set stays the same or decreases, then there is overfitting on the neural network.

(3) Testing dataset: is a third dataset not included in the training nor validation data sets. After all the training and validation cycles are complete, this dataset is used only for testing the final solution in order to measure the actual predictive power of the neural network on new examples.

-----------

We start by loading the simulated data from an external repository._____no_output_____

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import requests

SEQUENCES_URL = 'https://raw.githubusercontent.com/abidlabs/deep-learning-genomics-primer/master/sequences.txt'

sequences = requests.get(SEQUENCES_URL).text.split('\n')
sequences = list(filter(None, sequences))  # This removes empty sequences.

# Let's print the first few sequences.
pd.DataFrame(sequences, index=np.arange(1, len(sequences)+1),
             columns=['Sequences']).head()_____no_output_____

The next  step is to organize the data into a format that can be passed into a deep learning algorithm. Most deep learning algorithms accept data in the form of vectors or matrices (or more generally, tensors).

To get each DNA sequence in the form of a matrix, we use _one-hot encoding_, which encodes every base in a sequence in the form of a 4-dimensional vector, with a separate dimension for each base. We place a "1" in the dimension corresponding to the base found in the DNA sequence, and "0"s in all other slots. We then concatenate these 4-dimensional vectors together along the bases in the sequence to form a matrix.

In the cell below, we one-hot encode the simulated DNA sequences, and show an example of what the one-hot encoded sequence looks like:_____no_output_____

from sklearn.preprocessing import LabelEncoder, OneHotEncoder

# The LabelEncoder encodes a sequence of bases as a sequence of integers.
integer_encoder = LabelEncoder()
# The OneHotEncoder converts an array of integers to a sparse matrix where
# each row corresponds to one possible value of each feature.
one_hot_encoder = OneHotEncoder(categories='auto')
input_features = []

for sequence in sequences:
  integer_encoded = integer_encoder.fit_transform(list(sequence))
  integer_encoded = np.array(integer_encoded).reshape(-1, 1)
  one_hot_encoded = one_hot_encoder.fit_transform(integer_encoded)
  input_features.append(one_hot_encoded.toarray())

np.set_printoptions(threshold=40)
input_features = np.stack(input_features)
print("Example sequence\n-----------------------")
print('DNA Sequence #1:\n',sequences[0][:10],'...',sequences[0][-10:])
print('One hot encoding of Sequence #1:\n',input_features[0].T)Example sequence
-----------------------
DNA Sequence #1:
 CCGAGGGCTA ... CGCGGACACC
One hot encoding of Sequence #1:
 [[0. 0. 0. ... 1. 0. 0.]
 [1. 1. 0. ... 0. 1. 1.]
 [0. 0. 1. ... 0. 0. 0.]
 [0. 0. 0. ... 0. 0. 0.]]

Similarly, we can go ahead and load the labels (_response variables_). In this case, the labels are structured as follows: a "1" indicates that a protein bound to the sequence, while a "0" indicates that the protein did not. While we could use the labels as a vector, it is often easier to similarly one-hot encode the labels, as we did the features. We carry out that here:_____no_output_____

LABELS_URL = 'https://raw.githubusercontent.com/abidlabs/deep-learning-genomics-primer/master/labels.txt'

labels = requests.get(LABELS_URL).text.split('\n')
labels = list(filter(None, labels))  # removes empty sequences

one_hot_encoder = OneHotEncoder(categories='auto')
labels = np.array(labels).reshape(-1, 1)
input_labels = one_hot_encoder.fit_transform(labels).toarray()

print('Labels:\n',labels.T)
print('One-hot encoded labels:\n',input_labels.T)Labels:
 [['0' '0' '0' ... '0' '1' '1']]
One-hot encoded labels:
 [[1. 1. 1. ... 1. 0. 0.]
 [0. 0. 0. ... 0. 1. 1.]]

We also go ahead and split the data into training and test sets. The purpose of the test set is to ensure that we can observe the performance of the model on new data, not seen previously during training. At a later step, we will further partition the training set into a training and validation set._____no_output_____

from sklearn.model_selection import train_test_split

train_features, test_features, train_labels, test_labels = train_test_split(
    input_features, input_labels, test_size=0.25, random_state=42)_____no_output_____

## 2. Select the Architecture and Train_____no_output_____![alt text](https://github.com/abidlabs/deep-learning-genomics-primer/blob/master/Screenshot%20from%202018-08-01%2020-31-49.png?raw=true)_____no_output_____Next, we choose a neural network architecture to train the model. In this tutorial, we choose a simple 1D convolutional neural network (CNN), which is commonly used in deep learning for functional genomics applications.

A CNN learns to recognize patterns that are generally invariant across space, by trying to match the input sequence to a number of learnable "filters" of a fixed size. In our dataset, the filters will be motifs within the DNA sequences. The CNN may then learn to combine these filters to recognize a larger structure (e.g. the presence or absence of a transcription factor binding site).

We will use the deep learning library `Keras`. As of 2017, `Keras` has been integrated into `TensorFlow`,  which makes it very easy to construct neural networks. We only need to specify the kinds of layers we would like to include in our network, and the dimensionality of each layer. The CNN we generate in this example consists of the following layers:

- _Conv1D_: We define our convolutional layer to have 32 filters of size 12 bases.

- _MaxPooling1D_: After the convolution, we use a pooling layer to down-sample the output of the each of the 32 convolutional filters. Though not always required, this is a typical form of non-linear down-sampling used in CNNs.

- _Flatten_: This layer flattens the output of the max pooling layer, combining the results of the convolution and pooling layers across all 32 filters.

- _Dense_: The first Dense tensor creates a layer (dense_1) that compresses the representation of the flattened layer, resulting in smaller layer with 16 tensors, and the second Dense function converges the tensors into the output layer (dense_2) that consists of the two possible response values (0 or 1).

We can see the details of the architecture of the neural network we have created by running `model.summary()`, which prints the dimensionality and number of parameters for each layer in our network. _____no_output_____

from tensorflow.keras.layers import Conv1D, Dense, MaxPooling1D, Flatten
from tensorflow.keras.models import Sequential

model = Sequential()
model.add(Conv1D(filters=32, kernel_size=12,
                 input_shape=(train_features.shape[1], 4)))
model.add(MaxPooling1D(pool_size=4))
model.add(Flatten())
model.add(Dense(16, activation='relu'))
model.add(Dense(2, activation='softmax'))

model.compile(loss='binary_crossentropy', optimizer='adam',
              metrics=['binary_accuracy'])
model.summary()WARNING:tensorflow:From /usr/local/lib/python3.6/dist-packages/tensorflow/python/ops/resource_variable_ops.py:435: colocate_with (from tensorflow.python.framework.ops) is deprecated and will be removed in a future version.
Instructions for updating:
Colocations handled automatically by placer.
_________________________________________________________________
Layer (type)                 Output Shape              Param #
=================================================================
conv1d (Conv1D)              (None, 39, 32)            1568
_________________________________________________________________
max_pooling1d (MaxPooling1D) (None, 9, 32)             0
_________________________________________________________________
flatten (Flatten)            (None, 288)               0
_________________________________________________________________
dense (Dense)                (None, 16)                4624
_________________________________________________________________
dense_1 (Dense)              (None, 2)                 34
=================================================================
Total params: 6,226
Trainable params: 6,226
Non-trainable params: 0
_________________________________________________________________

Now, we are ready to go ahead and train the neural network. We will further divide the training set into a training and validation set. We will train only on the reduced training set, but plot the loss curve on both the training and validation sets. Once the loss for the validation set stops improving or gets worse throughout the learning cycles, it is time to stop training because the model has already converged and may be just overfitting._____no_output_____

history = model.fit(train_features, train_labels,
                    epochs=50, verbose=0, validation_split=0.25)

plt.figure()
plt.plot(history.history['loss'])
plt.plot(history.history['val_loss'])
plt.title('model loss')
plt.ylabel('loss')
plt.xlabel('epoch')
plt.legend(['train', 'validation'])
plt.show()WARNING:tensorflow:From /usr/local/lib/python3.6/dist-packages/tensorflow/python/ops/math_ops.py:3066: to_int32 (from tensorflow.python.ops.math_ops) is deprecated and will be removed in a future version.
Instructions for updating:
Use tf.cast instead.

Similarly, we can plot the accuracy of our neural network on the binary classification task. The metric used in this example is the _binary accuracy_, which calculates the proportion of predictions that match labels or response variables. Other metrics may be used in different tasks -- for example, the _mean squared error_ is typically used to measure the accuracy for continuous response variables (e.g. polygenic risk scores, total serum cholesterol level, height, weight and systolic blood pressure)._____no_output_____

plt.figure()
plt.plot(history.history['binary_accuracy'])
plt.plot(history.history['val_binary_accuracy'])
plt.title('model accuracy')
plt.ylabel('accuracy')
plt.xlabel('epoch')
plt.legend(['train', 'validation'])
plt.show()_____no_output_____

## 3. Evaluate_____no_output_____![alt text](https://github.com/abidlabs/deep-learning-genomics-primer/blob/master/Screenshot%20from%202018-08-01%2020-32-12.png?raw=true)_____no_output_____The best way to evaluate whether the network has learned to classify sequences is to evaluate its performance on a fresh test set consisting of data that it has not observed at all during training. Here, we evaluate the model on the test set and plot the results as a confusion matrix. Nearly every test sequence should be correctly classified._____no_output_____

from sklearn.metrics import confusion_matrix
import itertools

predicted_labels = model.predict(np.stack(test_features))
cm = confusion_matrix(np.argmax(test_labels, axis=1),
                      np.argmax(predicted_labels, axis=1))
print('Confusion matrix:\n',cm)

cm = cm.astype('float') / cm.sum(axis = 1)[:, np.newaxis]

plt.imshow(cm, cmap=plt.cm.Blues)
plt.title('Normalized confusion matrix')
plt.colorbar()
plt.xlabel('True label')
plt.ylabel('Predicted label')
plt.xticks([0, 1]); plt.yticks([0, 1])
plt.grid('off')
for i, j in itertools.product(range(cm.shape[0]), range(cm.shape[1])):
    plt.text(j, i, format(cm[i, j], '.2f'),
             horizontalalignment='center',
             color='white' if cm[i, j] > 0.5 else 'black')Confusion matrix:
 [[251   8]
 [  2 239]]

## 4. Interpret_____no_output_____![alt text](https://github.com/abidlabs/deep-learning-genomics-primer/blob/master/Screenshot%20from%202018-08-01%2020-32-31.png?raw=true)_____no_output_____Your results so far should allow you to conclude that the neural network is quite effective in learning to distinguish sequences that bind the protein from sequences that do not. But can we understand _why_ the neural network classifies a training point in the way that it does? To do so, we can compute a simple _saliency map_, which is the gradient of the model's prediction with respect to each individual nucleotide.

In other words, the saliency maps shows how the output response value changes with respect to a small changes in input nucleotide sequence. All the positive values in the gradients tell us that a small change to that nucleotide will change the output value. Hence, visualizing these gradients for a given input sequence, should provide some clues about what nucleotides form the binding motive that we are trying to identify._____no_output_____

import tensorflow.keras.backend as K

def compute_salient_bases(model, x):
  input_tensors = [model.input]
  gradients = model.optimizer.get_gradients(model.output[0][1], model.input)
  compute_gradients = K.function(inputs = input_tensors, outputs = gradients)

  x_value = np.expand_dims(x, axis=0)
  gradients = compute_gradients([x_value])[0][0]
  sal = np.clip(np.sum(np.multiply(gradients,x), axis=1),a_min=0, a_max=None)
  return sal_____no_output_____sequence_index = 1999  # You can change this to compute the gradient for a different example. But if so, change the coloring below as well.
sal = compute_salient_bases(model, input_features[sequence_index])

plt.figure(figsize=[16,5])
barlist = plt.bar(np.arange(len(sal)), sal)
[barlist[i].set_color('C1') for i in range(5,17)]  # Change the coloring here if you change the sequence index.
plt.xlabel('Bases')
plt.ylabel('Magnitude of saliency values')
plt.xticks(np.arange(len(sal)), list(sequences[sequence_index]));
plt.title('Saliency map for bases in one of the positive sequences'
          ' (green indicates the actual bases in motif)');_____no_output_____

The results above should show high saliency values for the bases _CGACCGAACTCC_ appearing in the DNA sequence. If you recall from the top of the document, this is exactly the motif that we embedded in the positive sequences! The raw saliency values may be non-zero for other bases as well -- the gradient-based saliency map method is not perfect, and there other more complex interpretation methods that are used in practice to obtain better results.

Furthermore, we may explore other architectures for our neural network to see if we can improve performance on the validation dataset. For example, we could choose different _hyper-parameters_, which are variables that define the network structure (e.g. the number of dense or convolutional layers, the dimensionality of each layer, etc.) and variables that determine how the network is trained (e.g. the number of epochs, the learning rate, etc.). Testing different hyper-parameter values or performing a hyper-parameter search grid are good practices that may help the deep learning procedure to obtain a clearer signal for classifying sequences and identifying the binding motif._____no_output_____## Acknowledgements _____no_output_____Thanks to Julia di lulio and Raquel Dias for helpful comments and suggestions in preparing this notebook._____no_output_____# GitHub Repository_____no_output_____If you found this tutorial helpful, kindly star the [associated GitHub repo](https://github.com/abidlabs/deep-learning-genomics-primer/blob/master/A_Primer_on_Deep_Learning_in_Genomics_Public.ipynb) so that it is more visible to others as well!_____no_output_____


# Notebook from helenxhou/course-content
Path: tutorials/W0D1_PythonWorkshop1/student/W0D1_Tutorial1.ipynb

_____no_output_____# Neuromatch Academy: Week 0, Day 1, Tutorial 1
# Python for NMA - LIF Neuron - Part I_____no_output_____

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

---
## Tutorial objectives
NMA students use Python skills to advance their understanding of neuroscience. Just like two legs that support and strengthen each other. One has "Python" written in it, and the other has "Neuro". And step-by-step they go.

In this notebook, we'll practice basic operations with Python variables, control flow, plotting,  and a sneak peek at `np.array`, the workhorse of scientific computation in Python.

Each new concept in Python will unlock a different aspect of our implementation of a **Leaky Integrate-and-Fire (LIF)** neuron. And as if it couldn't get any better, we'll visualize the evolution of its membrane potential in time, and extract its statistical properties!

Well then, let's start our walk today!_____no_output_____---
## Imports and helper functions
Please execute the cell(s) below to initialize the notebook environment._____no_output_____

# Import libraries
import numpy as np                 # import numpy
import matplotlib.pyplot as plt    # import matplotlib
from IPython import display_____no_output_____# @title Figure settings
%matplotlib inline
%config InlineBackend.figure_format = 'retina'
plt.style.use('ggplot')

fig_w, fig_h = (8, 6)
plt.rcParams['figure.figsize'] = (fig_w, fig_h)
plt.rcParams['font.size'] = 14
plt.rcParams['lines.markersize'] = 11
plt.rcParams['lines.markeredgecolor'] = 'none'
_____no_output_____

---
## Neuron model
A *membrane equation* and a *reset condition* define our *leaky-integrate-and-fire (LIF)* neuron:

\begin{align*}
\\
&\tau_m\,\frac{d}{dt}\,V(t) = E_{L} - V(t) + R\,I(t) &\text{if }\quad V(t) \leq V_{th}\\
\\
&V(t) = V_{reset} &\text{otherwise}\\
\\
\end{align*}

where $V(t)$ is the membrane potential, $\tau_m$ is the membrane time constant, $E_{L}$ is the leak potential, $R$ is the membrane resistance, $I(t)$ is the synaptic input current, $V_{th}$ is the firing threshold, and $V_{reset}$ is the reset voltage. We can also write $V_m$ for membrane potential - very convenient for plot labels.

The membrane equation is an *ordinary differential equation (ODE)* that describes the time evolution of membrane potential $V(t)$ in response to synaptic input and leaking of change across the cell membrane.

**Note that, in this tutorial the neuron model will not implement a spiking mechanism.**_____no_output_____### Exercise 1
We start by defining and initializing the main simulation variables.

**Suggestions**
* Modify the code below to print simulation parameters_____no_output_____

# Exercise 1
# insert your code here

# t_max = 150e-3   # second
# dt = 1e-3        # second
# tau = 20e-3      # second
# el = -60e-3      # milivolt
# vr = -70e-3      # milivolt
# vth = -50e-3     # milivolt
# r = 100e6        # ohm
# i_mean = 25e-11  # ampere

# print(t_max, dt, tau, el, vr, vth, r, i_mean)_____no_output_____

**SAMPLE OUTPUT**

```
0.15 0.001 0.02 -0.06 -0.07 -0.05 100000000.0 2.5e-10
```_____no_output_____[*Click for solution*](https://github.com/NeuromatchAcademy/course-content/tree/master//tutorials/W0D1_PythonWorkshop1/solutions/W0D1_Tutorial1_Solution_ea2e072e.py)

_____no_output_____### Exercise 2
![synaptic input](https://github.com/mpbrigham/colaboratory-figures/raw/master/nma/python-for-nma/synaptic_input.png)

We start with a sinusoidal model to simulate the synaptic input $I(t)$ given by:
\begin{align*}
\\
I(t)=I_{mean}\left(1+\sin\left(\frac{2 \pi}{0.01}\,t\right)\right)\\
\\
\end{align*}

Compute the values of synaptic input $I(t)$ between $t=0$ and $t=0.009$ with step $\Delta t=0.001$.

**Suggestions**
* Loop variable `step` for 10 steps (`step` takes values from `0` to `9`)
* At each time step
    * Compute the value of `t` with variables `step` and `dt`
    * Compute the value of `i`
    * Print `i`
* Use `np.pi` and `np.sin` for evaluating $\pi$ and $\sin(\cdot)$, respectively_____no_output_____

# Exercise 2
# initialize t
t = 0

# loop for 10 steps, variable 'step' takes values from 0 to 9
for step in range(10):
  t = step * dt

  # --> insert your code here
_____no_output_____

**SAMPLE OUTPUT**

```
2.5e-10
3.969463130731183e-10
4.877641290737885e-10
4.877641290737885e-10
3.9694631307311837e-10
2.5000000000000007e-10
1.0305368692688176e-10
1.2235870926211617e-11
1.223587092621159e-11
1.0305368692688186e-10
```_____no_output_____[*Click for solution*](https://github.com/NeuromatchAcademy/course-content/tree/master//tutorials/W0D1_PythonWorkshop1/solutions/W0D1_Tutorial1_Solution_1a190769.py)

_____no_output_____### Exercise 3
Print formatting is handy for displaying simulation parameters in a clean and organized form. Python 3.6 introduced the new string formatting [f-strings](https://www.python.org/dev/peps/pep-0498). Since we are dealing with variables of type `float`, we use `f'{x:.3f}'` for formatting `x` to three decimal points, and `f'{x:.4e}'` for four decimal points but in exponential notation.
```
x = 0.314159265
print(f'{x:.3f}')
--> 0.314

print(f'{x:.4e}')
--> 3.1416e-01
```

Repeat the loop from the previous exercise and print the values of `t` with three decimal points, and synaptic input $I(t)$ with four decimal points in exponential notation.

For additional formatting options with f-strings see [here](http://zetcode.com/python/fstring/).

**Suggestions**
* Print `t` and `i` with help of *f-strings* formatting_____no_output_____

# Exercise 3
# initialize step_end
step_end = 10

# loop for step_end steps
for step in range(step_end):
  t = step * dt

  # --> insert your code here
_____no_output_____

**SAMPLE OUTPUT**

```
0.000 2.5000e-10
0.001 3.9695e-10
0.002 4.8776e-10
0.003 4.8776e-10
0.004 3.9695e-10
0.005 2.5000e-10
0.006 1.0305e-10
0.007 1.2236e-11
0.008 1.2236e-11
0.009 1.0305e-10
```_____no_output_____[*Click for solution*](https://github.com/NeuromatchAcademy/course-content/tree/master//tutorials/W0D1_PythonWorkshop1/solutions/W0D1_Tutorial1_Solution_d4f77dbf.py)

_____no_output_____## ODE integration without spikes
In the next exercises, we now simulate the evolution of the membrane equation in discrete time steps, with a sufficiently small $\Delta t$.

We start by writing the time derivative $d/dt\,V(t)$ in the membrane equation without taking the limit $\Delta t \to 0$:

\begin{align*}
\\
\tau_m\,\frac{V\left(t+\Delta t\right)-V\left(t\right)}{\Delta t} &= E_{L} - V(t) + R\,I(t) \qquad\qquad (1)\\
\\
\end{align*}

The value of membrane potential $V\left(t+\Delta t\right)$ can be expressed in terms of its previous value $V(t)$ by simple algebraic manipulation. For *small enough* values of $\Delta t$, this provides a good approximation of the continuous-time integration.

This operation is an integration since we obtain a sequence $\{V(t), V(t+\Delta t), V(t+2\Delta t),...\}$ starting from the ODE. Notice how the ODE describes the evolution of $\frac{d}{dt}\,V(t)$, the derivative of $V(t)$, but not directly the evolution of $V(t)$. For the evolution of $V(t)$ we need to integrate the ODE, and in this tutorial, we will do a discrete-time integration using the Euler method. See [Numerical methods for ordinary differential equations](https://en.wikipedia.org/wiki/Numerical_methods_for_ordinary_differential_equations) for additional details._____no_output_____### Exercise 4
Compute the values of $V(t)$ between $t=0$ and $t=0.01$ with step $\Delta t=0.001$ and $V(0)=E_L$.

**Suggestions**
* Reorganize the Eq. (1) to isolate $V\left(t+\Delta t\right)$ on the left side, and express it as function of $V(t)$ and the other terms
* Initialize the membrane potential variable `v` to leak potential `el`
* Loop variable `step` for `10` steps
* At each time step
    * Compute the current value of `t`, `i`
    * Print the current value of `t` and `v`
    * Update the value of `v`
* At the end of the loop print the final value of `v`_____no_output_____

# Exercise 4
# initialize step_end and v
step_end = 10
v = el

# loop for step_end steps
for step in range(step_end):
  t = step * dt

  # --> insert your code here
_____no_output_____

**SAMPLE OUTPUT**

```
0.000 -6.0000e-02
0.001 -5.8750e-02
0.002 -5.6828e-02
0.003 -5.4548e-02
0.004 -5.2381e-02
0.005 -5.0778e-02
0.006 -4.9989e-02
0.007 -4.9974e-02
0.008 -5.0414e-02
0.009 -5.0832e-02
0.010 -5.0775e-02
```_____no_output_____[*Click for solution*](https://github.com/NeuromatchAcademy/course-content/tree/master//tutorials/W0D1_PythonWorkshop1/solutions/W0D1_Tutorial1_Solution_899c3c18.py)

_____no_output_____### Exercise 5
![synaptic input discrete](https://github.com/mpbrigham/colaboratory-figures/raw/master/nma/python-for-nma/synaptic_input_discrete.png)

Plot the values of $I(t)$ between $t=0$ and $t=0.024$.

**Suggestions**
* Increase `step_end`
* initialize the figure with `plt.figure`, set title, x and y labels with `plt.title`, `plt.xlabel` and `plt.ylabel`, respectively
* Replace printing command with plotting command `plt.plot` with keyword `'ko'` (short version for `color='k'` and `marker='o'`) for black small dots
* Use `plt.show()` at the end to display the plot_____no_output_____

# Exercise 5
# initialize step_end
step_end = 25

# initialize the figure
plt.figure()

# loop for step_end steps
for step in range(step_end):
  t = step * dt

  # --> insert your code here
_____no_output_____

[*Click for solution*](https://github.com/NeuromatchAcademy/course-content/tree/master//tutorials/W0D1_PythonWorkshop1/solutions/W0D1_Tutorial1_Solution_70724599.py)

*Example output:*

_____no_output_____### Exercise 6
Plot the values of $V(t)$ between $t=0$ and $t=t_{max}$.

**Suggestions**
* Compute the required number of steps with`int(t_max/dt)`
* Use plotting command for black small(er) dots with keyword `'k.'`_____no_output_____

# Exercise 6
# initialize step_end and v
# step_end = ... # insert your code here
v = el

# initialize the figure
plt.figure()
plt.title('$V_m$ with sinusoidal I(t)')
plt.xlabel('time (s)')
plt.ylabel('$V_m$ (V)');

# loop for step_end steps
for step in range(step_end):
  t = step * dt

  # --> insert your code here

_____no_output_____

[*Click for solution*](https://github.com/NeuromatchAcademy/course-content/tree/master//tutorials/W0D1_PythonWorkshop1/solutions/W0D1_Tutorial1_Solution_7e2e5131.py)

*Example output:*

_____no_output_____

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

---
## Random synaptic input
From the perspective of neurons, synaptic input is random (or stochastic). We'll improve the synaptic input model by introducing random input current with statistical properties similar to the previous exercise:

\begin{align*}
\\
I(t)=I_{mean}\left(1+0.1\sqrt{\frac{t_{max}}{\Delta t}}\,\xi(t)\right)\qquad\text{with }\xi(t)\sim U(-1,1)\\
\\
\end{align*}

where $U(-1,1)$ is the [uniform distribution](https://en.wikipedia.org/wiki/Uniform_distribution_(continuous)) with support $x\in[-1,1]$.

Random synaptic input $I(t)$ results in random time course for $V(t)$._____no_output_____### Exercise 7
Plot the values of $V(t)$ between $t=0$ and $t=t_{max}-\Delta t$ with random input $I(t)$.

Initialize the (pseudo) random number generator (RNG) to a fixed value to obtain the same random input each time.

The function `np.random.seed()` initializes the RNG, and `np.random.random()` generates samples from the uniform distribution between `0` and `1`.

**Suggestions**
* Use `np.random.seed()` to initialize the RNG to `0`
* Use `np.random.random()` to generate random input in range `[0,1]` at each timestep
* Multiply random input by an appropriate factor to expand the range to `[-1,1]`
* Verify that $V(t)$ has a random time course by changing the initial RNG value
* Alternatively, comment RNG initialization by typing `CTRL` + `\` in the relevant line_____no_output_____

# Exercise 7
# set random number generator
np.random.seed(2020)

# initialize step_end and v
step_end = int(t_max/dt)
v = el

# initialize the figure
plt.figure()
plt.title('$V_m$ with random I(t)')
plt.xlabel('time (s)')
plt.ylabel(r'$V_m$ (V)')

# loop for step_end steps
for step in range(step_end):
  t = step * dt

  # --> insert your code here
_____no_output_____

[*Click for solution*](https://github.com/NeuromatchAcademy/course-content/tree/master//tutorials/W0D1_PythonWorkshop1/solutions/W0D1_Tutorial1_Solution_e6bec4d5.py)

*Example output:*

_____no_output_____## Ensemble statistics
Multiple runs of the previous exercise may give the impression of periodic regularity in the evolution of $V(t)$. We'll collect the sample mean over $N=50$ realizations of $V(t)$  with random input to test such a hypothesis. The sample mean, sample variance and sample autocovariance at times $\left\{t, s\right\}\in[0,t_{max}]$, and for $N$ realizations $V_n(t)$ are given by:

\begin{align*}
\\
\left\langle V(t)\right\rangle &= \frac{1}{N}\sum_{n=1}^N V_n(t) & & \text{sample mean}\\
\left\langle (V(t)-\left\langle V(t)\right\rangle)^2\right\rangle &= \frac{1}{N-1} \sum_{n=1}^N \left(V_n(t)-\left\langle V(t)\right\rangle\right)^2 & & \text{sample variance} \\
\left\langle \left(V(t)-\left\langle V(t)\right\rangle\right)\left(V(s)-\left\langle V(s)\right\rangle\right)\right\rangle
&= \frac{1}{N-1} \sum_{n=1}^N \left(V_n(t)-\left\langle V(t)\right\rangle\right)\left(V_n(s)-\left\langle V(s)\right\rangle\right) & & \text{sample autocovariance}\\
\\
\end{align*}_____no_output_____### Exercise 8
Plot multiple realizations ($N=50$) of $V(t)$ by storing in a list the voltage of each neuron at time $t$.

Keep in mind that the plotting command `plt.plot(x, y)` requires `x` to have the same number of elements as `y`.

Mathematical symbols such as $\alpha$ and $\beta$ are specified as `$\alpha$` and `$\beta$` in [TeX markup](https://en.wikipedia.org/wiki/TeX). See additional details in [Writing mathematical expressions](https://matplotlib.org/3.2.2/tutorials/text/mathtext.html) in Matplotlib.

**Suggestions**
* Initialize a list `v_n` with `50` values of membrane leak potential `el`
* At each time step:
  * Plot `v_n` with keyword `'k.'` and parameter `alpha=0.05` to adjust the transparency (by default, `alpha=1`)
  * In the plot command, replace `t` from the previous exercises with a list of size `n` with values `t`
  * Loop over `50` realizations of random input
  * Update `v_n` with the values of $V(t)$

* Why is there a black dot at $t=0$?_____no_output_____

# Exercise 8
# set random number generator
np.random.seed(2020)

# initialize step_end, n and v_n
step_end = int(t_max/dt)
n = 50
# v_n = ... # --> insert your code here

# initialize the figure
plt.figure()
plt.title('Multiple realizations of $V_m$')
plt.xlabel('time (s)')
plt.ylabel('$V_m$ (V)')

# loop for step_end steps
for step in range(step_end):
  t = step * dt

  # --> insert your code here_____no_output_____

[*Click for solution*](https://github.com/NeuromatchAcademy/course-content/tree/master//tutorials/W0D1_PythonWorkshop1/solutions/W0D1_Tutorial1_Solution_1a09bff9.py)

*Example output:*

_____no_output_____### Exercise 9
Add the sample mean $\left\langle V(t)\right\rangle=\frac{1}{N}\sum_{n=1}^N V_n(t)$ to the plot.

**Suggestions**
* At each timestep:
  * Compute and store in `v_mean` the sample mean $\left\langle V(t)\right\rangle$ by summing the values of list `v_n` with `sum` and dividing by `n`
  * Plot $\left\langle V(t)\right\rangle$ with `alpha=0.8` and keyword `'C1.'` for blue (you can read more about [specifying colors](https://matplotlib.org/tutorials/colors/colors.html#sphx-glr-tutorials-colors-colors-py))
  * Loop over `50` realizations of random input
  * Update `v_n` with the values of $V(t)$_____no_output_____

# Exercise 9
# set random number generator
np.random.seed(2020)

# initialize step_end, n and v_n
step_end = int(t_max/dt)
n = 50
v_n = [el] * n

# initialize the figure
plt.figure()
plt.title('Multiple realizations of $V_m$')
plt.xlabel('time (s)')
plt.ylabel('$V_m$ (V)')

# loop for step_end steps
for step in range(step_end):
  t = step * dt

  # --> insert your code here

plt.show()_____no_output_____

[*Click for solution*](https://github.com/NeuromatchAcademy/course-content/tree/master//tutorials/W0D1_PythonWorkshop1/solutions/W0D1_Tutorial1_Solution_8ebb3387.py)

*Example output:*

_____no_output_____### Exercise 10
Add the sample standard deviation $\sigma(t)\equiv\sqrt{\text{Var}\left(t\right)}$ to the plot, with sample variance $\text{Var}(t) = \frac{1}{N-1} \sum_{n=1}^N \left(V_n(t)-\left\langle V(t)\right\rangle\right)^2$.

Use a list comprehension to collect the sample variance `v_var`. Here's an example to initialize a list with squares of `0` to `9`:
```
squares = [x**2 for x in range(10)]
print(squares)
--> [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
```

Why are we plotting $\sigma(t)$ rather than the $\text{Var}(t)$? What are the units of each and the units of $\left\langle V(t)\right\rangle$?

**Suggestions**
* At each timestep:
  * Compute and store in `v_mean` the sample mean $\left\langle V(t)\right\rangle$
  * Initialize a list `v_var_n` with the contribution of each $V_n(t)$ to $\text{Var}\left(t\right)$ with a list comprehension over values of `v_n`
  * Compute sample variance `v_var` by summing the values of `v_var_n` with `sum` and dividing by `n-1`
  * (alternative: loop over the values of `v_n` and add to `v_var` each contribution $V_n(t)$ and divide by `n-1` outside the loop)
  * Compute the standard deviation `v_std` with the function `np.sqrt`
  * Plot $\left\langle V(t)\right\rangle\pm\sigma(t)$ with `alpha=0.8` and keyword `'C7.'`_____no_output_____

# Exercise 10
# set random number generator
np.random.seed(2020)

# initialize n, v_n and step_end
step_end = int(t_max/dt)
n = 50
v_n = [el] * n

# initialize the figure
plt.figure()
plt.title('Multiple realizations of $V_m$')
plt.xlabel('time (s)')
plt.ylabel('$V_m$ (V)')

# loop for step_end steps
for step in range(step_end):
  t = step * dt
  v_mean = sum(v_n) / n

  # --> insert your code here_____no_output_____

[*Click for solution*](https://github.com/NeuromatchAcademy/course-content/tree/master//tutorials/W0D1_PythonWorkshop1/solutions/W0D1_Tutorial1_Solution_b5b8322a.py)

*Example output:*

_____no_output_____

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

---
## Using NumPy
The next set of exercises introduces `np.array`, the workhorse from the scientific computation package [NumPy](https://numpy.org). Numpy arrays the default for numerical data storage and computation and will help us separate computing steps from plotting.

![NumPy package](https://github.com/mpbrigham/colaboratory-figures/raw/master/nma/python-for-nma/numpy_logo_small.png)

In the previous exercises, we updated plots inside the main loop and stored intermediate results in lists for plotting them. The purpose was to simplify earlier exercises as much as possible. However, there are very few scenarios where this technique is necessary, and you should avoid it in the future. Using numpy arrays will significantly simplify our coding narrative by computing inside the main loop and plotting afterward.

Lists are much more natural for storing data for other purposes than computation. For example, lists are handy for storing numerical indexes and text._____no_output_____### Exercise 11
Rewrite the single neuron plot with random input from _Exercise 7_ with numpy arrays. The time range, voltage values, and synaptic current are initialized or pre-computed as numpy arrays before numerical integration.

**Suggestions**
* Use `np.linspace` to initialize a numpy array `t_range` with `num=step_end=100` values from `0` to `t_max`
* Use `np.ones` to initialize a numpy array `v` with `step_end` leak potential values `el`
* Pre-compute `step_end` synaptic current values in numpy array `syn` with `np.random.random(step_end)` for `step_end` random numbers
* Iterate for numerical integration of `v`
* Since `v[0]=el`, we should iterate for `step_end - 1` steps, for example by skipping `step=0`. Why?_____no_output_____

# Exercise 11
# set random number generator
np.random.seed(2020)

# initialize step_end, t_range, v and syn
step_end = int(t_max/dt)
t_range = np.linspace(0, t_max, num=step_end)
v = el * np.ones(step_end)

# --> insert your code here

plt.figure()
plt.title('$V_m$ with random I(t)')
plt.xlabel('time (s)')
plt.ylabel(r'$V_m$ (V)')

plt.plot(t_range, v, 'k.')
plt.show()_____no_output_____

[*Click for solution*](https://github.com/NeuromatchAcademy/course-content/tree/master//tutorials/W0D1_PythonWorkshop1/solutions/W0D1_Tutorial1_Solution_220d963a.py)

*Example output:*

_____no_output_____### Exercise 12
Let's practice using `enumerate` to iterate over the indexes and values of the synaptic current array `syn`.

**Suggestions**
* Iterate indexes and values of `syn` with `enumerate` in the `for` loop
* Plot `v` with keyword `'k'` for displaying a line instead of dots_____no_output_____

# Exercise 12
# set random number generator
np.random.seed(2020)

# initialize step_end, t_range, v and syn
step_end = int(t_max/dt)
t_range = np.linspace(0, t_max, num=step_end)
v = el * np.ones(step_end)
syn = i_mean * (1 + 0.1*(t_max/dt)**(0.5)*(2*np.random.random(step_end)-1))

# --> insert your code here

plt.figure()
plt.title('$V_m$ with random I(t)')
plt.xlabel('time (s)')
plt.ylabel(r'$V_m$ (V)')

plt.plot(t_range, v, 'k')
plt.show()_____no_output_____

[*Click for solution*](https://github.com/NeuromatchAcademy/course-content/tree/master//tutorials/W0D1_PythonWorkshop1/solutions/W0D1_Tutorial1_Solution_01b86875.py)

*Example output:*

_____no_output_____### Exercise 13
Plot multiple realizations ($N=50$) of $V(t)$ by storing the voltage of each neuron at time $t$ in a numpy array.

**Suggestions**
* Initialize a numpy array `v_n` of shape `(n,t_n)` with membrane leak potential values `el`
* Pre-compute synaptic current values in numpy array `syn` of shape `(n,t_n)`
* Iterate `t_n` steps with a `for` loop for numerical integration
* Plot results with a single plot command, by providing `v_n.T` to the plot function. `v_n.T` is the transposed version of `v_n` (with rows and columns swapped)._____no_output_____

# Exercise 13
# set random number generator
np.random.seed(2020)

# initialize step_end, n, t_range, v and syn
step_end = int(t_max/dt)
n = 50
t_range = np.linspace(0, t_max, num=step_end)
v_n = el * np.ones([n,step_end])

# --> insert your code here
_____no_output_____

[*Click for solution*](https://github.com/NeuromatchAcademy/course-content/tree/master//tutorials/W0D1_PythonWorkshop1/solutions/W0D1_Tutorial1_Solution_88775344.py)

*Example output:*

_____no_output_____### Exercise 14
Add sample mean $\left\langle V(t)\right\rangle$ and standard deviation $\sigma(t)\equiv\sqrt{\text{Var}\left(t\right)}$ to the plot.

`np.mean(v_n, axis=0)` computes mean over rows, i.e. mean for each neuron

`np.mean(v_n, axis=1)` computes mean over columns (axis `1`), i.e. mean for each time step

**Suggestions**
* Use `np.mean` and `np.std` with keyword `axis=0` to sum over neurons
* BONUS: Use `label` keyword in `plt.plot` to specify labels in each trace. Label only the last voltage trace to avoid labeling all `N` of them._____no_output_____

# Exercise 14
# set random number generator
np.random.seed(2020)

# initialize step_end, n, t_range, v and syn
step_end = int(t_max/dt)
n = 50
t_range = np.linspace(0, t_max, num=step_end)
v_n = el * np.ones([n,step_end])
syn = i_mean * (1 + 0.1*(t_max/dt)**(0.5)*(2*np.random.random([n,step_end])-1))

# loop for step_end - 1 steps
for step in range(1, step_end):

  v_n[:,step] = v_n[:,step-1] + dt/tau * (el - v_n[:,step-1] + r*syn[:,step])

# --> insert your code here

plt.figure()
plt.title('Multiple realizations of $V_m$')
plt.xlabel('time (s)')
plt.ylabel('$V_m$ (V)')

plt.plot(t_range, v_n[:-1].T, 'k', alpha=0.3)
plt.plot(t_range, v_n[-1], 'k', alpha=0.3, label='V(t)')

plt.show()_____no_output_____

[*Click for solution*](https://github.com/NeuromatchAcademy/course-content/tree/master//tutorials/W0D1_PythonWorkshop1/solutions/W0D1_Tutorial1_Solution_60a9cb82.py)

*Example output:*

_____no_output_____


# Notebook from xbresson/CS4243_2022
Path: codes/labs_lecture11/lab01_faster_R_CNN/faster_R_CNN_exercise.ipynb

# Lab 01 : Vanilla Faster R-CNN - exercise

The goal is to implement the forward pass of the vanilla faster CNN architecture at test time._____no_output_____

# For Google Colaboratory
import sys, os
if 'google.colab' in sys.modules:
    # mount google drive
    from google.colab import drive
    drive.mount('/content/gdrive')
    path_to_file = '/content/gdrive/My Drive/CS4243_codes/codes/labs_lecture11/lab01_faster_R_CNN
    print(path_to_file)
    # move to Google Drive directory
    os.chdir(path_to_file)
    !pwd_____no_output_____import torch
import torch.nn as nn
import torch.optim as optim
import utils
import time_____no_output_____#device= torch.device("cuda")
device= torch.device("cpu")
print(device)_____no_output_____

### Generate the dataset _____no_output_____

# Libraries
from torchvision.transforms import ToTensor
from PIL import Image
import matplotlib.pyplot as plt
import logging
logging.getLogger().setLevel(logging.CRITICAL) # remove warnings

# Import 5 object types
nb_class_objects = 5
objects = torch.zeros(nb_class_objects,7,7)
for k in range(nb_class_objects):
    objects[k,:,:] = 1-ToTensor()(Image.open('objects/obj'+str(k+1)+'.tif'))[0,:,:]
print(objects.size())

# Define the bounding boxes w.r.t. object type
def box_coord(label, x, y, w):
    offset = (w-1)// 2 + 0.5
    x = [x-offset, x-offset+w, x-offset+w, x-offset,   x-offset]
    y = [y-offset, y-offset,   y-offset+w, y-offset+w, y-offset]
    if label==0:
        color = 'r'; legend = 'Triangle'
    elif label==1:
        color = 'b'; legend = 'Cross'
    elif label==2:
        color = 'g'; legend = 'Star'
    elif label==3:
        color = 'y'; legend = 'Square'
    elif label==4:
        color = 'm'; legend = 'Ring'
    return x, y, color, legend_____no_output_____# Global constants
# im_size = image size
# ob_size = object size
# batch_size = batch size
# nb_objects = number of objects in the image
# nb_class_objects = number of object classes (we have 5 classes)
# hidden_dim = hidden dimension

im_size = 28
ob_size = 7
offset = (ob_size-1)// 2
batch_size = 2
nb_objects = 3
hidden_dim = 64

# Function that generate a batch of training data
def generate_batch_data(im_size, ob_size, batch_size, nb_objects, nb_class_objects):
    batch_images = torch.zeros(batch_size,im_size,im_size)
    batch_bboxes = torch.zeros(batch_size,nb_objects,4+1) # 2D-coord, width and height of boxes, and their class
    for b in range(batch_size):
        image = torch.zeros(im_size,im_size)
        bboxes = torch.zeros(nb_objects,4+1)
        class_objects = torch.LongTensor(nb_objects).random_(0,nb_class_objects)
        offset = (ob_size-1)// 2
        coord_objects = torch.LongTensor(nb_objects,2).random_(offset,im_size-offset)
        # coord_objects[k,0] = x-coordinate,  coord_objects[k,1] = y-coordinate
        for k in range(nb_objects):
            image[coord_objects[k,1]-offset:coord_objects[k,1]-offset+ob_size,coord_objects[k,0]-offset:coord_objects[k,0]-offset+ob_size] += objects[class_objects[k],:,:]; image[image>=1.0] = 1.0
            bboxes[k,:] = torch.Tensor([coord_objects[k,0],coord_objects[k,1],ob_size,ob_size,class_objects[k]])
        batch_images[b,:,:] = image
        batch_bboxes[b,:,:] = bboxes
    return batch_images, batch_bboxes

# Plot a mini-batch of images
batch_images, batch_bboxes = generate_batch_data(im_size, ob_size, batch_size, nb_objects, nb_class_objects)
print(batch_images.size())
print(batch_bboxes.size())
for b in range(batch_size):
    plt.imshow(batch_images[b,:,:], cmap='gray')
    for k in range(nb_objects):
        x, y, color, legend = box_coord(batch_bboxes[b,k,4],batch_bboxes[b,k,0],batch_bboxes[b,k,1],ob_size)
        plt.plot(x,y,color,label=legend)
    plt.legend(loc='best')
    plt.show()

_____no_output_____# Define the faster-R-CNN architecture
batch_size = 2 # for debug

class faster_R_CNN(nn.Module):

    def __init__(self):
        super(faster_R_CNN, self).__init__()
        # backbone convnet
        self.conv1 = nn.Conv2d(1,  hidden_dim,  kernel_size=3, padding=1 ) #  1x28x28 --> hidden_dimx 28x28
        self.conv2 = nn.Conv2d(hidden_dim, hidden_dim, kernel_size=3, padding=1 ) # hidden_dim x28x28 --> hidden_dim x28x28
        # Bbox classes
        self.linear_class = nn.Linear(hidden_dim* ob_size**2, nb_class_objects) # hidden_dim x7x7 --> nb_class_objects
        # Bbox anchors
        self.conv_anch = nn.Conv2d(hidden_dim, 1, kernel_size=ob_size, padding=offset )  # hidden_dim x28x28 --> 1x28x28

    def forward(self, x, bb, train_flag=True):
        # backbone convnet
        x = self.conv1(x) # [batch_size, hidden_dim, im_size, im_size]
        x = torch.relu(x)
        x = self.conv2(x) # [batch_size,hidden_dim, im_size, im_size]
        x = torch.relu(x)
        # Bbox anchors
        scores_bbox_anch = self.conv_anch(x).squeeze() # [batch_size, im_size, im_size]
        # Bbox classes
        if train_flag: # forward pass at training time
            bbox = []
            for b in range(batch_size):
                for k in range(nb_objects):
                    bbox.append(x[b,:,bb[b,k,1].long()-offset:bb[b,k,1].long()-offset+ob_size,bb[b,k,0].long()-offset:bb[b,k,0].long()-offset+ob_size])
            bbox = torch.stack(bbox, dim=0) # [batch_size*nb_objects, hidden_dim, ob_size, ob_size]
            bbox = bbox.view(-1, hidden_dim * ob_size**2) # [batch_size*nb_objects, hidden_dim*ob_size*ob_size]
            scores_bbox_class = self.linear_class(bbox) # [batch_size*nb_objects, nb_class_objects]
        else: # forward pass at test time
            batch_bbox = []
            for b in range(batch_size):
                # compute the coordinates of the top-K bbox anchor scores (K=nb_objects)
                # COMPLETE HERE
                scores_bbox_anch_b =
                idx_y =
                idx_x =
                # extract the top-K bboxes of size [batch_size, nb_objects, hidden_dim, ob_size, ob_size]
                # COMPLETE HERE
                bbox =
            # compute the class scores of the bbox
            # size of tensor scores_bbox_class is [batch_size*nb_objects, nb_class_objects]
            # COMPLETE HERE
            scores_bbox_class =
        return scores_bbox_class, scores_bbox_anch

# Instantiate the network
net = faster_R_CNN()
net = net.to(device)
print(net)
utils.display_num_param(net)

# Test the forward pass, backward pass and gradient update with a single batch
init_lr = 0.001
optimizer = torch.optim.Adam(net.parameters(), lr=init_lr)
batch_images, batch_bboxes = generate_batch_data(im_size, ob_size, batch_size, nb_objects, nb_class_objects)
optimizer.zero_grad()
scores_bbox_class, scores_bbox_anch = net(batch_images.unsqueeze(dim=1), batch_bboxes)
# Bbox class loss
labels_bbox_class = batch_bboxes[:,:,4].long().view(-1) # [batch_size*nb_objects]
loss_class = nn.CrossEntropyLoss()(scores_bbox_class, labels_bbox_class)
# Bbox anchors loss
labels_bbox_coord = batch_bboxes[:,:,0:2] # [batch_size, nb_objects, 2D]
labels_bbox_anch = torch.zeros(batch_size, im_size, im_size) # [batch_size, im_size, im_size]
for b in range(batch_size):
    for k in range(nb_objects):
        labels_bbox_anch[b,labels_bbox_coord[b,k,1].long(),labels_bbox_coord[b,k,0].long()] = 1
loss_anch = torch.nn.BCEWithLogitsLoss()(scores_bbox_anch, labels_bbox_anch)
loss = loss_class + loss_anch
loss.backward()
optimizer.step()
_____no_output_____# Training loop
net = faster_R_CNN()
net = net.to(device)
utils.display_num_param(net)

# Optimizer
init_lr = 0.001
optimizer = torch.optim.Adam(net.parameters(), lr=init_lr)

# Number of mini-batches per epoch
nb_batch = 10
batch_size = 10

start=time.time()
for epoch in range(10):

    running_loss = 0.0
    num_batches = 0

    for _ in range(nb_batch):

        # FORWARD AND BACKWARD PASS
        batch_images, batch_bboxes = generate_batch_data(im_size, ob_size, batch_size, nb_objects, nb_class_objects)
        optimizer.zero_grad()
        scores_bbox_class, scores_bbox_anch = net(batch_images.unsqueeze(dim=1), batch_bboxes)
        # Bbox class loss
        labels_bbox_class = batch_bboxes[:,:,4].long().view(-1) # [batch_size*nb_objects]
        loss_class = nn.CrossEntropyLoss()(scores_bbox_class, labels_bbox_class)
        # Bbox anchors loss
        labels_bbox_coord = batch_bboxes[:,:,0:2] # [batch_size, nb_objects, 2D]
        labels_bbox_anch = torch.zeros(batch_size, im_size, im_size) # [batch_size, im_size, im_size]
        for b in range(batch_size):
            for k in range(nb_objects):
                labels_bbox_anch[b,labels_bbox_coord[b,k,1].long(),labels_bbox_coord[b,k,0].long()] = 1
        loss_anch = torch.nn.BCEWithLogitsLoss()(scores_bbox_anch, labels_bbox_anch)
        loss = loss_class + loss_anch
        loss.backward()
        optimizer.step()

        # COMPUTE STATS
        running_loss += loss.detach().item()
        num_batches += 1

    # AVERAGE STATS THEN DISPLAY
    total_loss = running_loss/num_batches
    elapsed = (time.time()-start)/60
    print('epoch=',epoch, '\t time=', elapsed,'min', '\t lr=', init_lr  ,'\t loss=', total_loss )
    _____no_output_____# Test time

# select a batch of 2 images
batch_size = 2

# generate the batch of 2 images
batch_images, batch_bboxes = generate_batch_data(im_size, ob_size, batch_size, nb_objects, nb_class_objects)

# Forward pass during TEST time
#  Note the flag "train_flag=False"
scores_bbox_class, scores_bbox_anch = net(batch_images.unsqueeze(dim=1), batch_bboxes, train_flag=False)

# scores_bbox_class [batch_size*nb_objects, nb_class_objects]
bbox_class = torch.argmax(scores_bbox_class, dim=-1) # [batch_size*nb_objects]
bbox_class = bbox_class.view(batch_size, nb_objects) # [batch_size, nb_objects]

# Ground tructh Bbox anchors
labels_bbox_coord = batch_bboxes[:,:,0:2] # [batch_size, nb_objects, 2]
labels_bbox_anch = torch.zeros(batch_size, im_size, im_size) # [batch_size, im_size, im_size]
for b in range(batch_size):
    for k in range(nb_objects):
        labels_bbox_anch[b,labels_bbox_coord[b,k,1].long(),labels_bbox_coord[b,k,0].long()] = 1

# Plot the ground truth solution and the predicted solution
for b in range(batch_size):

    # Plot ground truth and prediction
    plt.imshow(batch_images[b,:,:], cmap='gray')
    for k in range(nb_objects):
        x, y, color, legend = box_coord(batch_bboxes[b,k,4],batch_bboxes[b,k,0],batch_bboxes[b,k,1],ob_size)
        plt.plot(x,y,color,label=legend)
    plt.legend(loc='best')
    plt.title('Ground Truth')
    plt.show()
    # Predicted anchor coordinates
    scores_bbox_anch_b = scores_bbox_anch[b,:,:].detach()
    scores_bbox_anch_b = scores_bbox_anch_b.view(-1)
    _, idx_largest = torch.sort(scores_bbox_anch_b, descending=True) # rank the scores from largest to smallest
    idx_largest = idx_largest[:nb_objects] # get the top scores
    idx_y = idx_largest//im_size        # compute y-coord of anchor locations
    idx_x = idx_largest - idx_y*im_size # compute x-coord of anchor locations
    # Predicted classes for the boxes
    bbox_class = torch.argmax(scores_bbox_class, dim=-1) # [batch_size*nb_objects]
    bbox_class = bbox_class.view(batch_size, nb_objects) # [batch_size, nb_objects]
    plt.imshow(batch_images[b,:,:], cmap='gray')
    for k in range(nb_objects):
        x, y, color, legend = box_coord(bbox_class[b,k],idx_x[k],idx_y[k],ob_size) # Predicted classes
        plt.plot(x,y,color,label=legend)
    plt.legend(loc='best')
    plt.title('Prediction')
    plt.show()

    # Plot ground truth and predicted locations of anchors
    plt.imshow(labels_bbox_anch[b,:,:], cmap='gray')
    plt.title('Ground Truth locations of anchors')
    plt.show()
    plt.imshow(scores_bbox_anch[b,:,:].detach(), cmap='gray')
    plt.title('Predicted locations of anchors')
    plt.show()
_____no_output_____