# -*- coding: utf-8 -*-
# __author__ = 'qingchen.zhang'

from biocluster.api.database.base import Base, report_check
import re
import json
import datetime
from bson.objectid import ObjectId
from types import StringTypes

class Circos(Base):

    def __init__(self, bind_object):
        super(Circos, self).__init__(bind_object)
        self._project_type = 'metaasv'
        self.task_id = ""
        self.name_id = dict()

    @report_check
    def add_barpie(self, params, from_otu_table, name=None, newick_id=None):
        if from_otu_table != 0 and not isinstance(from_otu_table, ObjectId):
            if isinstance(from_otu_table, StringTypes):
                from_otu_table = ObjectId(from_otu_table)
            else:
                self.bind_object.set_error("from_otu_table必须为ObjectId对象或其对应的字符串!")
        collection = self.db["asv"]
        result = collection.find_one({"_id": from_otu_table})
        if not result:
            self.bind_object.logger.error("无法根据传入的_id:{}circos在表里找到相应的记录".format(str(from_otu_table)))
            self.bind_object.set_error("在sg_otu表中找不到相应记录")
        project_sn = result['project_sn']
        self.task_id = result['task_id']
        if not name:
            name = "circos_" + datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
        insert_data = {
            "project_sn": project_sn,
            'task_id': self.task_id,
            'asv_id': str(from_otu_table),
            'name': self.bind_object.sheet.main_table_name if self.bind_object.sheet.main_table_name else name,
            "params": params,
            'status': 'end',
            'desc': 'Circos主表',
            'created_ts': datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
        }
        collection = self.db["circos"]
        inserted_id = collection.insert_one(insert_data).inserted_id
        return inserted_id

    @report_check
    def add_sg_otu_detail(self, file_path, main_id):
        self.bind_object.logger.info("开始导入circos_detail表")
        find_otu = self.db['circos'].find_one({"_id": ObjectId(main_id)})
        if find_otu:
            self.task_id = find_otu['task_id']
        else:
            self.bind_object.set_error("没有找到相关的主表信息")
        species_sort = []
        with open(file_path, 'rb') as r:
            lines = r.readlines()
            head = lines[0].strip('\r\n')
            head = re.split('\t', head)
            sample_list = head[1:]
            data_list = []
            for line in lines[1:]:
                line = line.strip().split('\t')
                sample_num = [float(x) for x in line[1:]]
                if line[0] not in species_sort:
                    species_sort.append(line[0].strip().split("; ")[-1].strip())
                otu_detail = {
                    "circos_id": ObjectId(main_id),
                    "species_name": line[0].strip().split("; ")[-1].strip()
                }
                values_dict = dict(zip(sample_list, sample_num))
                data_list.append(dict(otu_detail, **values_dict))
        try:
            collection = self.db['circos_detail']
            collection.insert_many(data_list)
        except Exception as e:
            self.bind_object.logger.error("导入circos_detail表格信息出错:{}".format(e))
        else:
            self.bind_object.logger.info("导入circos_detail表格成功")
        try:
            main_collection = self.db['circos']
            settled_params = {'software': "python-2.7"}
            settled_params_json = json.dumps(settled_params, sort_keys=True, separators=(',', ':'))
            specimen_sort = sample_list
            main_collection.update({"_id":ObjectId(main_id)},{"$set":{"settled_params":settled_params_json,
                                                                      "specimen_sort":specimen_sort,
                                                                      "main_id": ObjectId(main_id),
                                                                      "species_sort":species_sort,
                                                                      }})
        except Exception as e:
            self.bind_object.logger.error("更新circos表格信息出错:{}".format(e))
        else:
            self.bind_object.logger.info("更新circos表格成功")


##############
# Logic layer
##############

import numpy as np
import pandas as pd
import json
import glob
import os
import logging

# sort of print
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

# dealing with data computation

class PandasDataModel:

	def readDataset(self, path_confirmed, path_deaths, path_recovered):
		self.confirmed = pd.read_csv(path_confirmed, error_bad_lines=False)
		self.deaths = pd.read_csv(path_deaths, error_bad_lines=False)
		self.recovered = pd.read_csv(path_recovered, error_bad_lines=False)

	def writeDataset(self, path_confirmed, path_deaths, path_recovered):
		self.confirmed.to_csv(path_confirmed, index=False)
		self.confirmed.to_csv(path_deaths, index=False)
		self.confirmed.to_csv(path_recovered, index=False)

	def __init__(self, path_confirmed, path_deaths, path_recovered):

		logger.info("Starting up Data Model...")
		self.readDataset(path_confirmed, path_deaths, path_recovered)
		#logger.info(self.confirmed, self.deaths, self.recovered)
		self.dfconfirmed = self.confirmed.drop(['Province/State', 'Lat', 'Long'], axis=1).groupby('Country/Region').sum()
		self.dfdeaths = self.deaths.drop(['Province/State', 'Lat', 'Long'], axis=1).groupby('Country/Region').sum()
		self.dfrecovered = self.recovered.drop(['Province/State', 'Lat', 'Long'], axis=1).groupby('Country/Region').sum()
		#logger.info(list(self.dfconfirmed.columns), list(self.dfdeaths.columns), list(self.dfrecovered.columns))
		#logger.info(list(self.dfconfirmed.index), list(self.dfdeaths.index), list(self.dfrecovered.index))

		#{'row1': {'col1': 1, 'col2': 0.5}, 'row2': {'col1': 2, 'col2': 0.75} ...}
		self.confirmed_dict = self.dfconfirmed.to_dict(orient='index')
		self.death_dict = self.dfconfirmed.to_dict(orient='index')
		self.recovered_dict = self.dfconfirmed.to_dict(orient='index')

		self.confirmed_json = self.dfconfirmed.to_json()

		# My Code

		self.my_confirmed = self.dfconfirmed.pivot_table(index='Country/Region',aggfunc=sum).T
		self.my_deaths = self.dfdeaths.pivot_table(index='Country/Region',aggfunc=sum).T
		self.my_recovered = self.dfrecovered.pivot_table(index='Country/Region',aggfunc=sum).T
		self.confirmed_pt = self.my_confirmed['Portugal']
		self.deaths_pt = self.my_deaths['Portugal']
		self.recovered_pt = self.my_recovered['Portugal']
		self.confirmed_pt = pd.DataFrame(self.confirmed_pt)
		self.deaths_pt = pd.DataFrame(self.deaths_pt)
		self.recovered_pt = pd.DataFrame(self.recovered_pt)
		self.confirmed_pt = self.confirmed_pt.rename({'Portugal':'Confirmed'}, axis=1)
		self.deaths_pt = self.deaths_pt.rename({'Portugal':'Deaths'}, axis=1)
		self.recovered_pt = self.recovered_pt.rename({'Portugal':'Recovered'}, axis=1)
		self.result_pt = pd.concat([self.confirmed_pt, self.deaths_pt, self.recovered_pt], axis=1, join='outer')
		self.result_pt = self.result_pt.reset_index()
		self.result_dict = self.result_pt.to_dict(orient='index')
		self.list_pt = [ v for v in self.result_dict.values() ]

		# End My Code

		logger.info("Data Model built.")

	def getConfirmedListings(self):
    		return json.dumps(self.confirmed_dict, indent=2)

	def getDeathListings(self):
    		return json.dumps(self.death_dict, indent=2)

	def getRecoveredistings(self):
    		return json.dumps(self.recovered_dict, indent=2)

	def getCountryListining(self):
			return self.list_pt


from django.urls import  URLPattern, path
from .views import actor_home,actor_details,actor_delete,actor_create,actor_update
app_name='actor'

urlpatterns=[
   path('home/',actor_home,name='home'),
   path('details/<int:id>',actor_details,name='details'),
   path('delete/<int:id>',actor_delete,name='delete'),
   path('create/',actor_create,name='create'),
   path('update/<int:id>',actor_update,name='update'),

]


from typing import List
def missing_number_using_xor(input: List[int]) -> int:
    sequence = list(range(1, len(input)+1))
    result = 0
    for number in input:
        result ^= number
    for number in sequence:
        result ^= number
    return result

def missing_number_using_sum(input: List[int]) -> int:
    sequence = list(range(1, len(input)+1))
    sum_of_inputs = sum(input)
    sum_of_sequence = sum(sequence)
    return sum_of_sequence - sum_of_inputs


from Crypto.Util.strxor import strxor

f = open('output.txt','rb')
ciphertext = f.read()[:-1]

flag = b'hope{'

key5 = strxor(ciphertext[:5],flag)
plain = b''
i = 0
key = key5 + b'\x6e' + b'\xc1' + b'\x61'

while(i < len(ciphertext)):
    plain += strxor(ciphertext[i:i+len(key)],key)
    i += 8

print(plain)

DiceCTF 2022 @ HOPE/DESpicable you/ans.py


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Tue Oct  1 11:41:04 2019

@author: mlees
"""

import sys
sys.path.append('/Users/mlees/Documents/RESEARCH/InSAR_processing/postprocessing_scripts')
sys.path.append('/home/mlees/InSAR_processing/postprocessing_scripts')

from InSAR_postSBAS import *
import seaborn as sns

stationslist = np.loadtxt('drummer1_repo/GPS_stations_CV_and_Sierras.txt',dtype='str')

plt.ioff()
plt.figure(figsize=(18,12))
sns.set_context('talk')

gpsstations = import_GPS_stations()

import simplekml
kml = simplekml.Kml()

# Do UNAVCO stations
for stn in stationslist:
    plot_individual_gps_station_vertical(stn,update=0)
    plt.savefig('GPSPLOTS/%s.jpg' % stn)
    plt.cla()
    logical=gpsstations['# ID']==stn

    kml.newpoint(name="%s" % stn,coords=[(gpsstations['Longitude'][logical].values[0],gpsstations['Latitude'][logical].values[0])],description='' % stn )

newstationslist = ['CAHA','CAWO','LIND','CRCN','LEMA','HELB','DONO','RAPT']

newstations = pd.read_csv('South_Central_valley_stations.csv')
#%%
# Do NGL stations
for stn in newstationslist:
    plot_individual_gps_station_vertical(stn,sln_type='ngl',update=0)
    plt.savefig('GPSPLOTS/%s_ngl.jpg' % stn)
    plt.cla()
    idx = np.argwhere(np.isin(newstations['Station_Name'],stn))[0]

    kml.newpoint(name="%s_ngl" % stn,coords=[(newstations['Lon'][idx].values[0],newstations['Lat'][idx].values[0])],description='' % stn )

kml.save("CentralValleyplus_Sierra_GPS_timeseries_NEW.kml")

cmd ='/Applications/MATLAB_R2018b.app/bin/matlab -nosplash -nodesktop -nodisplay -r "GIS_kml2kmz(\'CentralValleyplus_Sierra_GPS_timeseries_NEW.kml\')"'
cmd ='matlab -nosplash -nodesktop -nodisplay -r "GIS_kml2kmz(\'CentralValleyplus_Sierra_GPS_timeseries_NEW.kml\')"'
print('Converting kml to kmz using MATLAB script..this might not work..')
print(cmd)
os.system(cmd)


from django.contrib import admin
from django.urls import path

from videos.views import (
    PopularThemesListView, RedirectToPopularThemeListView)

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', RedirectToPopularThemeListView.as_view(), name='main'),
    path('get_popular_themes/',
         PopularThemesListView.as_view(), name='popular_themes'),
]


from collections import Counter
A = [2,1,2,5,3,2]
cnt = Counter(A)
print(cnt)
# ans = int()
# for x,y in cnt.items():
#     # print(x,y)
#     if int(y)> 1:
#         print(int(x))

for j in cnt.keys():
    if cnt[j] > 1:
        print(j)


import argparse
import aioprocessing
import importlib
import json
import multiprocessing as multiproc
import re
from config import LOG_PATH
from datetime import datetime
from distutils.util import strtobool
from os.path import exists

SCENARIO_NAMES = {
    'Basic Scenario': 3,
    'Basic Authority Scenario': 3,
    'Basic Topic Scenario': 3
}

class Evaluator:
    def run(self):
        self.connector.start()
        if self.lock_mode == "lock":
            self.send_queue.put({'type': 'lock_webUI'})
            received_message = self.receive_pipe.recv()
        elif self.lock_mode == "unlock":
            self.send_queue.put({'type': 'unlock_webUI'})
            received_message = self.receive_pipe.recv()
        else:
            scenario_run_ids = []
            for scenario, raps in SCENARIO_NAMES.items():
                for i in range(0, raps):
                    print(f"Starting scenario '{scenario}' run {i+1}...")
                    run_message = {
                        'type': 'run_scenario',
                        'scenario': {'name': scenario},
                        'is_evaluator': True
                    }
                    self.send_queue.put(run_message)
                    received_message = self.receive_pipe.recv()
                    if received_message['type'] == 'scenario_run_id':
                        print(f"Got run id for '{scenario}' run {i+1}: {received_message['scenario_run_id']}")
                    else:
                        raise RuntimeError(f"Did not receive scenario run ID after starting scenario '{scenario}' "
                                           f"run {i+1}")
                    received_message = self.receive_pipe.recv()
                    if received_message['type'] == 'scenario_results':
                        receiving_time = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
                        print(f"Scenario '{scenario}' run {i+1} finished as '{received_message['scenario_run_id']}' "
                              f"at {receiving_time} executed under {received_message['supervisor_amount']} "
                              f"supervisors.\n\n")
                        if self.print_to_file:
                            json_path = LOG_PATH / 'evaluator_log.json'
                            with open(json_path, 'r+' if exists(json_path) else 'w+') as json_file:
                                try:
                                    data = json.load(json_file)
                                except json.decoder.JSONDecodeError:
                                    data = {}
                                json_file.seek(0)
                                data[received_message['scenario_run_id']] = {
                                    'datetime': receiving_time,
                                    'scenario': scenario,
                                    'run': i+1,
                                    'supervisor_amount': received_message['supervisor_amount']}
                                print(json.dumps(data, indent=4), file=json_file)
        exit_message = {
            'type': 'end_socket'
        }
        self.send_queue.put(exit_message)

    def __init__(self, director_hostname, connector, logger_str, sec_conn=False, lock_mode='', print_to_file=False):
        self.director_hostname = director_hostname
        self.logger_str = logger_str
        self.lock_mode = lock_mode
        self.print_to_file = print_to_file
        # setup multiprocessing environment
        self.send_queue = aioprocessing.AioQueue()
        self.manager = multiproc.Manager()
        self.pipe_dict = self.manager.dict()
        self.receive_pipe, self.pipe_dict["evaluator"] = aioprocessing.AioPipe(False)
        # get correct connector to director
        module = importlib.import_module("connectors." + re.sub("([A-Z])", "_\g<1>", connector).lower()[1:])
        connector_class = getattr(module, connector)
        self.connector = connector_class(director_hostname, 0, self.send_queue, self.pipe_dict, sec_conn,
                                         no_registration=True if lock_mode else False)

if __name__ == '__main__':
    parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter)
    parser.add_argument("-c", "--connector", default="ChannelsConnector", choices=['ChannelsConnector'],
                        help="The connector class to use for connecting to director.")
    parser.add_argument("-d", "--director", default="127.0.0.1:8000",
                        help="The hostname of the director, where the evaluation script shall register at.")
    parser.add_argument("-log", "--logger", default="FileLogger", choices=['FileLogger'],
                        help="The logger class to use for logging trust values during a scenario run.")
    parser.add_argument("-wss", "--sec-socket", type=lambda x: bool(strtobool(x)), nargs='?', const=True,
                        default=False, help="Whether to use a secure websocket connection to the director.")
    parser.add_argument("-L", "--lock_mode", default="", choices=['lock', 'unlock'],
                        help="Changes the evaluator script to only lock or unlock the webUI.")
    parser.add_argument("-p", "--print", type=lambda x: bool(strtobool(x)), nargs='?', const=True,
                        default=False, help="Whether the evaluator prints log information to a file.")
    args = parser.parse_args()
    # set multiprocessing start method
    multiproc.set_start_method('spawn')
    # init supervisor as class and execute
    evaluator = Evaluator(args.director, args.connector, args.logger, args.sec_socket, args.lock_mode, args.print)
    evaluator.run()


#!/usr/bin/env python
# coding: utf-8

# In[1]:

#错误笔记：
#1.最终运行时应把前面调用函数的测试部分全部注释掉，避免测试部分使用的参数和最终运行的
# 传入的参数重复，造成错误。

# In[2]:

get_ipython().run_line_magic('pwd', '')

# In[3]:

# cd E:\\jupyterCode

# In[4]:

# cd .ipynb_checkpoints

# In[5]:

#sklearn提供了用于数据挖掘和分析的简单有效的工具
#matplotlib是Python中常用的绘制图形的库
#testCases提供了一些测试示例用以评估函数的正确性
#planar_utils提供了此作业中使用的各种函数

# In[6]:

#1---------安装包
import numpy as np
import matplotlib.pyplot as plt
from testCases import *
import sklearn
import sklearn.datasets
import sklearn.linear_model
from planar_utils import plot_decision_boundary,sigmoid,load_planar_dataset,load_extra_datasets

get_ipython().run_line_magic('matplotlib', 'inline')
np.random.seed(1)

# In[31]:

#2-------------数据集①
#以下代码会将“flower” 2分类数据集加载到变量 X 和 Y中。
X,Y=load_planar_dataset()
#红色（y=0）和蓝色（y=1），使用matplotlib可视化数据集
plt.scatter(X[0,:],X[1,:],c=Y.reshape(X[0,:].shape),s=40,cmap=plt.cm.Spectral)

# In[8]:

#2-------------数据集②
"""
noisy_circles, noisy_moons, blobs, gaussian_quantiles, no_structure = load_extra_datasets()
datasets = {"noisy_circles": noisy_circles,
            "noisy_moons": noisy_moons,
            "blobs": blobs,
            "gaussian_quantiles": gaussian_quantiles}
dataset = "noisy_moons"
X,Y=datasets[dataset]
X, Y = X.T, Y.reshape(1, Y.shape[0])
if dataset == "blobs":
    Y = Y % 2

#plt.scatter(X[0, :], X[1, :], c=Y, s=40, cmap=plt.cm.Spectral)
#上一语句如出现问题请使用下面的语句：
plt.scatter(X[0, :], X[1, :], c=np.squeeze(Y), s=40, cmap=plt.cm.Spectral)
"""

# In[9]:

#现在有的：
#包含特征（x1，x2）的numpy数组（矩阵）X
#包含标签（红色：0，蓝色：1）的numpy数组（向量）Y。

#########################
#了解数据集：
#1.数据集中有多少个训练示例？
#2.变量“X”和“Y”的shape是什么？

# In[10]:

shape_X=X.shape
shape_Y=Y.shape
m=shape_X[1]
print('The shape of X is: '+str(shape_X))
print('The shape of Y is: '+str(shape_Y))
print('I have m = %d training examples!' % (m))

# In[11]:

#3------------简单的Logistic回归
#可以使用sklearn的内置函数来执行此操作，运行以下代码在数据集上训练逻辑回归分类器
clf=sklearn.linear_model.LogisticRegressionCV();
clf.fit(X.T,Y.T)

# In[32]:

#运行下面的代码绘制此模型的决策边界：
# 错误 plot_decision_boundary(lambda x:clf.predict(x),X,Y)
#squeeze()函数的功能是：从矩阵shape中，去掉维度为1的。
plot_decision_boundary(lambda x:clf.predict(x), X, np.squeeze(Y)) #绘制决策边界
plt.title("Logistic Regression")    #图标题
LR_predictions=clf.predict(X.T)     #预测结果
print("逻辑回归的准确性： %d " % float((np.dot(Y,LR_predictions)+np.dot(1 - Y,1 - LR_predictions))/float(Y.size)*100)+"% " + "(正确标记的数据点所占的百分比)")
#print(np.dot(Y,LR_predictions))
#print(np.dot(1 - Y,1 - LR_predictions))
#print(float(Y.size))
#print(Y)
#print(LR_predictions)
#print(Y.shape)
#print(LR_predictions.shape)

# In[13]:

#准确性只有47%的原因是数据集不是线性可分的，所以逻辑回归表现不佳，现在我们正式开始构建神经网络.
#采用激活函数a=tan（z）
#建立神经网络的一般方法是：
# 1.定义神经网络结构（输入单元的数量，隐藏单元的数量）
# 2.初始化模型的参数
# 3.循环：
#     实施前向传播
#     计算损失
#     实现向后传播
#     更新参数（梯度下降）
# 我们要它们合并到一个nn_model()函数中，
#当我们构建好了nn_model()并学习了正确的参数，我们就可以预测新的数据

# In[14]:

# 1.定义神经网络结构（输入单元的数量，隐藏单元的数量）
"""
 n_x:输入层的数量
 n_h：隐藏层的数量（本实验设置为4）
 n_y：输出层的数量
"""
def layer_sizes(X,Y):
    """
    参数：
     X - 输入数据集,维度为（输入的数量，训练/测试的数量）
     Y - 标签，维度为（输出的数量，训练/测试数量）

    返回：
     n_x - 输入层的数量
     n_h - 隐藏层的数量
     n_y - 输出层的数量
    """
    n_x=X.shape[0]  #输入层
    n_h=4 #隐藏层，硬编码为4
    n_y=Y.shape[0] #输出层

    return (n_x,n_h,n_y)
"""
#测试layer_sizes
print("==========layer_sizes==========")
X_asses,Y_asses=layer_sizes_test_case()
(n_x,n_h,n_y)=layer_sizes(X_asses,Y_asses)
print("输入层的节点数量为：n_x="+str(n_x))
print("隐藏层的节点数量为：n_h="+str(n_h))
print("输出层的节点数量为：n_y="+str(n_y))
"""

# In[15]:

# 2.初始化模型的参数
"""
np.random.randn(a,b)*0.01来随机初始化一个维度为（a，b）的矩阵
理解：
W是随机生成了符合高斯分布的数，再*0.01，使得W很小，
为何要随机生成？ 因为如果像b一样全赋0值，就会产生对称性，假如hidden layer有两个由输入值产生的神经元，则由于对称性，无论是正向传播还是反向传播，这两个本应表示不同特征的神经元所做的运算都是一致的，即对称的，not well，所以随机生成了一些数赋值给矩阵W，计算b就不用担心对称性的问题，故直接赋0值
而*0.01使得W很小是因为，可以参照激活函数sigmoid和tanh，当W很大，用W * X+b=a得到的a很大，再用对a用激活函数如sigmoid(a)，由于a很大了，sigmoid(a)中的a会趋向正无穷或负无穷，则函数值sigmoid(a)趋向于一个平缓的趋势，在梯度下降的时候计算的梯度很小，会导致学习的很慢，故使得W取一个很小的值(激活函数图sigmoid,tanh在网上可以很容易找到)。不过在某些情况下不取0.01.会取其他的比较小的值

np.zeros((a,b)) 用零初始化矩阵（a，b）
"""

def initialize_parameters(n_x,n_h,n_y):
    """
    参数：
     n_x - 输入层节点的数量
     n_h - 隐藏层节点的数量
     n_y - 输出层节点的数量
    返回：
    parameters - 包含参数的字典：
    W1-权重矩阵，维度为(n_h,n_x)
    b1-偏向量，维度为(n_h,1)
    w2-权重矩阵，维度为(n_y,n_h)
    b2-偏向量，维度为(n_y,1)

    """
    np.random.seed(2)#指定一个随机种子，以便于你的输出与我们的一样。
    """
    我们在调用random.rand()时，每次产生的数都是随机的。但是，当我们预先
    使用 random.seed(x) 设定好种子之后，其中的 x 可以是任意数字，
    如10，这个时候，先调用它的情况下，使用 random() 生成的随机数将会是同一个。
    """
    W1=np.random.randn(n_h,n_x)*0.01
    b1=np.zeros(shape=(n_h,1))
    W2=np.random.randn(n_y,n_h)*0.01
    b2=np.zeros(shape=(n_y,1))

    #使用断言确保我的数据格式是正确的
    assert(W1.shape==(n_h,n_x))
    assert(b1.shape==(n_h,1))
    assert(W2.shape==(n_y,n_h))
    assert(b2.shape==(n_y,1))

    #用字典储存，这样返回字典，从字典中参数值获取value。就不用返回一大堆了。
    parameters={
        "W1":W1,
        "b1":b1,
        "W2":W2,
        "b2":b2
    }

    return parameters
"""
#测试initialize_parameters
print("==========initialize_parameters==========")
#X_asses,Y_asses=layer_sizes_test_case()
#(n_x,n_h,n_y)=layer_sizes(X_asses,Y_asses)

n_x , n_h , n_y = initialize_parameters_test_case()
parameters = initialize_parameters(n_x , n_h , n_y)
print("W1 = " + str(parameters["W1"]))
print("b1 = " + str(parameters["b1"]))
print("W2 = " + str(parameters["W2"]))
print("b2 = " + str(parameters["b2"]))
"""

# In[16]:

# 3.循环：
    # --实施前向传播。
    # 我们可以使用sigmoid()函数，也可以使用np.tanh()函数。
    # 这里我们用np.tanh()函数

def forward_propagation(X,parameters):
    """
    参数：
        X- 维度为（n_x,m）的输入数据 ,因为有m个训练集
        parameters -初始化函数(initialize_parameters)的输出
    返回：
        A2-使用sigmoid（）函数计算的第二次激活后的数值
        cache-包含"Z1","A1","Z2","A2"的字典类型数量

    """
    W1= parameters["W1"]             #注意是[],不是()
    b1= parameters["b1"]
    W2= parameters["W2"]
    b2= parameters["b2"]

    #前向传播计算A2
    Z1=np.dot(W1,X)+b1
    A1=np.tanh(Z1)
    Z2=np.dot(W2,A1)+b2
    A2=sigmoid(Z2)
    #A2=np.tanh(Z2)
    #使用断言确保我的数据格式是正确的
    assert(A2.shape==(1,X.shape[1])) #?????????为什么是X.shape[1]
    #assert(A2.shape==(n_y,1)) #不能用这个，因为n_y不是传入的参数了
    cache={
        "Z1":Z1,
        "A1":A1,
        "Z2":Z2,
        "A2":A2
    }

    return (A2,cache)
"""
#测试forward_propagation
print("==========forward_propagation==========")
X,parameters = forward_propagation_test_case()
A2,cache = forward_propagation(X,parameters)
#求其中一个用A2 =forward_propagation(X,parameters)[0]

print("Z1 = " + str(cache["Z1"]))
print("A1 = " + str(cache["A1"]))
print("Z2 = " + str(cache["Z2"]))
print("A2 = " + str(cache["A2"]))

#numpy.mean(a, axis, dtype, out，keepdims )函数。本次实验选择返回实数的。

axis 不设置值，对 m*n 个数求均值，返回一个实数
axis = 0：压缩行，对各列求均值，返回 1* n 矩阵
axis =1 ：压缩列，对各行求均值，返回 m *1 矩阵
"""
"""
print(np.mean(cache["Z1"]), np.mean(cache["A1"]), np.mean(cache["Z2"]), np.mean(cache["A2"]))
"""

# In[17]:

# 3.循环：
    # --计算损失（损失函数/代价函数）
def compute_cost(A2,Y,parameters):
        """
        参数：
        A2-使用sigmoid（）函数计算的第二次激活后的数值
        Y-“True”标签向量，维度为（1，数量）
        parameters-一个包含W1，b1，W2和b2的字典类型的变量
        返回：
        成本-交叉熵成本给定的方程

        """
        m=Y.shape[1]
        W1=parameters["W1"]
        W2=parameters["W2"]

        #计算成本
        logprobs=np.multiply(np.log(A2),Y)+np.multiply(np.log(1-A2),1-Y)
        cost=-(np.sum(logprobs))/m
        cost=float(np.squeeze(cost))
        #使用断言确保我的数据格式是正确的
        """
        isinstance(object,classinfo)
        Python中的 isinstance() 函数，是Python中的一个内置函数，用来判断一个函数是否是一个已知的类型

        """
        assert(isinstance(cost,float))

        return cost
"""
#测试compute_cost
print("==========compute_cost==========")
A2 , Y_assess , parameters = compute_cost_test_case()
cost = compute_cost(A2 , Y_assess , parameters)
print(str(cost))
"""

# In[18]:

0.6929198937761266

# In[19]:

# 3.循环：
    # --实现向后传播
    #tanh（a）是激活函数，求导后为1-a^2
    #
def backward_propagation(parameters,cache,X,Y):
        """
        使用六个方程来搭建反向传播函数。
        参数：
        parameters-包含W1，W2的字典
        cache-包含Z1，A1，Z2，A2的字典
        X-输入数据，维度为（2，数量）
        Y-“True”标签，维度为（1，数量）

        返回：
        grads-包含W和b的字典
        """
        m=X.shape[1]

        W1=parameters["W1"]
        W2=parameters["W2"]
        A1=cache["A1"]
        A2=cache["A2"]

#np.sum(A, axis = 1, keepdims = True)axis=1 以竖轴为基准 ，同行相加keepdims主要用于保持矩阵的二维特性
        dZ2=A2-Y
        dW2=(1/m)*np.dot(dZ2,A1.T)
        db2=(1/m)*(np.sum(dZ2,axis=1,keepdims=True))
        dZ1=np.dot(W2.T,dZ2)*(1-np.power(A1,2))
        dW1=(1/m)*np.dot(dZ1,X.T)
        db1=(1/m)*(np.sum(dZ1,axis=1,keepdims=True))
        grads={
            "dW2":dW2,
            "db2":db2,
            "dW1":dW1,
            "db1":db1
        }

        return grads
"""
#测试backward_propagation
print("==========backward_propagation==========")
parameters,cache,X,Y= backward_propagation_test_case()
grads = backward_propagation(parameters,cache,X,Y)
print("dW1 = " + str(grads["dW1"]))
print("db1 = " + str(grads["db1"]))
print("dW2 = " + str(grads["dW2"]))
print("db2 = " + str(grads["db2"]))
"""

# In[20]:

# 3.循环：
    # --更新参数（梯度下降）。
def update_parameters(parameters,grads,learning_rate=5.1):
    """
    使用给出的梯度下降更新规则，更新参数
        参数：
        parameters-包含W1，W2的字典
        grads-包含dW1,db1,dW2,db2的字典
        learning_rate-学习速率
        返回：
        parameters-包含更新参数的字典类型的变量。

    """
    W1,W2= parameters["W1"], parameters["W2"]
    b1,b2= parameters["b1"], parameters["b2"]

    dW1 = grads["dW1"]
    db1 = grads["db1"]
    dW2 = grads["dW2"]
    db2 = grads["db2"]
    #使用更新规则方程

    W1=W1-learning_rate*dW1
    b1=b1-learning_rate*db1
    W2=W2-learning_rate*dW2
    b2=b2-learning_rate*db2
    parameters={
        "W1":W1,
        "b1":b1,
        "W2":W2,
        "b2":b2
    }

    return parameters
"""
#测试update_parameters
print("==========backward_propagation==========")
parameters,grads= update_parameters_test_case()
parameters = update_parameters(parameters,grads)
print("W1 = " + str(parameters["W1"]))
print("b1 = " + str(parameters["b1"]))
print("W2 = " + str(parameters["W2"]))
print("b2 = " + str(parameters["b2"]))
"""

# In[21]:

# 我们要它们合并到一个nn_model()函数中，当我们构建好了nn_model()并学习了正确的参数，
#我们就可以预测新的数据。
#神经网络模型必须以正确的顺序使用先前的功能
"""
定义的函数
layer_sizes (X,Y)
initialize_parameters (n_x,n_h,n_y)
forward_propagation (X,parameters)
compute_cost(A2,Y,parameters)
backward_propagation(parameters,cache,X,Y)
update_parameters(parameters,grads,learning_rate)
"""
def nn_model(X,Y,n_h,num_iterations = 10000,print_cost=False):
    """
    参数：
    X-数据集，维度为（2，示例数）
    Y-标签，维度为（1，示例数）
    n_h-隐藏层的数量
    num_iterations-梯度下降循环中的迭代次数
    print_cost-如果为True，则每1000次迭代打印一次成本数值(代价函数)
    返回：
    parameters-模型学习的参数，它们可以用来进行预测。
    """

    np.random.seed(3)
    n_x=layer_sizes(X,Y)[0]
    n_y=layer_sizes(X,Y)[2]

    parameters=initialize_parameters(n_x,n_h,n_y)
    W1 = parameters["W1"]
    b1 = parameters["b1"]
    W2 = parameters["W2"]
    b2 = parameters["b2"]

    for i in range(0,num_iterations):
        A2,cache=forward_propagation(X,parameters)
        cost=compute_cost(A2,Y,parameters)
        grads=backward_propagation(parameters,cache,X,Y)
        parameters=update_parameters(parameters,grads)

        if print_cost and i%1000==0:
                print("第",i,"次循环，成本为: "+str(cost))

    return parameters
"""
#测试nn_model
print("==========nn_model==========")
X,Y= nn_model_test_case()
parameters = nn_model(X,Y,4,num_iterations=10000,print_cost=False)
print("W1 = " + str(parameters["W1"]))
print("b1 = " + str(parameters["b1"]))
print("W2 = " + str(parameters["W2"]))
print("b2 = " + str(parameters["b2"]))
"""

# In[33]:

#预测predict()来使用模型进行预测，使用前向传播来预测结果
def predict(parameters,X):
    """
    使用学习的参数，为X中的每个示例预测一个类

    参数：
    parameters-包含参数的字典
    X-输入数据(n_x,m) n_x个特征，m个样本即示例

    返回：
    predictions-我们模型预测的向量(红色：0/蓝色：1)

    """
    A2,cache=forward_propagation(X,parameters)
    #np.round()函数的做用：对给定的数组进行四舍五入。
    #但是需要特别注意的是,当整数部分以0结束时,round函数一律是向下取整。
    predictions=np.round(A2)

    return predictions
"""
#测试perdict
print("==========perdict==========")
parameters,X= predict_test_case()
predictions = predict(parameters,X)
# X以m * n矩阵举例
# np.mean(X) 矩阵中所有元素求均值
# np.mean(X,0) 压缩行，对各列求均值,返回 1* n 矩阵
# np.mean(X,1) 压缩列，对各行求均值,返回 m *1 矩阵
print("预测的平均值= "+str(np.mean(predictions)))
"""

# In[34]:

parameters = nn_model(X, Y, n_h = 4, num_iterations=10000, print_cost=True)
plot_decision_boundary(lambda x: predict(parameters, x.T), X, np.squeeze(Y))
plt.title("Decision Boundary for hidden layer size " + str(4))    #图标题
plt.show()
predictions = predict(parameters, X)     #预测结果
print ('准确率: %d' % float((np.dot(Y, predictions.T) + np.dot(1 - Y, 1 - predictions.T)) / float(Y.size) * 100) + '%')

# In[35]:

#更改隐藏层节点数量
#我们上面的实验把隐藏层定为4个节点，现在我们更改隐藏层里面的节点数量，看一看节点数量是否会对结果造成影响
plt.figure(figsize=(16,32))
hidden_layer_sizes=[1,2,3,4,5,20,50] #隐藏层的数量
#enumerate() 函数用于将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列
for i,n_h in enumerate(hidden_layer_sizes):
    #位置是由三个整型数值构成,第一个代表行数,第二个代表列数,第三个代表索引位置
    #这个函数用来表示把figure分成nrows*ncols的子图表示， 索引值，表示把图画在第plot_number个位置（从左下角到右上角）
    plt.subplot(5,2,i+1)
    plt.title("Hidden Layer of size %d" % n_h)
    parameters=nn_model(X,Y,n_h,num_iterations=5000)
    plot_decision_boundary(lambda x:predict(parameters,x.T),X,np.squeeze(Y))
    predictions=predict(parameters,X)
    accuracy=float((np.dot(Y,predictions.T)+np.dot(1-Y,1-predictions.T))/float(Y.size)*100)
    print("隐藏层的节点数量：{} ，准确率{} %".format(n_h,accuracy))

# In[36]:

"""
结论：
较大的模型（具有更多隐藏单元）能够更好地适应训练集，直到最终的最大模型过度拟合数据。
最好的隐藏层大小似乎在n_h = 5附近。实际上，这里的值似乎很适合数据，而且不会引起过度拟合。
我们还将在后面学习有关正则化的知识，它允许我们使用非常大的模型（如n_h = 50），而不会出现太多过度拟合。

"""

# In[26]:

##【可选】探索
"""
1. 当改变sigmoid激活或ReLU激活的tanh激活时会发生什么？
2. 改变learning_rate的数值会发生什么
3. 如果我们改变数据集呢？
"""

# In[27]:

#1. 当改变sigmoid激活或ReLU激活的tanh激活时会发生什么？
#1.1 当改变sigmoid激活为tanh激活时：准确率降低为89%
#1.2 当改变sigmoid激活为tanh激活时：

# In[28]:

#2. 改变learning_rate的数值会发生什么（learning_rate为1.2， 90%）
#2.1 learning_rate为0.1  89%
#2.2 learning_rate增大为5.1 也为90%

# In[29]:

#3. 如果我们改变数据集呢？
#逻辑回归正确性为87%
#------------>>>>单隐藏层，隐藏节点数量为4 的决策边界提高为96%
#------------>>>>单隐藏层，隐藏节点数量为20 的决策边界提高为97%

# In[30]:

#知识点
"""
1.tanh激活函数通常比隐藏层单元的sigmoid激活函数效果更好，因为其输出的平均值更接近于零，因此它将数据集中在下一层是更好的选择
2.您正在构建一个识别黄瓜（y = 1）与西瓜（y = 0）的二元分类器。 你会推荐哪一种激活函数用于输出层？
sigmoid函数,因为sigmoid函数的输出值可以很容易地理解为概率。
3.你已经为所有隐藏的单位建立了一个使用tanh激活的网络。使用np.random.randn(…, …)*1000将权重初始化为相对较大的值。会发生什么？
tanh对于较大的值变得平坦，这导致其梯度接近于零。 这减慢了优化算法。这将导致tanh的输入也非常大，从而导致梯度接近于零。因此，优化算法将变得缓慢。

"""

# In[ ]:

# In[ ]:


from termcolor import colored
import copy

def eventPrint(operations, alg):
    print("{} edit distance actions are listed below:".format(alg))
    print("Total action count = {}\n".format(len(operations)))

    for op in operations:
        print("\t- {}".format(op))
    print()

def backtrack(alg, dataMatrix, dim1, dim2, word1, word2, result):

    ops = [] # Events which are either an insertion, a deletion, a substitution, or a replacement.
    pathMatrix = copy.deepcopy(dataMatrix) # To store the colored path, I've hold a path matrix.
    currentPoint = dataMatrix[dim1][dim2]
    cLat, cLon = dim1, dim2 # cLat and cLon are currentLatitude and currentLongitude
    while True:
        if cLat == 0 and cLon == 0: # Exit condition
            pathMatrix[cLat][cLon] = colored(dataMatrix[0][0], "cyan", attrs=["bold"])
            break
        if cLat > 0 and cLon > 0:
            diag = dataMatrix[cLat-1][cLon-1] # Diagonal value of current position
            insertion = dataMatrix[cLat][cLon-1] # Upper value of current position
            deletion = dataMatrix[cLat-1][cLon] # Left value of current position
            # If algorithm is Damerau-Levenshtein, "substitution" event can happen.
            if alg == "Damerau-Levenshtein":
                # check for primary conditions
                if dataMatrix[cLat - 2][cLon - 2] < min(diag, insertion, deletion):
                    # check for remaining conditions
                    # word1[cLat - 1] != word2[cLon - 1] and
                    # word1[cLat - 1] == word2[cLon - 2] and
                    # word1[cLat - 2] == word2[cLon - 1] is
                    # a condition for subsequent unique two
                    # characters that occurs at a reversed
                    # pattern at each word. e.g. "ab" and "ba"
                    if cLat > 2 and cLon > 2 and word1[cLat - 1] != word2[cLon - 1] and word1[cLat - 1] == word2[cLon - 2] and word1[cLat - 2] == word2[cLon - 1]:
                        ops.append("substitution between {} and {}".format(colored(word1[cLat-1], "red", attrs=["bold"]), colored(word2[cLon-1], "red", attrs=["bold"])))
                        pathMatrix[cLat][cLon] = colored(dataMatrix[cLat][cLon], "cyan", attrs=["bold"]) # setting the path as colored
                        pathMatrix[cLat-1][cLon-1] = colored(dataMatrix[cLat-1][cLon-1], "cyan", attrs=["bold"]) # setting the path as colored
                        currentPoint = dataMatrix[cLat - 2][cLon - 2] # setting new position with new latitude and longitude
                        cLat, cLon = cLat - 2, cLon - 2 # setting new latitude and longitude
                        continue
            # it checks the replacement
            # first since it brings the
            # current position more
            # closer than the other two
            # events.
            if diag < currentPoint:
                ops.append("replacement of {} with {}".format(colored(word1[cLat-1], "red", attrs=["bold"]), colored(word2[cLon-1], "red", attrs=["bold"])))
                pathMatrix[cLat][cLon] = colored(dataMatrix[cLat][cLon], "cyan", attrs=["bold"])
                currentPoint = diag # setting the new value
                cLat, cLon = cLat-1, cLon-1 # setting the new position
                continue
            # if replacement isn't valid,
            # it checks insertion and deletion
            # subsequently.
            if insertion < currentPoint:
                ops.append("insertion of {}".format(colored(word2[cLon-1], "red", attrs=["bold"])))
                pathMatrix[cLat][cLon] = colored(dataMatrix[cLat][cLon], "cyan", attrs=["bold"])
                currentPoint = insertion # setting the new value
                cLat, cLon = cLat, cLon - 1 # setting the new position
                continue
            # check for deletion operation
            if deletion < currentPoint:
                ops.append("deletion of {}".format(colored(word1[cLat-1], "red", attrs=["bold"])))
                pathMatrix[cLat][cLon] = colored(dataMatrix[cLat][cLon], "cyan", attrs=["bold"])
                currentPoint = deletion # setting the new value
                cLat, cLon = cLat - 1, cLon # setting the new position
                continue
            # if all operations are invalid,
            # it checks for equality option
            # where at this position, both words
            # have the same letter. If so, it
            # continues from the diagonal,
            # but no operations are done since
            # it is meaningless to apply cost on
            # an operation where character ¶
            # turns into character ¶.
            if diag == currentPoint:
                pathMatrix[cLat][cLon] = colored(dataMatrix[cLat][cLon], "cyan", attrs=["bold"])
                cLat, cLon = cLat - 1, cLon -1 # setting the new position
                continue
        else:
            # if the algorithm reaches out the borders
            # these control statements are designed to
            # direct the position to the final position of
            # the algorithm, which is the starting point of
            # the edit distance table, [0,0]
            if cLat == 0:
                # if latitude is 0, it means no character
                # to insert, thus only deletion is available.
                ops.append("insertion of {}".format(colored(word2[cLon-1], "red", attrs=["bold"])))
                pathMatrix[cLat][cLon] = colored(dataMatrix[cLat][cLon], "cyan", attrs=["bold"])
                cLon = cLon - 1 # setting the new longitude
            else:
                # if latitude is not 0, it means longitude is 0
                # since the code doesn't enter the exit condition block
                # and it can only be a insertion operation.
                ops.append("deletion of {}".format(colored(word1[cLat-1], "red", attrs=["bold"])))
                pathMatrix[cLat][cLon] = colored(dataMatrix[cLat][cLon], "cyan", attrs=["bold"])
                cLat = cLat - 1 # setting the new latitude

    # printing the output of the task
    print("{} edit distance between {} and {} = {}\n".format(
        colored(alg, "yellow", attrs=["bold"]),
        colored(word1, "green", attrs=["reverse"]),
        colored(word2, "green", attrs=["reverse"]),
        colored(result, "red", attrs=["bold"])
    ))

    # since backtracking stores the events in a reversed manner,
    # a reversal of the eventlist would be the corrent sequence of events.
    ops.reverse()
    # printing the events
    eventPrint(ops, alg)

    print("The {} Edit Distance Table:\n".format(colored(alg, "red", attrs=["bold"])))

    # visually enhanced edit distance table is printed here.
    maxDim = len(str(dim1 * dim2))
    for lat in range(dim1):
        for lon in range(dim2):
            # calculates the length of
            # the greatest integer in
            # the table and organizes
            # the table based on that information.
            print(pathMatrix[lat+1][lon+1], end=" "*(maxDim - len(str(dataMatrix[lat][lon]))))
        print()
    print()


from urllib.parse import urlsplit, urljoin
from datetime import timedelta
from werkzeug.routing import RequestRedirect
from werkzeug.exceptions import NotFound, MethodNotAllowed
from flask import current_app, redirect, url_for, request, g
from flask.views import MethodView
from flask_smorest import abort
from swpt_pythonlib.utils import i64_to_u64, u64_to_i64
from swpt_pythonlib.swpt_uris import (
    parse_debtor_uri,
    parse_account_uri,
    make_debtor_uri,
)
from swpt_creditors.schemas import (
    examples,
    DebtorIdentitySchema,
    AccountIdentitySchema,
    AccountSchema,
    AccountConfigSchema,
    AccountDisplaySchema,
    AccountExchangeSchema,
    AccountKnowledgeSchema,
    AccountInfoSchema,
    AccountLedgerSchema,
    ObjectReferencesPageSchema,
    AccountsPaginationParamsSchema,
    LedgerEntriesPaginationParamsSchema,
    LedgerEntriesPageSchema,
)
from swpt_creditors import procedures
from swpt_creditors import inspect_ops
from .common import context, ensure_creditor_permissions, Blueprint
from .specs import DID, CID
from . import specs

def _parse_peg_account_uri(creditor_id: int, base_url: str, uri: str) -> int:
    Error = procedures.PegDoesNotExist

    try:
        scheme, netloc, path, *rest = urlsplit(urljoin(base_url, uri))
    except ValueError:
        raise Error()

    base_url_scheme, base_url_netloc, *_ = urlsplit(request.base_url)
    if (
        any(rest)
        or (scheme and scheme != base_url_scheme)
        or (netloc and netloc != base_url_netloc)
    ):
        raise Error()

    try:
        endpoint, params = current_app.url_map.bind("localhost").match(path)
    except (NotFound, RequestRedirect, MethodNotAllowed):
        raise Error()

    if (
        endpoint != "accounts.AccountEndpoint"
        or params["creditorId"] != creditor_id
    ):
        raise Error()

    return params["debtorId"]

accounts_api = Blueprint(
    "accounts",
    __name__,
    url_prefix="/creditors",
    description="""**Create, view, update, and delete accounts. View accounts'
    ledgers.** Every account record consists of a "master" account
    object, which contains references to several account sub-objects
    (account configuration, account status, account ledger etc). Each
    of these sub-objects contain different kind of information about
    the account, and each sub-object can be updated separately.""",
)
accounts_api.before_request(ensure_creditor_permissions)

@accounts_api.route("/<i64:creditorId>/account-lookup", parameters=[CID])
class AccountLookupEndpoint(MethodView):
    @accounts_api.arguments(
        AccountIdentitySchema, example=examples.ACCOUNT_IDENTITY_EXAMPLE
    )
    @accounts_api.response(200, DebtorIdentitySchema)
    @accounts_api.doc(
        operationId="accountLookup", security=specs.SCOPE_ACCESS_MODIFY
    )
    def post(self, account_identity, creditorId):
        """Given an account identity, find the debtor's identity.

        This can be useful, for example, when the creditor wants to
        send money to some other creditor's account, but he does not
        know if he already has an account with the same debtor (that
        is: the debtor of the other creditor's account).

        """

        try:
            debtorId, _ = parse_account_uri(account_identity["uri"])
        except ValueError:
            abort(
                422,
                errors={"json": {"uri": ["The URI can not be recognized."]}},
            )

        return {"uri": make_debtor_uri(debtorId)}

@accounts_api.route("/<i64:creditorId>/debtor-lookup", parameters=[CID])
class DebtorLookupEndpoint(MethodView):
    @accounts_api.arguments(
        DebtorIdentitySchema, example=examples.DEBTOR_IDENTITY_EXAMPLE
    )
    @accounts_api.response(204)
    @accounts_api.doc(
        operationId="debtorLookup",
        security=specs.SCOPE_ACCESS_MODIFY,
        responses={
            204: specs.NO_ACCOUNT_WITH_THIS_DEBTOR,
            303: specs.ACCOUNT_EXISTS,
        },
    )
    def post(self, debtor_identity, creditorId):
        """Try to find an existing account with a given debtor.

        This is useful when the creditor wants not know if he already
        has an account with a given debtor.

        **Note:** A 422 error will be returned when the debtor's
        identity can not be recognized.

        """

        try:
            debtorId = parse_debtor_uri(debtor_identity["uri"])
        except ValueError:
            abort(
                422,
                errors={"json": {"uri": ["The URI can not be recognized."]}},
            )

        if procedures.has_account(creditorId, debtorId):
            location = url_for(
                "accounts.AccountEndpoint",
                _external=True,
                creditorId=creditorId,
                debtorId=debtorId,
            )
            return redirect(location, code=303)

@accounts_api.route("/<i64:creditorId>/accounts/", parameters=[CID])
class AccountsEndpoint(MethodView):
    @accounts_api.arguments(AccountsPaginationParamsSchema, location="query")
    @accounts_api.response(200, ObjectReferencesPageSchema(context=context))
    @accounts_api.doc(
        operationId="getAccountsPage", security=specs.SCOPE_ACCESS_READONLY
    )
    def get(self, params, creditorId):
        """Return a collection of accounts belonging to a given creditor.

        The returned object will be a fragment (a page) of a paginated
        list. The paginated list contains references to all `Account`s
        belonging to a given creditor. The returned fragment will not
        be sorted in any particular order.

        """

        try:
            prev = (
                u64_to_i64(int(params["prev"])) if "prev" in params else None
            )
        except ValueError:
            abort(422, errors={"query": {"prev": ["Invalid value."]}})

        n = current_app.config["APP_ACCOUNTS_PER_PAGE"]
        debtor_ids = procedures.get_account_debtor_ids(
            creditorId, count=n, prev=prev
        )
        items = [
            {"uri": f"{i64_to_u64(debtor_id)}/"} for debtor_id in debtor_ids
        ]

        if len(debtor_ids) < n:
            # The last page does not have a 'next' link.
            return {
                "uri": request.full_path,
                "items": items,
            }

        return {
            "uri": request.full_path,
            "items": items,
            "next": f"?prev={i64_to_u64(debtor_ids[-1])}",
        }

    @accounts_api.arguments(
        DebtorIdentitySchema, example=examples.DEBTOR_IDENTITY_EXAMPLE
    )
    @accounts_api.response(
        201, AccountSchema(context=context), headers=specs.LOCATION_HEADER
    )
    @accounts_api.doc(
        operationId="createAccount",
        security=specs.SCOPE_ACCESS_MODIFY,
        responses={303: specs.ACCOUNT_EXISTS, 403: specs.FORBIDDEN_OPERATION},
    )
    def post(self, debtor_identity, creditorId):
        """Create account.

        **Note:** This is an idempotent operation.

        """

        try:
            debtorId = parse_debtor_uri(debtor_identity["uri"])
        except ValueError:
            abort(
                422,
                errors={"json": {"uri": ["The URI can not be recognized."]}},
            )

        location = url_for(
            "accounts.AccountEndpoint",
            _external=True,
            creditorId=creditorId,
            debtorId=debtorId,
        )
        try:
            inspect_ops.allow_account_creation(creditorId, debtorId)
            account = procedures.create_new_account(creditorId, debtorId)
        except inspect_ops.ForbiddenOperation:  # pragma: no cover
            abort(403)
        except procedures.CreditorDoesNotExist:
            abort(404)
        except procedures.AccountExists:
            return redirect(location, code=303)

        inspect_ops.register_account_creation(creditorId, debtorId)
        return account, {"Location": location}

@accounts_api.route(
    "/<i64:creditorId>/accounts/<i64:debtorId>/", parameters=[CID, DID]
)
class AccountEndpoint(MethodView):
    @accounts_api.response(200, AccountSchema(context=context))
    @accounts_api.doc(
        operationId="getAccount",
        security=specs.SCOPE_ACCESS_READONLY,
    )
    def get(self, creditorId, debtorId):
        """Return account.

        The returned `Account` object encompasses all the avilable
        information for a particular account. This includes the
        follwing sub-objects:

        * `AccountConfig`
        * `AccountLedger`
        * `AccountInfo`
        * `AccountDisplay`
        * `AccountExchange`
        * `AccountKnowledge`

        Note that when one of those sub-objects gets changed, a
        `LogEntry` for the change in the particular sub-object will be
        added to the log, but a `LogEntry` for the change in the
        encompassing `Account` object *will not be added to the log*.

        """

        return procedures.get_account(creditorId, debtorId) or abort(404)

    @accounts_api.response(204)
    @accounts_api.doc(
        operationId="deleteAccount",
        security=specs.SCOPE_ACCESS_MODIFY,
        responses={403: specs.FORBIDDEN_ACCOUNT_DELETION},
    )
    def delete(self, creditorId, debtorId):
        """Delete account.

        This operation will succeed only if all of the following
        conditions are true:

        1. There are no other accounts pegged to this account.

        2. The account is marked as safe for deletion, or unsafe
           deletion is allowed for the account, or a `debtorName` has
           not been set for the account.

        """

        deleted = False
        inspect_ops.decrement_account_number(creditorId, debtorId)
        try:
            procedures.delete_account(creditorId, debtorId)
            deleted = True
        except procedures.UnsafeAccountDeletion:
            abort(403)
        except procedures.ForbiddenPegDeletion:
            abort(403)
        except (
            procedures.CreditorDoesNotExist,
            procedures.AccountDoesNotExist,
        ):
            return
        finally:
            # NOTE: We decremented the account number before trying to
            # delete the account. Now if the deletion has been
            # unsuccessful, we should increment the account number
            # again. This guarantees that in case of a crash, the
            # difference between the recorded number of accounts and
            # the real number of accounts will always be in users'
            # favor.
            deleted or inspect_ops.increment_account_number(
                creditorId, debtorId
            )

@accounts_api.route(
    "/<i64:creditorId>/accounts/<i64:debtorId>/config", parameters=[CID, DID]
)
class AccountConfigEndpoint(MethodView):
    @accounts_api.response(200, AccountConfigSchema(context=context))
    @accounts_api.doc(
        operationId="getAccountConfig", security=specs.SCOPE_ACCESS_READONLY
    )
    def get(self, creditorId, debtorId):
        """Return account's configuration."""

        return procedures.get_account_config(creditorId, debtorId) or abort(
            404
        )

    @accounts_api.arguments(AccountConfigSchema)
    @accounts_api.response(200, AccountConfigSchema(context=context))
    @accounts_api.doc(
        operationId="updateAccountConfig",
        security=specs.SCOPE_ACCESS_MODIFY,
        responses={403: specs.FORBIDDEN_OPERATION, 409: specs.UPDATE_CONFLICT},
    )
    def patch(self, account_config, creditorId, debtorId):
        """Update account's configuration.

        **Note:** This is a potentially dangerous operation which may
        require a PIN. Also, normally this is an idempotent operation,
        but when an incorrect PIN is supplied, repeating the operation
        may result in the creditor's PIN being blocked.

        """

        try:
            inspect_ops.allow_account_reconfig(creditorId, debtorId)
            if not g.pin_reset_mode:
                procedures.verify_pin_value(
                    creditor_id=creditorId,
                    secret=current_app.config["PIN_PROTECTION_SECRET"],
                    pin_value=account_config.get("optional_pin"),
                    pin_failures_reset_interval=timedelta(
                        days=current_app.config["APP_PIN_FAILURES_RESET_DAYS"]
                    ),
                )
            config = procedures.update_account_config(
                creditor_id=creditorId,
                debtor_id=debtorId,
                is_scheduled_for_deletion=account_config[
                    "is_scheduled_for_deletion"
                ],
                negligible_amount=account_config["negligible_amount"],
                allow_unsafe_deletion=account_config["allow_unsafe_deletion"],
                config_data=account_config["config_data"],
                latest_update_id=account_config["latest_update_id"],
            )
        except (inspect_ops.ForbiddenOperation, procedures.WrongPinValue):
            abort(403)
        except (
            procedures.CreditorDoesNotExist,
            procedures.AccountDoesNotExist,
        ):
            abort(404)
        except procedures.UpdateConflict:
            abort(
                409, errors={"json": {"latestUpdateId": ["Incorrect value."]}}
            )

        inspect_ops.register_account_reconfig(creditorId, debtorId)
        return config

@accounts_api.route(
    "/<i64:creditorId>/accounts/<i64:debtorId>/display", parameters=[CID, DID]
)
class AccountDisplayEndpoint(MethodView):
    @accounts_api.response(200, AccountDisplaySchema(context=context))
    @accounts_api.doc(
        operationId="getAccountDisplay", security=specs.SCOPE_ACCESS_READONLY
    )
    def get(self, creditorId, debtorId):
        """Return account's display settings."""

        return procedures.get_account_display(creditorId, debtorId) or abort(
            404
        )

    @accounts_api.arguments(AccountDisplaySchema)
    @accounts_api.response(200, AccountDisplaySchema(context=context))
    @accounts_api.doc(
        operationId="updateAccountDisplay",
        security=specs.SCOPE_ACCESS_MODIFY,
        responses={403: specs.FORBIDDEN_OPERATION, 409: specs.UPDATE_CONFLICT},
    )
    def patch(self, account_display, creditorId, debtorId):
        """Update account's display settings.

        A 422 error will be returned if there is another account with
        the same `debtorName`.

        **Note:** This is a potentially dangerous operation which may
        require a PIN. Also, normally this is an idempotent operation,
        but when an incorrect PIN is supplied, repeating the operation
        may result in the creditor's PIN being blocked.

        """

        try:
            if not g.pin_reset_mode:
                procedures.verify_pin_value(
                    creditor_id=creditorId,
                    secret=current_app.config["PIN_PROTECTION_SECRET"],
                    pin_value=account_display.get("optional_pin"),
                    pin_failures_reset_interval=timedelta(
                        days=current_app.config["APP_PIN_FAILURES_RESET_DAYS"]
                    ),
                )
            display = procedures.update_account_display(
                creditor_id=creditorId,
                debtor_id=debtorId,
                debtor_name=account_display.get("optional_debtor_name"),
                amount_divisor=account_display["amount_divisor"],
                decimal_places=account_display["decimal_places"],
                unit=account_display.get("optional_unit"),
                known_debtor=account_display["known_debtor"],
                latest_update_id=account_display["latest_update_id"],
            )
        except procedures.WrongPinValue:
            abort(403)
        except (
            procedures.CreditorDoesNotExist,
            procedures.AccountDoesNotExist,
        ):
            abort(404)
        except procedures.UpdateConflict:
            abort(
                409, errors={"json": {"latestUpdateId": ["Incorrect value."]}}
            )
        except procedures.DebtorNameConflict:
            abort(
                422,
                errors={
                    "json": {
                        "debtorName": [
                            "Another account with the same debtorName already"
                            " exist."
                        ]
                    }
                },
            )

        return display

@accounts_api.route(
    "/<i64:creditorId>/accounts/<i64:debtorId>/exchange", parameters=[CID, DID]
)
class AccountExchangeEndpoint(MethodView):
    @accounts_api.response(200, AccountExchangeSchema(context=context))
    @accounts_api.doc(
        operationId="getAccountExchange", security=specs.SCOPE_ACCESS_READONLY
    )
    def get(self, creditorId, debtorId):
        """Return account's exchange settings."""

        return procedures.get_account_exchange(creditorId, debtorId) or abort(
            404
        )

    @accounts_api.arguments(AccountExchangeSchema)
    @accounts_api.response(200, AccountExchangeSchema(context=context))
    @accounts_api.doc(
        operationId="updateAccountExchange",
        security=specs.SCOPE_ACCESS_MODIFY,
        responses={403: specs.FORBIDDEN_OPERATION, 409: specs.UPDATE_CONFLICT},
    )
    def patch(self, account_exchange, creditorId, debtorId):
        """Update account's exchange settings.

        **Note:** This is a potentially dangerous operation which may
        require a PIN. Also, normally this is an idempotent operation,
        but when an incorrect PIN is supplied, repeating the operation
        may result in the creditor's PIN being blocked.

        """

        optional_peg = account_exchange.get("optional_peg")
        try:
            if not g.pin_reset_mode:
                procedures.verify_pin_value(
                    creditor_id=creditorId,
                    secret=current_app.config["PIN_PROTECTION_SECRET"],
                    pin_value=account_exchange.get("optional_pin"),
                    pin_failures_reset_interval=timedelta(
                        days=current_app.config["APP_PIN_FAILURES_RESET_DAYS"]
                    ),
                )
            exchange = procedures.update_account_exchange(
                creditor_id=creditorId,
                debtor_id=debtorId,
                policy=account_exchange.get("optional_policy"),
                min_principal=account_exchange["min_principal"],
                max_principal=account_exchange["max_principal"],
                peg_exchange_rate=optional_peg
                and optional_peg["exchange_rate"],
                peg_debtor_id=optional_peg
                and _parse_peg_account_uri(
                    creditor_id=creditorId,
                    base_url=request.full_path,
                    uri=optional_peg["account"]["uri"],
                ),
                latest_update_id=account_exchange["latest_update_id"],
            )
        except procedures.WrongPinValue:
            abort(403)
        except (
            procedures.CreditorDoesNotExist,
            procedures.AccountDoesNotExist,
        ):
            abort(404)
        except procedures.UpdateConflict:
            abort(
                409, errors={"json": {"latestUpdateId": ["Incorrect value."]}}
            )
        except procedures.InvalidPolicyName:  # pragma: nocover
            abort(422, errors={"json": {"policy": ["Invalid policy name."]}})
        except procedures.PegDoesNotExist:
            abort(
                422,
                errors={
                    "json": {
                        "peg": {
                            "account": {"uri": ["Account does not exist."]}
                        }
                    }
                },
            )

        return exchange

@accounts_api.route(
    "/<i64:creditorId>/accounts/<i64:debtorId>/knowledge",
    parameters=[CID, DID],
)
class AccountKnowledgeEndpoint(MethodView):
    @accounts_api.response(200, AccountKnowledgeSchema(context=context))
    @accounts_api.doc(
        operationId="getAccountKnowledge",
        security=specs.SCOPE_ACCESS_READONLY,
        responses={409: specs.UPDATE_CONFLICT},
    )
    def get(self, creditorId, debtorId):
        """Return account's stored knowledge.

        The returned object contains previously stored knowledge about
        the account.

        """

        return procedures.get_account_knowledge(creditorId, debtorId) or abort(
            404
        )

    @accounts_api.arguments(AccountKnowledgeSchema)
    @accounts_api.response(200, AccountKnowledgeSchema(context=context))
    @accounts_api.doc(
        operationId="updateAccountKnowledge",
        security=specs.SCOPE_ACCESS_MODIFY,
        responses={409: specs.UPDATE_CONFLICT},
    )
    def patch(self, account_knowledge, creditorId, debtorId):
        """Update account's stored knowledge.

        This operation should be performed when an important knowledge
        about the account needs to be stored. In addition to the
        properties defined in the `AccountKnowledge` schema, the
        passed object may contain any other properties, which will be
        stored as well. The total length of the stored data can not
        exceed 8000 bytes (JSON, UTF-8 encoded, excluding `type` and
        `latestUpdateId` properties).

        **Example:** To be able to detect a change in the interest
        rate on the account, the client application can use this
        endpoint to store the interest rate acknowledged by the user,
        and later, compare the stored value with the current interest
        rate on the account. This way, the change in the interest rate
        will be correctly detected, even if the user uses several
        different client devices (or applications).

        """

        try:
            knowledge = procedures.update_account_knowledge(
                creditorId,
                debtorId,
                latest_update_id=account_knowledge["latest_update_id"],
                data=account_knowledge["data"],
            )
        except procedures.AccountDoesNotExist:
            abort(404)
        except procedures.UpdateConflict:
            abort(
                409, errors={"json": {"latestUpdateId": ["Incorrect value."]}}
            )

        return knowledge

@accounts_api.route(
    "/<i64:creditorId>/accounts/<i64:debtorId>/info", parameters=[CID, DID]
)
class AccountInfoEndpoint(MethodView):
    @accounts_api.response(200, AccountInfoSchema(context=context))
    @accounts_api.doc(
        operationId="getAccountInfo", security=specs.SCOPE_ACCESS_READONLY
    )
    def get(self, creditorId, debtorId):
        """Return account's status information."""

        return procedures.get_account_info(creditorId, debtorId) or abort(404)

@accounts_api.route(
    "/<i64:creditorId>/accounts/<i64:debtorId>/ledger", parameters=[CID, DID]
)
class AccountLedgerEndpoint(MethodView):
    @accounts_api.response(200, AccountLedgerSchema(context=context))
    @accounts_api.doc(
        operationId="getAccountLedger", security=specs.SCOPE_ACCESS_READONLY
    )
    def get(self, creditorId, debtorId):
        """Return account's ledger."""

        return procedures.get_account_ledger(creditorId, debtorId) or abort(
            404
        )

@accounts_api.route(
    "/<i64:creditorId>/accounts/<i64:debtorId>/entries", parameters=[CID, DID]
)
class AccountLedgerEntriesEndpoint(MethodView):
    @accounts_api.arguments(
        LedgerEntriesPaginationParamsSchema, location="query"
    )
    @accounts_api.response(
        200,
        LedgerEntriesPageSchema(context=context),
        example=examples.ACCOUNT_LEDGER_ENTRIES_EXAMPLE,
    )
    @accounts_api.doc(
        operationId="getAccountLedgerEntriesPage",
        security=specs.SCOPE_ACCESS_READONLY,
    )
    def get(self, params, creditorId, debtorId):
        """Return a collection of ledger entries for a given account.

        The returned object will be a fragment (a page) of a paginated
        list. The paginated list contains ledger entries for a given
        account. The returned fragment, and all the subsequent
        fragments, will be sorted in reverse-chronological order
        (bigger `entryId`s go first).

        """

        n = current_app.config["APP_LEDGER_ENTRIES_PER_PAGE"]
        prev = params["prev"]
        stop = params["stop"]
        try:
            ledger_entries = procedures.get_account_ledger_entries(
                creditorId, debtorId, count=n, prev=prev, stop=stop
            )
        except procedures.AccountDoesNotExist:  # pragma: no cover
            abort(404)

        if len(ledger_entries) < n:
            # The last page does not have a 'next' link.
            return {
                "uri": request.full_path,
                "items": ledger_entries,
            }

        return {
            "uri": request.full_path,
            "items": ledger_entries,
            "next": f"?prev={ledger_entries[-1].entry_id}&stop={stop}",
        }


import numpy as np
from scipy import sparse
from .robinBC1D import robinBC1D

def robinBC2D(k, m, dx, n, dy, a, b):
    """Returns a two-dimensional mimetic boundary operator that
    imposes a boundary condition of Robin's type

    Arguments:
        k (int): Order of accuracy
        m (int): Number of cells along x-axis
        dx (float): Step size along x-axis
        n (int): Number of cells along y-axis
        dy (float): Step size along y-axis
        a (float): Dirichlet Coefficient
        b (float): Neumann Coefficient

    Returns:
        :obj:`ndarray` containing discrete boundary operator of Robin's type
    """

    Bm = robinBC1D(k, m, dx, a, b)
    Bn = robinBC1D(k, n, dy, a, b)

    Im = sparse.eye(m+2, dtype=np.float, format='csr')

    In = sparse.eye(n+2, dtype=np.float, format='csr')
    In[0, 0] = 0.
    In[-1, -1] = 0.

    BC1 = sparse.kron(In, Bm, format='csr')
    BC2 = sparse.kron(Bn, Im, format='csr')

    return BC1 + BC2

if __name__ == '__main__':
    print(robinBC2D(2, 5, 1, 6, 1, 5, 10)[49, :])


#!/usr/bin/env python
# coding: utf-8

# # Continuous Control
#
# ---
#
# You are welcome to use this coding environment to train your agent for the project.  Follow the instructions below to get started!
#
# ### 1. Start the Environment
#
# Run the next code cell to install a few packages.  This line will take a few minutes to run!

# In[1]:

#get_ipython().system('pip -q install ./python')

# The environments corresponding to both versions of the environment are already saved in the Workspace and can be accessed at the file paths provided below.
#
# Please select one of the two options below for loading the environment.

# In[2]:

from unityagents import UnityEnvironment
import numpy as np

# select this option to load version 1 (with a single agent) of the environment
env = UnityEnvironment(file_name='Reacher.exe')

# select this option to load version 2 (with 20 agents) of the environment
# env = UnityEnvironment(file_name='/data/Reacher_Linux_NoVis/Reacher.x86_64')

# Environments contain **_brains_** which are responsible for deciding the actions of their associated agents. Here we check for the first brain available, and set it as the default brain we will be controlling from Python.

# In[3]:

# get the default brain
brain_name = env.brain_names[0]
brain = env.brains[brain_name]

# ### 2. Examine the State and Action Spaces
#
# Run the code cell below to print some information about the environment.

# In[4]:

# reset the environment
env_info = env.reset(train_mode=True)[brain_name]

# number of agents
num_agents = len(env_info.agents)
print('Number of agents:', num_agents)

# size of each action
action_size = brain.vector_action_space_size
print('Size of each action:', action_size)

# examine the state space
states = env_info.vector_observations
state_size = states.shape[1]
print('There are {} agents. Each observes a state with length: {}'.format(states.shape[0], state_size))
print('The state for the first agent looks like:', states[0])

# ### 3. Take Random Actions in the Environment
#
# In the next code cell, you will learn how to use the Python API to control the agent and receive feedback from the environment.
#
# Note that **in this coding environment, you will not be able to watch the agents while they are training**, and you should set `train_mode=True` to restart the environment.

# In[5]:

env_info = env.reset(train_mode=True)[brain_name]      # reset the environment
states = env_info.vector_observations                  # get the current state (for each agent)
scores = np.zeros(num_agents)                          # initialize the score (for each agent)
while True:
    actions = np.random.randn(num_agents, action_size) # select an action (for each agent)
    actions = np.clip(actions, -1, 1)                  # all actions between -1 and 1
    env_info = env.step(actions)[brain_name]           # send all actions to tne environment
    next_states = env_info.vector_observations         # get next state (for each agent)
    rewards = env_info.rewards                         # get reward (for each agent)
    dones = env_info.local_done                        # see if episode finished
    scores += env_info.rewards                         # update the score (for each agent)
    states = next_states                               # roll over states to next time step
    if np.any(dones):                                  # exit loop if episode finished
        break
print('Total score (averaged over agents) this episode: {}'.format(np.mean(scores)))

# When finished, you can close the environment.

# In[6]:

#env.close()

# ### 4. It's Your Turn!
#
# Now it's your turn to train your own agent to solve the environment!  A few **important notes**:
# - When training the environment, set `train_mode=True`, so that the line for resetting the environment looks like the following:
# ```python
# env_info = env.reset(train_mode=True)[brain_name]
# ```
# - To structure your work, you're welcome to work directly in this Jupyter notebook, or you might like to start over with a new file!  You can see the list of files in the workspace by clicking on **_Jupyter_** in the top left corner of the notebook.
# - In this coding environment, you will not be able to watch the agents while they are training.  However, **_after training the agents_**, you can download the saved model weights to watch the agents on your own machine!

# In[7]:

import numpy as np

import torch
import torch.nn as nn
import torch.nn.functional as F

def hidden_init(layer):
    fan_in = layer.weight.data.size()[0]
    lim = 1. / np.sqrt(fan_in)
    return (-lim, lim)

class Actor(nn.Module):
    """Actor (Policy) Model."""

    def __init__(self, state_size, action_size, seed, fc1_units=400, fc2_units=300, bn_mode=0):
        """Initialize parameters and build model.
        Params
        ======
            state_size (int): Dimension of each state
            action_size (int): Dimension of each action
            seed (int): Random seed
            fc1_units (int): Number of nodes in first hidden layer
            fc2_units (int): Number of nodes in second hidden layer
            bn_mode (int): Use Batch Normalization - 0=disabled, 1=BN before Activation, 2=BN after Activation (3, 4 are alt. versions of 1, 2)
        """
        super(Actor, self).__init__()
        self.seed = torch.manual_seed(seed)

        # Dense layers
        self.fc1 = nn.Linear(state_size, fc1_units)
        self.fc2 = nn.Linear(fc1_units, fc2_units)
        self.fc3 = nn.Linear(fc2_units, action_size)

        # Normalization layers
        self.bn1 = nn.BatchNorm1d(fc1_units)
        if bn_mode!=2:
            self.bn2 = nn.BatchNorm1d(fc2_units)
        if bn_mode==3:
            self.bn3 = nn.BatchNorm1d(action_size)
        self.bn_mode=bn_mode

        self.reset_parameters()

        #print("[INFO] Actor initialized with parameters : state_size={} action_size={} seed={} fc1_units={} fc2_units={} bn_mode={}".format(state_size, action_size, seed, fc1_units, fc2_units, bn_mode))

    def reset_parameters(self):
        self.fc1.weight.data.uniform_(*hidden_init(self.fc1))
        self.fc2.weight.data.uniform_(*hidden_init(self.fc2))
        self.fc3.weight.data.uniform_(-3e-3, 3e-3)

    def forward(self, state):
        """Build an actor (policy) network that maps states -> actions."""

        # Reshape the state to comply with Batch Normalization
        if state.dim() == 1:
            state = torch.unsqueeze(state,0)

        if self.bn_mode==0:
            # Batch Normalization disabled
            x = F.relu(self.fc1(state))
            x = F.relu(self.fc2(x))
            return F.tanh(self.fc3(x))
        elif self.bn_mode==1:
            # Batch Normalization before Activation
            x = self.fc1(state)
            x = self.bn1(x)
            x = F.relu(x)
            x = self.fc2(x)
            x = self.bn2(x)
            x = F.relu(x)
            x = self.fc3(x)
            return F.tanh(x)
        elif self.bn_mode==2:
            # Batch Normalization after Activation
            x = F.relu(self.fc1(state))
            x = self.bn1(x)
            x = F.relu(self.fc2(x))
            return F.tanh(self.fc3(x))
        elif self.bn_mode==3:
            # Batch Normalization before Activation (alternate version)
            x = self.fc1(state)
            x = self.bn1(x)
            x = F.relu(x)
            x = self.fc2(x)
            x = self.bn2(x)
            x = F.relu(x)
            x = self.fc3(x)
            x = self.bn3(x)
            return F.tanh(x)
        elif self.bn_mode==4:
            # Batch Normalization after Activation  (alternate version)
            x = F.relu(self.fc1(state))
            x = self.bn1(x)
            x = F.relu(self.fc2(x))
            x = self.bn2(x)
            return F.tanh(self.fc3(x))

class Critic(nn.Module):
    """Critic (Value) Model."""

    def __init__(self, state_size, action_size, seed, fcs1_units=400, fc2_units=300, bn_mode=0):
        """Initialize parameters and build model.
        Params
        ======
            state_size (int): Dimension of each state
            action_size (int): Dimension of each action
            seed (int): Random seed
            fcs1_units (int): Number of nodes in the first hidden layer
            fc2_units (int): Number of nodes in the second hidden layer
            bn_mode (int): Use Batch Norm. - 0=disabled, 1=BN before Activation, 2=BN after Activation (3, 4 are alt. versions of 1, 2)
        """
        super(Critic, self).__init__()
        self.seed = torch.manual_seed(seed)

        # Dense layers
        self.fcs1 = nn.Linear(state_size, fcs1_units)
        self.fc2 = nn.Linear(fcs1_units+action_size, fc2_units)
        self.fc3 = nn.Linear(fc2_units, 1)

        # Normalization layers
        self.bn1 = nn.BatchNorm1d(fcs1_units)
        if bn_mode>2:
            self.bn2 = nn.BatchNorm1d(fc2_units)
        self.bn_mode=bn_mode

        self.reset_parameters()

        #print("[INFO] CRITIC initialized with parameters : state_size={} action_size={} seed={} fcs1_units={} fc2_units={} bn_mode={}".format(state_size, action_size, seed, fcs1_units, fc2_units, bn_mode))

    def reset_parameters(self):
        self.fcs1.weight.data.uniform_(*hidden_init(self.fcs1))
        self.fc2.weight.data.uniform_(*hidden_init(self.fc2))
        self.fc3.weight.data.uniform_(-3e-3, 3e-3)

    def forward(self, state, action):
        """Build a critic (value) network that maps (state, action) pairs -> Q-values."""

        # Reshape the state to comply with Batch Normalization
        if state.dim() == 1:
            state = torch.unsqueeze(state,0)

        if self.bn_mode==0:
            # Batch Normalization disabled
            xs = F.relu(self.fcs1(state))
            x = torch.cat((xs, action), dim=1)
            x = F.relu(self.fc2(x))
            return self.fc3(x)
        elif self.bn_mode==1:
            # Batch Normalization before Activation
            xs = self.fcs1(state)
            xs = self.bn1(xs)
            xs = F.relu(xs)
            x = torch.cat((xs, action), dim=1)
            x = self.fc2(x)
            x = F.relu(x)
            return self.fc3(x)
        elif self.bn_mode==2:
            # Batch Normalization after Activation
            xs = F.relu(self.fcs1(state))
            xs = self.bn1(xs)
            x = torch.cat((xs, action), dim=1)
            x = F.relu(self.fc2(x))
            return self.fc3(x)
        elif self.bn_mode==3:
            # Batch Normalization before Activation (alternate version)
            xs = self.fcs1(state)
            xs = self.bn1(xs)
            xs = F.relu(xs)
            x = torch.cat((xs, action), dim=1)
            x = self.fc2(x)
            x = self.bn2(x)
            x = F.relu(x)
            return self.fc3(x)
        elif self.bn_mode==4:
            # Batch Normalization after Activation (alternate version)
            xs = F.relu(self.fcs1(state))
            xs = self.bn1(xs)
            x = torch.cat((xs, action), dim=1)
            x = F.relu(self.fc2(x))
            x = self.bn2(x)
            return self.fc3(x)

# In[8]:

import numpy as np
import random
import copy
from collections import namedtuple, deque

from model import Actor, Critic

import torch
import torch.nn.functional as F
import torch.optim as optim

device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")

class Agent():
    def __init__(self, state_size, action_size, random_seed=10,
                actor_fc1_units=400, actor_fc2_units=300,
                critic_fcs1_units=400, critic_fc2_units=300,
                buffer_size=int(1e5), batch_size=128,
                gamma=0.99, tau=1e-3 , bn_mode=0,
                lr_actor=1e-4, lr_critic=1e-4, weight_decay=0,
                add_ounoise=True, mu=0., theta=0.15, sigma=0.2):

        """Initialize an Agent object.

        Params
        ======
            state_size (int): dimension of each state
            action_size (int): dimension of each action
            random_seed (int): random seed
            actor_fc1_units (int): number of units for the layer 1 in the actor model
            actor_fc2_units (int): number of units for the layer 2 in the actor model
            critic_fcs1_units (int): number of units for the layer 1 in the critic model
            critic_fc2_units (int): number of units for the layer 2 in the critic model
            buffer_size (int) : replay buffer size
            batch_size (int) : minibatch size
            gamma (int) : discount factor
            tau (int) : for soft update of target parameter
            bn_mode (int): Use Batch Norm - 0=disabled, 1=BN before Activation, 2=BN after Activation (3, 4 are alt. versions of 1, 2)
            lr_actor (int) : learning rate of the actor
            lr_critic (int) : learning rate of the critic
            weight_decay (int) : L2 weight decay
            add_ounoise (bool) : Add Ornstein-Uhlenbeck noise
            mu (float) : Ornstein-Uhlenbeck noise parameter
            theta (float) : Ornstein-Uhlenbeck noise parameter
            sigma (float) : Ornstein-Uhlenbeck noise parameter
        """

        print("\n[INFO]ddpg constructor called with parameters: state_size={} action_size={} random_seed={} actor_fc1_units={} actor_fc2_units={} critic_fcs1_units={} critic_fc2_units={} buffer_size={} batch_size={} gamma={} tau={} bn_mode={} lr_actor={} lr_critic={} weight_decay={} add_ounoise={} mu={} theta={} sigma={} \n".format(state_size, action_size, random_seed,
                                                          actor_fc1_units, actor_fc2_units, critic_fcs1_units, critic_fc2_units,
                                                          buffer_size, batch_size, gamma, tau,
                                                          bn_mode, lr_actor, lr_critic, weight_decay,
                                                          add_ounoise, mu, theta,sigma))

        self.state_size = state_size
        self.action_size = action_size
        self.seed = random.seed(random_seed)
        self.buffer_size=buffer_size
        self.batch_size=batch_size
        self.gamma=gamma
        self.tau=tau
        self.lr_actor=lr_actor
        self.lr_critic=lr_critic
        self.weight_decay=weight_decay
        self.add_ounoise=add_ounoise

        # Actor Network (w/ Target Network)
        self.actor_local = Actor(state_size, action_size, random_seed, actor_fc1_units, actor_fc2_units, bn_mode=bn_mode).to(device)
        self.actor_target = Actor(state_size, action_size, random_seed, actor_fc1_units, actor_fc2_units, bn_mode=bn_mode).to(device)
        self.actor_optimizer = optim.Adam(self.actor_local.parameters(), lr=lr_actor)

        # Make sure the Target Network has the same weight values as the Local Network
        for target, local in zip(self.actor_target.parameters(), self.actor_local.parameters()):
            target.data.copy_(local.data)

        # Critic Network (w/ Target Network)
        self.critic_local = Critic(state_size, action_size, random_seed, critic_fcs1_units, critic_fc2_units, bn_mode=bn_mode).to(device)
        self.critic_target = Critic(state_size, action_size, random_seed, critic_fcs1_units, critic_fc2_units, bn_mode=bn_mode).to(device)
        self.critic_optimizer = optim.Adam(self.critic_local.parameters(), lr=lr_critic, weight_decay=self.weight_decay)

        # Make sure the Target Network has the same weight values as the Local Network
        for target, local in zip(self.critic_target.parameters(), self.critic_local.parameters()):
            target.data.copy_(local.data)

        # Noise process
        self.noise = OUNoise(size=action_size, seed=random_seed,
                             mu=mu, theta=theta, sigma=sigma)

        # Replay memory
        self.memory = ReplayBuffer(action_size, buffer_size, batch_size, random_seed)

    def step(self, state, action, reward, next_state, done):
        """Save experience in replay memory, and use random sample from buffer to learn."""
        # Save experience / reward
        self.memory.add(state, action, reward, next_state, done)

        # Learn, if enough samples are available in memory
        if len(self.memory) > self.batch_size:
            experiences = self.memory.sample()
            self.learn(experiences, self.gamma)

    def act(self, state):
        """Returns actions for given state as per current policy."""
        state = torch.from_numpy(state).float().to(device)
        self.actor_local.eval()
        with torch.no_grad():
            action = self.actor_local(state).cpu().data.numpy()
        self.actor_local.train()
        if self.add_ounoise:
            action += self.noise.sample()
        return np.clip(action, -1, 1)

    def reset(self):
        self.noise.reset()

    def learn(self, experiences, gamma):
        """Update policy and value parameters using given batch of experience tuples.
        Q_targets = r + γ * critic_target(next_state, actor_target(next_state))
        where:
            actor_target(state) -> action
            critic_target(state, action) -> Q-value

        Params
        ======
            experiences (Tuple[torch.Tensor]): tuple of (s, a, r, s', done) tuples
            gamma (float): discount factor
        """
        states, actions, rewards, next_states, dones = experiences

        # ---------------------------- update critic ---------------------------- #
        # Get predicted next-state actions and Q values from target models
        actions_next = self.actor_target(next_states)
        Q_targets_next = self.critic_target(next_states, actions_next)
        # Compute Q targets for current states (y_i)
        Q_targets = rewards + (gamma * Q_targets_next * (1 - dones))
        # Compute critic loss
        Q_expected = self.critic_local(states, actions)
        critic_loss = F.mse_loss(Q_expected, Q_targets)
        # Minimize the loss (using gradient clipping)
        self.critic_optimizer.zero_grad()
        critic_loss.backward()
        torch.nn.utils.clip_grad_norm(self.critic_local.parameters(), 1)
        self.critic_optimizer.step()

        # ---------------------------- update actor ---------------------------- #
        # Compute actor loss
        actions_pred = self.actor_local(states)
        actor_loss = -self.critic_local(states, actions_pred).mean()
        # Minimize the loss
        self.actor_optimizer.zero_grad()
        actor_loss.backward()
        self.actor_optimizer.step()

        # ----------------------- update target networks ----------------------- #
        self.soft_update(self.critic_local, self.critic_target, self.tau)
        self.soft_update(self.actor_local, self.actor_target, self.tau)

    def soft_update(self, local_model, target_model, tau):
        """Soft update model parameters.
        θ_target = τ*θ_local + (1 - τ)*θ_target

        Params
        ======
            local_model: PyTorch model (weights will be copied from)
            target_model: PyTorch model (weights will be copied to)
            tau (float): interpolation parameter
        """
        for target_param, local_param in zip(target_model.parameters(), local_model.parameters()):
            target_param.data.copy_(tau*local_param.data + (1.0-tau)*target_param.data)

class OUNoise:
    """Ornstein-Uhlenbeck process."""

    def __init__(self, size, seed, mu=0., theta=0.15, sigma=0.2):
        """Initialize parameters and noise process."""
        self.mu = mu * np.ones(size)
        self.theta = theta
        self.sigma = sigma
        self.seed = random.seed(seed)
        self.reset()

    def reset(self):
        """Reset the internal state (= noise) to mean (mu)."""
        self.state = copy.copy(self.mu)

    def sample(self):
        """Update internal state and return it as a noise sample."""
        x = self.state
        dx = self.theta * (self.mu - x) + self.sigma * np.array([random.random() for i in range(len(x))])
        self.state = x + dx
        return self.state

class ReplayBuffer:
    """Fixed-size buffer to store experience tuples."""

    def __init__(self, action_size, buffer_size, batch_size, seed):
        """Initialize a ReplayBuffer object.
        Params
        ======
            buffer_size (int): maximum size of buffer
            batch_size (int): size of each training batch
        """
        self.action_size = action_size
        self.memory = deque(maxlen=buffer_size)  # internal memory (deque)
        self.batch_size = batch_size
        self.experience = namedtuple("Experience", field_names=["state", "action", "reward", "next_state", "done"])
        self.seed = random.seed(seed)

    def add(self, state, action, reward, next_state, done):
        """Add a new experience to memory."""
        e = self.experience(state, action, reward, next_state, done)
        self.memory.append(e)

    def sample(self):
        """Randomly sample a batch of experiences from memory."""
        experiences = random.sample(self.memory, k=self.batch_size)

        states = torch.from_numpy(np.vstack([e.state for e in experiences if e is not None])).float().to(device)
        actions = torch.from_numpy(np.vstack([e.action for e in experiences if e is not None])).float().to(device)
        rewards = torch.from_numpy(np.vstack([e.reward for e in experiences if e is not None])).float().to(device)
        next_states = torch.from_numpy(np.vstack([e.next_state for e in experiences if e is not None])).float().to(device)
        dones = torch.from_numpy(np.vstack([e.done for e in experiences if e is not None]).astype(np.uint8)).float().to(device)

        return (states, actions, rewards, next_states, dones)

    def __len__(self):
        """Return the current size of internal memory."""
        return len(self.memory)

# In[9]:

# Reset the environment
env_info = env.reset(train_mode=True)[brain_name]

# number of agents
num_agents = len(env_info.agents)

# size of each action
ENV_ACTION_SIZE = brain.vector_action_space_size

# size of the state space
states = env_info.vector_observations
ENV_STATE_SIZE = states.shape[1]

# In[10]:

# Agent default hyperparameters
BUFFER_SIZE = int(1e5)  # replay buffer size
BATCH_SIZE = 128        # minibatch size
GAMMA = 0.99            # discount factor
TAU = 1e-3              # for soft update of target parameters
LR_ACTOR = 1e-4         # learning rate of the actor
LR_CRITIC = 1e-3        # learning rate of the critic
WEIGHT_DECAY = 0        # L2 weight decay
ACTOR_FC1_UNITS = 400   # Number of units for the layer 1 in the actor model
ACTOR_FC2_UNITS = 300   # Number of units for the layer 2 in the actor model
CRITIC_FCS1_UNITS = 400 # Number of units for the layer 1 in the critic model
CRITIC_FC2_UNITS = 300  # Number of units for the layer 2 in the critic model
BN_MODE = 0             # Use Batch Norm. - 0=disabled, 1=BN before Activation, 2=BN after Activation (3, 4 are alt. versions of 1, 2)
ADD_OU_NOISE = True     # Add Ornstein-Uhlenbeck noise
MU = 0.                 # Ornstein-Uhlenbeck noise parameter
THETA = 0.15            # Ornstein-Uhlenbeck noise parameter
SIGMA = 0.2             # Ornstein-Uhlenbeck noise parameter

def ddpg(n_episodes=5000, max_t=500,
         state_size=ENV_STATE_SIZE, action_size=ENV_ACTION_SIZE, random_seed=10,
         actor_fc1_units=ACTOR_FC1_UNITS, actor_fc2_units=ACTOR_FC2_UNITS,
         critic_fcs1_units=CRITIC_FCS1_UNITS, critic_fc2_units=CRITIC_FC2_UNITS,
         buffer_size=BUFFER_SIZE, batch_size=BATCH_SIZE, bn_mode=BN_MODE,
         gamma=GAMMA, tau=TAU, lr_actor=LR_ACTOR, lr_critic=LR_CRITIC, weight_decay=WEIGHT_DECAY,
         add_ounoise=ADD_OU_NOISE, mu=MU, theta=THETA, sigma=SIGMA):

    # Instantiate the Agent
    agent = Agent(state_size=state_size,action_size=action_size, random_seed=random_seed,
                  actor_fc1_units=actor_fc1_units, actor_fc2_units=actor_fc2_units,
                  critic_fcs1_units=critic_fcs1_units, critic_fc2_units=critic_fc2_units,
                  buffer_size=buffer_size, batch_size=batch_size, bn_mode=bn_mode,
                  gamma=gamma, tau=tau,
                  lr_actor=lr_actor, lr_critic=lr_critic, weight_decay=weight_decay,
                  add_ounoise=add_ounoise, mu=mu, theta=theta, sigma=sigma)

    scores_deque = deque(maxlen=100)
    scores = []

    print("\nStart training:")
    for i_episode in range(1, n_episodes+1):

        # Reset the env and get the state (Single Agent)
        env_info = env.reset(train_mode=True)[brain_name]
        state = env_info.vector_observations[0]

        # Reset the DDPG Agent (Reset the internal state (= noise) to mean mu)
        agent.reset()

        # Reset the score
        score = 0

        for t in range(max_t):
            action = agent.act(state)                   # select an action

            env_info=env.step(action)[brain_name]        # send action to the environment
            next_state = env_info.vector_observations[0] # get next state (Single Agent)
            reward = env_info.rewards[0]                 # get reward (Single Agent)
            done = env_info.local_done[0]                # see if episode finished (Single Agent)

            #if i_episode<2:
            #    print("Debug: steps={} reward={} done={}".format(t,reward,done))

            # Save experience in replay memory, and use random sample from buffer to learn
            agent.step(state, action, reward, next_state, done)

            state = next_state
            score += reward
            if done:
                #print("Episode {} has terminated at step {}".format(i_episode, t))
                break

        # Save scores and compute average score over last 100 episodes
        scores_deque.append(score)
        scores.append(score)
        avg_score = np.mean(scores_deque)

        print('\rEpisode {}\tAverage Score: {:.2f}\tScore: {:.2f}'.format(i_episode, avg_score, score), end="")
        if i_episode % 100 == 0:
            torch.save(agent.actor_local.state_dict(), 'checkpoint_actor.pth')
            torch.save(agent.critic_local.state_dict(), 'checkpoint_critic.pth')
            # Early stop
            if avg_score > 30:
                print('\rEnvironment solved in {} episodes with an Average Score of {:.2f}'.format(i_episode, avg_score))
                return scores
            print('\rEpisode {}\tAverage Score: {:.2f}'.format(i_episode, avg_score))
    return scores

# In[11]:

def plot_training(scores):
    # Plot the Score evolution during the training
    fig = plt.figure()
    ax = fig.add_subplot(111)
    ax.tick_params(axis='x', colors='deepskyblue')
    ax.tick_params(axis='y', colors='deepskyblue')
    plt.plot(np.arange(1, len(scores)+1), scores, color='deepskyblue')
    plt.ylabel('Score', color='deepskyblue')
    plt.xlabel('Episode #', color='deepskyblue')
    plt.show()

# In[12]:

scores = ddpg(n_episodes=1500, max_t=1000,
              actor_fc1_units=256, actor_fc2_units=128,
              critic_fcs1_units=256, critic_fc2_units=128, bn_mode=2,
              gamma=0.99, tau=1e-3, lr_actor=2e-4, lr_critic=2e-4, weight_decay=0.,
              add_ounoise=True, mu=0., theta=0.15, sigma=0.1 )

plot_training(scores)

# In[13]:

import matplotlib.pyplot as plt

# Set plotting options
#get_ipython().run_line_magic('matplotlib', 'inline')
plt.style.use('ggplot')
np.set_printoptions(precision=3, linewidth=120)

# In[14]:

plot_training(scores)


from pox.core import core
from pox.lib.revent import *

log = core.getLogger()

class RecentlyChanged (Event):
  """
  Fire when topology changed recently.

  Fire only once even if many changes happen in a short period of
  time.
  """
  pass

class SimpleTopology (EventMixin):
  """Maintain the network topology in operation."""
  _eventMixin_events = {RecentlyChanged}

  def __init__(self):
      super(SimpleTopology, self).__init__()
      self._nodes = {}
      self._timer = False
      core.listen_to_dependencies(self)

  def get_other_end (self, dpid, port_no):
    ### Get opposite node's name, port number, MAC address
    node = self.get_node_by_dpid(dpid)
    port = self._get_port_by_port_no(node, port_no)
    oe = port.get('other_end', {})
    o_id = oe.get('node_name')
    o_port_no = oe.get('port_no')
    o_mac  = oe.get('mac')

    return o_id, o_port_no, o_mac

  def non_switch_node (self, node_name):
    node = self.get_node_by_name(node_name)
    # type seems to be always -1

    # This is a bit of a hack, since VNF started by a netconf agent is
    # added as a RemoteSwitch with dpid=-1.  We should really use
    # RemoteHost instead.  But that does not exists at the moment.
    if node.get('dpid'):
      return node.get('dpid') < 0
    return True

  def get_node_ip_pairs (self):
    r = []
    for node_name, node in self._nodes.iteritems():
      if self.non_switch_node(node_name):
        for port in self._get_ports(node).itervalues():
          if not port.get('ip'):
            continue
          r.append((node_name, port['ip']))
    return r

  def get_node_by_name (self, name):
    if name not in self._nodes:
      self._nodes[name] = {}
    return self._nodes.get(name)

  def get_node_by_dpid (self, dpid):
    for node in self._nodes.itervalues():
      if node.get('dpid') == dpid:
        return node
    else:
      return None

  def _get_ports (self, node):
    if 'ports' not in node:
      node['ports'] = {}
    return node['ports']

  def _get_port_by_port_no (self, node, port_no):
    for port in self._get_ports(node).itervalues():
      if port.get('port_no') == port_no:
        return port
    else:
      return {}

  def _update_port (self, node1_name, intf1_name, node2_name, intf2_name):
    n1 = self.get_node_by_name(node1_name)
    n2 = self.get_node_by_name(node2_name)

    port1 = {}
    for port in self._get_ports(n1).itervalues():
      if port.get('id') == intf1_name:
        port1 = port
        break
    else:
      n1['ports'][intf1_name] = port1

    port2 = {}
    for port in self._get_ports(n2).itervalues():
      if port.get('id') == intf2_name:
        port2 = port
        break
    else:
      n2['ports'][intf2_name] = port2

    port1['other_end'] = port2
    port2['other_end'] = port1

  def _delete_port (self, node1_name, node2_name):
    delete = []
    node1 = self.get_node_by_name(node1_name)
    for intf_name, port in node1['ports'].iteritems():
      other_end = port.get('other_end')
      if other_end:
        if port['other_end']['node_name'] == node2_name:
          delete.append(intf_name)
    for intf_name in delete:
      del node1['ports'][intf_name]

  def _handle_NetworkManagerMininet_LinkChange (self, event):
    e = event
    log.debug('_handle_LinkChange:%s', repr(e.__dict__))
    if e.delete:
      self._delete_port(e.node1, e.node2)
    else:
      self._update_port(e.node1, e.intf1.name, e.node2, e.intf2.name)
    self._start_timer()

  def _handle_NetworkManagerMininet_NodeChange (self, event):
    log.debug('_handle_NodeChange:%s', repr(event.__dict__))

    if event.name is None:
      log.warn('NodeChange event has no name (%s)' , repr(event.__dict__))
      return
    if event.intf is None:
      log.warn('Missing info from Interface (%s)', event.name)
      return

    node_name = event.name
    if node_name not in self._nodes:
      self._nodes[node_name] = {}
    ports = self._nodes[event.name].get('ports', {})
    for intf_name, intf in event.intf.iteritems():
      port_no = intf.get('port')
      port = ports.get(intf_name, {})
      port['mac'] = intf['mac']
      port['ip'] = intf['ip']
      port['id'] = intf_name
      port['node_name'] = event.name
      port['port_no'] = port_no
      ports[intf_name] = port

    self._nodes[node_name]['ports'] = ports
    self._nodes[node_name]['dpid'] = event.dpid
    self._start_timer()

  def _start_timer (self):
    if self._timer:
      return # already ticking
    self._timer = True
    core.callDelayed(1, self._handle_timer)

  def _handle_timer (self):
    core.raiseLater(self, RecentlyChanged)
    self._timer = False

def launch ():
  core.register(SimpleTopology())


from collections import defaultdict

d = defaultdict(list)
m, n = list(map(int, input().split()))

# getting the string and index position on the dict list
for i in range(m):
    d[input()].append(str(i+1))

# getting the string position of group B in group A:
for j in range(n):
    print(" ".join(d[input()]) or -1)


"""
本文件用于测试当前的环境。
1. 查看Torch配置是否正确。
2. 查看当前的GPU配置是否符合预期。
"""

import torch
# import tensorflow

def LogPyTorchGPU():
    print(">>>>>  PyTorch GPU info:")
    if torch.cuda.is_available():
        print("    CUDA is available")
    else:
        print("    CUDA is not available")

    gpu_count = torch.cuda.device_count()
    for i in range(0, gpu_count):
        print("    GPU %d: %s" % (i, torch.cuda.get_device_name(i)))

    gpu_current = torch.cuda.current_device()
    print("    Current GPU: %s" % torch.cuda.get_device_name(gpu_current))

def LogPyTorch():
    print(">>>>>  PyTorch info:")
    print("    version: %s" % torch.__version__)

# def LogTensorflow():
#     print(">>>>>  Tensorflow info:")
#     print("    version: %s" % tensorflow.__version__)

def main():
    LogPyTorch()
    LogPyTorchGPU()

    # LogTensorflow()

main()