## Todos
1. Correct CDR3 kmerization.

## Requirements

```python
from __future__ import unicode_literals, print_function, division
from io import open
import unicodedata
import string
import re
import random
import pandas as pd
import r2python

import torch
import torch.nn as nn
from torch import optim
import torch.nn.functional as F
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

```

```python
NULL_token = 0
SOS_token = 1
EOS_token = 2

class Lang:
    def __init__(self, name):
        self.name = name
        self.word2index = {}
        self.word2count = {}
        self.index2word = {0: 'NULL', 1: 'SOS', 2: 'EOS'}
        self.n_words = 2

    def addSentence(self, sentence):
        for word in sentence.split(' '):
            self.addWord(word)

    def addWord(self, word):
        if word not in self.word2index:
            self.word2index[word] = self.n_words
            self.word2count[word] = 1
            self.index2word[self.n_words] = word
            self.n_words +=1
        else:
            self.word2count[word] += 1
```

```python
# Turn a Unicode string to plain ASCII, thanks to
# http://stackoverflow.com/a/518232/2809427
def unicodeToAscii(s):
    return ''.join(
        c for c in unicodedata.normalize('NFD', s)
        if unicodedata.category(c) != 'Mn'
    )

# Lowercase, trim, and remove non-letter characters

def normalizeString(s):
    s = unicodeToAscii(s.lower().strip())
    s = re.sub(r"([.!?])", r" \1", s)
    s = re.sub(r"[^a-zA-Z.!?]+", r" ", s)
    return s
```

```python
def readLangs(lang1, lang2, reverse=False):
    print("Reading lines ...")

    # Read the file and split into lines
    lines = open('data/%s-%s.txt' %(lang1, lang2), encoding='utf-8').\
        read().strip().split('\n')

    # Split every line into pairs and normalize
    pairs = [[normalizeString(s) for s in l.split('\t')] for l in lines]

    # Reverse pairs, make Lang instances
    if reverse:
        pairs = [list(reversed(p)) for p in pairs]
        input_lang = Lang(lang2)
        output_lang = Lang(lang1)
    else:
        input_lang = Lang(lang1)
        output_lang = Lang(lang2)

    return input_lang, output_lang, pairs

```

```python
def indexesFromSentence(lang, sentence):
    indexes = [lang.word2index[word] for word in sentence.split(' ')]
    indexes.append(EOS_token)
    return indexes

def tensorFromSentence(lang, sentence):
    indexes = indexesFromSentence(lang, sentence)
    return torch.tensor(indexes, dtype=torch.long, device=device).view(-1, 1)

# ## check if the view is accurate
# def tensorFromSentences(lang, sentences):
#     indexes_mat = [indexesFromSentence(lang, sentence) for sentence in sentences]
#     return torch.tensor(indexes_mat, dtype=torch.long, device=device).view(-1, 1)

def tensorsFromPair(pair):
    input_tensor = tensorFromSentence(input_lang, pair[0])
    target_tensor_mat = tensorFromSentences(output_lang, pair[1])
    prior_z_np = np.ones(length(target_tensor_mat))/length(target_tensor_mat)
    prior_z =  torch.tensor(prior_z_np, dtype=torch.long, device=device)
    return (input_tensor, target_tensor, prior_z)
# len(pairs[0])
# random.choice(pairs)

```

```python
def readInput_cdr3b(lang, langfile):
    print("Reading %s ..." %(lang))

    # Read the file and split into lines
    df = pd.read_csv(langfile, "\t")

    df = df[['kmer.sentence', 'patient']]
    input_lang = Lang(lang)

    print("Counting words...")
    for kmerS in df['kmer.sentence']:
        input_lang.addSentence(kmerS)

    df['kmer.tensor'] = df['kmer.sentence'].apply(lambda x: tensorFromSentence(input_lang, x))

    return input_lang, df

def readInput_epitope(lang, langfile, nfold=None):
    """The module reads CDR3 and Epitopes of and create lists for each tcga patients
    output are
    cdr3s_list: list of cdr3 per common patient,
    neoantigens_list: list of Epitope per common patient,
    negative_patients: For each patient list of other patients that does not share any neoantigen with the patient

    approx schematic of the function

    # read files
    # find common paitents
    # convert to tensor
    # create list
    """
    def create_neoantigen_list_from_patient_list(patients_lists, nfold=None):
        """
        Ouput neoantigen for patients list
        """

        df_common = df[df['patient'].isin(patients_lists)]
        neoantigens_list = []
        for patient_inx, patient in zip(range(len(patients_lists)), patients_lists):
            neoantigen_curr = df_common[df_common['patient'] == patient]['kmer.tensor']
            neoantigens_list.append(neoantigen_curr)

        neoantigens_list = [neoantigen.values.tolist() for neoantigen in neoantigens_list]
        return neoantigens_list

    df = pd.read_csv(langfile, "\t")

    input_lang = Lang(lang)
    print("Counting words...")
    for kmerS in df['kmer.sentence']:
        input_lang.addSentence(kmerS)

    df['kmer.tensor'] = df['kmer.sentence'].apply(lambda x: tensorFromSentence(input_lang, x))
    common_patients = r2python.unique(df['patient'])
    if nfold is None:
        train_patients_list = common_patients
        trainData = create_neoantigen_list_from_patient_list(train_patients_list)
        testData = None
    else:
        ### this is not working
        train_patients_list = r2python.sample(common_patients, size=int((nfold - 1) * len(common_patients) / nfold))
        test_patients_list = r2python.setdiff(common_patients, train_patients_list)
        trainData = create_neoantigen_list_from_patient_list(train_patients_list)
        testData = create_neoantigen_list_from_patient_list(test_patients_list)

    dataset = {
        "trainData": trainData,
        "testData": testData
    }
    return input_lang, dataset, df
```

```python
MAX_LENGTH = 30

```

```python
# def format2tensorList(List):
#     # assumes that indices are in sorted order
#     tensorList = []
#     total_indices = int(List[len(List)-1][1])
#     initial_index = 1
#     for inx in range(total_indices):
#         tensorList_set = []
#         curr

def prepareData(cdr3b, cdr3bfile, epitope, epitopefile):
    cdr3b_lang,  df_cdr3 = readInput_cdr3b(cdr3b, cdr3bfile)
    print("Read %s cdr3bs " % len(df_cdr3.index))

    epitope_lang,  epitope_patient_list, df_epitope = readInput_epitope(epitope, epitopefile)
    print("Read %s epitope " % len(epitope_patient_list))

    # epitope NULL sentence
    epitope_lang.addSentence('NULL')
    epitopee_null = tensorFromSentence(epitope_lang, 'NULL')

#     for pair in epitope_n_index:
#         epitope_lang.addSentence(pair[0])

#     epitope_matList = format2ListofList(epitope_lang, epitope_n_index)
#         epitope_mat_lens.addLens(pair[1])

#     convert epitope to list of matrices for fast indexing

    print("Counted words:")
    print(cdr3b_lang.name, cdr3b_lang.n_words)
    print(epitope_lang.name, epitope_lang.n_words)
    return cdr3b_lang, epitope_lang, df_cdr3, epitope_patient_list, epitopee_null, df_epitope

cdr3b_lang, epitope_lang, df_cdr3, epitope_patient_list, epitope_null, df_epitope = prepareData('cdr3b', 'data/tcga_extended_tcr_final_200.txt','eptiope', 'data/all.9mers_200.txt')
# print(random.choice(pairs))
```

    Reading cdr3b ...
    Counting words...
    Read 999 cdr3bs
    Counting words...
    Read 2 epitope
    Counted words:
    cdr3b 3062
    eptiope 5168

```python
# print([random.choice(pairs) for i in range(10)])
# pairs[1:10]
```

## Encoder

```python
class EncoderRNN(nn.Module):
    def __init__(self, input_size, hidden_size):
        super(EncoderRNN, self).__init__()
        self.hidden_size = hidden_size

        self.embedding = nn.Embedding(input_size, hidden_size)
        self.gru = nn.GRU(hidden_size, hidden_size)

    def forward(self, input, hidden):
        embedded = self.embedding(input).view(1,1,-1)
        output = embedded
        output, hidden = self.gru(output, hidden)
        return output, hidden

    def initHidden(self):
        return torch.zeros(1,1, self.hidden_size, device=device)

```

## Decoder

```python
class DecoderRNN(nn.Module):
    def __init__(self,  hidden_size, output_size):
        super(DecoderRNN, self).__init__()
        self.hidden_size = hidden_size

        self.embedding = nn.Embedding(output_size, hidden_size)
        self.gru = nn.GRU(hidden_size, hidden_size)
        self.out = nn.Linear(hidden_size, output_size)
        self.softmax = nn.Softmax(dim=1)

    def forward(self, input, hidden):
        output = self.embedding(input).view(1,1,-1)
        output = F.relu(output)
        output, hidden = self.gru(output, hidden)
        output = self.softmax(output)
        return output, hidden

    def initHidden(self):
        return torch.zeros(1, 1, self.hidden_size, device=device)
```

## Attention Decoder

```python
class AttnDecoderRNN(nn.Module):
    def __init__(self,  hidden_size, output_size, dropout_p =0.1, max_length = MAX_LENGTH):
        super(AttnDecoderRNN, self).__init__()
        self.hidden_size = hidden_size
        self.output_size = output_size
        self.dropout_p = dropout_p
        self.max_length = max_length

        self.embedding = nn.Embedding(self.output_size, self.hidden_size)
        self.attn = nn.Linear(self.hidden_size * 2, self.max_length)
        self.attn_combine = nn.Linear(self.hidden_size * 2, self.hidden_size)
        self.dropout = nn.Dropout(self.dropout_p)
        self.gru = nn.GRU(hidden_size, hidden_size)
        self.out = nn.Linear(hidden_size, output_size)
#         self.softmax = nn.Softmax(dim=1)

    def forward(self, input, hidden, encoder_outputs):
        embedded = self.embedding(input).view(1, 1, -1)
        embedded = self.dropout(embedded)

#         attn_weights = self.attn(torch.cat((embedded[0], hidden[0]), 1))
#         attn_weights = F.softmax(attn_weights, dim=1)
        attn_weights = F.softmax(
            self.attn(torch.cat((embedded[0], hidden[0]), 1)), dim=1)
        attn_applied = torch.bmm(attn_weights.unsqueeze(0),
                                encoder_outputs.unsqueeze(0))

        output = torch.cat((embedded[0], attn_applied[0]), 1)
        output = self.attn_combine(output).unsqueeze(0)

        output= F.relu(output)
        output, hidden = self.gru(output, hidden)

        output = F.log_softmax(self.out(output[0]), dim=1)
        return output, hidden, attn_weights

    def initHidden(self):
        return torch.zeros(1, 1, self.hidden_size, device=device)
```

## Training

## Training the model
In this model, we are generating Epitope (X) from the CDR3b (Y). This weird convention is for traditional reason.
So the input is CDR3B and output are Antigens.

\begin{align}
log P(X/Y) &= \sum_{x} log P(x/Y) \\
            &= \sum_{x} log \sum_z P(x,z/Y) \\
            &= \sum_{x} log \sum_z P(z/Y) P(x/z,Y) \\
            &= \sum_{x} log \mathbf{E}_z  P(x/z,Y)
\end{align}

### TODO : Add prior proportial to expression, mhc1 presentation, clonality of mutation
### TODO : Add prior proportial to its clonality of CDR3

```python
teacher_forcing_ratio = 0.5

def train_set(input_tensor_list, target_tensor, input_null, prior_z, encoder, decoder, encoder_optimizer,
              decoder_optimizer, criterion, max_length=MAX_LENGTH):
    ''' The function inputs cdr3bs and antigens of a set (patient) and for probability of each cdr3b generated by
    each antigens. And  also calculate the loss'''
    encoder_hidden = encoder.initHidden()

    loss = 0

    encoder_optimizer.zero_grad()
    decoder_optimizer.zero_grad()
#     print(len(input_tensor_list))

    inputs_num = len(input_tensor_list)
    for input_inx in range(inputs_num + 1):
#         print(len(input_tensor_list))
#         print("inx:")
#         print(input_inx)
        if input_inx > 0:
            prior_z_curr = prior_z[1]
            input_tensor = input_tensor_list[input_inx-1]
        else:
            prior_z_curr = prior_z[0]
            input_tensor = input_null

        input_length = input_tensor.size(0)
        target_length = target_tensor.size(0)
    #     print(target_length)
        encoder_outputs = torch.zeros(max_length, encoder.hidden_size,
                                      device=device)

        for ei in range(input_length):
            encoder_output, encoder_hidden = encoder(
                input_tensor[ei], encoder_hidden)

            encoder_outputs[ei] = encoder_output[0, 0]

        decoder_input = torch.tensor([[SOS_token]], device=device)

        decoder_hidden = encoder_hidden

        use_teacher_forcing = True if random.random() < teacher_forcing_ratio else False

        if use_teacher_forcing:
            for di in range(target_length):
                decoder_output, decoder_hidden, decoder_attention = decoder(
                    decoder_input, decoder_hidden, encoder_outputs)
                loss += prior_z_curr * criterion(decoder_output, target_tensor[di])
                decoder_input = target_tensor[di]  # Teacher forcing

        else:
            # Without teacher forcing: use its own predictions as the next input
            for di in range(target_length):
                decoder_output, decoder_hidden, decoder_attention = decoder(
                    decoder_input, decoder_hidden, encoder_outputs)
                topv, topi = decoder_output.topk(1)
                decoder_input = topi.squeeze().detach()  # detach from history as input

                loss += prior_z_curr * criterion(decoder_output, target_tensor[di])
                if decoder_input.item() == EOS_token:
                    break

    loss.backward()

    encoder_optimizer.step()
    decoder_optimizer.step()

    return loss.item() / target_length

```

```python
import time
import math

def asMinutes(s):
    m = math.floor(s / 60)
    s -= m * 60
    return '%dm %ds' % (m, s)

def timeSince(since, percent):
    now = time.time()
    s = now - since
    es = s / (percent)
    rs = es - s
    return '%s (- %s)' % (asMinutes(s), asMinutes(rs))
```

```python
def trainIters(encoder, decoder, n_iters, total_patients, print_every=1000, plot_every=100, learning_rate=0.01):
    start = time.time()
    plot_losses = []
    print_loss_total = 0  # Reset every print_every
    plot_loss_total = 0  # Reset every plot_every

    encoder_optimizer = optim.SGD(encoder.parameters(), lr=learning_rate)
    decoder_optimizer = optim.SGD(decoder.parameters(), lr=learning_rate)
    training_inx = r2python.sample_int(total_patients, size=n_iters, replace=True)
#     training_pairs = [tensorsFromPair(random.choice(pairs))
#                       for i in range(n_iters)]
    criterion = nn.NLLLoss()

    for iter in range(1, n_iters + 1):
        inx_curr = training_inx[iter -1]
        input_tensor_list = epitope_patient_list['trainData'][inx_curr]
#         print(len(input_tensor_list))
#         print(input_tensor_list[0])
        target_tensor = df_cdr3['kmer.tensor'][inx_curr]
        prior_z = [0.5, 0.5/len(input_tensor_list)]  # prior of NULL distribution, uniform prior

        loss = train_set(input_tensor_list, target_tensor, epitope_null, prior_z, encoder,
                     decoder, encoder_optimizer, decoder_optimizer, criterion)
        print_loss_total += loss
        plot_loss_total += loss

        if iter % print_every == 0:
            print_loss_avg = print_loss_total / print_every
            print_loss_total = 0
            print('%s (%d %d%%) %.4f' % (timeSince(start, iter / n_iters),
                                         iter, iter / n_iters * 100, print_loss_avg))

        if iter % plot_every == 0:
            plot_loss_avg = plot_loss_total / plot_every
            plot_losses.append(plot_loss_avg)
            plot_loss_total = 0

    showPlot(plot_losses)
```

```python
import matplotlib.pyplot as plt
plt.switch_backend('agg')
import matplotlib.ticker as ticker
import numpy as np

def showPlot(points):
    plt.figure()
    fig, ax = plt.subplots()
    # this locator puts ticks at regular intervals
    loc = ticker.MultipleLocator(base=0.2)
    ax.yaxis.set_major_locator(loc)
    plt.plot(points)
```

## Evaluation

```python
def evaluate(encoder, decoder, sentence, max_length=MAX_LENGTH):
    with torch.no_grad():
#         print(sentence)
        input_tensor = tensorFromSentence(cdr3b_lang, sentence)
        input_length = input_tensor.size()[0]
        encoder_hidden = encoder.initHidden()

        encoder_outputs = torch.zeros(max_length, encoder.hidden_size, device=device)

        for ei in range(input_length):
            encoder_output, encoder_hidden = encoder(input_tensor[ei],
                                                     encoder_hidden)
            encoder_outputs[ei] += encoder_output[0, 0]

        decoder_input = torch.tensor([[SOS_token]], device=device)  # SOS

        decoder_hidden = encoder_hidden

        decoded_words = []
        decoder_attentions = torch.zeros(max_length, max_length)

        for di in range(max_length):
            decoder_output, decoder_hidden, decoder_attention = decoder(
                decoder_input, decoder_hidden, encoder_outputs)
            decoder_attentions[di] = decoder_attention.data
            topv, topi = decoder_output.data.topk(1)
            if topi.item() == EOS_token:
                decoded_words.append('<EOS>')
                break
            else:
                decoded_words.append(epitope_lang.index2word[topi.item()])

            decoder_input = topi.squeeze().detach()

        return decoded_words, decoder_attentions[:di + 1]
```

```python
def evaluateRandomly(encoder, decoder, total_patients = 3, n=10, ):
    for i in range(n):
#         pair = random.choice(pairs)

        training_inx = r2python.sample_int(total_patients, size=1, replace=True)
#         print(training_inx)
#         print(type(training_inx))

        inx_curr = training_inx[0]
        epitope_curr = df_epitope['kmer.sentence'][inx_curr]
        cdr3b_curr = df_cdr3['kmer.sentence'][inx_curr]

        print('>', cdr3b_curr)
        print('=', epitope_curr)
        output_words, attentions = evaluate(encoder, decoder, cdr3b_curr)
        output_sentence = ' '.join(output_words)
        print('<', output_sentence)
        print('')
```

## TRAINING AND EVALUATING

```python
hidden_size = 256
encoder1 = EncoderRNN(epitope_lang.n_words, hidden_size).to(device)
attn_decoder1 = AttnDecoderRNN(hidden_size, cdr3b_lang.n_words, dropout_p=0.1).to(device)
total_patients = 3
trainIters(encoder1, attn_decoder1, n_iters=4, total_patients=total_patients, print_every=1)
```

```python
evaluateRandomly(encoder1, attn_decoder1)
```

    > ^^^^C ^^^CS ^^CSA ^CSAE CSAEQ SAEQG AEQGA EQGAV QGAVT GAVTG AVTGE VTGEL TGELF
    = ^^^^Y ^^^YS ^^YST ^YSTK YSTKH STKHS TKHSK KHSKL HSKLY SKLY$ KLY$$ LY$$$ Y$$$$
    < <EOS>

    > ^^^^C ^^^CA ^^CAS ^CASS CASSL ASSLF SSLFP SLFPL LFPLA FPLAD PLADE LADEQ ADEQF
    = ^^^^S ^^^ST ^^STK ^STKH STKHS TKHSK KHSKL HSKLY SKLYI KLYI$ LYI$$ YI$$$ I$$$$
    < <EOS>

    > ^^^^C ^^^CA ^^CAS ^CASS CASST ASSTD SSTDR STDRA TDRAY DRAYE RAYEQ AYEQY YEQYF
    = ^^^^L ^^^LY ^^LYS ^LYST LYSTK YSTKH STKHS TKHSK KHSKL HSKL$ SKL$$ KL$$$ L$$$$
    < <EOS>

    > ^^^^C ^^^CA ^^CAS ^CASS CASST ASSTD SSTDR STDRA TDRAY DRAYE RAYEQ AYEQY YEQYF
    = ^^^^L ^^^LY ^^LYS ^LYST LYSTK YSTKH STKHS TKHSK KHSKL HSKL$ SKL$$ KL$$$ L$$$$
    < <EOS>

    > ^^^^C ^^^CA ^^CAS ^CASS CASST ASSTD SSTDR STDRA TDRAY DRAYE RAYEQ AYEQY YEQYF
    = ^^^^L ^^^LY ^^LYS ^LYST LYSTK YSTKH STKHS TKHSK KHSKL HSKL$ SKL$$ KL$$$ L$$$$
    < <EOS>

    > ^^^^C ^^^CA ^^CAS ^CASS CASSL ASSLF SSLFP SLFPL LFPLA FPLAD PLADE LADEQ ADEQF
    = ^^^^S ^^^ST ^^STK ^STKH STKHS TKHSK KHSKL HSKLY SKLYI KLYI$ LYI$$ YI$$$ I$$$$
    < <EOS>

    > ^^^^C ^^^CA ^^CAS ^CASS CASST ASSTD SSTDR STDRA TDRAY DRAYE RAYEQ AYEQY YEQYF
    = ^^^^L ^^^LY ^^LYS ^LYST LYSTK YSTKH STKHS TKHSK KHSKL HSKL$ SKL$$ KL$$$ L$$$$
    < <EOS>

    > ^^^^C ^^^CA ^^CAS ^CASS CASST ASSTD SSTDR STDRA TDRAY DRAYE RAYEQ AYEQY YEQYF
    = ^^^^L ^^^LY ^^LYS ^LYST LYSTK YSTKH STKHS TKHSK KHSKL HSKL$ SKL$$ KL$$$ L$$$$
    < <EOS>

    > ^^^^C ^^^CS ^^CSA ^CSAE CSAEQ SAEQG AEQGA EQGAV QGAVT GAVTG AVTGE VTGEL TGELF
    = ^^^^Y ^^^YS ^^YST ^YSTK YSTKH STKHS TKHSK KHSKL HSKLY SKLY$ KLY$$ LY$$$ Y$$$$
    < <EOS>

    > ^^^^C ^^^CA ^^CAS ^CASS CASSL ASSLF SSLFP SLFPL LFPLA FPLAD PLADE LADEQ ADEQF
    = ^^^^S ^^^ST ^^STK ^STKH STKHS TKHSK KHSKL HSKLY SKLYI KLYI$ LYI$$ YI$$$ I$$$$
    < <EOS>

```python
%matplotlib inline
output_words, attentions = evaluate(
    encoder1, attn_decoder1, "^^^^C ^^^CA ^^CAS ^CASS CASSL ASSLF SSLFP SLFPL LFPLA FPLAD PLADE LADEQ ADEQF")
plt.matshow(attentions.numpy())
```

```python
def showAttention(input_sentence, output_words, attentions):
    # Set up figure with colorbar
    fig = plt.figure()
    ax = fig.add_subplot(111)
    cax = ax.matshow(attentions.numpy(), cmap='bone')
    fig.colorbar(cax)

    # Set up axes
    ax.set_xticklabels([''] + input_sentence.split(' ') +
                       ['<EOS>'], rotation=90)
    ax.set_yticklabels([''] + output_words)

    # Show label at every tick
    ax.xaxis.set_major_locator(ticker.MultipleLocator(1))
    ax.yaxis.set_major_locator(ticker.MultipleLocator(1))

    plt.show()

def evaluateAndShowAttention(input_sentence):
    output_words, attentions = evaluate(
        encoder1, attn_decoder1, input_sentence)
    print('input =', input_sentence)
    print('output =', ' '.join(output_words))
    showAttention(input_sentence, output_words, attentions)

evaluateAndShowAttention("^^^^C ^^^CS ^^CSA ^CSAE CSAEQ SAEQG AEQGA EQGAV QGAVT GAVTG AVTGE VTGEL TGELF")

# evaluateAndShowAttention("elle est trop petit .")

# evaluateAndShowAttention("je ne crains pas de mourir .")

# evaluateAndShowAttention("c est un jeune directeur plein de talent .")

```

We will create a kmer from CDR3 and antigen, with stride =1. The line will end with ".".


# Satellite-image-based surface reconstruction in `MicMac`
> A tutorial

- toc: true
- badges: true
- comments: true
- categories: [jupyter]
- image: images/chart-preview.png

## Goal

In this tutorial we will introduce you to satellite image processing in MicMac. The goal of the exercise is to compute the surface and generate an orthohoto given a set of *modern* satellite images and their RPC geolocalisations. After setting-up MicMac and downloading the dataset, the pipeline is as follows:
  1. Tie-points extraction
  2. RPC-bundle adjustement
  3. Surface computation

    3.1. ***Method1***: Matching in object geometry

    3.3. ***Method2***: Matching in image geometry and fusion

*Contact: ewelina.rupnik(at)ign.fr*

## Projet set-up

### Download and compile MicMac & dependencies

```
import os
from os.path import exists, join, basename, splitext
import numpy as np
import cv2
import matplotlib.pyplot as plt

Dependencies_install = True
MicMac_clone = True
MicMac_cmake = True
MicMac_build = True

YOUR_PATH = '/content/'
!cd $YOUR_PATH
!pwd

if Dependencies_install:
  !apt update
  !apt install -y cmake
  !pip install dlib
  !apt-get install imagemagick proj-bin exiv2
  !pip install wget gdown

if MicMac_clone:
  if not exists(YOUR_PATH+'micmac/'):
    git_repo_url = 'https://github.com/micmacIGN/micmac.git'
    !git clone $git_repo_url

if MicMac_cmake:
  !cd micmac
  if not exists(YOUR_PATH+'micmac/build'):
    !mkdir $YOUR_PATH"micmac/build"

  !cd $YOUR_PATH"micmac/build"
  !cmake $YOUR_PATH"micmac" -DBUILD_POISSON=OFF

  if MicMac_build:
    !make install -j28

```

### Add environmental variable & download the dataset

The dataset consists of:
  * 4 images (tif)
  * 4 corresponding RPCs (xml)
  * `WGS84toUTM.xml` with the definition of a projection coordinate system (*proj4* format)

```
import os
os.environ['PATH'] += ":/content/micmac/bin/"
!echo $PATH

# if you can see the commands printed to the screen, everything is OK
!mm3d
```

```
# download data
dataset_url = 'https://drive.google.com/uc?id=18hmQL5kIqhcnR5ahp8IUsMZxLv7jvjgB'
!gdown $dataset_url -O "satellite_data.tar.gz"

# unpack
if not exists(YOUR_PATH+'satellite_data'):
  !mkdir $YOUR_PATH'satellite_data'
!tar -xf satellite_data.tar.gz -C $YOUR_PATH'satellite_data'
%cd $YOUR_PATH'satellite_data'

# utility functions to visualise tie-points
utils_url='https://drive.google.com/uc?id=1ATO1Nz_aXApxVnm6l7x1xappGXtcjuvp'
!gdown $utils_url -O "mm3d_utils.py"
```

## 1. Extract SIFT tie-points

  - **computation strategy**: there exist several predefined *strategies* to compute tie-points: `Line`, `All`, `MulScale`, `File`. We will use the `All` strategy where tie-points are searched between all possible pairs. Refer to MicMac documentation for the other modes.
  - **image resolution**: tie-points extraction is very costly, and to limit the computation time we usually downsampled the images; in this example, indicate resolution of `-1` which means full-resolution images; otherwise, if set to, e.g., `2000`, the images will be downsampled such that the larger image dimension (typically the width) will have `2000` pixels; the other dimension will have a size that is proportionally smaller;
  - **ExpTxt=1**: the extracted tie-points will be saved in a text format (as opossed to the default dat format)
  - **results**: tie-points are stored in the `Homol` directory. For instance, tie-points correponding to image `Im1.tif` will be stored in `Homol/PastisIm1.tif/` directory. If `Im1.tif` overlaps with `Im2.tif` and `Im3.tif`, their tie-points will be stored in `Homol/PastisIm1.tif/Im2.tif.dat` and `Homol/PastisIm1.tif/Im3.tif.dat`, respectively. If you chose to export in the text format, the `dat` extension will be replaced with `txt`.

*Note: Intermediary results are stored in the `Pastis` directory. It takesa  significant amount of space and is not used at later processing stages, therefore you may delete it.*

```
!mm3d Tapioca All .*tif -1 ExpTxt=1 @ExitOnBrkp
```

### Visualise tie-points

Read any pair of images and visalise their tie-points.

```
import mm3d_utils

aIm1 = cv2.imread('TPMM_0435.tif',cv2.IMREAD_IGNORE_ORIENTATION)
aIm2 = cv2.imread('TPMM_0566.tif',cv2.IMREAD_IGNORE_ORIENTATION)

TPtsVec = mm3d_utils.ImportHom("Homol/PastisTPMM_0435.tif/TPMM_0566.tif.txt")

mm3d_utils.plot_images([np.asarray(aIm1),np.asarray(aIm2)])
mm3d_utils.plot_tiepts2(np.asarray(TPtsVec,dtype=float))
```

## 2. RPC-bundle adjustment

### Read the RPCs in DIMAP format

This function reads the DIMAP format RPCs and converts it to a *MicMac* format. Several parameters are specified here:

  - `(.*).tif` this is the pattern of input images (note the dot preceding the star which is the posix convention)

  - `\$1.xml` is the corresponding pattern of RPC files; I use here a regular expression that associates the image name with its corresponding RPC file name; you may also run the command independently for each image if you're not familiar with regular expressions;

  - `RPC-d0` is the directory name where the converted files will be stored; it will serve as input in the following step, i.e., the bundle adjustment;

  - `Degre=0`, the degree of the polynomial correction;

  By choosing a zero-degree polynomial we will correct the satellite's geolocalisation by modelling a 3D image shift; please refer to [Rupnik et al., 2016] for more on the method.

  - `ChSys=WGS84toUTM.xml` definition of the projection coordinate sytem; MicMac expects that the processing coordinate frame is euclidean and all three coordinates have the same unit. The RPCs are expressed in geographical coordinates which are neither euclidean, nor unique in terms of units. To overcome that, MicMac will transfer, on the fly, the RPCs to a user-defined coordinate system, in this exemple defined in the `WGS84toUTM.xml` file. The definition of the coordinate system follows the `proj4` library convention. You can retrieve the code corresponding to the coordinate frame of your interest from `https://spatialreference.org/`

 Rupnik, E., Deseilligny, M.P., Delorme, A. and Klinger, Y., 2016. Refined satellite image orientation in the free open-source photogrammetric tools Apero/Micmac. ISPRS Annals of the Photogrammetry, Remote Sensing and Spatial Information Sciences, 3, p.83.

```
!mm3d Convert2GenBundle -help @ExitOnBrkp
!mm3d Convert2GenBundle "(.*).tif" "\$1.xml" RPC-d0 ChSys=WGS84toUTM.xml Degre=0  @ExitOnBrkp
```

    *****************************
    *  Help for Elise Arg main  *
    *****************************
    Mandatory unnamed args :
      * string :: {Name of Image}
      * string :: {Name of input Orientation File}
      * string :: {Directory of output Orientation (MyDir -> Oi-MyDir)}
    Named args :
      * [Name=ChSys] string :: {Change coordinate file (MicMac XML convention)}
      * [Name=Degre] INT :: {Degre of polynomial correction (Def=2)}
      * [Name=Type] string :: {Type of sensor (see eTypeImporGenBundle)}
      * [Name=PertubAng] REAL :: {Type of sensor (see eTypeImporGenBundle)}
    "(.*).tif": 4 matches.

### Run the adjustment

Within the bundle adjustemnt, MicMac will estimate the parameters of $D_x$ and $D_y$ functions, that are the polynomials you have defined in `Convert2GenBundle` method. In this example our observations are the tie-points. It is also possible to include ground control points.

$\begin{equation}
\begin{split}
y = g (\phi, \lambda, h) +  {D_y (x,y)}\\
x = h (\phi, \lambda, h) +  {D_x (x,y)}
\end{split}
\end{equation}$

where $g$ and $h$ are the RPC functions, and

$\begin{equation}
\begin{split}
D_y (x,y) = \sum_{i=0}^m \sum_{j=0}^n a_{ij} \cdot x^i y^j \\
D_x (x,y) = \sum_{i=0}^m \sum_{j=0}^n b_{ij} \cdot x^i y^j
\end{split}
\end{equation}$

The input parameters:
- `RPC-d0` is the folder with the initial geolocalisation
- `RPC-d0_adj` is the folder where the adjusted geoloc is saved
- `ExpTxt=1` indicates that tie-points are stored in text format

```
!mm3d Campari ".*tif" RPC-d0 RPC-d0-adj ExpTxt=1 @ExitOnBrkp
```

### Interpreting the results

One way to asses the quality of the adjustment is to look at the tie-points residual (for more sophisticated quality estimates see `MMTestOrient` in MicMac documentation).

The bundle adjustment is carried out in several iterations. Let's look at image `TPMM_0435.tif` in the last iteration:

> `RES:[TPMM_0435.tif][g] ER2 0.24636 Nn 100 Of 11753 Mul 5171 Mul-NN 5171 Time 1.15821`

* `0.24636` pixels is the mean residual calculated over all tie-points (i.e., $\sigma$ of the bundle)

* `Nn 100` means that 100$\%$ of tie-points were considered as inliers

* `11753` there were as many tie-points found

* `5171` there were as many multiple tie-points found (out of the `11753`), i.e., tie-points observed in at least 3 images;

## 3. Surface reconstruction

We will now calculate the surface with the semi-global dense image matching [Deseilligny \& Paparoditis, 2006].

  Deseilligny, M. and Paparoditis, N., 2006. A multiresolution and optimization-based image matching approach: An application to surface reconstruction from SPOT5-HRS stereo imagery. Archives of Photogrammetry, Remote Sensing and Spatial Information Sciences, 36(1/W41), pp.1-5.

### ***Method1***: Matching in object geometry

The computation will be carried out in the so-called terrain geometry, where the optimization is defined in the `(X,Y)` of the object space, and we are looking for the most optimal `Z`-coordinates (see Figure below). This geometry is well adapted to 2.5D surface computation.

Figure. Matching in object geometry.

 The input parameters are:
  - `UrbanMNE` is a predefined term and it defines a number of processing parameters (e.g., low regularization, small matching wodows, terrain geometry)

  - `.*tif` is the image set that will be used in the processing

  - `RPC-d0-adj` is the name of the directory containing the geoloclisation

  - `SzW=2` defines the matching window size, i.e., with size set to 2, the window size is ```5x5```

  - `Regul` is the regularization term $\alpha$; in `UrbanMNE` it is by default set to `0.02` becase in urban zones we're typically interested in reconstructing fine details; the dataset used in this example, however, represents a smooth surface so we're just fine with a more agressive regularization; moreover, our images are quite noisy and by adding more regularization we will avoid noisy surface reconstructions;

  - `DoOrtho=1`, this parameters wil force MicMac to create individual orthomosaic, i.e., rectify each image; the rectified images are stored in `Ortho-MEC-Malt/Ort_*.tif`; to create the final orthophotomap we will still need to do mosaicing with `Tawny` (later in this tutorial);

  - `NbVI=2` sets the necessary minimum number of images for MicMac to compute the surface; by default the value is set to 3 which means that in areas with only two overlapping images, the surface will not be computed;

  - `EZA=1`, this parameter will force the output surface raster to save the Z-coordinates in their absolute values; without explicitly forcing MicMac to do that, to avoid having to store large values inside the raster tiff, it will apply a normalisation (normalisation parameters `OrigineAlti` and `ResolutionAlti` are to be found in the accompanying xml files);

```
!mm3d Malt UrbanMNE ".*tif" RPC-d0-adj SzW=2 Regul=0.2 DoOrtho=1 NbVI=2 EZA=1 @ExitOnBrkp
```

#### Reading the output files

The matching is carried out at multi-resolutions, i.e., we first calculate the surface using low resolution images (top-most level of the image pyramid), then we propagate the solution to lower levels and refine it, so long we have not reached the bottom of the image pyramid. The surface reconstructions at each level are stored inside the `MEC-Malt` directory. Here's how to decipher the files:

- `MEC-Malt/Z_Num8_DeZoom1_STD-MALT.tif` represents the surface raster at the highest resolution;
- `MEC-Malt/Z_Num8_DeZoom1_STD-MALT.xml` is its metadata file that encodes the georeferencing; let's assume you'd like to convert a pixel $(i,j)$ from its image coordinates to its georeferenced coordinates (i.e., object coordinates):

> `Z-coordinate normalised` : $Z^{img}_{i,j} = Z\_Num\_File^{img}(i,j)$,

> `Z-coordinate` : $Z^{obj}_{i,j} = OrigineAlti + ResolutionAlti \cdot Z^{img}_{i,j}$

> `XY-coordinates` : $(X,Y) = OriginePlani + ResolutionAlti \cdot (i,j)$

- `MEC-Malt/Masq_STD-MALT_DeZoomX.tif` is a binary mask file that is a result of your input mask (if you used one) and a mask that is automatically calculated in the matching optimisation phase;

- `MEC-Malt/Correl_STD-MALT_Num_X.tif` are the images with storing the correlation scores (it is not pure correlation, it is the correlation store after the aggregation step)

#### Create a grayshaded DSM

Represent the surface in form of a grayshading. To visually asses the quality of your surface, it is much more intuitive than just looking at the depth/Z image.

```
!mm3d GrShade MEC-Malt/Z_Num8_DeZoom1_STD-MALT.tif ModeOmbre=IgnE Mask=MEC-Malt/Masq_STD-MALT_DeZoom1.tif @ExitOnBrkp
```

#### Visualise the grayshaded surface

```
surface_shade_im = cv2.imread("MEC-Malt/Z_Num8_DeZoom1_STD-MALTShade.tif",cv2.IMREAD_IGNORE_ORIENTATION)

fig, ax = plt.subplots(figsize=(30, 10))
ax.imshow(surface_shade_im,cmap="gray")
plt.tight_layout()
```

#### Generate an orthophotmap

`Tawny` will mosaic the per-image orthopĥotomosaics created in `Malt` (i.e., `Ortho-MEC-Malt/Ort_*.tif`), during matching. It will additionally perform some basic radiometry equalization. The output orthoimage is stored in `Ortho-MEC-Malt/Orthophotomosaic.tif`, and its georeferencing is stored in `Ortho-MEC-Malt/Orthophotomosaic.twf`.

```
!mm3d Tawny Ortho-MEC-Malt/ @ExitOnBrkp
```

#### Visualise the orthophoto

```
ortho_im = cv2.imread("Ortho-MEC-Malt/Orthophotomosaic.tif",cv2.IMREAD_IGNORE_ORIENTATION)

fig, ax = plt.subplots(figsize=(30, 10))
ax.imshow(ortho_im,cmap='gray')
plt.tight_layout()
```

### ***Method2***: Multiview matching in image geometry and fusion

The computation will be carried out in the image geometry, where the optimization is defined in the `(x,y)` of the image space, and we are looking for the most optimal `depths` (see Figure below). This geometry is well adapted to true 3D surface reconstruction. Because the individual reconstructions are computed in image coordinate frames, a fusion will but carried out at the end.

Figure. Matching in image geometry.

The multiview pipeline is as follows [Rupnik et al., 2018]:

1. Extract tie-points and do RPC-bundle adjustement (done previously)
2. Do N per-stereo (or per-M image as we are not bound by the number of images) dense matching (`Malt GeomImage`)
3. Transform the N depth maps to a common coordinate frame (`NuageBascule`)
4. Fuse the N depth maps into one (`SMDM`)

Figure. Multiview image matching and fusion pipeline.

 Rupnik, E., Pierrot-Deseilligny, M. and Delorme, A., 2018. 3D reconstruction from multi-view VHR-satellite images in MicMac. ISPRS Journal of Photogrammetry and Remote Sensing, 139, pp.201-211.

#### Do two per-triplet image matching

We will compute two surfaces using two different subsets of the images.

Input parameters:

  - `GeomImage` is a predefined term and it defines a number of processing parameters (e.g., low regularization, small matching wodows, image geometry)

  - `TPMM_(0435|0566|1088).*tif` is the image set that will be used in the processing; in this example it is an image triplet; we take 3 **consecutive images** to make sure that the $\frac{B}{H}$ ratios in the set are relatively small

  - `RPC-d0-adj` is the name of the directory containing the geolocalisation

  - `Master=TPMM_0566.tif` is the master image, i.e., the optimization is defined over each pixel of that image

  - `SzW=2` and `Regul=0.2`, similarily to `Method1`, we add regularization and use bigger correlation windows because: (1) the geometry of the surface is smooth (i.e., no discontinuities), and (2) the images are quiet noisy;

  - `NbVI=2` sets the necessary minimum number of images for MicMac to compute the surface; by default the value is set to 3 which means that in areas with only two overlapping images, the surface will not be computed;

```
#the first triplet
!mm3d Malt GeomImage "TPMM_(0435|0566|1088).*tif" RPC-d0-adj Master=TPMM_0566.tif SzW=1 Regul=0.1 NbVI=2 ZPas=1 @ExitOnBrkp
```

```
# the second triplet
!mm3d Malt GeomImage "TPMM_(0566|1088|1216).*tif" RPC-d0-adj Master=TPMM_1088.tif SzW=1 Regul=0.1 NbVI=2 ZPas=1   @ExitOnBrkp
```

#### Transform the depth maps to a common coordinate frame

  * The $1^{st}$ command `Malt UrbanMNE` does image matching in ground geometry (we've used it before), but since we indicate `DoMEC=0` it will not calculate the matching, all it will do is to create metadata defining the coordinate frame of the ground geometry. The output, as before, is stored in `MEC-Malt` folder;

  * The $2^{nd}$ command `NuageBascule` will apply the transformation; the parameters are:
    * `MM-Malt-Img-TPMM_0566/NuageImProf_STD-MALT_Etape_8.xml` is the metadata file defining the input coordinate frame
    * `MEC-Malt/NuageImProf_STD-MALT_Etape_8.xml` is the metadata file defining the target coordinate frame (i.e., it is the terrain geometry)
    * `Fusion/DSM_Tri1.xml` is the output metadata file, i.e., the input file transformed to the target coordinate frame; it will be accompagned by several other files containing the surface itself, the mask and the correlation image (see the inside of the `Fusion` folder);

```
# define the common frame
!mm3d Malt UrbanMNE ".*tif" RPC-d0-adj DoMEC=0  @ExitOnBrkp

# create a directory that will store the fused surface
!mkdir Fusion

# do 3D spatial similarity of the first triplet depth map
!mm3d NuageBascule MM-Malt-Img-TPMM_0566/NuageImProf_STD-MALT_Etape_8.xml MEC-Malt/NuageImProf_STD-MALT_Etape_8.xml Fusion/DSM_Tri1.xml @ExitOnBrkp

# do 3D spatial similarity of the second triplet depth map
!mm3d NuageBascule MM-Malt-Img-TPMM_1088/NuageImProf_STD-MALT_Etape_8.xml MEC-Malt/NuageImProf_STD-MALT_Etape_8.xml Fusion/DSM_Tri2.xml @ExitOnBrkp
```

#### Fuse the individual depth maps

The fusion takes all the surfaces specified by the regular expression and merges it. The fusion takes into account the correlation images and treats it as confidence maps.

Input parameters:

* `Fusion/DSM_Tri.*xml` the subset of surfaces that will be merged;

Tha result is saved to `Fusion/Fusion_Prof.tif`, there is a corresponding mask and a correlation map named with `_Mask` and `Correl` postfixes, respectively.

```
!mm3d SMDM Fusion/DSM_Tri.*xml  @ExitOnBrkp
```

#### Visualise in grayshade and export to ply

```
# grayshade
!mm3d GrShade Fusion/Fusion_Prof.tif Out=Fusion/Fusion_GShade.tif ModeOmbre=IgnE @ExitOnBrkp

surface_fused_shade_im = cv2.imread("Fusion/Fusion_GShade.tif",cv2.IMREAD_IGNORE_ORIENTATION)

fig, ax = plt.subplots(1,2,figsize=(15, 15))
ax[0].imshow(surface_fused_shade_im,cmap="gray")
ax[1].imshow(surface_shade_im,cmap="gray")
plt.tight_layout()

# export to ply
#!mm3d Nuage2Ply Fusion/Fusion.xml Out=Fusion.ply
```


```
!nvidia-smi
```

    Mon Jun  7 00:59:45 2021
    +-----------------------------------------------------------------------------+
    | NVIDIA-SMI 465.27       Driver Version: 460.32.03    CUDA Version: 11.2     |
    |-------------------------------+----------------------+----------------------+
    | GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
    | Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
    |                               |                      |               MIG M. |
    |===============================+======================+======================|
    |   0  Tesla T4            Off  | 00000000:00:04.0 Off |                    0 |
    | N/A   33C    P8     9W /  70W |      0MiB / 15109MiB |      0%      Default |
    |                               |                      |                  N/A |
    +-------------------------------+----------------------+----------------------+

    +-----------------------------------------------------------------------------+
    | Processes:                                                                  |
    |  GPU   GI   CI        PID   Type   Process name                  GPU Memory |
    |        ID   ID                                                   Usage      |
    |=============================================================================|
    |  No running processes found                                                 |
    +-----------------------------------------------------------------------------+

```
#!pip install pymatgen==2020.12.31
!pip install pymatgen==2019.11.11
!pip install --pre graphdot
!pip install gdown
```

    Requirement already satisfied: pymatgen==2019.11.11 in /usr/local/lib/python3.7/dist-packages (2019.11.11)
    Requirement already satisfied: ruamel.yaml>=0.15.6 in /usr/local/lib/python3.7/dist-packages (from pymatgen==2019.11.11) (0.17.7)
    Requirement already satisfied: pydispatcher>=2.0.5 in /usr/local/lib/python3.7/dist-packages (from pymatgen==2019.11.11) (2.0.5)
    Requirement already satisfied: tabulate in /usr/local/lib/python3.7/dist-packages (from pymatgen==2019.11.11) (0.8.9)
    Requirement already satisfied: requests in /usr/local/lib/python3.7/dist-packages (from pymatgen==2019.11.11) (2.23.0)
    Requirement already satisfied: scipy>=1.0.1 in /usr/local/lib/python3.7/dist-packages (from pymatgen==2019.11.11) (1.4.1)
    Requirement already satisfied: spglib>=1.9.9.44 in /usr/local/lib/python3.7/dist-packages (from pymatgen==2019.11.11) (1.16.1)
    Requirement already satisfied: sympy in /usr/local/lib/python3.7/dist-packages (from pymatgen==2019.11.11) (1.7.1)
    Requirement already satisfied: palettable>=3.1.1 in /usr/local/lib/python3.7/dist-packages (from pymatgen==2019.11.11) (3.3.0)
    Requirement already satisfied: matplotlib>=1.5 in /usr/local/lib/python3.7/dist-packages (from pymatgen==2019.11.11) (3.2.2)
    Requirement already satisfied: monty>=3.0.2 in /usr/local/lib/python3.7/dist-packages (from pymatgen==2019.11.11) (2021.5.9)
    Requirement already satisfied: pandas in /usr/local/lib/python3.7/dist-packages (from pymatgen==2019.11.11) (1.1.5)
    Requirement already satisfied: networkx>=2.2 in /usr/local/lib/python3.7/dist-packages (from pymatgen==2019.11.11) (2.5.1)
    Requirement already satisfied: numpy>=1.14.3 in /usr/local/lib/python3.7/dist-packages (from pymatgen==2019.11.11) (1.19.5)
    Requirement already satisfied: ruamel.yaml.clib>=0.1.2; platform_python_implementation == "CPython" and python_version < "3.10" in /usr/local/lib/python3.7/dist-packages (from ruamel.yaml>=0.15.6->pymatgen==2019.11.11) (0.2.2)
    Requirement already satisfied: idna<3,>=2.5 in /usr/local/lib/python3.7/dist-packages (from requests->pymatgen==2019.11.11) (2.10)
    Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.7/dist-packages (from requests->pymatgen==2019.11.11) (2020.12.5)
    Requirement already satisfied: urllib3!=1.25.0,!=1.25.1,<1.26,>=1.21.1 in /usr/local/lib/python3.7/dist-packages (from requests->pymatgen==2019.11.11) (1.24.3)
    Requirement already satisfied: chardet<4,>=3.0.2 in /usr/local/lib/python3.7/dist-packages (from requests->pymatgen==2019.11.11) (3.0.4)
    Requirement already satisfied: mpmath>=0.19 in /usr/local/lib/python3.7/dist-packages (from sympy->pymatgen==2019.11.11) (1.2.1)
    Requirement already satisfied: python-dateutil>=2.1 in /usr/local/lib/python3.7/dist-packages (from matplotlib>=1.5->pymatgen==2019.11.11) (2.8.1)
    Requirement already satisfied: kiwisolver>=1.0.1 in /usr/local/lib/python3.7/dist-packages (from matplotlib>=1.5->pymatgen==2019.11.11) (1.3.1)
    Requirement already satisfied: pyparsing!=2.0.4,!=2.1.2,!=2.1.6,>=2.0.1 in /usr/local/lib/python3.7/dist-packages (from matplotlib>=1.5->pymatgen==2019.11.11) (2.4.7)
    Requirement already satisfied: cycler>=0.10 in /usr/local/lib/python3.7/dist-packages (from matplotlib>=1.5->pymatgen==2019.11.11) (0.10.0)
    Requirement already satisfied: pytz>=2017.2 in /usr/local/lib/python3.7/dist-packages (from pandas->pymatgen==2019.11.11) (2018.9)
    Requirement already satisfied: decorator<5,>=4.3 in /usr/local/lib/python3.7/dist-packages (from networkx>=2.2->pymatgen==2019.11.11) (4.4.2)
    Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.7/dist-packages (from python-dateutil>=2.1->matplotlib>=1.5->pymatgen==2019.11.11) (1.15.0)
    Requirement already satisfied: graphdot in /usr/local/lib/python3.7/dist-packages (0.8a17)
    Requirement already satisfied: scipy>=1.3.0 in /usr/local/lib/python3.7/dist-packages (from graphdot) (1.4.1)
    Requirement already satisfied: treelib>=1.6.1 in /usr/local/lib/python3.7/dist-packages (from graphdot) (1.6.1)
    Requirement already satisfied: networkx>=2.4 in /usr/local/lib/python3.7/dist-packages (from graphdot) (2.5.1)
    Requirement already satisfied: ase>=3.17 in /usr/local/lib/python3.7/dist-packages (from graphdot) (3.21.1)
    Requirement already satisfied: kahypar>=1.1.4 in /usr/local/lib/python3.7/dist-packages (from graphdot) (1.1.6)
    Requirement already satisfied: tqdm>=4.55 in /usr/local/lib/python3.7/dist-packages (from graphdot) (4.61.0)
    Requirement already satisfied: mendeleev>=0.7 in /usr/local/lib/python3.7/dist-packages (from graphdot) (0.7.0)
    Requirement already satisfied: pymatgen==2019.11.11 in /usr/local/lib/python3.7/dist-packages (from graphdot) (2019.11.11)
    Requirement already satisfied: numba>=0.51.0 in /usr/local/lib/python3.7/dist-packages (from graphdot) (0.51.2)
    Requirement already satisfied: numpy>=1.17 in /usr/local/lib/python3.7/dist-packages (from graphdot) (1.19.5)
    Requirement already satisfied: sympy>=1.3 in /usr/local/lib/python3.7/dist-packages (from graphdot) (1.7.1)
    Requirement already satisfied: pandas>=0.24 in /usr/local/lib/python3.7/dist-packages (from graphdot) (1.1.5)
    Requirement already satisfied: pycuda>=2019 in /usr/local/lib/python3.7/dist-packages (from graphdot) (2021.1)
    Requirement already satisfied: future in /usr/local/lib/python3.7/dist-packages (from treelib>=1.6.1->graphdot) (0.16.0)
    Requirement already satisfied: decorator<5,>=4.3 in /usr/local/lib/python3.7/dist-packages (from networkx>=2.4->graphdot) (4.4.2)
    Requirement already satisfied: matplotlib>=2.0.0 in /usr/local/lib/python3.7/dist-packages (from ase>=3.17->graphdot) (3.2.2)
    Requirement already satisfied: pyfiglet<0.9,>=0.8.post1 in /usr/local/lib/python3.7/dist-packages (from mendeleev>=0.7->graphdot) (0.8.post1)
    Requirement already satisfied: SQLAlchemy<2.0.0,>=1.3.23 in /usr/local/lib/python3.7/dist-packages (from mendeleev>=0.7->graphdot) (1.4.15)
    Requirement already satisfied: colorama<0.5.0,>=0.4.4 in /usr/local/lib/python3.7/dist-packages (from mendeleev>=0.7->graphdot) (0.4.4)
    Requirement already satisfied: six<2.0.0,>=1.15.0 in /usr/local/lib/python3.7/dist-packages (from mendeleev>=0.7->graphdot) (1.15.0)
    Requirement already satisfied: Pygments<3.0.0,>=2.8.0 in /usr/local/lib/python3.7/dist-packages (from mendeleev>=0.7->graphdot) (2.9.0)
    Requirement already satisfied: requests in /usr/local/lib/python3.7/dist-packages (from pymatgen==2019.11.11->graphdot) (2.23.0)
    Requirement already satisfied: palettable>=3.1.1 in /usr/local/lib/python3.7/dist-packages (from pymatgen==2019.11.11->graphdot) (3.3.0)
    Requirement already satisfied: spglib>=1.9.9.44 in /usr/local/lib/python3.7/dist-packages (from pymatgen==2019.11.11->graphdot) (1.16.1)
    Requirement already satisfied: ruamel.yaml>=0.15.6 in /usr/local/lib/python3.7/dist-packages (from pymatgen==2019.11.11->graphdot) (0.17.7)
    Requirement already satisfied: monty>=3.0.2 in /usr/local/lib/python3.7/dist-packages (from pymatgen==2019.11.11->graphdot) (2021.5.9)
    Requirement already satisfied: pydispatcher>=2.0.5 in /usr/local/lib/python3.7/dist-packages (from pymatgen==2019.11.11->graphdot) (2.0.5)
    Requirement already satisfied: tabulate in /usr/local/lib/python3.7/dist-packages (from pymatgen==2019.11.11->graphdot) (0.8.9)
    Requirement already satisfied: setuptools in /usr/local/lib/python3.7/dist-packages (from numba>=0.51.0->graphdot) (57.0.0)
    Requirement already satisfied: llvmlite<0.35,>=0.34.0.dev0 in /usr/local/lib/python3.7/dist-packages (from numba>=0.51.0->graphdot) (0.34.0)
    Requirement already satisfied: mpmath>=0.19 in /usr/local/lib/python3.7/dist-packages (from sympy>=1.3->graphdot) (1.2.1)
    Requirement already satisfied: python-dateutil>=2.7.3 in /usr/local/lib/python3.7/dist-packages (from pandas>=0.24->graphdot) (2.8.1)
    Requirement already satisfied: pytz>=2017.2 in /usr/local/lib/python3.7/dist-packages (from pandas>=0.24->graphdot) (2018.9)
    Requirement already satisfied: mako in /usr/local/lib/python3.7/dist-packages (from pycuda>=2019->graphdot) (1.1.4)
    Requirement already satisfied: pytools>=2011.2 in /usr/local/lib/python3.7/dist-packages (from pycuda>=2019->graphdot) (2021.2.7)
    Requirement already satisfied: appdirs>=1.4.0 in /usr/local/lib/python3.7/dist-packages (from pycuda>=2019->graphdot) (1.4.4)
    Requirement already satisfied: cycler>=0.10 in /usr/local/lib/python3.7/dist-packages (from matplotlib>=2.0.0->ase>=3.17->graphdot) (0.10.0)
    Requirement already satisfied: pyparsing!=2.0.4,!=2.1.2,!=2.1.6,>=2.0.1 in /usr/local/lib/python3.7/dist-packages (from matplotlib>=2.0.0->ase>=3.17->graphdot) (2.4.7)
    Requirement already satisfied: kiwisolver>=1.0.1 in /usr/local/lib/python3.7/dist-packages (from matplotlib>=2.0.0->ase>=3.17->graphdot) (1.3.1)
    Requirement already satisfied: greenlet!=0.4.17; python_version >= "3" in /usr/local/lib/python3.7/dist-packages (from SQLAlchemy<2.0.0,>=1.3.23->mendeleev>=0.7->graphdot) (1.1.0)
    Requirement already satisfied: importlib-metadata; python_version < "3.8" in /usr/local/lib/python3.7/dist-packages (from SQLAlchemy<2.0.0,>=1.3.23->mendeleev>=0.7->graphdot) (4.0.1)
    Requirement already satisfied: urllib3!=1.25.0,!=1.25.1,<1.26,>=1.21.1 in /usr/local/lib/python3.7/dist-packages (from requests->pymatgen==2019.11.11->graphdot) (1.24.3)
    Requirement already satisfied: idna<3,>=2.5 in /usr/local/lib/python3.7/dist-packages (from requests->pymatgen==2019.11.11->graphdot) (2.10)
    Requirement already satisfied: chardet<4,>=3.0.2 in /usr/local/lib/python3.7/dist-packages (from requests->pymatgen==2019.11.11->graphdot) (3.0.4)
    Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.7/dist-packages (from requests->pymatgen==2019.11.11->graphdot) (2020.12.5)
    Requirement already satisfied: ruamel.yaml.clib>=0.1.2; platform_python_implementation == "CPython" and python_version < "3.10" in /usr/local/lib/python3.7/dist-packages (from ruamel.yaml>=0.15.6->pymatgen==2019.11.11->graphdot) (0.2.2)
    Requirement already satisfied: MarkupSafe>=0.9.2 in /usr/local/lib/python3.7/dist-packages (from mako->pycuda>=2019->graphdot) (2.0.1)
    Requirement already satisfied: zipp>=0.5 in /usr/local/lib/python3.7/dist-packages (from importlib-metadata; python_version < "3.8"->SQLAlchemy<2.0.0,>=1.3.23->mendeleev>=0.7->graphdot) (3.4.1)
    Requirement already satisfied: typing-extensions>=3.6.4; python_version < "3.8" in /usr/local/lib/python3.7/dist-packages (from importlib-metadata; python_version < "3.8"->SQLAlchemy<2.0.0,>=1.3.23->mendeleev>=0.7->graphdot) (3.7.4.3)
    Requirement already satisfied: gdown in /usr/local/lib/python3.7/dist-packages (3.6.4)
    Requirement already satisfied: requests in /usr/local/lib/python3.7/dist-packages (from gdown) (2.23.0)
    Requirement already satisfied: tqdm in /usr/local/lib/python3.7/dist-packages (from gdown) (4.61.0)
    Requirement already satisfied: six in /usr/local/lib/python3.7/dist-packages (from gdown) (1.15.0)
    Requirement already satisfied: chardet<4,>=3.0.2 in /usr/local/lib/python3.7/dist-packages (from requests->gdown) (3.0.4)
    Requirement already satisfied: idna<3,>=2.5 in /usr/local/lib/python3.7/dist-packages (from requests->gdown) (2.10)
    Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.7/dist-packages (from requests->gdown) (2020.12.5)
    Requirement already satisfied: urllib3!=1.25.0,!=1.25.1,<1.26,>=1.21.1 in /usr/local/lib/python3.7/dist-packages (from requests->gdown) (1.24.3)

```
%matplotlib inline
import io
import sys
sys.path.append('/usr/local/lib/python3.6/site-packages/')
import os
import urllib
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

import graphdot
from graphdot import Graph
from graphdot.graph.adjacency import AtomicAdjacency
from graphdot.graph.reorder import rcm
from graphdot.kernel.marginalized import MarginalizedGraphKernel # https://graphdot.readthedocs.io/en/latest/apidoc/graphdot.kernel.marginalized.html
from graphdot.kernel.marginalized.starting_probability import Uniform
from graphdot.model.gaussian_process import (
    GaussianProcessRegressor,
    LowRankApproximateGPR
)
from graphdot.kernel.fix import Normalization
import graphdot.microkernel as uX
import ase.io
```

    /usr/local/lib/python3.7/dist-packages/graphdot/graph/__init__.py:24: UserWarning: Cannot import RDKit, `graph.from_rdkit()` will be unavailable.

      'Cannot import RDKit, `graph.from_rdkit()` will be unavailable.\n'

```
from google.colab import drive
drive.mount('/content/gdrive', force_remount=True)
```

    Mounted at /content/gdrive

```
#cd gdrive/MyDrive/Google\ Colab/Covid-Data
%cd gdrive/MyDrive/Covid-Data/
```

    /content/gdrive/.shortcut-targets-by-id/1wtzMcocuK8kPsz8K0ktjCZPkv567W6M2/Covid-Data

```
!pwd
```

    /content/gdrive/.shortcut-targets-by-id/1wtzMcocuK8kPsz8K0ktjCZPkv567W6M2/Covid-Data

```
files  = ['uncharged_NSP15_6W01_A_3_H.Orderable_zinc_db_enaHLL.2col.csv.1.xz']
dataset = pd.read_pickle(files[0])
#frames = [pd.read_pickle(f) for f in files]
#dataset = pd.concat(frames)
```

```
dataset.head()
```

      energy
      smiles
      graphs

      0
      -2.265697
      Cc1nc(no1)c2cncnc2[C@@H]3CCCN(C3)C(=O)Cc4ccccc4
      Graph(nodes={'!i': [ 0, 1, 2, 3, 4, 5, 6, 7, 8...

      1
      -2.351519
      Cc1ccc(cc1)c2nnc(n2N)SCC(=O)Nc3ccccc3OC
      Graph(nodes={'!i': [ 0, 1, 2, 3, 4, 5, 6, 7, 8...

      2
      -3.308996
      CCOC(=O)Cn1c2ccccc2nc1[C@H]3CC(=O)N(C3)c4ccc(c...
      Graph(nodes={'!i': [ 0, 1, 2, 3, 4, 5, 6, 7, 8...

      3
      -3.760179
      CC[C@H](C)NC(=O)[C@@H](CC)N(Cc1ccccc1)C(=O)Cc2...
      Graph(nodes={'!i': [ 0, 1, 2, 3, 4, 5, 6, 7, 8...

      4
      -2.895577
      Cc1cccc2c1ccn2CCC(=O)N3CCc4cc(c(cc4C3)OC)OC
      Graph(nodes={'!i': [ 0, 1, 2, 3, 4, 5, 6, 7, 8...

```
target = 'energy'
N_train = 2500
N_test = 5000
```

```
np.random.seed(0)
# select train and test data
train_sel = np.random.choice(len(dataset), N_train, replace=False)
test_sel = np.random.choice(np.setxor1d(np.arange(len(dataset)), train_sel), N_test, replace=False)
train = dataset.iloc[train_sel]
test = dataset.iloc[test_sel]
```

```
# import graphdot.microkernel as uX
gpr = GaussianProcessRegressor(
    # kernel is the covariance function of the gaussian process (GP)
    kernel=Normalization( # kernel equals to normalization -> normalizes a kernel using the cosine of angle formula, k_normalized(x,y) = k(x,y)/sqrt(k(x,x)*k(y,y))
        # graphdot.kernel.fix.Normalization(kernel), set kernel as marginalized graph kernel, which is used to calculate the similarity between 2 graphs
        # implement the random walk-based graph similarity kernel as Kashima, H., Tsuda, K., & Inokuchi, A. (2003). Marginalized kernels between labeled graphs. ICML
        MarginalizedGraphKernel(
            # node_kernel - A kernelet that computes the similarity between individual nodes
            # uX - graphdot.microkernel - microkernels are positive-semidefinite functions between individual nodes and edges of graphs
            node_kernel=uX.Additive( # addition of kernal matrices: sum of k_a(X_a, Y_a) cross for a in features
                # uX.Constant - a kernel that returns a constant value, always mutlipled with other microkernels as an adjustable weight
                #   c, the first input arg. as 0.5, (0.01, 10) the lower and upper bounds of c that is allowed to vary during hyperpara. optimizartion
                # uX.KroneckerDelta - a kronecker delta returns 1 when two features are equal and return h (the first input arg here, which is 0.5 in this case) otherwise
                #   (0.1, 0.9) the lower and upper bounds that h is allowed to vary during hyperpara. optimization
                aromatic=uX.Constant(0.5, (0.01, 10.0)) * uX.KroneckerDelta(0.5,(0.1, 0.9)),
                atomic_number=uX.Constant(0.5, (0.01, 10.0)) * uX.KroneckerDelta(0.8,(0.1, 0.9)),
                # uX.SquareExponential
                #
                charge=uX.Constant(0.5, (0.01, 10.0)) * uX.SquareExponential(1.0),
                chiral=uX.Constant(0.5, (0.01, 10.0)) * uX.KroneckerDelta(0.5,(0.1, 0.9)),
                hcount=uX.Constant(0.5, (0.01, 10.0)) * uX.SquareExponential(1.0),
                hybridization=uX.Constant(0.5, (0.01, 10.0)) * uX.KroneckerDelta(0.5,(0.1, 0.9)),
                # uX.Convolution - a convolutional microkernel which averages evaluations of a base microkernel between pairs pf elememts of two variable-length feature sequences
                # uX.KroneckerDelta as the base kernel
                ring_list=uX.Constant(0.5, (0.01, 100.0)) * uX.Convolution(uX.KroneckerDelta(0.5,(0.1, 0.9)))
            ).normalized,
            # edge_kernel - A kernelet that computes the similarity between individual edge
            edge_kernel=uX.Additive(
                aromatic=uX.Constant(0.5, (0.01, 10.0)) * uX.KroneckerDelta(0.5,(0.1, 0.9)),
                conjugated=uX.Constant(0.5, (0.01, 10.0)) * uX.KroneckerDelta(0.5,(0.1, 0.9)),
                order=uX.Constant(0.5, (0.01, 10.0)) * uX.KroneckerDelta(0.8,(0.1, 0.9)),
                ring_stereo=uX.Constant(0.5, (0.01, 10.0)) * uX.KroneckerDelta(0.8,(0.1, 0.9)),
                stereo=uX.Constant(0.5, (0.01, 10.0)) * uX.KroneckerDelta(0.8,(0.1, 0.9))
            ).normalized,
            p=Uniform(1.0, p_bounds='fixed'), # the strating probability of the random walk on each node
            q=0.05 # the probability for the random walk to stop during each step
        )
    ),
    alpha=1e-4, # value added to the diagonal of the kernel matrix during fitting
    optimizer=True, # default optimizer of L-BFGS-B based on scipy.optimize.minimize
    normalize_y=True, # normalize the y values so taht the means and variance is 0 and 1, repsectively. Will be reversed when predicions are returned
    regularization='+', # alpha (1e-4 in this case) is added to the diagonals of the kernal matrix
)
```

```
#gpr.fit(train.graphs, train[target], repeat=3, verbose=True)
# using the molecular graph to predict the energy
gpr.fit(train.graphs, train[target], repeat=1, verbose=True)
```

    |        logP|       dlogP|     y^T.K.y|     log|K| |     Cond(K)|  GPU time|  CPU time|
    |------------|------------|------------|------------|------------|----------|----------|
    |  9.2486e+06|  5.6192e+07|  9.2684e+06|      -19798|  2.2708e+07|   1.3e+02|       1.9|
    |  7.0456e+06|   5.459e+07|  7.0642e+06|      -18684|  2.1866e+07|   1.2e+02|       1.8|
    |  4.8866e+06|   4.702e+07|  4.9039e+06|      -17315|  2.0461e+07|   1.2e+02|       1.9|
    |  4.1676e+06|  4.0965e+07|  4.1844e+06|      -16750|  1.9706e+07|   1.2e+02|       1.9|
    |   3.645e+06|  3.5011e+07|  3.6612e+06|      -16274|  1.8948e+07|   1.1e+02|       1.8|
    |   3.326e+06|  3.0749e+07|   3.342e+06|      -15946|  1.8322e+07|   1.1e+02|       1.9|
    |  3.1778e+06|  2.8881e+07|  3.1936e+06|      -15786|  1.8126e+07|   1.1e+02|       1.9|
    |  3.1776e+06|   2.825e+07|  3.1934e+06|      -15766|  1.7637e+07|     1e+02|         2|
    |  3.1162e+06|  2.8107e+07|  3.1319e+06|      -15708|  1.7756e+07|   1.1e+02|       1.8|
    |  3.1047e+06|  2.7919e+07|  3.1204e+06|      -15701|  1.7957e+07|   1.1e+02|       1.8|
    |  3.0991e+06|  2.7884e+07|  3.1148e+06|      -15693|  1.7887e+07|   1.1e+02|       1.9|
    |   3.095e+06|  2.7843e+07|  3.1107e+06|      -15687|  1.7852e+07|   1.1e+02|       1.9|
    |  3.0915e+06|  2.7966e+07|  3.1071e+06|      -15682|  1.7831e+07|   1.1e+02|       1.8|
    |  3.0881e+06|   2.783e+07|  3.1037e+06|      -15679|  1.7843e+07|   1.1e+02|       1.8|
    |  3.0867e+06|   2.776e+07|  3.1024e+06|      -15678|   1.787e+07|   1.1e+02|       1.8|
    |  3.0846e+06|  2.7689e+07|  3.1002e+06|      -15676|   1.786e+07|   1.1e+02|       1.8|
    |  3.0826e+06|  2.7772e+07|  3.0982e+06|      -15674|  1.7888e+07|   1.1e+02|       1.9|
    |  3.0805e+06|  2.7692e+07|  3.0962e+06|      -15672|  1.7856e+07|   1.1e+02|       1.8|
    |  3.0793e+06|  2.7664e+07|   3.095e+06|      -15671|  1.7848e+07|   1.1e+02|       1.8|
    |  3.0798e+06|  2.7691e+07|  3.0955e+06|      -15671|  1.7831e+07|   1.1e+02|       1.8|
    |  3.0801e+06|  2.7681e+07|  3.0957e+06|      -15671|  1.7844e+07|   1.1e+02|       1.8|
    |  3.0801e+06|  2.7683e+07|  3.0958e+06|      -15671|  1.7848e+07|   1.1e+02|       1.8|
    |  3.0798e+06|  2.7676e+07|  3.0955e+06|      -15671|  1.7842e+07|   1.1e+02|       1.8|
    |  3.0802e+06|  2.7682e+07|  3.0959e+06|      -15671|  1.7842e+07|   1.1e+02|       1.8|
    |  3.0803e+06|   2.769e+07|   3.096e+06|      -15671|  1.7846e+07|   1.1e+02|       1.8|
    Optimization result:
          fun: 3080341.1690479927
     hess_inv: <25x25 LbfgsInvHessProduct with dtype=float64>
          jac: array([ 2.76768564e+03,  1.00160522e+03,  2.54343743e+02,  1.40084657e+02,
            5.36770586e+04,  1.30209697e+03,  0.00000000e+00, -2.38738171e+03,
            1.03163205e+03,  4.94424731e+03,  2.30155351e-28,  6.29779563e+02,
            3.92013539e+02, -5.64968699e+03,  6.56766654e+04,  3.29971017e+03,
            7.68444927e+03,  1.17837847e+03,  1.08177407e+04, -1.00557608e+03,
            9.33575125e+02,  1.03686271e+03,  0.00000000e+00, -4.50509909e+03,
            1.46569168e+03])
      message: b'CONVERGENCE: REL_REDUCTION_OF_F_<=_FACTR*EPSMCH'
         nfev: 25
          nit: 19
       status: 0
      success: True
            x: array([-9.21034037, -3.75355856, -1.66811626,  1.68654281, -2.30258509,
           -4.22954205,  0.        , -2.96635524, -2.05158011,  2.30258509,
           -2.55160117, -3.58149271, -1.74784929,  1.87961491, -2.30258509,
            0.84642637, -2.30258509,  1.1526716 , -2.30258509, -2.89153277,
           -1.36733597, -3.40148924, -0.22314355, -2.02166239, -1.9295367 ])

    <graphdot.model.gaussian_process.gpr.GaussianProcessRegressor at 0x7f6bf13ae190>

```
gpr.kernel.hyperparameters
```

    starting_probability : Uniform
    	p : 1.0
    stopping_probability : 0.00010000000000000009
    node_kernel : Composite
    	aromatic : Multiply
    		lhs : Constant
    			c : 0.02343420535068002
    		rhs : KroneckerDelta
    			h : 0.18860200789175988
    	atomic_number : Multiply
    		lhs : Constant
    			c : 5.400776905055444
    		rhs : KroneckerDelta
    			h : 0.10000000000000002
    	charge : Multiply
    		lhs : Constant
    			c : 0.0145590563621646
    		rhs : SquareExponential
    			length_scale : 1.0
    	chiral : Multiply
    		lhs : Constant
    			c : 0.05149063984443944
    		rhs : KroneckerDelta
    			h : 0.12853164901299938
    	hcount : Multiply
    		lhs : Constant
    			c : 10.000000000000002
    		rhs : SquareExponential
    			length_scale : 0.07795674396128768
    	hybridization : Multiply
    		lhs : Constant
    			c : 0.02783411902444354
    		rhs : KroneckerDelta
    			h : 0.17414808329497075
    	ring_list : Multiply
    		lhs : Constant
    			c : 6.550981640628747
    		rhs : Convolution
    			base : KroneckerDelta
    				h : 0.10000000000000002
    edge_kernel : Composite
    	aromatic : Multiply
    		lhs : Constant
    			c : 2.331300736214766
    		rhs : KroneckerDelta
    			h : 0.10000000000000002
    	conjugated : Multiply
    		lhs : Constant
    			c : 3.166641607522053
    		rhs : KroneckerDelta
    			h : 0.10000000000000002
    	order : Multiply
    		lhs : Constant
    			c : 0.05549109210250382
    		rhs : KroneckerDelta
    			h : 0.25478481015091287
    	ring_stereo : Multiply
    		lhs : Constant
    			c : 0.033323606027544916
    		rhs : KroneckerDelta
    			h : 0.8
    	stereo : Multiply
    		lhs : Constant
    			c : 0.1324351226762118
    		rhs : KroneckerDelta
    			h : 0.14521546104851712

```
mu = gpr.predict(train.graphs)
```

```
plt.scatter(train[target], mu)
plt.show()
```

```
print('Training set')
print('MAE:', np.mean(np.abs(train[target] - mu)))
print('RMSE:', np.std(train[target] - mu))
```

    Training set
    MAE: 0.2074369392067814
    RMSE: 0.3195501910711287

```
mu_test = gpr.predict(test.graphs)
```

```
plt.scatter(test[target], mu_test)
plt.show()
```

```
print('Test set')
print('MAE:', np.mean(np.abs(test[target] - mu_test)))
print('RMSE:', np.std(test[target] - mu_test))
```

    Test set
    MAE: 1.2461703788060545
    RMSE: 1.6040210737240486

```
gpr2 = GaussianProcessRegressor(
    kernel=Normalization(
        MarginalizedGraphKernel(
            node_kernel=uX.Additive(
                aromatic=uX.Constant(0.5, (0.01, 10.0)) * uX.KroneckerDelta(0.5,(0.1, 0.9)),
                atomic_number=uX.Constant(0.5, (0.01, 10.0)) * uX.KroneckerDelta(0.8,(0.1, 0.9)),
                charge=uX.Constant(0.5, (0.01, 10.0)) * uX.SquareExponential(1.0),
                chiral=uX.Constant(0.5, (0.01, 10.0)) * uX.KroneckerDelta(0.5,(0.1, 0.9)),
                hcount=uX.Constant(0.5, (0.01, 10.0)) * uX.SquareExponential(1.0),
                hybridization=uX.Constant(0.5, (0.01, 10.0)) * uX.KroneckerDelta(0.5,(0.1, 0.9)),
                ring_list=uX.Constant(0.5, (0.01, 100.0)) * uX.Convolution(uX.KroneckerDelta(0.5,(0.1, 0.9)))
            ).normalized,
            edge_kernel=uX.Additive(
                aromatic=uX.Constant(0.5, (0.01, 10.0)) * uX.KroneckerDelta(0.5,(0.1, 0.9)),
                conjugated=uX.Constant(0.5, (0.01, 10.0)) * uX.KroneckerDelta(0.5,(0.1, 0.9)),
                order=uX.Constant(0.5, (0.01, 10.0)) * uX.KroneckerDelta(0.8,(0.1, 0.9)),
                ring_stereo=uX.Constant(0.5, (0.01, 10.0)) * uX.KroneckerDelta(0.8,(0.1, 0.9)),
                stereo=uX.Constant(0.5, (0.01, 10.0)) * uX.KroneckerDelta(0.8,(0.1, 0.9))
            ).normalized,
            p=Uniform(1.0, p_bounds='fixed'),
            q=0.05
        )
    ),
    alpha=1e-2, #different from gpr in alpha where gpr's alpha is 1e-4
    optimizer=True,
    normalize_y=True,
    regularization='+',
)
```

```
#gpr2.fit(train.graphs, train[target], repeat=3, verbose=True)
gpr2.fit(train.graphs, train[target], repeat=1, verbose=True)
```

    |        logP|       dlogP|     y^T.K.y|     log|K| |     Cond(K)|  GPU time|  CPU time|
    |------------|------------|------------|------------|------------|----------|----------|
    |   1.667e+05|   1.928e+05|  1.7773e+05|      -11034|  2.2742e+05|   1.3e+02|       1.7|
    |  1.5585e+05|  2.2171e+05|  1.6671e+05|      -10857|  2.2218e+05|   1.3e+02|       1.7|
    |  1.3711e+05|  2.4873e+05|  1.4756e+05|      -10447|  2.0803e+05|   1.2e+02|       1.8|
    |  1.3286e+05|  2.3627e+05|   1.432e+05|      -10338|  2.0336e+05|   1.1e+02|       1.7|
    |  1.2977e+05|  2.2744e+05|  1.4002e+05|      -10249|  1.9714e+05|   1.1e+02|       1.7|
    |  1.3006e+05|  2.1429e+05|  1.4032e+05|      -10258|  2.0119e+05|     1e+02|       1.7|
    |  1.2923e+05|  2.2103e+05|  1.3946e+05|      -10238|  1.9906e+05|   1.1e+02|       1.7|
    |  1.2888e+05|  2.2097e+05|  1.3911e+05|      -10228|  1.9865e+05|   1.1e+02|       1.7|
    |  1.2843e+05|  2.2338e+05|  1.3864e+05|      -10211|  1.9582e+05|   1.1e+02|       1.7|
    |  1.2837e+05|  2.2244e+05|  1.3858e+05|      -10212|   1.969e+05|   1.1e+02|       1.7|
    |  1.2836e+05|  2.2225e+05|  1.3857e+05|      -10212|  1.9696e+05|   1.1e+02|       1.7|
    |  1.2831e+05|  2.2155e+05|  1.3852e+05|      -10211|  1.9715e+05|   1.1e+02|       1.7|
    |  1.2826e+05|  2.2184e+05|  1.3847e+05|      -10209|  1.9704e+05|   1.1e+02|       1.7|
    |  1.2821e+05|  2.2181e+05|  1.3841e+05|      -10207|  1.9671e+05|   1.1e+02|       1.7|
    |  1.2819e+05|  2.2165e+05|   1.384e+05|      -10207|  1.9664e+05|   1.1e+02|       1.8|
    |  1.2818e+05|  2.2125e+05|  1.3838e+05|      -10207|  1.9671e+05|   1.1e+02|       1.8|
    |  1.2817e+05|  2.2114e+05|  1.3838e+05|      -10207|  1.9673e+05|   1.1e+02|       1.7|
    |  1.2815e+05|  2.2077e+05|  1.3836e+05|      -10207|  1.9677e+05|   1.1e+02|       1.7|
    |  1.2812e+05|   2.185e+05|  1.3833e+05|      -10209|  1.9695e+05|   1.1e+02|       1.7|
    |  1.2809e+05|   2.178e+05|   1.383e+05|      -10209|    1.97e+05|   1.1e+02|       1.7|
    |  1.2807e+05|  2.1764e+05|  1.3828e+05|      -10208|  1.9695e+05|   1.1e+02|       1.7|
    |  1.2805e+05|  2.1597e+05|  1.3826e+05|      -10209|  1.9693e+05|   1.1e+02|       1.7|
    |  1.2805e+05|   2.172e+05|  1.3825e+05|      -10208|  1.9694e+05|   1.1e+02|       1.7|
    |  1.2804e+05|  2.1693e+05|  1.3825e+05|      -10208|  1.9694e+05|   1.1e+02|       1.8|
    |  1.2804e+05|  2.1674e+05|  1.3825e+05|      -10208|  1.9694e+05|   1.1e+02|       1.7|
    |  1.2804e+05|  2.1691e+05|  1.3825e+05|      -10208|  1.9694e+05|   1.1e+02|       1.7|
    |  1.2805e+05|  2.1694e+05|  1.3825e+05|      -10208|  1.9694e+05|   1.1e+02|       1.7|
    |  1.2804e+05|  2.1694e+05|  1.3825e+05|      -10208|  1.9694e+05|   1.1e+02|       1.7|
    |  1.2804e+05|  2.1694e+05|  1.3825e+05|      -10208|  1.9694e+05|   1.1e+02|       1.7|
    |  1.2804e+05|  2.1694e+05|  1.3825e+05|      -10208|  1.9694e+05|   1.1e+02|       1.8|
    |  1.2805e+05|  2.1694e+05|  1.3825e+05|      -10208|  1.9694e+05|   1.1e+02|       1.7|
    Optimization result:
          fun: 128045.20193517115
     hess_inv: <25x25 LbfgsInvHessProduct with dtype=float64>
          jac: array([ 21.67035135,   5.42797631,   0.91584794,   6.62684845,
           636.88297304,  10.22203408,   0.        ,   6.13155297,
             0.80318432, -45.79780911,   0.        ,   9.32446636,
             2.06137402,   7.94609186, 767.483899  ,  -8.19180354,
            92.93654292,   7.92503084, 118.62629465,   3.36719643,
            38.71995733,   7.96834618,   0.        , -11.08475724,
             7.2860016 ])
      message: b'CONVERGENCE: REL_REDUCTION_OF_F_<=_FACTR*EPSMCH'
         nfev: 31
          nit: 21
       status: 0
      success: True
            x: array([-9.21034037, -4.51710969, -1.98267626,  1.72521606, -2.30258509,
           -4.60517019,  0.        , -4.60517019, -1.8070945 ,  2.30258509,
           -2.88031384, -3.6019383 , -2.30258509,  1.91177723, -2.30258509,
            0.28746812, -2.30258509,  0.54884256, -2.30258509, -0.5686784 ,
           -2.30258509, -4.35637808, -0.22314355, -2.55717239, -2.21179129])

    <graphdot.model.gaussian_process.gpr.GaussianProcessRegressor at 0x7f6bf00630d0>

```
mu = gpr2.predict(train.graphs)
plt.scatter(train[target], mu)
plt.show()
```

```
print('Training set')
print('MAE:', np.mean(np.abs(train[target] - mu)))
print('RMSE:', np.std(train[target] - mu))
```

    Training set
    MAE: 0.7238389142657052
    RMSE: 0.9634943206325994

```
mu_test = gpr2.predict(test.graphs)
plt.scatter(test[target], mu_test)
plt.show()
```

```
print('Test set')
print('MAE:', np.mean(np.abs(test[target] - mu_test)))
print('RMSE:', np.std(test[target] - mu_test))
```

    Test set
    MAE: 0.9561539409612109
    RMSE: 1.2284268143181998


# Introduction to Biomechanics

> Marcos Duarte, Renato Naville Watanabe
> [Laboratory of Biomechanics and Motor Control](http://pesquisa.ufabc.edu.br/bmclab)
> Federal University of ABC, Brazil

## Biomechanics @ UFABC

```python
from IPython.display import IFrame
IFrame('http://pesquisa.ufabc.edu.br/bmclab', width='100%', height=500)
```

##  Biomechanics

The etymology of the word *Biomechanics* is evident:

\begin{equation}
\text{Biomechanics} := \text{bios} \, (\text{life}) + \text{mechanics}
\label{eq_biomechanics}
\end{equation}

Professor Herbert Hatze, on a letter to the editors of the Journal of Biomechanics in 1974, proposed a (very good) definition for *the science called Biomechanics*:

> "*Biomechanics is the study of the structure and function of biological systems by means of the methods of mechanics.*"
> Hatze (1974) [The meaning of the term biomechanics](https://github.com/demotu/BMC/blob/master/courses/HatzeJB74biomechanics.pdf).

### Biomechanics & Mechanics - Hatze

And Hatze, advocating for *Biomechanics to be a science of its own*, argues that Biomechanics **is not** simply Mechanics of (applied to) living systems:

> "*It would not be correct to state that 'Biomechanics is the study of the mechanical aspects of the structure and function of biological systems' because biological systems do not have mechanical aspects. They only have biomechanical aspects (otherwise mechanics, as it exists, would be sufficient to describe all phenomena which we now call biomechanical features of biological systems).*" Hatze (1974)

### Biomechanics vs. Mechanics - Hatze

To support this argument, Hatze illustrates the difference between Biomechanics and the application of Mechanics, with an example of a javelin throw: studying the mechanics aspects of the javelin flight trajectory (use existing knowledge about aerodynamics and ballistics) vs. studying the biomechanical aspects of the phase before the javelin leaves the thrower’s hand (there are no established mechanical models for this system).

### Biomechanics & Mechanics - Fung

Professor Yuan-Cheng Fung, another great biomechanicist, is (much) less enthusiastic than Hatze about Biomechanics to be a science of its own, according to Fung:
> "*Biomechanics is mechanics applied to biology*" Fung (1993).

But this definition does not mean Fung demotes the importance of Biomechanics, a few pages later he stated:
> "*Biomechanics has participated in virtually every modern advance of medical science and technology*" Fung (1993).

### Branches of Mechanics

**A good knowledge of Mechanics is a necessary condition, but not sufficient!, to have a good knowledge of Biomechanics**.

In fact, only a subset of Mechanics matters to Biomechanics, the Classical Mechanics subset, the domain of mechanics for bodies with moderate speeds $(\ll 3.10^8 m/s!)$ and not very small $(\gg 3.10^{-9} m!)$ as shown in the following diagram:

Figure. Domains of mechanics (image from http://en.wikipedia.org/wiki/Classical_mechanics).

### Biomechanics & other Sciences I

One last point about the excellent letter from Hatze, already in 1974 he points for the following problem:

> "*The use of the term biomechanics imposes rather severe restrictions on its meaning because of the established definition of the term, mechanics. This is unfortunate,  since the synonym Biomechanics, as it is being understood by the majority of biomechanists today, has a much wider meaning.*" Hatze (1974)

Although the term Biomechanics may sound new to you, it's not rare that people think the use of methods outside the realm of Mechanics as Biomechanics.
For instance, electromyography and thermography are two methods that although may be useful in Biomechanics, particularly the former, they clearly don't have any relation with Mechanics; Electromagnetism and Thermodynamics are other [branches of Physics](https://en.wikipedia.org/wiki/Branches_of_physics), although there is considerable overlapping between Mechanics and Thermodynamics.

### Biomechanics & Engineering

Even seeing Biomechanics as a field of Science, as argued by Hatze, it's also possible to refer to Engineering Biomechanics considering that Engineering is "*the application of scientific and mathematical principles to practical ends*" [[The Free Dictionary](http://www.thefreedictionary.com/engineering)] and particularly that "*Engineering Mechanics is the application of Mechanics to solve problems involving common engineering elements*" [[Wikibooks]](https://en.wikibooks.org/wiki/Engineering_Mechanics), and, last but not least, that Biomedical engineering is the application of engineering principles and design concepts to medicine and biology for healthcare purposes [[Wikipedia](https://en.wikipedia.org/wiki/Biomedical_engineering)].

### Applications of Biomechanics

Biomechanics matters to fields of science and technology related to biology and health and it's also relevant for the development of synthetic systems inspired on biological systems, as in robotics. To illustrate the variety of applications of Biomechanics, this is the current list of topics covered in the Journal of Biomechanics:

```python
from IPython.display import IFrame
IFrame('https://www.sciencedirect.com/journal/journal-of-biomechanics/about/aims-and-scope', width='100%', height=500)
```

### On the branches of Mechanics I

Mechanics is a branch of the physical sciences that is concerned with the state of rest or motion of bodies that are subjected to the action of forces. In general, this subject can be subdivided into three branches: rigid-body mechanics, deformable-body mechanics, and fluid mechanics (Hibbeler, 2012; Ruina and Rudra, 2019).

(Classical) Mechanics is typically partitioned in Statics and Dynamics (Hibbeler, 2012; Ruina and Rudra, 2019).
In turn, Dynamics is divided in **Kinematics** and **Kinetics**.
This classification is clear; dynamics is the study of the motions of bodies and Statics is the study of forces in the absence of changes in motion. Kinematics is the study of motion without considering its possible causes (forces) and Kinetics is the study of the possible causes of motion.

Figure. Branches of mechanics (image from https://en.wikibooks.org/wiki/Engineering_Statics/Introduction).

### On the branches of Mechanics and Biomechanics

Nevertheless, it's common in Biomechanics to adopt a slightly different classification: to partition it between Kinematics and Kinetics, and then Kinetics into Statics and Dynamics (David Winter, Nigg & Herzog, and Vladimir Zatsiorsky, among others, use this classification in their books). The rationale is that we first separate the study of motion considering or not its causes (forces). The partition of (Bio)Mechanics in this way is useful because is simpler to study and describe (measure) the kinematics of human motion and then go to the more complicated issue of understanding (measuring) the forces related to the human motion.

Anyway, these different classifications reveal a certain contradiction between Mechanics (particularly from an engineering point of view) and Biomechanics; some scholars will say that this taxonomy in Biomechanics is simply wrong and it should be corrected to align with the Mechanics. Be aware.

### The future of Biomechanics

(Human) Movement Science combines many disciplines of science (such as, Physiology, Biomechanics, and Psychology) for the study of human movement. Professor Benno Nigg claims that with the growing concern for the well-being of humankind, Movement Science will have an important role:
> Movement science will be one of the most important and most recognized science fields in the twenty-first century... The future discipline of movement science has a unique opportunity to become an important contributor to the well-being of mankind.
Nigg BM (1993) [Sport  science  in  the twenty-first  century](http://www.ncbi.nlm.nih.gov/pubmed/8230394). Journal of Sports Sciences, 77, 343-347.

And so Biomechanics will also become an important contributor to the well-being of humankind.

### Biomechanics  and the Biomedical Engineering at UFABC I

At the university level, the study of Mechanics is typically done in the disciplines Statics and Dynamics (rigid-body mechanics), Strength of Materials (deformable-body mechanics), and Mechanics of Fluids (fluid mechanics). Consequently, the study on Biomechanics must also cover these topics for a greater understanding of the structure and function of biological systems.

### Biomechanics  and the Biomedical Engineering at UFABC II

The Biomedical Engineering degree at UFABC covers these topics for the study of biological systems in different courses: Ciência dos Materiais Biocompatíveis, Modelagem e Simulação de Sistemas Biomédicos, Métodos de Elementos Finitos aplicados a Sistemas Biomédicos, Mecânica dos Fluidos, Caracterização de Biomateriais, Sistemas Biológicos, and last but not least, [Biomecânica I](http://demotu.org/ensino/biomecanica-i/) & Biomecânica II.

How much of biological systems is in fact studied in these disciplines varies a lot. Anyway, none of these courses cover the study of human motion with implications to health, rehabilitation, and sports, except the last course. This is the reason why the courses [Biomecânica I](http://demotu.org/ensino/biomecanica-i/) & II focus on the analysis of the human movement.

### More on Biomechanics

The Wikipedia page on biomechanics is a good place to read more about Biomechanics:

```python
from IPython.display import IFrame
IFrame('http://en.m.wikipedia.org/wiki/Biomechanics', width='100%', height=400)
```

## History of Biomechanics

Biomechanics progressed basically with the advancements in Mechanics and with the invention of instrumentations for measuring mechanical quantities and computing.

The development of Biomechanics was only possible because people became more interested in the understanding of the structure and function of biological systems and to apply these concepts to the progress of the humankind.

## Aristotle (384-322 BC)
Aristotle was the first to have written about the movement of animals in his works *On the Motion of Animals (De Motu Animalium)* and *On the Gait of Animals (De Incessu Animalium)* [[Works by Aristotle]](http://classics.mit.edu/Browse/index-Aristotle.html).

Aristotle clearly already knew what we nowadays refer as Newton's third law of motion:
"*For as the pusher pushes so is the pushed pushed, and with equal force.*" [Part 3, [On the Motion of Animals](http://classics.mit.edu/Aristotle/motion_animals.html)]

### Aristotle & the Scientific Revolution I

Although Aristotle's contributions were invaluable to humankind, to make his discoveries he doesn't seem to have employed anything similar to what we today refer as [scientific method](https://en.wikipedia.org/wiki/Scientific_method) (the systematic observation, measurement, and experiment, and the formulation, testing, and modification of hypotheses).

Most of the Physics of Aristotle was ambiguous or incorrect; for example, for him there was no motion without a force. He even deduced that speed was proportional to force and inversely proportional to resistance [[Book VII, Physics](http://classics.mit.edu/Aristotle/physics.7.vii.html)]. Perhaps Aristotle was too influenced by the observation of motion of a body under the action of a friction force, where this notion is not at all unreasonable.

### Aristotle & the Scientific Revolution II

If Aristotle performed any observation/experiment at all in his works, he probably was not good on that as, ironically, evinced in this part of his writing:
> "Males have more teeth than females in the case of men, sheep, goats, and swine; in the case of other animals observations have not yet been made". Aristotle [The History of Animals](http://classics.mit.edu/Aristotle/history_anim.html).

## Leonardo da Vinci (1452-1519)

Contributions of Leonardo to Biomechanics:
 - Studies on the proportions of humans and animals
 - Anatomy studies of the human body, especially the foot
 - Studies on the mechanical function of muscles

Figure. *"Le proporzioni del corpo umano secondo Vitruvio", also known as the [Vitruvian Man](https://en.wikipedia.org/wiki/Vitruvian_Man), drawing by [Leonardo da Vinci](https://en.wikipedia.org/wiki/Leonardo_da_Vinci) circa 1490 based on the work of [Marcus Vitruvius Pollio](https://en.wikipedia.org/wiki/Vitruvius) (1st century BC), depicting a man in supposedly ideal human proportions (image from [Wikipedia](https://en.wikipedia.org/wiki/Vitruvian_Man)).*

## Giovanni Alfonso Borelli (1608-1679)

 - [The father of biomechanics](https://en.wikipedia.org/wiki/Giovanni_Alfonso_Borelli); the first to use modern scientific method into 'Biomechanics' in his book [De Motu Animalium](http://www.e-rara.ch/doi/10.3931/e-rara-28707).
 - Proposed that the levers of the musculoskeletal system magnify motion rather than force.
 - Calculated the forces required for equilibrium in various joints of the human body  before Newton published the laws of motion.

Figure. *Excerpt from the book De Motu Animalium*.

## More on the history of Biomechanics

See:
- http://courses.washington.edu/bioen520/notes/History_of_Biomechanics_(Martin_1999).pdf
- [http://biomechanics.vtheatre.net/doc/history.html](http://biomechanics.vtheatre.net/doc/history.html)
- Chapter 1 of Nigg and Herzog (2006) [Biomechanics of the Musculo-skeletal System](https://books.google.com.br/books?id=hOIeAQAAIAAJ&dq=editions:ISBN0470017678)

### The International Society of Biomechanics

The biomechanics community has an official scientific society, the [International Society of Biomechanics](http://isbweb.org/), with a journal, the [Journal of Biomechanics](http://www.jbiomech.com), and an e-mail list, the [Biomch-L](http://biomch-l.isbweb.org):

```python
from IPython.display import IFrame
IFrame('https://biomch-l.isbweb.org/forums/2-General-Discussion', width='100%', height=400)
```

## Biomechanics by (BMClab) examples

Biomechanics has been applied to many different problems; let's see a few examples of applications of Biomechanics:
- Clinical gait analysis: [http://pesquisa.ufabc.edu.br/bmclab/servicos/cga/](http://pesquisa.ufabc.edu.br/bmclab/servicos/cga/)
- Biomechanics of sports: [http://pesquisa.ufabc.edu.br/bmclab/biomecanica-do-chute-de-bicicleta/](http://pesquisa.ufabc.edu.br/bmclab/biomecanica-do-chute-de-bicicleta/)

### Examples of Biomechanics Classes around the World

```python
from IPython.display import IFrame
IFrame('http://pages.uoregon.edu/karduna/biomechanics/bme.htm', width='100%', height=400)
```

## Biomechanics classes @ UFABC

 - **[Biomecânica I](http://pesquisa.ufabc.edu.br/bmclab/ensino/biomecanica-i/)**
 - **[Biomecânica II](http://pesquisa.ufabc.edu.br/bmclab/ensino/biomecanica-ii/)**

The book [*Introduction to Statics and Dynamics*](http://ruina.tam.cornell.edu/Book/index.html), written by Andy Ruina and Rudra Pratap, is an excellent reference (a rigorous and yet didactic presentation of Mechanics for undergraduate students) on Classical Mechanics and the authors kindly offer the book freely available online.
We will use this book as the main reference on Mechanics and Mathematics in our course.

## Problems

1. Go to [Biomechanics Classes on the Web](http://pages.uoregon.edu/karduna/biomechanics/) to visit websites of biomechanics classes around the world and find out how biomechanics is studied in different fields.
2. Find examples of applications of biomechanics in different areas.
3. Watch the video [The Weird World of Eadweard Muybridge](http://youtu.be/5Awo-P3t4Ho) to learn about [Eadweard Muybridge](http://en.wikipedia.org/wiki/Eadweard_Muybridge), an important person to the development of instrumentation for biomechanics.
4. Think about practical problems in nature that can be studied in biomechanics with simple approaches (simple modeling and low-tech methods) or very complicated approaches (complex modeling and high-tech methods).
5. What the study in the biomechanics of athletes, children, elderlies, persons with disabilities, other animals, and computer animation for the cinema industry may have in common and different?
6. Visit the website of the Laboratory of Biomechanics and Motor Control at UFABC and find out what we do and if there is anything you are interested in.
7. Is there anything in biomechanics that interests you? How could you pursue this interest?

## References

- [Biomechanics - Wikipedia, the free encyclopedia](http://en.wikipedia.org/wiki/Biomechanics)
- [Mechanics - Wikipedia, the free encyclopedia](http://en.wikipedia.org/wiki/Mechanics)
- [International Society of Biomechanics](http://isbweb.org/)
- [Biomech-l, the biomechanics' e-mail list](http://biomch-l.isbweb.org/)
- [Journal of Biomechanics' aims](http://www.jbiomech.com/aims)
- A Genealogy of Biomechanics
- Fung Y-C (1993) [Biomechanics: mechanical properties ofliving tissues](https://books.google.com.br/books?id=yx3aBwAAQBAJ). 2nd ed. Springer.
- Hatze H (1974) [The meaning of the term biomechanics](https://github.com/demotu/BMC/blob/master/courses/HatzeJB74biomechanics.pdf). Journal of Biomechanics, 7, 189–190.
- Hibbeler RC (2012) [Engineering Mechanics: Statics](http://books.google.com.br/books?id=PSEvAAAAQBAJ). 13 edition. Prentice Hall.
- Nigg BM and Herzog W (2006) [Biomechanics of the Musculo-skeletal System](https://books.google.com.br/books?id=hOIeAQAAIAAJ&dq=editions:ISBN0470017678). 3rd Edition. Wiley.
- Ruina A, Rudra P (2019) [Introduction to Statics and Dynamics](http://ruina.tam.cornell.edu/Book/index.html). Oxford University Press.
- Winter DA (2009) [Biomechanics and motor control of human movement](http://books.google.com.br/books?id=_bFHL08IWfwC). 4 ed. Hoboken: Wiley.
- Zatsiorsky VM (1997) [Kinematics of Human Motion](http://books.google.com.br/books/about/Kinematics_of_Human_Motion.html?id=Pql_xXdbrMcC&redir_esc=y). Champaign, Human Kinetics.
- Zatsiorsky VM (2002) [Kinetics of human motion](http://books.google.com.br/books?id=wp3zt7oF8a0C). Human Kinetics.