import pygame
import pymunk
import numpy as np
import os
import sys

def resource_path(relative_path):
    try:
        # PyInstaller creates a temp folder and stores path in _MEIPASS
        base_path = sys._MEIPASS
    except Exception:
        base_path = os.path.abspath(".")

    return os.path.join(base_path, relative_path)

class Player(pygame.sprite.Sprite):
    def __init__(self, space, init_pos=(0, 0), image_shape=None, is_player=True, is_geosynch=False, moon_center=(0, 0),
                 boost_sound=None):
        super().__init__()
        self.game_over = False
        self.rocket_boost_sound = boost_sound
        self.is_geosynch = is_geosynch
        self.is_player = is_player
        self.image = pygame.image.load(resource_path("images/catstronaut.png"))
        self.moon_center = moon_center
        if image_shape:
            self.image = pygame.transform.scale(self.image, image_shape)
            self.image = pygame.transform.flip(self.image, True, True)
        self.rect = self.image.get_bounding_rect()
        width = self.rect.width
        height = self.rect.height
        self.space = space
        self.pos = pygame.Vector2(init_pos)

        vs = [(-width / 2, -height / 2), (width / 2, -height / 2),
              (width / 2, height / 2), (-width / 2, height / 2)]
        mass = 5
        moment = pymunk.moment_for_poly(mass, vs)
        self.body = pymunk.Body(mass, moment)
        self.body.force = (0, -250)
        self.body.position = self.pos.x, -self.pos.y + 500
        self.shape = pymunk.Poly(self.body, vs)
        self.shape.friction = 0.5
        self.space.add(self.body, self.shape)
        self.can_jump = True

    def update(self, events, dt, other_sprites=None):
        pressed = pygame.key.get_pressed()
        non_player_sprites = other_sprites.copy()
        non_player_sprites.remove(self)
        is_collided = pygame.sprite.spritecollideany(self, non_player_sprites)
        lateral_strength = 20 if is_collided else 5
        if self.game_over is False:
            if is_collided:
                self.can_jump = True
            if self.body.position.x < self.moon_center[0] + 50:
                move = pygame.Vector2((0, 0))
                if pressed[pygame.K_w] and self.body.velocity[1] < 10 and self.can_jump:
                    self.can_jump = False
                    move += pygame.Vector2((0, 1)) * 300
                    pygame.mixer.Channel(1).play(self.rocket_boost_sound)
                if pressed[pygame.K_a] and np.abs(self.body.velocity[1]) < 20:
                    move += pygame.Vector2((-1, 0)) * lateral_strength
                if pressed[pygame.K_d] and np.abs(self.body.velocity[1]) < 20:
                    move += pygame.Vector2((1, 0)) * lateral_strength
                if self.body.position.x > 550:
                    self.body.apply_impulse_at_local_point(move)
                    self.body.force = (0, -250)

                if self.body.position.x < 550:
                    self.body.force = (1000, 200)
                self.pos = pygame.Vector2(self.body.position.x, -self.body.position.y+500)
                self.rect.center = self.pos

                self.body.angle = 0
            else:
                self.game_over = True

                self.body.angle += 0.05

        elif (np.abs(self.body.position.x-self.moon_center[0]) > 20 or
              np.abs((-self.body.position.y+500)-self.moon_center[1]) > 20):
            applied_force = (-15*(self.body.position.x-self.moon_center[0]),
                             15*((-self.body.position.y+500)-self.moon_center[1]))
            self.body.force = applied_force

class Satellite(pygame.sprite.Sprite):
    def __init__(self, space, image_filename=None, init_pos=(0, 0), init_velocity=(0, 0), mass=1, image_shape=None,
                 is_geosynch=False, is_player=False, screen_height=None, init_angular_velocity=0):
        if screen_height is None:
            screen_height = 1500
        self.screen_height = screen_height
        super().__init__()
        self.satellite_images_health = None
        if image_filename is None:
            self.satellite_images_health = {'low': resource_path("images/satellite_large_low_health.png"),
                                            'med': resource_path("images/satellite_large_med_health.png"),
                                            'high': resource_path("images/satellite_large_high_health.png"),
                                            }
            image_filename = self.satellite_images_health['high']

        self.image = pygame.image.load(image_filename)
        self.is_geosynch = is_geosynch
        self.is_player = is_player

        self.health = 6
        if image_shape:
            self.image = pygame.transform.scale(self.image, image_shape)
        self.rect = self.image.get_bounding_rect()
        width = self.rect.width
        height = self.rect.height
        self.space = space
        self.pos = pygame.Vector2(init_pos)

        vs = [(-width / 2, -height / 2), (width / 2, -height / 2),
              (width / 2, height / 2), (-width / 2, height / 2)]
        moment = pymunk.moment_for_poly(mass, vs)
        self.body = pymunk.Body(mass, moment)
        self.body.position = self.pos.x, -self.pos.y + 500
        self.shape = pymunk.Poly(self.body, vs)
        self.shape.friction = 0.5
        self.body.velocity = init_velocity
        self.body.angular_velocity = init_angular_velocity
        self.space.add(self.body, self.shape)

    def update(self, events, dt, other_sprites=None):
        self.pos = pygame.Vector2(self.body.position[0], -self.body.position[1]+500)
        self.rect.center = self.pos

        non_player_sprites = other_sprites.copy()
        non_player_sprites.remove(self)
        is_collided = pygame.sprite.spritecollideany(self, non_player_sprites)
        satellite_to_return = None

        if self.satellite_images_health:
            if 3 < self.health <= 5:
                self.image = pygame.image.load(self.satellite_images_health['med'])
            if self.health <= 3:
                self.image = pygame.image.load(self.satellite_images_health['low'])

        if is_collided and self.is_geosynch and is_collided.is_player is False and is_collided.is_geosynch is False:
            self.health -= 1
            is_collided.space.remove(is_collided.body, is_collided.shape)
            is_collided.kill()

        if self.health < 0 and self.is_geosynch is True:
            self.space.remove(self.body, self.shape)
            self.kill()
            new_vect = pygame.Vector2(self.body.position[0], -self.body.position[1] + 500)
            satellite_to_return = Satellite(self.space, resource_path("images/sputnik_custom.png"), init_pos=new_vect,
                                            init_velocity=(0, 5), screen_height=self.screen_height)

        self.die(other_sprites)
        return satellite_to_return

    def die(self, other_sprites):
        if self.rect.y > self.screen_height or self.rect.y < -100 or self.rect.x + 500 < other_sprites.sprites()[0].pos.x:
            self.kill()
            self.space.remove(self.body, self.shape)


import logging
from timeit import default_timer as timer

import numpy as np

from opensfm import dataset
from opensfm import io
from opensfm import log
from opensfm import matching
from opensfm import pairs_selection
from opensfm.context import parallel_map

logger = logging.getLogger(__name__)

class Command:
    name = 'match_features'
    help = 'Match features between image pairs'

    def add_arguments(self, parser):
        parser.add_argument('dataset', help='dataset to process')

    def run(self, args):
        data = dataset.DataSet(args.dataset)
        images = data.images()
        exifs = {im: data.load_exif(im) for im in images}
        pairs, preport = pairs_selection.match_candidates_from_metadata(images, images, exifs, data)

        num_pairs = sum(len(c) for c in pairs.values())
        logger.info('Matching {} image pairs'.format(num_pairs))

        ctx = Context()
        ctx.data = data
        ctx.cameras = ctx.data.load_camera_models()
        ctx.exifs = exifs
        args = list(match_arguments(pairs, ctx))

        start = timer()
        processes = ctx.data.config['processes']
        parallel_map(match, args, processes)
        end = timer()

        with open(ctx.data.profile_log(), 'a') as fout:
            fout.write('match_features: {0}\n'.format(end - start))
        self.write_report(data, preport, pairs, end - start)

    def write_report(self, data, preport, pairs, wall_time):
        pair_list = []
        for im1, others in pairs.items():
            for im2 in others:
                pair_list.append((im1, im2))

        report = {
            "wall_time": wall_time,
            "num_pairs": len(pair_list),
            "pairs": pair_list,
        }
        report.update(preport)
        data.save_report(io.json_dumps(report), 'matches.json')

class Context:
    pass

def match_arguments(pairs, ctx):
    for i, (im, candidates) in enumerate(pairs.items()):
        yield im, candidates, i, len(pairs), ctx

def match(args):
    """Compute all matches for a single image"""
    log.setup()

    im1, candidates, i, n, ctx = args
    logger.info('Matching {}  -  {} / {}'.format(im1, i + 1, n))

    config = ctx.data.config
    matcher_type = config['matcher_type']
    robust_matching_min_match = config['robust_matching_min_match']

    im1_matches = {}

    for im2 in candidates:
        # symmetric matching
        t = timer()
        p1, f1, c1 = ctx.data.load_features(im1)
        p2, f2, c2 = ctx.data.load_features(im2)

        if matcher_type == 'WORDS':
            w1 = ctx.data.load_words(im1)
            w2 = ctx.data.load_words(im2)
            matches = matching.match_words_symmetric(f1, w1, f2, w2, config)
        elif matcher_type == 'FLANN':
            i1 = ctx.data.load_feature_index(im1, f1)
            i2 = ctx.data.load_feature_index(im2, f2)
            matches = matching.match_flann_symmetric(f1, i1, f2, i2, config)
        elif matcher_type == 'BRUTEFORCE':
            matches = matching.match_brute_force_symmetric(f1, f2, config)
        else:
            raise ValueError("Invalid matcher_type: {}".format(matcher_type))

        logger.debug('{} - {} has {} candidate matches'.format(
            im1, im2, len(matches)))
        if len(matches) < robust_matching_min_match:
            im1_matches[im2] = []
            continue

        # robust matching
        t_robust_matching = timer()
        camera1 = ctx.cameras[ctx.exifs[im1]['camera']]
        camera2 = ctx.cameras[ctx.exifs[im2]['camera']]

        rmatches = matching.robust_match(p1, p2, camera1, camera2, matches,
                                         config)

        if len(rmatches) < robust_matching_min_match:
            im1_matches[im2] = []
            continue
        im1_matches[im2] = rmatches
        logger.debug('Robust matching time : {0}s'.format(
            timer() - t_robust_matching))

        logger.debug("Full matching {0} / {1}, time: {2}s".format(
            len(rmatches), len(matches), timer() - t))
    ctx.data.save_matches(im1, im1_matches)


from __future__ import print_function
import argparse
import json

import numpy as np

import chainer
from chainer import training
from chainer.training import extensions

import datasets
import nets
import utils

def main():
    parser = argparse.ArgumentParser(description='Image Comprehension')
    parser.add_argument('--batchsize', '-b', type=int, default=64,
                        help='Number of images in each mini-batch')
    parser.add_argument('--learnrate', '-l', type=float, default=1e-4,
                        help='Learning rate')
    parser.add_argument('--epoch', '-e', type=int, default=40,
                        help='Number of sweeps over the dataset to train')
    parser.add_argument('--unit', '-u', type=int, default=512,
                        help='Number of units')
    parser.add_argument('--layer', type=int, default=1)
    parser.add_argument('--dropout', '-d', type=float, default=0.3,
                        help='Dropout rate for MLP')
    parser.add_argument('--gpu', '-g', type=int, default=0,
                        help='GPU ID (negative value indicates CPU)')
    parser.add_argument('--vocab', '-v', required=True,
                        help='Text file of vocab')
    parser.add_argument('--out', '-o', default='result',
                        help='Directory to output the result')
    parser.add_argument('--dataset', default='mscoco',
                        choices=['mscoco', 'flickr8k', 'flickr30k'])
    parser.add_argument('--beam', type=int, default=5)
    parser.add_argument('--print-sentence-mod', type=int, default=200)
    parser.add_argument('--resume')
    parser.add_argument('--resume-rnn')
    parser.add_argument('--resume-wordemb')
    parser.add_argument('--init-output-by-embed', action='store_true')
    args = parser.parse_args()
    print(json.dumps(args.__dict__, indent=2))

    print('read vocab')
    vocab = json.load(open(args.vocab))

    print('read dataset')
    directory = datasets.get_default_dataset_path(args.dataset)
    train, valid = datasets.get_caption_dataset(
        vocab, directory, ['train', 'val'])

    print('# train', len(train))
    print('# valid', len(valid))
    # this number is the number of references
    # it shuld be the number of images

    print('setup model')
    np.random.seed(777)
    model = nets.RNNDecoder(
        args.layer, len(vocab), args.unit,
        dropout=args.dropout,
        eos_id=vocab['<eos>'])
    if args.gpu >= 0:
        chainer.cuda.get_device_from_id(args.gpu).use()
        model.to_gpu()

    print('setup trainer')
    print('       optimizer')
    optimizer = chainer.optimizers.Adam(args.learnrate)
    optimizer.setup(model)

    if args.resume:
        print('load model', args.resume)
        chainer.serializers.load_npz(args.resume, model)
    if args.resume_rnn:
        print('load RNN model', args.resume_rnn)
        utils.load_npz_partially(args.resume_rnn, model,
                                 target_words=['rnn/'])
    if args.resume_wordemb:
        print('load Word Embedding model', args.resume_wordemb)
        utils.load_npz_partially(args.resume_wordemb, model,
                                 target_words=['embed/'])
    if args.init_output_by_embed:
        print('copy Word Embedding to Output Matrix')
        model.output.W.data[:] = model.embed.W.data

    print('       iterator')
    model.xp.random.seed(777)
    train_iter = chainer.iterators.SerialIterator(train, args.batchsize)
    valid_iter = chainer.iterators.SerialIterator(valid, args.batchsize,
                                                  repeat=False, shuffle=False)

    print('       updater')
    updater = training.StandardUpdater(
        train_iter, optimizer,
        converter=utils.convert, device=args.gpu,
        loss_func=model.calculate_loss)
    trainer = training.Trainer(updater, (args.epoch, 'epoch'), out=args.out)

    print('       extensions')
    iter_per_epoch = len(train) // args.batchsize
    log_trigger = (iter_per_epoch // 1, 'iteration')
    eval_trigger = (log_trigger[0] * 1, 'iteration')  # every 1 epoch

    trainer.extend(extensions.Evaluator(
        valid_iter, model,
        converter=utils.convert, device=args.gpu,
        eval_func=model.evaluate),
        trigger=eval_trigger)

    trainer.extend(utils.SentenceEvaluater(
        model, valid, vocab, 'val/',
        batchsize=args.batchsize,
        device=args.gpu,
        k=args.beam,
        print_sentence_mod=args.print_sentence_mod),
        trigger=eval_trigger)

    record_trigger = training.triggers.MaxValueTrigger(
        'val/bleu',
        trigger=eval_trigger)
    trainer.extend(extensions.snapshot_object(
        model, 'best_model.npz'),
        trigger=record_trigger)

    trainer.extend(extensions.LogReport(trigger=log_trigger),
                   trigger=log_trigger)
    trainer.extend(extensions.PrintReport(
        ['epoch', 'iteration',
         'main/perp', 'validation/main/perp',
         # 'main/acc', 'validation/main/acc',
         'val/bleu',
         'val/rouge',
         'val/cider',
         'val/meteor',
         'elapsed_time']),
        trigger=log_trigger)

    if eval_trigger[0] % log_trigger[0] != 0:
        print('eval_trigger % log_trigger != 0.\n'
              'So, some evaluation results can not be logged and shown.')

    trainer.extend(extensions.ProgressBar())

    if args.resume:
        print(extensions.Evaluator(
            valid_iter, model,
            converter=utils.convert, device=args.gpu,
            eval_func=model.evaluate)())

    print('log_trigger ', log_trigger)
    print('eval_trigger', eval_trigger)
    print('START training. # iter/epoch=', iter_per_epoch)

    trainer.run()

if __name__ == '__main__':
    main()


import pytest
import numpy as np
import scipy.io.wavfile

from pathlib import Path

from pesq import pesq, NoUtterancesError, PesqError

def test():
    data_dir = Path(__file__).parent.parent / 'audio'
    ref_path = data_dir / 'speech.wav'
    deg_path = data_dir / 'speech_bab_0dB.wav'

    sample_rate, ref = scipy.io.wavfile.read(ref_path)
    sample_rate, deg = scipy.io.wavfile.read(deg_path)

    score = pesq(ref=ref, deg=deg, fs=sample_rate, mode='wb')

    assert score == 1.0832337141036987, score

    score = pesq(ref=ref, deg=deg, fs=sample_rate, mode='nb')

    assert score == 1.6072081327438354, score

def test_no_utterances_nb_mode():
    SAMPLE_RATE = 8000
    silent_ref = np.zeros(SAMPLE_RATE)
    deg        = np.random.randn(SAMPLE_RATE)

    with pytest.raises(NoUtterancesError) as e:
        pesq(ref=silent_ref, deg=deg, fs=SAMPLE_RATE, mode='nb')

    score = pesq(ref=silent_ref, deg=deg, fs=SAMPLE_RATE, mode='nb',
        on_error=PesqError.RETURN_VALUES)

    assert score == PesqError.NO_UTTERANCES_DETECTED, score

def test_no_utterances_wb_mode():
    SAMPLE_RATE = 16000
    silent_ref = np.zeros(SAMPLE_RATE)
    deg        = np.random.randn(SAMPLE_RATE)

    with pytest.raises(NoUtterancesError) as e:
        pesq(ref=silent_ref, deg=deg, fs=SAMPLE_RATE, mode='wb')

    score = pesq(ref=silent_ref, deg=deg, fs=SAMPLE_RATE, mode='wb',
        on_error=PesqError.RETURN_VALUES)

    assert score == PesqError.NO_UTTERANCES_DETECTED, score


import pandas as pd
import os
import os.path
import argparse
import re
from Bio import SeqIO
from Bio.Seq import Seq
from Bio.SeqRecord import SeqRecord
import time
from cigar import Cigar
from collections import Counter
from TEinsertion_bamConverter import bamConverter
import numpy as np
import warnings
warnings.filterwarnings('ignore')

pd.set_option("display.max_column",40)

parser=argparse.ArgumentParser()
parser.add_argument("-Bed1","--bed1")
parser.add_argument("-Bed2","--bed2")
parser.add_argument("-bam","--bamFile")
parser.add_argument("-file","--File")
args=parser.parse_args()

b1=args.bed1
b2=args.bed2
bamfile=args.bamFile

ali_file=args.File

def map_ratio(sub_f):
	r_len=int(list(sub_f["ReadLen"])[0])
	l=zip(sub_f["ReadStart"],sub_f["ReadEnd"])
	a=np.array([0]*r_len)
	for i in l:
		a[i[0]:i[1]+1]=1
	return list(a).count(1)

def getChimeric_reads(bamfile):

	bamConverter().ConverAlignment(bamfile)
	f=pd.read_table(bamfile+"_AligTable.tsv",header=0,sep="\t")
	#f=pd.read_table(bamfile,header=0,sep="\t")
	f=f.sort_values(["Readname","ReadStart","ReadEnd"])
	s1=f.drop_duplicates(["Readname"],keep="first").shape[0]
	f["m"]=0
	for r in set(f["Readname"]):
		sub_f=f.loc[f["Readname"]==r]
		p=map_ratio(sub_f)
		f.loc[f["Readname"]==r,"m"]=p
	f["p"]=f["m"]/f["ReadLen"]
	f=f.loc[f["p"]>=0.9]
	f.to_csv(bamfile+"_fuR.tsv",index=None,sep="\t")
	s2=f.drop_duplicates(["Readname"],keep="first").shape[0]
	print("Total reads %s; Fully aligned reads %s (%s)"%(s1,s2,round(s2/s1*100,2)))
getChimeric_reads(bamfile)
#getChimeric_reads(ali_file)

def getChimeric_two(file1,file2):
	f1=pd.read_table(file1,header=0,sep="\t")
	f2=pd.read_table(file2,header=0,sep="\t")
	f=pd.merge(f1,f2,on=["3"],how="inner")
	#f=f.loc[f["0_y"]!="HMS-Beagle"]
	f=f.loc[f["0_x"]!=f["0_y"]]
	f=f.loc[(f["read_end_x"]<=f["read_start_y"]) | (f["read_start_x"]>=f["read_end_y"])]
	f["Map1_len"]=f["read_end_x"]-f["read_start_x"]+1
	f["Map2_len"]=f["read_end_y"]-f["read_start_y"]+1
	f=f[["3","read_length_x","0_x","1_x","2_x","5_x","read_start_x","read_end_x","Map1_len","0_y","1_y","2_y","5_y","read_start_y","read_end_y","Map2_len"]]
	f.columns=["read","read_length","Ref1","Ref1_s","Ref1_e","Ref1_dir","read_start_ref1","read_end_ref1","Map1_len","Ref2","Ref2_s","Ref2_e","Ref2_dir","read_start_ref2","read_end_ref2","Map2_len"]
	f=f.sort_values(["read","Map1_len","Map2_len"],ascending=[True,False,False])
	r=f.drop_duplicates(["read"],keep="first")
#getChimeric_two(b1,b2)


from PIL import Image
import os
import numpy as np
from glob import glob
import random
import matplotlib.pyplot as plt
import tensorflow as tf
import pickle
import pandas as pd

class PH_predict:
    def __init__(self, path):
        self.model = tf.keras.models.load_model('./VGG_model.h5') #이전에 학습한 VGG모델의 가중치와 여러가지를 들고옴
        self.path = path

        self.realT = [] # 사진이 담길 리스트

    def getFilesInFolder(self, path): # 폴더 안에 있는 파일의 주소를 가져옴
        realT_path = [ y for x in os.walk(path) for y in glob(os.path.join(x[0], '*.jpg'))]

        for local_path in realT_path: # read_image and target
            self.realT.append(Image.open(local_path))

    def resize_img(self, dim): # 찍어온 이미지를 사이즈를 일정하게 바꿈

        for i in range(len(self.realT)):
            self.realT[i] = np.array(self.realT[i].resize((dim, dim)))

        # list -> numpy
        self.realT = np.array(self.realT)

    def normalize_max(self, maxVal): #Max정규화를 하는 함수
        self.realT = self.realT / maxVal

    def pred_start(self): # 실제로 예측을 하는 함수
        # print(self.realT.shape) # (512, 512) -> (1, 512, 512)

        pred = self.model.predict(self.realT) #예측을 하고 예측한 데이터를 pred에 저장
        pred_PH = pred * 10 # pred에 10을 곱해서 수치를 맞춘다.

        self.realT = []

        print(pred_PH.shape)
        #B8~11
        PH = pd.DataFrame([0,0,0,0,0,0],index=['A', 'B', 'C', 'D', 'E', 'F'], columns = ["PH"])
        real_PH = pd.DataFrame(pred_PH, index=['1', '2', '3', '4', '5', '6', '7'], columns=['PH'])
        PH = PH.append(real_PH)
        PH.drop(['A', 'B', 'C', 'D', 'E', 'F'])
        print(PH)
        PH.to_excel('./PH.xlsx', sheet_name='new_name')

        return pred_PH

    def predict(self, dim): # 위에 있는 함수를 시작함

        self.getFilesInFolder(self.path)
        self.resize_img(dim)
        self.normalize_max(10)

        return self.pred_start()

A = PH_predict("./realT_crop/")
B = A.predict(256)

print('#################')
print(B)


'''
fuck rna
fuck deep learning
'''
# -*- coding: utf-8 -*-

import numpy as np

from keras.models import Model
from keras.layers import Input, Dropout, Embedding, LSTM
from keras.layers import Activation, dot, TimeDistributed
from keras.layers import concatenate, Dense, Bidirectional
from keras.models import model_from_json
from keras.callbacks import TensorBoard
from time import time

str_sequence = list()
str_structure = list()

with open('sequence.txt') as f:
    for line in f.readlines():
        line = line.strip()
        str_sequence.append(str(line))

with open('structure_convented.txt') as f:
    for line in f.readlines():
        line = line.strip()
        str_structure.append(str(line))

# create sequence & structure data matrix

list_sequence = list()
list_structure = list()

for word in str_sequence:
    list_sequence.append(word.split(' '))

for word in str_structure:
    list_structure.append(word.split(' '))

# get the number of unique tokens in each language
print('SEQ: total unique tokens:', len(set([word for sent in list_sequence for word in sent])))
print('SEQ: total unique tokens:', len(set([word for sent in list_structure for word in sent])))

# get the maximum input lengths for each language
# assuming normal distribution, mean + 2 stds = 97.8% of lengths
# add one for SOS tag
fuck_var = max([len(s)+1 for s in list_sequence])
maxlen = fuck_var
print('SEQ: maximum string len :', max([len(s)+1 for s in list_sequence]))
print('SEQ: avg len + 2 stdevs :', np.mean([len(s)+1 for s in list_sequence]) + 2 * np.std([len(s) for s in list_sequence]))
print()
print('STR: avg len + 2 stdevs :', np.mean([len(s)+1 for s in list_structure]) + 2 * np.std([len(s) for s in list_structure]))
print('STR: maximum string len :', max([len(s)+1 for s in list_structure]))

def make_arrays(tokens, maxvocab=6000, maxlen=12, pad = '_PAD_', unk = '_UNK_', sos = 'Ⓑ', padfrom = 'end'):
    """integer-index and pad tokenized text"""
    from collections import Counter
    import numpy as np

    # get a single list of all words
    words = [word for sent in tokens for word in sent]
    # get the count of each word and sort by frequency (highest to lowest)
    # this is just by convention
    counts = Counter(words)
    words = sorted(list(set(words)), key=counts.get, reverse=True)
    # truncate to desired vocabulary and add PAD and UNK/OOV symbols
    words = words[:maxvocab-3]
    words.insert(0, sos)
    words.insert(0, pad)
    words.insert(-1, unk)
    # create dictionaries
    tok2idx = dict(zip(words, [i for i in range(len(words))]))
    idx2tok = dict(zip([i for i in range(len(words))], words))

    # index each sentence
    idxes = []
    for tok_sent in tokens:
        # pad and truncate
        tok_sent = tok_sent[:maxlen-1]
        tok_sent.insert(0, sos)
        while len(tok_sent) < maxlen:
            if padfrom == 'end':
                tok_sent.append(pad)
            else:
                tok_sent.insert(0, pad)
        # convert to indices and add
        idxes.append([tok2idx.get(word, tok2idx[unk]) for word in tok_sent])

    # convert to numpy array and return
    return np.array(idxes), tok2idx, idx2tok

# index and pad
seq_idxs, seq2idx, idx2seq = make_arrays(list_sequence, maxvocab=100, maxlen=fuck_var, padfrom = 'start')
str_idxs, str2idx, idx2str = make_arrays(list_structure, maxvocab=100, maxlen=fuck_var, padfrom = 'end')

print('seq_idxs = ' + str(seq_idxs))
print('seq2idx = ' + str(seq2idx))
print('idx2seq = ' + str(idx2seq))
print('str_idxs = ' + str(str_idxs))
print('str2idx = ' + str(str2idx))
print('idx2str = ' + str(idx2str))

# create output targets from decoder inputs
# this shifts the alignment my one (= predict *next* character) and adds extra dim
str_outs = np.expand_dims(np.hstack((str_idxs[:,1:], np.zeros(shape=(str_idxs.shape[0], 1)))), axis=-1)
print('str_outs = ' + str(str_outs))

# hyperparameters
SEQ_VOCAB  = len(seq2idx)      # how many unique words in the input language
SEQ_EMBED  = 1024               # low long are the character vectors in our input embedding space
MAX_IN_LEN = seq_idxs.shape[1] # how long is the sentence vector

STR_VOCAB    = len(str2idx)    # how many unique words in the output language
STR_EMBED    = 1024             # low long are the character vectors in our input embedding space
MAX_OUT_LEN  = str_idxs.shape[1]

# rnn size
HIDDEN_SIZE = 1024              # how big is the recurrent cell
DROP_RATE   = 0.4             # what is our dropout frequency

print('MAX_IN_LEN = ' + str(MAX_IN_LEN))
print('MAX_OUT_LEN = ' + str(MAX_OUT_LEN))

# bidirectional encoder
encoder_input = Input(shape=(MAX_IN_LEN,), name='encoder_input')

# korean embedding layer and dropout
encoder_embed = Embedding(SEQ_VOCAB, SEQ_EMBED, mask_zero=True, name='encoder_embed')(encoder_input)
encoder_embed = Dropout(DROP_RATE)(encoder_embed)

# two-layer bidirectional LSTM with final states
# divide the HIDDEN_SIZE by two because this is TWO LSTMS stacked
encoder_hout1, fwd_h1, fwd_c1, bck_h1, bck_c1 = Bidirectional(LSTM(int(HIDDEN_SIZE/2), return_sequences=True, return_state=True), name='encoder_lstm1')(encoder_embed)
encoder_hout2, fwd_h2, fwd_c2, bck_h2, bck_c2 = Bidirectional(LSTM(int(HIDDEN_SIZE/2), return_sequences=True, return_state=True), name='encoder_lstm2')(encoder_hout1)

# state concatenation (h, c states for layers 1 and 2)
state_h1 = concatenate([fwd_h1, bck_h1])
state_c1 = concatenate([fwd_c1, bck_c1])
state_h2 = concatenate([fwd_h2, bck_h2])
state_c2 = concatenate([fwd_c2, bck_c2])

# decoder
decoder_input = Input(shape=(MAX_OUT_LEN,), name='decoder_input')

# english embedding layer and dropout
decoder_embed = Embedding(STR_VOCAB, STR_EMBED, mask_zero=True, name='decoder_embed')(decoder_input)
decoder_embed = Dropout(DROP_RATE)(decoder_embed)

# two-layer LSTM initialized with encoder states
decoder_hout1 = LSTM(HIDDEN_SIZE, return_sequences=True, name='decoder_lstm1')(decoder_embed, initial_state=[state_h1, state_c1])
decoder_hout2 = LSTM(HIDDEN_SIZE, return_sequences=True, name='decoder_lstm2')(decoder_hout1, initial_state=[state_h2, state_c2])

# Luong global dot attention
# score function from the Luong apper = dot
score     = dot([decoder_hout2, encoder_hout2], axes=[2, 2], name='attn_dotprod')
# turn score to "attention dist." for weighted sum
attention = Activation('softmax', name='attn_softmax')(score)

# do the attention-weighted sum using dot product
context   = dot([attention, encoder_hout2], axes=[2, 1], name='cont_dotprod')

# 'stacked' the context vector with the decoder guess == 'attention vector'
context   = concatenate([context, decoder_hout2], name='cont_concats')

# activation
context   = TimeDistributed(Dense(HIDDEN_SIZE*2, activation='tanh'), name='cont_dnstanh')(context)

# guess which english letter
output    = TimeDistributed(Dense(STR_VOCAB, activation='softmax'))(context)

# our model takes as input the encoder and decoder, and as target the shifted output we made
model = Model([encoder_input, decoder_input], [output])

# compile the model with defined optimizer and loss function
model.compile(optimizer='adam', loss='sparse_categorical_crossentropy')

model.summary()

EPOCHS = 10

history = model.fit([seq_idxs, str_idxs], str_outs,
                    batch_size=32,
                    epochs=EPOCHS,
                    verbose=1,
                    callbacks=[TensorBoard(log_dir='/output')])

# # save

# # this is the model architecture (no weights)
json_model = model.to_json()
with open('seq2str_model.json', 'w') as outfile:
    outfile.write(json_model)

# # this is model architecture (minus some params), and weights
model.save('seq2str_model.h5')

model.load_weights('seq2str_model.h5')


#-*- coding: utf-8 -*-

from DQN_agent import DQNModel
from DoubleDQN_agent import DoubleDQNModel
from DuelingDQN_agent import DuelingDQNModel
from atari import AtariPlayer
import paddle.fluid as fluid
import gym
import argparse
import cv2
from tqdm import tqdm
from expreplay import ReplayMemory, Experience
import numpy as np
import os

from datetime import datetime
from atari_wrapper import FrameStack, MapState, FireResetEnv, LimitLength
from collections import deque

UPDATE_FREQ = 4

#MEMORY_WARMUP_SIZE = 2000
MEMORY_SIZE = 1e6
MEMORY_WARMUP_SIZE = MEMORY_SIZE // 20
IMAGE_SIZE = (84, 84)
CONTEXT_LEN = 4
ACTION_REPEAT = 4  # aka FRAME_SKIP
UPDATE_FREQ = 4

def run_train_episode(agent, env, exp):
    total_reward = 0
    state = env.reset()
    step = 0
    while True:
        step += 1
        context = exp.recent_state()
        context.append(state)
        context = np.stack(context, axis=0)
        action = agent.act(context, train_or_test='train')
        next_state, reward, isOver, _ = env.step(action)
        exp.append(Experience(state, action, reward, isOver))
        # train model
        # start training
        if len(exp) > MEMORY_WARMUP_SIZE:
            if step % UPDATE_FREQ == 0:
                batch_all_state, batch_action, batch_reward, batch_isOver = exp.sample_batch(
                    args.batch_size)
                batch_state = batch_all_state[:, :CONTEXT_LEN, :, :]
                batch_next_state = batch_all_state[:, 1:, :, :]
                agent.train(batch_state, batch_action, batch_reward,
                            batch_next_state, batch_isOver)
        total_reward += reward
        state = next_state
        if isOver:
            break
    return total_reward, step

def get_player(rom, viz=False, train=False):
    env = AtariPlayer(
        rom,
        frame_skip=ACTION_REPEAT,
        viz=viz,
        live_lost_as_eoe=train,
        max_num_frames=60000)
    env = FireResetEnv(env)
    env = MapState(env, lambda im: cv2.resize(im, IMAGE_SIZE))
    if not train:
        # in training, context is taken care of in expreplay buffer
        env = FrameStack(env, CONTEXT_LEN)
    return env

def eval_agent(agent, env):
    episode_reward = []
    for _ in tqdm(xrange(30), desc='eval agent'):
        state = env.reset()
        total_reward = 0
        step = 0
        while True:
            step += 1
            action = agent.act(state, train_or_test='test')
            state, reward, isOver, info = env.step(action)
            total_reward += reward
            if isOver:
                break
        episode_reward.append(total_reward)
    eval_reward = np.mean(episode_reward)
    return eval_reward

def train_agent():
    env = get_player(args.rom, train=True)
    test_env = get_player(args.rom)
    exp = ReplayMemory(args.mem_size, IMAGE_SIZE, CONTEXT_LEN)
    action_dim = env.action_space.n

    if args.alg == 'DQN':
        agent = DQNModel(IMAGE_SIZE, action_dim, args.gamma, CONTEXT_LEN,
                         args.use_cuda)
    elif args.alg == 'DoubleDQN':
        agent = DoubleDQNModel(IMAGE_SIZE, action_dim, args.gamma, CONTEXT_LEN,
                               args.use_cuda)
    elif args.alg == 'DuelingDQN':
        agent = DuelingDQNModel(IMAGE_SIZE, action_dim, args.gamma, CONTEXT_LEN,
                                args.use_cuda)
    else:
        print('Input algorithm name error!')
        return

    with tqdm(total=MEMORY_WARMUP_SIZE) as pbar:
        while len(exp) < MEMORY_WARMUP_SIZE:
            total_reward, step = run_train_episode(agent, env, exp)
            pbar.update(step)

    # train
    test_flag = 0
    save_flag = 0
    pbar = tqdm(total=1e8)
    recent_100_reward = []
    total_step = 0
    max_reward = None
    save_path = os.path.join(args.model_dirname, '{}-{}'.format(
        args.alg, os.path.basename(args.rom).split('.')[0]))
    while True:
        # start epoch
        total_reward, step = run_train_episode(agent, env, exp)
        total_step += step
        pbar.set_description('[train]exploration:{}'.format(agent.exploration))
        pbar.update(step)

        if total_step // args.test_every_steps == test_flag:
            pbar.write("testing")
            eval_reward = eval_agent(agent, test_env)
            test_flag += 1
            print("eval_agent done, (steps, eval_reward): ({}, {})".format(
                total_step, eval_reward))

            if max_reward is None or eval_reward > max_reward:
                max_reward = eval_reward
                fluid.io.save_inference_model(save_path, ['state'],
                                              agent.pred_value, agent.exe,
                                              agent.predict_program)
    pbar.close()

if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument(
        '--alg',
        type=str,
        default='DQN',
        help='Reinforcement learning algorithm, support: DQN, DoubleDQN, DuelingDQN'
    )
    parser.add_argument(
        '--use_cuda', action='store_true', help='if set, use cuda')
    parser.add_argument(
        '--gamma',
        type=float,
        default=0.99,
        help='discount factor for accumulated reward computation')
    parser.add_argument(
        '--mem_size',
        type=int,
        default=1000000,
        help='memory size for experience replay')
    parser.add_argument(
        '--batch_size', type=int, default=64, help='batch size for training')
    parser.add_argument('--rom', help='atari rom', required=True)
    parser.add_argument(
        '--model_dirname',
        type=str,
        default='saved_model',
        help='dirname to save model')
    parser.add_argument(
        '--test_every_steps',
        type=int,
        default=100000,
        help='every steps number to run test')
    args = parser.parse_args()
    train_agent()


#!/usr/bin/python
#######
# Using ThermalGen to translate the normal proteins to thermal stable protein
# makrliou 2020/8/11
######
import tensorflow as tf
import tensorflow_addons as tfa
import numpy as np
import sequence_handler as sh

def Inference(seq, model_path="./models/nontherm2therm_G.h5", seq_len = 512):
    ThermalGen = tf.keras.models.load_model("./models/nontherm2therm_G.h5")
    #print(ThermalGen.summary())

    candidate_seq = sh.seq2index(seq)
    candidate_seq = tf.keras.preprocessing.sequence.pad_sequences([candidate_seq], value=0, maxlen=seq_len)
    candidate_seq = tf.one_hot(candidate_seq, depth=21)

    Therm_seq = sh.index2seq(tf.argmax(ThermalGen(candidate_seq), axis=-1))[0]

    return Therm_seq
pass

hemoglobin_seq = 'MHSSIVLATVLFVAIASASKTRELCMKSLEHAKVGTSKEAKQDGIDLYKHMFEHYPAMKKYFKHRENYTPADVQKDPFFIKQGQNILLACHVLCATYDDRETFDAYVGELMARHERDHVKVPNDVWNHFWEHFIEFLGSKTTLDEPTKHAWQEIGKEFSHEISHHGRHSVRDHCMNSLEYIAIGDKEHQKQNGIDLYKHMFEHYPHMRKAFKGRENFTKEDVQKDAFFVNKDTRFCWPFVCCDSSYDDEPTFDYFVDALMDRHIKDDIHLPQEQWHEFWKLFAEYLNEKSHQHLTEAEKHAWSTIGEDFAHEADKHAKAEKDHHEGEHKEEHH'

Therm_seq = Inference(hemoglobin_seq)
print(Therm_seq)


import sys
sys.path.append('/media/Diske/projects/Pint/iPERCore')
import os
import time
import tqdm
import unittest
import torch
import torch.nn.functional as F
from torch.utils.data import DataLoader
import cv2
import numpy as np
from easydict import EasyDict
import logging

from iPERCore.tools.human_digitalizer.renders import SMPLRenderer
from iPERCore.tools.human_pose3d_estimators.spin.runner import SPINRunner
from iPERCore.tools.utils.visualizers.visdom_visualizer import VisdomVisualizer
from iPERCore.data.processed_video_dataset import ProcessedVideoDataset
# from iPERCore.services.options.options_base import BaseOptions
from iPERCore.services.options.options_train import TrainOptions
from iPERCore.tools.trainers.base import FlowCompositionForTrainer

# from lib.mesh_util import *
# from lib.sample_util import *
# from lib.train_util import *
# from lib.data import *
from lib.model import *

# pix2pix
from models.pix2pix_model import Pix2PixModel

def main(opt):
    # init device
    device = torch.device("cuda:0")
    # data root
    os.makedirs(opt.checkpoints_path, exist_ok=True)
    os.makedirs(opt.results_path, exist_ok=True)
    os.makedirs('%s/%s' % (opt.checkpoints_path, opt.name), exist_ok=True)
    os.makedirs('%s/%s' % (opt.results_path, opt.name), exist_ok=True)
    os.makedirs('%s/%s/imgs' % (opt.checkpoints_path, opt.name), exist_ok=True)
    # init logging
    logging.basicConfig(level=logging.INFO,
        filename='{}/{}/train.log'.format(opt.checkpoints_path, opt.name),
        filemode='a',
        format='%(asctime)s-[line:%(lineno)d]-: %(message)s')
    # init vis
    visualizer = VisdomVisualizer(
        env="visualizer",
        ip="http://localhost",  # need to be replaced.
        port=8097  # need to be replaced.
    )

    # model initial
    pre_model = Pint_Model(opt).to(device)
    pix_model = Pix2PixModel(opt)
    pix_model.setup(opt, device)
    # flow_comp = FlowCompositionForTrainer(opt=opt).to(device)
    # flow_comp.eval()
    # render = SMPLRenderer().to(device)
    # f_uvs2img = render.get_f_uvs2img(1)
    # load pretrain
    pre_model.load_state_dict(torch.load(opt.pre_model_path, map_location=device))
    if opt.continue_train:
        pix_model.load_pretrain(opt,device) # load pix_model pretrained.
        if opt.resume_epoch < 0:
            model_path_C = '%s/%s/netC_latest' % (opt.checkpoints_path, opt.name)
        else:
            model_path_C = '%s/%s/netC_epoch_%d' % (opt.checkpoints_path, opt.name, str(20))

        print('Resuming from ', model_path_C)
        pre_model.load_state_dict(torch.load(model_path_C, map_location=device))
    # optimizer
    optimizer_pre_model = torch.optim.Adam(pre_model.parameters(), lr=opt.learning_rateC)
    lr = opt.learning_rateC
    #dataset
    train_dataset = ProcessedVideoDataset(opt, is_for_train=True)

    trainloader = torch.utils.data.DataLoader(
        train_dataset,
        batch_size=opt.batch_size,
        shuffle=opt.serial_batches,
        num_workers=opt.num_workers,
        pin_memory=True,
        drop_last=True,
        sampler=None)
    data_len = len(trainloader)
    print('the dataset size is:', data_len)
    # model initial
    for epoch in tqdm(range(opt.n_epochs+opt.n_epochs_decay)):
        for i_train_batch, train_batch in enumerate(trainloader):
            # iter_start_time = time.time()
            with torch.no_grad():

                images = train_batch["images"].to(device, non_blocking=True)
                # aug_bg = train_batch["bg"].to(device, non_blocking=True)
                smpls = train_batch["smpls"].to(device, non_blocking=True)
                masks = train_batch["masks"].to(device, non_blocking=True)
                # offsets = train_batch["offsets"].to(device, non_blocking=True) if "offsets" in train_batch else 0
                # links_ids = train_batch["links_ids"].to(device, non_blocking=True) if "links_ids" in inputs else None
                # opt.num_source
                # src_img = images[:, 0:opt.num_source].contiguous()
                # src_smpl = smpls[:, 0:opt.num_source].contiguous()
                # tsf_img = images[:, opt.num_source:].contiguous()
                # tsf_smpl = smpls[:, opt.num_source:].contiguous()
                # src_mask = masks[:, 0:opt.num_source].contiguous()
                # ref_mask = masks[:, opt.num_source:].contiguous()

                # input_G_bg, input_G_src, input_G_tsf, Tst, Ttt, src_mask, tsf_mask, head_bbox, body_bbox, uv_img, ref_uv, ref_fim, ref_wim = \
                # flow_comp(src_img, tsf_img, src_smpl, tsf_smpl, src_mask=src_mask, ref_mask=ref_mask)
                # conduct flow composition # TODO: tsf_mask is delated, which may cause difference
                # _, _, input_G_tsf, _, _, _, tsf_mask, head_bbox, _, uv_img, ref_uv, ref_fim, ref_wim = \
                #     flow_comp(src_img, tsf_img, src_smpl, tsf_smpl, src_mask=src_mask, ref_mask=ref_mask)
                # visualizer.vis_named_img("mask", tsf_mask)
                # visualizer.vis_named_img('tsf_img_masked', (1-tsf_mask).expand(-1,3,-1,-1)*tsf_img.squeeze(1))
                # visualizer.vis_named_img('uv_img', uv_img)
                # visualizer.vis_named_img('ref_uv', ref_uv.squeeze(1))
                '''
                input_G_tsf: B,nt,6,512,512
                tsf_mask: B,nt,1,512,512
                head_bbox: B ,4
                uv: B ,3 ,512 ,512
                ref_uv: B ,nt ,3 ,512 ,512
                ref_fim: B ,512 ,512
                ref_wim: B ,512 ,512 ,3
                '''
            fix_image,complete_feat, loss1, loss2  = pre_model(uv_img,ref_uv) # complete_feat:B 16 512 512
            # visualizer for the fix_image
            if not (i_train_batch+epoch*data_len) % opt.Train.display_freq_s:
                visualizer.vis_named_img('checkpoint: {},epoch: {},iter: {}, fix_img'.format(opt.pre_model_path,epoch, i_train_batch), fix_image)
            Tuv2src = render.cal_bc_transform(f_uvs2img.expand(head_bbox.shape[0],-1,-1,-1), ref_fim, ref_wim)
            HD_img = F.grid_sample(complete_feat, Tuv2src)
            HD_img_full = torch.cat((HD_img, input_G_tsf.squeeze(1)[:,3:]),dim=1)
            pix_model.set_input_with_face(HD_img_full, tsf_img.squeeze(1), head_bbox)
            pix_model.optimize_parameters(optimizer_pre_model,A_vis_feat, B_vis_uv,complete_feat, complete_feat_B)

            if not (i_train_batch+epoch*data_len) % opt.Train.print_freq_s:
                logging.log(logging.INFO, 'epoch: {},iter: {},lr0: {},lr1: {}'.format(
                    epoch, i_train_batch,optimizer_pre_model.param_groups[0]['lr'],pix_model.get_current_losses()))
                logging.log(logging.INFO, 'loss_G_pre_train:{} loss_G_GAN:{} loss_G_L1 :{} loss_G_percetual:{} loss_G_face:{}'.format(
                    pix_model.loss_G_pre_train.item(),pix_model.loss_G_GAN.item(),pix_model.loss_G_L1.item(),pix_model.loss_G_percetual.item(),pix_model.loss_G_face.item()))

if __name__ == "__main__":
    opt = TrainOptions().parse()
    main(opt)


import tensorflow as tf
import numpy as np
from tensorflow.keras.preprocessing import image
import matplotlib.pyplot as plt
import cv2
import os
import PIL

pre_train = tf.keras.models.load_model('./left_right_final.h5')
dir_path = './Testing/'
for i in os.listdir(dir_path):

    if i != ('.DS_Store'):
      img_path = (dir_path + i)
      img = image.load_img(dir_path+i,target_size=(200,200,3))
      X = image.img_to_array(img)
      x = np.expand_dims(X,axis=0)
      images = np.vstack([x])
      val = pre_train.predict(images)
      if val == 1:
        pre = "Detected: Right"
      else:
        pre = "Detected: Left"
      print("Predicted Orientation is: ",pre)
      #display image and the prediction text over it
      disp_img = cv2.imread(img_path)
      #display prediction text over the image
      cv2.putText(disp_img, pre, (20,20), cv2.FONT_HERSHEY_TRIPLEX , 0.8, (0,0,0))
      #show the image
      cv2.imshow('result',disp_img)
      cv2.waitKey(0)


from drl_grasping.algorithms.common.features_extractor import OctreeCnnFeaturesExtractor
from drl_grasping.algorithms.common.octree_replay_buffer import preprocess_stacked_octree_batch
from stable_baselines3.common.policies import register_policy
from stable_baselines3.common.torch_layers import BaseFeaturesExtractor
from stable_baselines3.common.utils import is_vectorized_observation
from stable_baselines3.common.vec_env.obs_dict_wrapper import ObsDictWrapper
from sb3_contrib.tqc.policies import Actor, Critic
from sb3_contrib.tqc.policies import TQCPolicy
from torch import nn
from typing import Any, Dict, List, Optional, Tuple, Type
import gym
import numpy as np
import torch as th

import ocnn

class ActorOctreeCnn(Actor):
    """
    Actor network (policy) for TQC.
    Overriden to not preprocess observations (unnecessary conversion into float)

    :param observation_space: Obervation space
    :param action_space: Action space
    :param net_arch: Network architecture
    :param features_extractor: Network to extract features
        (a CNN when using images, a nn.Flatten() layer otherwise)
    :param features_dim: Number of features
    :param activation_fn: Activation function
    :param use_sde: Whether to use State Dependent Exploration or not
    :param log_std_init: Initial value for the log standard deviation
    :param full_std: Whether to use (n_features x n_actions) parameters
        for the std instead of only (n_features,) when using gSDE.
    :param sde_net_arch: Network architecture for extracting features
        when using gSDE. If None, the latent features from the policy will be used.
        Pass an empty list to use the states as features.
    :param use_expln: Use ``expln()`` function instead of ``exp()`` when using gSDE to ensure
        a positive standard deviation (cf paper). It allows to keep variance
        above zero and prevent it from growing too fast. In practice, ``exp()`` is usually enough.
    :param clip_mean: Clip the mean output when using gSDE to avoid numerical instability.
    :param normalize_images: Whether to normalize images or not,
         dividing by 255.0 (True by default)
    """

    def __init__(
        self,
        observation_space: gym.spaces.Space,
        action_space: gym.spaces.Space,
        net_arch: List[int],
        features_extractor: nn.Module,
        features_dim: int,
        activation_fn: Type[nn.Module] = nn.ReLU,
        use_sde: bool = False,
        log_std_init: float = -3,
        full_std: bool = True,
        sde_net_arch: Optional[List[int]] = None,
        use_expln: bool = False,
        clip_mean: float = 2.0,
        normalize_images: bool = True,
    ):
        super(ActorOctreeCnn, self).__init__(
            observation_space=observation_space,
            action_space=action_space,
            net_arch=net_arch,
            features_extractor=features_extractor,
            features_dim=features_dim,
            activation_fn=activation_fn,
            use_sde=use_sde,
            log_std_init=log_std_init,
            full_std=full_std,
            sde_net_arch=sde_net_arch,
            use_expln=use_expln,
            clip_mean=clip_mean,
            normalize_images=normalize_images)

    def extract_features(self, obs: th.Tensor) -> th.Tensor:
        """
        Preprocess the observation if needed and extract features.
        Overriden to skip pre-processing (for some reason it converts tensor to Float)

        :param obs:
        :return:
        """
        assert self.features_extractor is not None, "No features extractor was set"
        return self.features_extractor(obs)
        # OVERRIDDEN
        # preprocessed_obs = preprocess_obs(obs, self.observation_space, normalize_images=self.normalize_images)
        # return self.features_extractor(preprocessed_obs)

class CriticOctreeCnn(Critic):
    """
    Critic network (q-value function) for TQC.

    :param observation_space: Obervation space
    :param action_space: Action space
    :param net_arch: Network architecture
    :param features_extractor: Network to extract features
        (a CNN when using images, a nn.Flatten() layer otherwise)
    :param features_dim: Number of features
    :param activation_fn: Activation function
    :param normalize_images: Whether to normalize images or not,
         dividing by 255.0 (True by default)
    :param share_features_extractor: Whether the features extractor is shared or not
        between the actor and the critic (this saves computation time)
    """

    def __init__(
        self,
        observation_space: gym.spaces.Space,
        action_space: gym.spaces.Space,
        net_arch: List[int],
        features_extractor: nn.Module,
        features_dim: int,
        activation_fn: Type[nn.Module] = nn.ReLU,
        normalize_images: bool = True,
        n_quantiles: int = 25,
        n_critics: int = 2,
        share_features_extractor: bool = True,
    ):
        super(CriticOctreeCnn, self).__init__(
            observation_space=observation_space,
            action_space=action_space,
            net_arch=net_arch,
            features_extractor=features_extractor,
            features_dim=features_dim,
            activation_fn=activation_fn,
            normalize_images=normalize_images,
            n_quantiles=n_quantiles,
            n_critics=n_critics,
            share_features_extractor=share_features_extractor)

    def extract_features(self, obs: th.Tensor) -> th.Tensor:
        """
        Preprocess the observation if needed and extract features.
        Overriden to skip pre-processing (for some reason it converts tensor to Float)

        :param obs:
        :return:
        """
        assert self.features_extractor is not None, "No features extractor was set"
        return self.features_extractor(obs)
        # OVERRIDDEN
        # preprocessed_obs = preprocess_obs(obs, self.observation_space, normalize_images=self.normalize_images)
        # return self.features_extractor(preprocessed_obs)

class OctreeCnnPolicy(TQCPolicy):
    """
    Policy class (with both actor and critic) for TQC.
    Overriden to not preprocess observations (unnecessary conversion into float)

    :param observation_space: Observation space
    :param action_space: Action space
    :param lr_schedule: Learning rate schedule (could be constant)
    :param net_arch: The specification of the policy and value networks.
    :param activation_fn: Activation function
    :param use_sde: Whether to use State Dependent Exploration or not
    :param log_std_init: Initial value for the log standard deviation
    :param sde_net_arch: Network architecture for extracting features
        when using gSDE. If None, the latent features from the policy will be used.
        Pass an empty list to use the states as features.
    :param use_expln: Use ``expln()`` function instead of ``exp()`` when using gSDE to ensure
        a positive standard deviation (cf paper). It allows to keep variance
        above zero and prevent it from growing too fast. In practice, ``exp()`` is usually enough.
    :param clip_mean: Clip the mean output when using gSDE to avoid numerical instability.
    :param features_extractor_class: Features extractor to use.
    :param features_extractor_kwargs: Keyword arguments
        to pass to the feature extractor.
    :param normalize_images: Whether to normalize images or not,
         dividing by 255.0 (True by default)
    :param optimizer_class: The optimizer to use,
        ``th.optim.Adam`` by default
    :param optimizer_kwargs: Additional keyword arguments,
        excluding the learning rate, to pass to the optimizer
    :param share_features_extractor: Whether to share or not the features extractor
        between the actor and the critic (this saves computation time)
    """

    def __init__(
        self,
        observation_space: gym.spaces.Space,
        action_space: gym.spaces.Space,
        lr_schedule,
        net_arch: Optional[List[int]] = None,
        activation_fn: Type[nn.Module] = nn.ReLU,
        use_sde: bool = False,
        log_std_init: float = -3,
        sde_net_arch: Optional[List[int]] = None,
        use_expln: bool = False,
        clip_mean: float = 2.0,
        features_extractor_class: Type[BaseFeaturesExtractor] = OctreeCnnFeaturesExtractor,
        features_extractor_kwargs: Optional[Dict[str, Any]] = None,
        normalize_images: bool = True,
        optimizer_class: Type[th.optim.Optimizer] = th.optim.Adam,
        optimizer_kwargs: Optional[Dict[str, Any]] = None,
        n_quantiles: int = 25,
        n_critics: int = 2,
        share_features_extractor: bool = True,
        separate_networks_for_stacks: bool = True,
        debug_write_octree: bool = False,
    ):
        features_extractor_kwargs.update(
            {'separate_networks_for_stacks': separate_networks_for_stacks})
        super(OctreeCnnPolicy, self).__init__(
            observation_space=observation_space,
            action_space=action_space,
            lr_schedule=lr_schedule,
            net_arch=net_arch,
            activation_fn=activation_fn,
            use_sde=use_sde,
            log_std_init=log_std_init,
            sde_net_arch=sde_net_arch,
            use_expln=use_expln,
            clip_mean=clip_mean,
            features_extractor_class=features_extractor_class,
            features_extractor_kwargs=features_extractor_kwargs,
            normalize_images=normalize_images,
            optimizer_class=optimizer_class,
            optimizer_kwargs=optimizer_kwargs,
            n_quantiles=n_quantiles,
            n_critics=n_critics,
            share_features_extractor=share_features_extractor)

        self._separate_networks_for_stacks = separate_networks_for_stacks
        self._debug_write_octree = debug_write_octree

    def make_actor(self, features_extractor: Optional[BaseFeaturesExtractor] = None) -> Actor:
        actor_kwargs = self._update_features_extractor(
            self.actor_kwargs, features_extractor)
        return ActorOctreeCnn(**actor_kwargs).to(self.device)

    def make_critic(self, features_extractor: Optional[BaseFeaturesExtractor] = None) -> Critic:
        critic_kwargs = self._update_features_extractor(
            self.critic_kwargs, features_extractor)
        return CriticOctreeCnn(**critic_kwargs).to(self.device)

    def predict(
        self,
        observation: np.ndarray,
        state: Optional[np.ndarray] = None,
        mask: Optional[np.ndarray] = None,
        deterministic: bool = False,
    ) -> Tuple[np.ndarray, Optional[np.ndarray]]:
        """
        Overriden to create proper Octree batch.
        Get the policy action and state from an observation (and optional state).

        :param observation: the input observation
        :param state: The last states (can be None, used in recurrent policies)
        :param mask: The last masks (can be None, used in recurrent policies)
        :param deterministic: Whether or not to return deterministic actions.
        :return: the model's action and the next state
            (used in recurrent policies)
        """
        if isinstance(observation, dict):
            observation = ObsDictWrapper.convert_dict(observation)
        else:
            observation = np.array(observation)

        vectorized_env = is_vectorized_observation(
            observation, self.observation_space)

        if self._debug_write_octree:
            ocnn.write_octree(th.from_numpy(observation[-1]), 'octree.octree')

        # Make batch out of tensor (consisting of n-stacked octrees)
        octree_batch = preprocess_stacked_octree_batch(
            observation, self.device, separate_batches=self._separate_networks_for_stacks)

        with th.no_grad():
            actions = self._predict(octree_batch,
                                    deterministic=deterministic)
        # Convert to numpy
        actions = actions.cpu().numpy()

        if isinstance(self.action_space, gym.spaces.Box):
            if self.squash_output:
                # Rescale to proper domain when using squashing
                actions = self.unscale_action(actions)
            else:
                # Actions could be on arbitrary scale, so clip the actions to avoid
                # out of bound error (e.g. if sampling from a Gaussian distribution)
                actions = np.clip(
                    actions, self.action_space.low, self.action_space.high)

        if not vectorized_env:
            if state is not None:
                raise ValueError(
                    "Error: The environment must be vectorized when using recurrent policies.")
            actions = actions[0]

        return actions, state

register_policy("OctreeCnnPolicy", OctreeCnnPolicy)


from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import numpy as np
import torch
from torch import multiprocessing as mp
from torch.autograd import Variable

from receptor.agents.agent import BaseAgent
from receptor.core import losses
from receptor.core.policy import GreedyPolicy
from receptor.utils import utils, torch_utils

class DeepQ(BaseAgent):
    def __init__(self, net, device='cuda:0', logdir='', name='', *args, **kwargs):
        """Constructs Deep Q-Learning agent.
         Includes the following implementations:
            1. Human-level control through deep reinforcement learning, Mnih et al., 2015.
            2. Dueling Network Architectures for Deep Reinforcement Learning, Wang et al., 2015.
                See `models.DeepQModel`.
            3. Deep Reinforcement Learning with Double Q-learning, Hasselt et al., 2016.
                See `use_double` argument.
            4. Prioritized Experience Replay, Schaul et al., 2015.
                See `core.replay.ProportionalReplay`.

        See `core.BaseDeepQ`.
        Args:
            net (models.AbstractFactory): Network factory.
            device (str): TensorFlow device, used for graph creation.
        """
        super(DeepQ, self).__init__(net=net, device=device, name=name, saver_keep=saver_keep)
        self.use_double = use_double
        self._target_freq = targetfreq
        self.policy = policy
        self.net = net
        self.last_sync = mp.Value('l', 0)

        self.target_net = target_net
        if target_net is None:
            import copy
            self.target_net = copy.deepcopy(self.net)
        self.net.to(device)
        self.target_net.to(device)
        self.target_net.last_sync = mp.Value('l', self.obs_step.value)
        self.target_net.eval()
        self.opt = optimizer
        self.trainable_net = net if trainable_net is None else trainable_net
        if self.opt is None:
            self.opt = torch.optim.RMSprop(self.trainable_net.parameters(),
                                           lr=0.00025, alpha=0.9, momentum=0.95, eps=0.01)
        elif isinstance(self.opt, dict):
            self.opt = torch_utils.create_optim(self.opt, self.trainable_net.parameters())
        if restore_from:
            self.load(restore_from)
        # tensor_utils.add_observation_summary(self.net['input'], self.env)
        # tf.summary.histogram('agent/action', self.actions)
        # tf.summary.histogram('agent/action_values', self.net['value'])
        # tf.summary.scalar('agent/learning_rate', self.opt.lr)
        # tf.summary.scalar('metrics/loss', loss)
        # tf.summary.scalar('metrics/avg_Q', tf.reduce_mean(q_next_max))
        # if restore_from and tf.train.latest_checkpoint(restore_from):
        #     self.load(restore_from)

    def predict_on_batch(self, obs_batch):
        """Computes action-values for given batch of observations."""
        obs_batch = Variable(torch.from_numpy(np.asarray(obs_batch, dtype='float32')))
        obs_batch = obs_batch.to(self.device)
        with torch.no_grad():
            return self.net(obs_batch)['Q']

    def act(self, obs):
        """Computes action with greedy policy for given observation."""
        action_values = self.predict_on_batch([obs])
        return GreedyPolicy.select_action(self.env, action_values.data.cpu().numpy())

    def explore(self, obs):
        action_values = self.predict_on_batch([obs])
        return self.policy.select_action(self.env, action_values.data.cpu().numpy(),
                                         step=self.obs_step)

    def load(self, checkpoint):
        super(DeepQ, self).load(checkpoint)
        self.sync_target_network()

    def sync_target_network(self):
        """Syncs target network with behaviour network."""
        self.target_net.load_state_dict(self.trainable_net.state_dict())

    def train_on_batch(self, obs, actions, rewards, term, obs_next, traj_ends,
                       lr=None, gamma=0.99, aux_losses=(), summarize=False, importance=None):
        with self.train_step.get_lock():
            if self.train_step.value == self._target_freq:
                self.sync_target_network()
            self.train_step.value += 1
        with self.obs_step.get_lock():
            self.obs_step.value += len(obs)
        with self.episode_step.get_lock():
            self.episode_step.value += sum(term)
        torch_utils.set_lr(self.opt, lr)

        obs = Variable(torch.from_numpy(np.asarray(obs, dtype='f'))).to(self.device)
        actions = Variable(torch.from_numpy(np.asarray(actions, dtype='f'))).to(self.device)
        obs_next = Variable(torch.from_numpy(np.asarray(obs_next, dtype='f'))).to(self.device)
        importance = Variable(torch.from_numpy(np.asarray(importance, dtype='f'))).to(self.device)
        if importance is not None:
            importance = Variable(
                torch.from_numpy(np.asarray(importance, dtype='f'))).to(self.device)
        terms = torch.from_numpy(np.asarray(term, dtype='uint8'))
        rewards = torch.from_numpy(np.asarray(rewards, dtype='f'))
        traj_ends = torch.from_numpy(np.asarray(traj_ends, dtype='uint8'))

        with torch.no_grad():
            if self.use_double:
                q_idx = self.net(obs_next)['Q'].max(1)[1]
                q_next = self.target_net(obs_next)['Q']
                q_next_max = torch.where(q_idx.to(torch.uint8), q_next[:, 1], q_next[:, 0])
            else:
                q_next_max = self.target_net(obs_next)['Q'].max(1)[0]

        target = utils.discount_trajectory(rewards, terms, traj_ends, gamma,
                                           q_next_max.data.cpu().numpy())
        self.net.zero_grad()
        self.trainable_net.zero_grad()
        output = self.net(obs)
        qloss = losses.QLoss(1.0)
        compositor = losses.LossCompositor([qloss])
        compositor.add(aux_losses)
        loss = compositor.loss(agent=self, output=output, action=actions,
                               reward=torch.from_numpy(target).to(self.device),
                               term=terms, importance=importance)
        loss.backward()
        # torch_utils.clip_grads(self.net, -1, 1)
        torch_utils.copy_grads(from_net=self.net, to_net=self.trainable_net, device=self.device)
        self.opt.step()
        return loss, output, target


import cv2
import numpy as np
from object_detector import ObjectDetection
from tensorflow.keras.models import load_model
from tensorflow.keras.applications.vgg16 import preprocess_input
import pydirectinput as pd

model = 'models\\model_new_2.h5'
model = load_model(model)
pd.PAUSE = 0.001  # Default is 0.1 so to avoid lagging

def preprocess_img(img_for_pred):
    img_width, img_height = 200, 300
    img_pred = cv2.resize(img_for_pred, (img_height, img_width))

    img_new = np.zeros((img_width, img_height, 3))  # Converting channel 1 image to channel 3
    img_new[:, :, 0] = img_pred
    img_new[:, :, 1] = img_pred
    img_new[:, :, 2] = img_pred

    img_pred = np.reshape(img_new, [1, img_width, img_height, 3])  # Reshaping to shape that keras accepts
    img_pred = preprocess_input(img_pred)
    return img_pred

cap = cv2.VideoCapture(1)

p1_blue = (300, 0)
p2_blue = (640, 200)

p1_hand = (0, 0)
p2_hand = (200, 300)

# lower_hsv_blue = (97, 114, 43)  # blue
lower_hsv_blue = (38, 37, 116)  # 6: 02 pm
# lower_hsv_hand = (30, 0, 143)  # hands2
lower_hsv_hand = (0, 55, 0)  # 5:31 pm
# lower_hsv_hand = 0

obj_blue = ObjectDetection(draw_contour=1, hands_or_blue=1, p1=p1_blue, p2=p2_blue, lower_hsv=lower_hsv_blue)
obj_hand = ObjectDetection(draw_contour=1, hands_or_blue=0, p1=p1_hand, p2=p2_hand, lower_hsv=lower_hsv_hand)

while True:
    _, img = cap.read()
    #img = cv2.resize(img, (800, 700))
    img = cv2.flip(img, 2)
    img_pre_hand, img_hand = obj_hand.preprocess(img)  # Get preprocessed image
    img_contour_hand, _ = obj_hand.contours(img_pre_hand, img_hand)  # Get image with contours

    img_pre_blue, img_blue = obj_blue.preprocess(img)
    img_contour_blue, kill = obj_blue.contours(img_pre_blue, img_blue)

    # Prediction changes only starts from here
    img_pre = preprocess_img(img_pre_hand)  # Converting to keras form of input
    result = model.predict_classes(img_pre)

    if result == 0:
        text = "Up"
        pd.press('Up')
    elif result == 1:
        text = "Down"
        pd.press('Down')
    elif result == 2:
        text = "Left"
        pd.press('Left')
    else:
        text = "Right"
        pd.press('Right')

    if kill:
        text = "Kill"
        pd.press('Space')

    cv2.putText(img_contour_blue, text, (300, 90), cv2.FONT_HERSHEY_SIMPLEX, 3, (150, 75, 94), 2, cv2.LINE_AA)
    cv2.imshow('Hands', img_pre_hand)  # ROI hands image
    cv2.imshow('Blue', img_pre_blue)
    cv2.imshow('Image', img_contour_blue)

    k = cv2.waitKey(1) & 0xff
    if k == 27:  # If backspace break
        break

cv2.destroyAllWindows()


from collections import defaultdict

import networkx

from angr.analyses.cfg.cfg_utils import CFGUtils

from .edge import Edge

class EdgeRouter(object):
    def __init__(self, graph, col_map, row_map, node_locs, max_col, max_row):
        """
        :param networkx.DiGraph graph:  The graph to route edges on.
        """
        self._graph = graph

        self._rows = row_map
        self._cols = col_map
        self._node_locations = node_locs
        self._max_row = max_row
        self._max_col = max_col

        # Mark whether a spot (col, row) is valid or not for putting edges.
        self._edge_valid = None
        # A map between spots (col, row) and all vertical edges at that spot.
        self.vertical_edges = None
        # A map between spots (col, row) and all horizontal edges at that spot.
        self.horizontal_edges = None

        self._in_edges = defaultdict(list)
        self._out_edges = defaultdict(list)

        self.edges = self._route_edges()

    def _route_edges(self):
        """
        Route edges between nodes.

        :return:                       None
        """

        self._prepare_edge_routing()

        edges = [ ]

        for src, dst in self._graph.edges_iter():
            edge = self._route_edge(src, dst)
            edges.append(edge)

        self._set_in_edge_indices()
        self._set_out_edge_indices()

        return edges

    def _route_edge(self, src, dst):
        """
        Find a series of grids to route an edge from the source node to the destination node.

        :param src:         The beginning of the edge to route.
        :param dst:         The end of the edge to route.
        :return:            None
        """

        MOVE_LEFT = 0
        NO_MOVE = 1
        MOVE_RIGHT = 2

        # build the edge
        edge = Edge(src, dst)

        start_col, start_row = self._node_locations[src]
        end_col, end_row = self._node_locations[dst]

        # start from the middle of the block
        start_col += 1
        end_col += 1
        # start from the next row
        start_row += 1

        if start_row < end_row:
            min_row, max_row = start_row, end_row
        else:
            max_row, min_row = start_row, end_row

        # find a vertical column to route the edge to the target node
        col = start_col
        if self._edge_available(col, min_row, max_row):
            pass
        else:
            offset = 1
            while True:
                if self._edge_available(col + offset, min_row, max_row):
                    col = col + offset
                    break
                if self._edge_available(col - offset, min_row, max_row):
                    col = col - offset
                    break
                offset += 1

        if col != start_col:
            # generate a line to move to the target column

            if start_col < col:
                min_col, max_col = start_col, col
                move = MOVE_RIGHT
            else:
                max_col, min_col = start_col, col
                move = MOVE_LEFT

            idx = self._assign_edge_to(edge, 'horizontal', min_col, start_row, max_col - min_col)
            edge.add_point(col, start_row, idx)
            edge.add_move(move)

        if start_row != end_row:
            # generate a line to move to the target row
            idx = self._assign_edge_to(edge, 'vertical', col, min(start_row + 1, end_row), abs(end_row - start_row) + 1)
            edge.add_point(col, end_row, idx)

        if col != end_col:
            # generate a line to move to the target column
            if col < end_col:
                min_col, max_col = col, end_col
                move = MOVE_RIGHT
            else:
                max_col, min_col = col, end_col
                move = MOVE_LEFT
            idx = self._assign_edge_to(edge, 'horizontal', min_col, end_row, max_col - min_col)
            edge.add_point(end_col, end_row, idx)
            edge.add_move(move)

        self._add_edge(edge)

        return edge

    def _prepare_edge_routing(self):
        """
        Create and initialize necessary data structions for edge routing.

        :return: None
        """

        self._edge_valid = [ ]
        for col in xrange(self._max_col + 2):
            self._edge_valid.append([True] * (self._max_row + 1))
        for col, row in self._node_locations.itervalues():
            # edges should not overlap with existing nodes
            self._edge_valid[col][row] = False
            self._edge_valid[col + 1][row] = False

        self.vertical_edges = [ ]
        self.horizontal_edges = [ ]

        for col in xrange(self._max_col + 2):
            v_edges = [ ]
            h_edges = [ ]
            for row in xrange(self._max_row + 3):
                v_edges.append({ })
                h_edges.append({ })
            self.vertical_edges.append(v_edges)
            self.horizontal_edges.append(h_edges)

    def _assign_edge_to(self, edge, sort, col, row, blocks, index=None):

        if sort == 'vertical':
            d = self.vertical_edges
        elif sort == 'horizontal':
            d = self.horizontal_edges
        else:
            raise ValueError('_assign_edge_to(): Unsupported edge sort "%s".' % sort)

        if sort == 'vertical':
            if index is None:
                index = self._find_vertical_available_edge_index(col, row, row + blocks)
            for r in xrange(row, row + blocks + 1):
                d[col][r][index] = edge

        else:  # sort == 'horizontal'
            if index is None:
                index = self._find_horizontal_available_edge_index(col, col + blocks, row)
            for col_ in xrange(col, col + blocks + 1):
                d[col_][row][index] = edge

        return index

    def _edge_available(self, col, start_row, end_row):

        for i in xrange(start_row, end_row):
            if not self._edge_valid[col][i]:
                return False
        return True

    def _first_unused_index(self, indices):

        # find the first unused index
        last_i = None
        for i in sorted(indices):
            if last_i is None or i == last_i + 1:
                last_i = i
            else:
                # we found a gap
                return last_i + 1

        return 0 if last_i is None else last_i + 1

    def _find_vertical_available_edge_index(self, col, start_row, end_row):

        # collect all used indices
        indices = set()

        for row in xrange(start_row, end_row + 1):
            if self.vertical_edges[col][row]:
                indices.update(self.vertical_edges[col][row].iterkeys())

        return self._first_unused_index(indices)

    def _find_horizontal_available_edge_index(self, start_col, end_col, row):

        # collect all used indices
        indices = set()

        for col in xrange(start_col, end_col + 1):
            if self.horizontal_edges[col][row]:
                indices.update(self.horizontal_edges[col][row].iterkeys())

        return self._first_unused_index(indices)

    def _add_edge(self, edge):
        """
        Add an edge.

        :param Edge edge:   The Edge instance to add.
        :return:            None
        """

        self._out_edges[edge.src].append(edge)
        self._in_edges[edge.dst].append(edge)

    def _set_in_edge_indices(self):

        # assign in-edge indices
        for _, edges in self._in_edges.iteritems():
            max_idx = None

            if len(edges) == 2:
                edges = sorted(edges, key=lambda edge: edge.last_move, reverse=True) # sort by their last horizontal move

            for idx, edge in enumerate(edges):
                edge.end_index = idx
                if idx > max_idx:
                    max_idx = idx
            for edge in edges:
                edge.max_end_index = max_idx

    def _set_out_edge_indices(self):

        for _, edges in self._out_edges.iteritems():
            max_idx = None
            if len(edges) == 2:
                edges = sorted(edges, key=lambda edge: edge.first_move)  # sort by their first horizontal move

            for idx, edge in enumerate(edges):
                edge.start_index = idx
                if edge.start_index > max_idx:
                    max_idx = edge.start_index
            for edge in edges:
                edge.max_start_index = max_idx

class GraphLayouter(object):
    def __init__(self, graph, node_sizes):
        self.graph = graph
        self._node_sizes = node_sizes

        self._cols = None
        self._rows = None
        self._max_col = None
        self._max_row = None
        self._locations = None

        self._vertical_edges = None
        self._horizontal_edges = None

        self._grid_max_vertical_id = { }
        self._grid_max_horizontal_id = { }
        self._row_to_nodes = { }
        self._row_heights = [ ]
        self._col_widths = [ ]
        self._grid_coordinates = { }

        self.edges = [ ]
        self.node_coordinates = { }

        self._layout()

    def _layout(self):

        self._initialize()

        # order the nodes
        ordered_nodes = CFGUtils.quasi_topological_sort_nodes(self.graph)

        # conver the graph to an acylic graph
        acyclic_graph = self._to_acyclic_graph(self.graph, ordered_nodes=ordered_nodes)

        # assign row and column to each node
        self._assign_grid_locations(self.graph, acyclic_graph, ordered_nodes=ordered_nodes)

        # edge routing
        edge_router = EdgeRouter(self.graph, self._cols, self._rows, self._locations, self._max_col, self._max_row)
        self.edges = edge_router.edges
        self._vertical_edges = edge_router.vertical_edges
        self._horizontal_edges = edge_router.horizontal_edges

        # determine row and column sizes
        self._make_grids()

        # determine the maximum index for each grid
        self._set_max_grid_edge_id()

        # calculate coordinates of nodes
        self._calculate_coordinates()

    def _initialize(self):
        self._cols = { }
        self._rows = { }
        self._locations = { }

    def _to_acyclic_graph(self, graph, ordered_nodes=None):
        """
        Convert a given DiGraph into an acyclic graph.

        :param networkx.DiGraph graph: The graph to convert.
        :param list ordered_nodes:     A list of nodes sorted in a topological order.
        :return:                       The converted acyclic graph.
        """

        if ordered_nodes is None:
            # take the quasi-topological order of the graph
            ordered_nodes = CFGUtils.quasi_topological_sort_nodes(graph)

        acyclic_graph = networkx.DiGraph()

        # add each node and its edge into the graph
        visited = set()
        for node in ordered_nodes:
            visited.add(node)
            acyclic_graph.add_node(node)
            for successor in graph.successors_iter(node):
                if successor not in visited:
                    acyclic_graph.add_edge(node, successor)

        return acyclic_graph

    def _assign_grid_locations(self, graph, acyclic_graph, ordered_nodes=None):
        """
        Assign locations to each node in the graph in a bottom-up manner.

        :param networkx.DiGraph graph:          The original graph.
        :param networkx.DiGraph acyclic_graph:  The acyclic graph to work on.
        :param list ordered_nodes:      A list of nodes sorted in a topological order.
        :return:                        None
        """

        if ordered_nodes is None:
            # take the quasi-topological order of the graph
            ordered_nodes = CFGUtils.quasi_topological_sort_nodes(acyclic_graph)

        self._assign_rows(graph, acyclic_graph, ordered_nodes)
        self._assign_columns(graph, acyclic_graph, ordered_nodes)

    def _assign_rows(self, graph, acyclic_graph, ordered_nodes):

        row_to_nodes = defaultdict(list)

        global_max_row = 0

        min_rows, max_rows = {}, {}

        """
        # assign min row ID top-down
        for node in ordered_nodes:
            if node not in min_rows:
                min_rows[node] = 0
            row = min_rows[node]
            for successor in acyclic_graph.successors_iter(node):
                if successor not in min_rows or min_rows[successor] > row + 1:
                    min_rows[successor] = row + 1
        """

        # assign max row ID using DFS
        for node in ordered_nodes:
            if node not in max_rows:
                max_rows[node] = 0
            row = max_rows[node]
            global_max_row = max(global_max_row, row)
            for successor in acyclic_graph.successors_iter(node):
                if successor not in max_rows or max_rows[successor] < row + 1:
                    max_rows[successor] = row + 1
                    global_max_row = max(global_max_row, row + 1)

        self._max_row = global_max_row

        """
        for node in reversed(ordered_nodes):
            row = 0
            for successor in graph.successors_iter(node):
                succ_row = max_rows.get(successor, None)
                if succ_row is not None and succ_row + 1 > row:
                    row = succ_row + 1
            max_rows[node] = row
            global_max_row = max(global_max_row, row)

        self._max_row = global_max_row

        # invert row IDs
        for node in ordered_nodes:
            max_rows[node] = self._max_row - max_rows[node]
        """

        # determine row ID for each node
        for node in ordered_nodes:
            # we want to push each node as far up as possible, unless it is the return node
            row = max_rows[node]
            self._rows[node] = row
            row_to_nodes[row].append(node)

        for row in row_to_nodes.keys():
            row_to_nodes[row] = sorted(row_to_nodes[row], key=lambda n_: n_.addr, reverse=True)

        self._row_to_nodes = row_to_nodes

    def _assign_columns(self, graph, acyclic_graph, ordered_nodes):

        global_max_col = 0

        # assign column ID bottom-up
        for row_idx in reversed(self._row_to_nodes.keys()):
            row_nodes = self._row_to_nodes[row_idx]

            next_min_col, next_max_col = 1, 2

            for i, node in enumerate(row_nodes):
                successors = acyclic_graph.successors(node)

                min_col, max_col = None, None

                for successor in successors:
                    if successor in self._cols:
                        succ_col = self._cols[successor]
                        if min_col is None or succ_col < min_col:
                            min_col = succ_col
                        if max_col is None or succ_col > max_col:
                            max_col = succ_col + 1

                if min_col is None and max_col is None:
                    min_col, max_col = next_min_col, next_max_col
                else:
                    if min_col < next_min_col:
                        min_col = next_min_col
                    if max_col < next_min_col:
                        max_col = next_min_col + 1

                # now assign a column ID to the current node
                col = (min_col + max_col) / 2
                self._cols[node] = col
                self._locations[node] = (col, row_idx)
                global_max_col = max(global_max_col, col)

                # update min_col and max_col for the next iteration
                next_min_col = max_col + 1
                next_max_col = next_min_col + 1

        self._max_col = global_max_col + 1

    def _make_grids(self):
        """
        Determine the width of each column and the height of each row.

        :return: None
        """

        self._row_heights = [ None ] * (self._max_row + 2)
        self._col_widths = [ None ] * (self._max_col + 2)

        for node in self.graph.nodes_iter():
            col, row = self._locations[node]

            width, height = self._node_sizes[node]

            if self._row_heights[row] < height:
                self._row_heights[row] = height

            if self._col_widths[col] < width / 2:
                self._col_widths[col] = width / 2
            if col + 1 < len(self._col_widths) and self._col_widths[col + 1] < width / 2:
                self._col_widths[col + 1] = width / 2

        # the left-most and the right-most column do not have any node assigned to it
        self._col_widths[0] = 20
        self._col_widths[-1] = 20

    def _set_max_grid_edge_id(self):
        """
        For each grid, calculate the maximum edge index for both horizontal edges and vertical edges.

        :return: None
        """

        # horizontal edges
        for col, row_edges in enumerate(self._horizontal_edges):
            for row, edges in enumerate(row_edges):
                if not edges:
                    continue
                key = (col, row)
                self._grid_max_horizontal_id[key] = max(edges.iterkeys())

        # vertical edges
        for col, row_edges in enumerate(self._vertical_edges):
            for row, edges in enumerate(row_edges):
                if not edges:
                    continue
                key = (col, row)
                self._grid_max_vertical_id[key] = max(edges.iterkeys())

    def _calculate_coordinates(self):
        """
        Calculate coordinates for each grid, and then calculate coordinates for each node.

        :return: None
        """

        ROW_MARGIN = 16
        COL_MARGIN = 16
        HORIZONTAL_EDGE_GAP = 5

        row_max_ids = { }
        for col, row in self._grid_max_horizontal_id.keys():
            if row not in row_max_ids:
                row_max_ids[row] = self._grid_max_horizontal_id[(col, row)]
            elif self._grid_max_horizontal_id[(col, row)] > row_max_ids[row]:
                row_max_ids[row] = self._grid_max_horizontal_id[(col, row)]

        y = 0
        for row in xrange(self._max_row + 2):
            x = 0
            for col in xrange(self._max_col + 2):
                self._grid_coordinates[(col, row)] = (x, y)
                x += self._col_widths[col] + COL_MARGIN
            if self._row_heights[row] is None:
                self._row_heights[row] = 0

            margin_height = ROW_MARGIN * 2
            if row in row_max_ids:
                margin_height += HORIZONTAL_EDGE_GAP * (row_max_ids[row] + 2)
            y += self._row_heights[row] + margin_height

        # nodes
        for node in self.graph.nodes_iter():
            col, row = self._locations[node]
            grid_x, grid_y = self._grid_coordinates[(col, row)]
            grid_a_width, grid_b_width = self._col_widths[col], self._col_widths[col + 1]
            grid_height = self._row_heights[row]
            node_width, node_height = self._node_sizes[node]

            self.node_coordinates[node] = (grid_x + ((grid_a_width + grid_b_width) / 2 - node_width / 2),
                                           grid_y + (grid_height / 2 - node_height / 2)
                                           )

        # edges
        for edge in self.edges:

            src_node_x, src_node_y = self.node_coordinates[edge.src]
            src_node_width, src_node_height = self._node_sizes[edge.src]

            dst_node_x, dst_node_y = self.node_coordinates[edge.dst]
            dst_node_width, dst_node_height = self._node_sizes[edge.dst]

            # dst_node_col, dst_node_row = self._locations[edge.dst]

            # start point
            start_point_x_base = src_node_x + src_node_width / 2 - 5 * ((edge.max_start_index + 1) / 2)
            start_point_x = self._indexed_x(start_point_x_base, edge.start_index, edge.max_start_index)
            start_point = (start_point_x, src_node_y + src_node_height)
            edge.add_coordinate(*start_point)

            prev_col, prev_row = self._locations[edge.src]
            prev_col += 1
            prev_row += 1
            x, y_base = start_point[0], start_point[1] + ROW_MARGIN

            if edge.points:
                next_col, next_row, next_idx = edge.points[0]
                y = self._indexed_y(y_base, next_idx, self._grid_max_horizontal_id[(next_col, next_row)])
            else:
                y = y_base

            # add a line that moves downwards from the exit
            edge.add_coordinate(x, y)
            # set previous x and y
            prev_x, prev_y = x, y

            # each point on the edge

            for point_id, (col, row, _) in enumerate(edge.points):
                if col == prev_col:
                    assert row != prev_row
                    # vertical
                    x = prev_x

                    base_y = self._grid_coordinates[(col, row - 1)][1] + self._row_heights[row - 1] + ROW_MARGIN
                    if point_id == len(edge.points) - 1:
                        y = base_y  # TODO: is this correct?
                    else:
                        next_col, next_row, next_idx = edge.points[point_id + 1]
                        y = self._indexed_y(base_y, next_idx, self._grid_max_horizontal_id[(next_col, next_row)])

                elif row == prev_row:
                    assert col != prev_col
                    # horizontal
                    if point_id == len(edge.points) - 1:
                        base_x = dst_node_x + dst_node_width / 2
                        x = self._indexed_x(base_x, edge.end_index, edge.max_end_index)
                    else:
                        next_col, next_row, next_idx = edge.points[point_id + 1]
                        base_x = self._grid_coordinates[(col, row)][0]
                        x = self._indexed_x(base_x, next_idx, self._grid_max_vertical_id[(next_col, next_row)])

                    y = prev_y

                else:
                    # the impossible branch
                    assert False

                edge.add_coordinate(x, y)

                # update prev_*
                prev_col, prev_row = col, row
                prev_x, prev_y = x, y

            # the last point, which is always at the top of the destination node
            base_x = dst_node_x + dst_node_width / 2 - 5 * ((edge.max_end_index + 1) / 2)
            x = self._indexed_x(base_x, edge.end_index, edge.max_end_index)
            if x != prev_x:
                # add an extra coordinate to move horizontally
                edge.add_coordinate(x, prev_y)
            end_point = (x, dst_node_y - 6)
            edge.add_coordinate(*end_point)

    def _indexed_x(self, base_x, idx, max_idx):

        MARGIN = 10
        return base_x + idx * MARGIN

    def _indexed_y(self, base_y, idx, max_idx):

        MARGIN = 5
        return base_y + idx * MARGIN