"""Implements the Perceptron & Adaline Learning Algorithm
Author: Rajan Subramanian
"""

from __future__ import annotations

from typing import Optional

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from marketlearn.learning.linear_models.base import NeuralBase
from numpy.typing import NDArray

class Perceptron(NeuralBase):
    """Implements the Perceptron Learning Algorithm"""

    def __init__(self, eta: float = 0.01, niter: int = 50, bias: bool = True):
        self.eta = eta
        self.niter = niter
        self.bias = bias
        self.thetas = None
        self.weights = None
        self.degree = 1

    def _fit_batch(self, X: NDArray, y: NDArray) -> Perceptron:
        """fits training data

        Parameters
        ----------
        X : np.ndarray, shape=(n_samples, p_features)
            n_samples is number of instances i.e rows
            p_features is number of features (dimension of data)
        y : np.ndarray
            response variable

        Returns
        -------
        Perception
            object with fitted parameters
        """
        # Add bias unit to design matrix
        degree, bias = self.degree, self.bias
        X = self.make_polynomial(X, degree, bias)

        # Initialize weights to 0
        self.thetas = np.zeros(X.shape[1])
        weights = {}
        index = -1
        converged = False

        while not converged:
            index += 1
            prev_weights = self.thetas.copy()
            # for each example in training set
            for xi, target in zip(X, y):
                # update weights if there are misclassifications
                if target != self.predict(xi):
                    self.thetas += target * xi

            weights[index] = self.thetas.copy()
            if (prev_weights == self.thetas).all():
                converged = True
        self.weights = pd.DataFrame.from_dict(
            weights, orient="index", columns=["bias", "weight1", "weight2"]
        )
        return self

    def _fit_online(self, X: NDArray, y: NDArray) -> Perceptron:
        R = (np.sum(np.abs(X) ** 2, axis=-1) ** (0.5)).max()
        bias = 0
        # Initialize weights to 0
        self.thetas = np.zeros(X.shape[1] + 1)
        mistakes = 0
        mistakes_from_previous_iteration = 0
        while True:
            for xi, target in zip(X, y):
                if target * (self.net_input(xi, self.thetas[1:]) + bias) <= 0:
                    self.thetas[1:] += self.eta * target * xi
                    bias += self.eta * target * R * R
                    mistakes += 1
            if mistakes_from_previous_iteration == mistakes:
                break
            mistakes_from_previous_iteration = mistakes

        self.thetas[0] = bias
        return self

    def fit(self, X: NDArray, y: NDArray, learner="batch") -> Perceptron:
        msg = "Incorrect learner type, supports one of batch or online"
        if learner == "batch":
            return self._fit_batch(X, y)
        elif learner == "online":
            return self._fit_online(X, y)
        else:
            raise LookupError(msg)

    def predict(self, X: NDArray, thetas: Optional[NDArray] = None) -> int:
        """Activation function to determine if neuron should fire or not

        Parameters
        ----------
        X : np.ndarray
            design matrix that includes the bias
        thetas : Union[np.ndarray, None], optional
            weights from fitting, by default None

        Returns
        -------
        np.ndarray
            predictions
        """
        if thetas is None:
            return 1 if self.net_input(X, self.thetas) >= 0 else -1
        return 1 if self.net_input(X, thetas) >= 0 else -1

    def plot_decision_boundary(self, inputs, targets, weights):
        for input, target in zip(inputs, targets):
            plt.plot(input[0], input[1], "ro" if (target == 1.0) else "bo")

        slope = -weights[1] / weights[2]
        intercept = -weights[0] / weights[2]
        for i in np.linspace(np.amin(inputs[:, :1]), np.amax(inputs[:, :1])):
            y = (slope * i) + intercept
            plt.plot(i, y, "ko")

class Adaline(NeuralBase):
    """Implements the Adaptive Linear Neuron by Bernard Widrow
    via Batch Gradient Descent

    Notes:
        - The cost function is given by J(w) = 1/2 ||(yi - yhat)||
    """

    def __init__(self, eta: float = 0.01, niter: int = 50, bias: bool = True):
        """Default Constructor used to initialize the Adaline model"""
        self.eta = eta
        self.niter = niter
        self.cost = []
        self.bias = bias
        self.thetas = None
        self.degree = 1

    def fit(self, X: NDArray, y: NDArray) -> Adaline:
        """fits training data via batch gradient descent

        Parameters
        ----------
        X : np.ndarray, shape=(n_samples, p_features)
            n_samples is number of instances i.e rows
            p_features is number of features (dimension of data)
        y : np.ndarray
            response variable

        Returns
        -------
        Perception
            object with fitted parameters
        """
        # add bias + weights for each neuron
        self.thetas = np.zeros(shape=1 + X.shape[1])

        # Add bias unit to design matrix
        degree = self.degree
        X = self.make_polynomial(X, degree=degree, bias=True)

        for _ in range(self.niter):
            error = y - self.activation(self.net_input(X, self.thetas))
            self.thetas += self.eta * np.transpose(X).dot(error)
            self.cost.append((error.transpose() @ error) / 2.0)
        return self

    def activation(self, X: NDArray) -> NDArray:
        """Computes the linear activation function
        given by f(w'x) = w'x

        Parameters
        ----------
        X : np.ndarray
            output from the netinput function

        Returns
        -------
        np.ndarray
            activation function
        """
        return X

    def predict(self, X: NDArray, thetas: Optional[NDArray]) -> float:
        """Computes the class label after activation

        Parameters
        ----------
        X : np.ndarray
            [description]
        thetas : Union[np.ndarray, None], optional
            [description], by default None
        """
        if thetas is None:
            if self.activation(self.net_input(X, self.thetas)) >= 0.0:
                return 1
            else:
                return -1
        else:
            return 1 if self.activation(self.net_input(X, thetas)) >= 0.0 else -1


import torch
import torchtext
import pandas as pd
from natsort import natsorted
import os, pickle,random
from glob import glob

def get_latest_model(model_path):
    model_list = glob(os.path.join(model_path, '*.pth'))
    if len(model_list) <=0:
        print('No Model Found!!!')
        return None
    model_list = natsorted(model_list)

    return model_list[-1]

class DateDataset(torchtext.data.Dataset):
    def __init__(self, data_filename, fields, strip_flag=False, **kwargs):
        # fields: [('id', ID), ('text', TEXT), ('label', LABEL)]
        examples = []
        train_data = pd.read_csv(data_filename, header=None, delimiter='_' )
        if len(fields)==2:
            inputs, targets = train_data[0].tolist(), train_data[1].tolist()

            for line in zip(inputs,targets):
                if strip_flag: line = [l.strip() for l in line]
                examples.append(torchtext.data.Example.fromlist(line, fields))
        else:
            inputs = train_data[0].tolist()
            for line in inputs:
                if strip_flag: line = line.strip()
                examples.append(torchtext.data.Example.fromlist([line], fields))

        super(DateDataset, self).__init__(examples, fields, **kwargs)

def load_data(data_filename,vocab_filename, fields,strip_flag, train_flag=False,batch_size=32,device=None):
    if device is None: device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
    train_data = DateDataset(data_filename, fields,strip_flag)

    TEXT = fields[0][1]

    # vocab --> pickle파일
    if (not (os.path.exists(vocab_filename))):
        TEXT.build_vocab(train_data, min_freq=1, max_size=100)   # build_vocab 단계를 거처야, 단어가 숫자로 mapping된다.
        # inference를 대비하여 vocab를 저장해 두어야 한다.
        with open(vocab_filename, 'wb') as f:
            pickle.dump(TEXT.vocab, f)

    else:
        with open(vocab_filename,'rb') as f:
            TEXT.vocab = pickle.load(f)

    vocab_size = len(TEXT.vocab)
    print('단어 집합의 크기 : {}'.format(vocab_size))

    print(dict(TEXT.vocab.stoi))  # 단어 dict   ----> inference를 대비해서 저장해 두어야 함.

    if not train_flag:
        return train_data, TEXT

    # vocab 생성 후, 쪼개기...
    train_data, valid_data = train_data.split(split_ratio=0.9,random_state=random.seed(100))
    print('train size: ', len(train_data), 'valid size: ', len(valid_data))

    train_loader = torchtext.data.Iterator(dataset=train_data, batch_size = batch_size,shuffle=True,device=device)
    valid_loader = torchtext.data.Iterator(dataset=valid_data, batch_size = len(valid_data),shuffle=False,device=device)

    return train_loader, valid_loader, TEXT

def load_data_test():
    #TEXT = torchtext.data.Field(sequential=True, tokenize=list,batch_first=True,include_lengths=False,lower=True)
    TEXT = torchtext.data.Field(sequential=True, tokenize=list,batch_first=False,include_lengths=False,init_token='<sos>',eos_token='<eos>',lower=True)  # src, target 모두에 sos,eos가 붇는다.
    fields = [('src',TEXT),('target',TEXT)]

    train_loader, valid_loader, TEXT = load_data(data_filename='date.txt',vocab_filename = 'vocab.pickle', fields=fields,
                                                 strip_flag= True,train_flag=True,batch_size=4)

    vocab_size = len(TEXT.vocab)

    for i, d in enumerate(train_loader):
        print(d.src.shape, d.target.shape, d.src, d.target)
        if i>2: break

if __name__ == '__main__':
    load_data_test()


from itertools import chain

from lxml.builder import ElementMaker
try:
    from django.core.urlresolvers import reverse
except ImportError:
    from django.urls import reverse
from django.shortcuts import get_object_or_404

from eoxserver.core.config import get_eoxserver_config
from eoxserver.core.util.xmltools import (
    XMLEncoder, NameSpace, NameSpaceMap
)
from eoxserver.resources.coverages import models
from eoxserver.services.opensearch.formats import get_formats
from eoxserver.services.opensearch.extensions import get_extensions
from eoxserver.services.opensearch.config import OpenSearchConfigReader

class OpenSearch11DescriptionEncoder(XMLEncoder):
    content_type = "application/opensearchdescription+xml"

    def __init__(self, search_extensions):
        ns_os = NameSpace("http://a9.com/-/spec/opensearch/1.1/", None)
        self.ns_param = ns_param = NameSpace(
            "http://a9.com/-/spec/opensearch/extensions/parameters/1.0/",
            "parameters"
        )
        ns_atom = NameSpace("http://www.w3.org/2005/Atom", "atom")
        nsmap = NameSpaceMap(ns_os, ns_param, ns_atom)
        for search_extension in search_extensions:
            nsmap.add(search_extension.namespace)
        self.OS = ElementMaker(namespace=ns_os.uri, nsmap=nsmap)
        self.PARAM = ElementMaker(namespace=ns_param.uri, nsmap=nsmap)
        self.ATOM = ElementMaker(namespace=ns_atom.uri, nsmap=nsmap)
        self.search_extensions = search_extensions

    def encode_description(self, request, collection, result_formats):
        """ Encode an OpenSearch 1.1 description document.
        """
        OS = self.OS
        description = OS("OpenSearchDescription",
            OS("ShortName",
                collection.identifier if collection is not None else ""
            ),
            OS("Description")
        )
        for method in ("GET", "POST"):
            description.extend([
                self.encode_url(
                    request, collection, result_format, method
                )
                for result_format in result_formats
            ])
        description.extend([
            OS("Contact"),
            OS("Tags", "CEOS-OS-BP-V1.1/L1"),
            OS("LongName"),
            OS("Developer"),
            OS("Attribution"),
            OS("SyndicationRight", "open"),
            OS("AdultContent"),
            OS("Language"),
            OS("InputEncoding"),
            OS("OutputEncoding")
        ])
        return description

    def encode_url(self, request, collection, result_format, method):
        """ Encode a single opensearch URL, either for a specific collection, or
            the whole service.
        """
        if collection is not None:
            search_url = reverse("opensearch:collection:search",
                kwargs={
                    "collection_id": collection.identifier,
                    "format_name": result_format.name
                }
            )
        else:
            search_url = reverse("opensearch:search",
                kwargs={
                    "format_name": result_format.name
                }
            )

        conf = OpenSearchConfigReader(get_eoxserver_config())
        search_url = request.build_absolute_uri(search_url)

        default_parameters = (
            dict(name="q", type="searchTerms", profiles=[
            ]),
            dict(name="count", type="count", min=0, max=conf.max_count),
            dict(name="startIndex", type="startIndex", min=0),
        )

        parameters = list(chain(default_parameters, *[
            [
                dict(parameter, **{"namespace": search_extension.namespace})
                for parameter in search_extension.get_schema(
                    collection,
                    models.Collection if collection is None else models.Product
                )
            ] for search_extension in self.search_extensions
        ]))

        query_template = "&".join(
            "%s={%s%s%s%s}" % (
                parameter["name"],
                parameter["namespace"].prefix
                if "namespace" in parameter else "",
                ":" if "namespace" in parameter else "",
                parameter["type"],
                "?" if parameter.get("optional", True) else ""
            )
            for parameter in parameters
        )

        url = self.OS("Url", *[
                self.encode_parameter(parameter, parameter.get("namespace"))
                for parameter in parameters
            ],
            type=result_format.mimetype,
            template="%s?%s" % (search_url, query_template)
            if method == "GET" else search_url,
            rel="results" if collection is not None else "collection", ** {
                self.ns_param("method"): method,
                self.ns_param("enctype"): "application/x-www-form-urlencoded",
                "indexOffset": "0"
            }
        )

        return url

    def encode_parameter(self, parameter, namespace):
        options = parameter.pop("options", [])
        profiles = parameter.pop("profiles", [])

        attributes = {"name": parameter["name"]}
        if namespace:
            attributes["value"] = "{%s:%s}" % (
                namespace.prefix, parameter.pop("type")
            )
        else:
            attributes["value"] = "{%s}" % parameter.pop("type")

        if 'min' in parameter:
            attributes['minInclusive'] = str(parameter['min'])

        if 'max' in parameter:
            attributes['maxInclusive'] = str(parameter['max'])

        pattern = parameter.get("pattern")
        if pattern:
            attributes["pattern"] = pattern

        return self.PARAM("Parameter", *[
            self.PARAM("Option", value=option, label=option)
            for option in options
        ] + [
            self.ATOM("link",
                rel="profile", href=profile["href"], title=profile["title"]
            )
            for profile in profiles
        ], minimum="0" if parameter.get("optional", True) else "1", maximum="1",
            **attributes
        )

class OpenSearch11DescriptionHandler(object):
    def handle(self, request, collection_id=None):
        collection = None
        if collection_id:
            collection = get_object_or_404(models.Collection,
                identifier=collection_id
            )

        encoder = OpenSearch11DescriptionEncoder([
            extension() for extension in get_extensions()
        ])
        return (
            encoder.serialize(
                encoder.encode_description(
                    request, collection, [format_() for format_ in get_formats()]
                )
            ),
            encoder.content_type
        )


from datetime import datetime

import pytz
from pydantic import BaseModel, root_validator, validator

from db import MessageType

class MessageModel(BaseModel):
    chat: int
    utctime: int
    msg_type: str
    nick: str
    text: str = ""
    outgoing: bool

    @validator("msg_type", pre=True)
    def get_message_type(cls, v):
        return MessageType(v).name

    @validator("utctime", pre=True)
    def get_timestamp(cls, v: datetime):
        dt_utc = pytz.utc.normalize(pytz.utc.localize(v))
        return int(dt_utc.timestamp() * 1000)

    @validator("chat", pre=True)
    def get_chat_id(cls, v):
        return v.id

    class Config:
        orm_mode = True

class ChatModel(BaseModel):
    id: int
    jid: str
    name: str
    is_muc: bool

    @root_validator()
    def add_type(cls, values):
        values["type"] = "muc" if values["is_muc"] else "user"
        return values

    class Config:
        orm_mode = True


import os, bs4, html, json, codecs
from bs4 import BeautifulSoup

class Rongbay:
    path = "/home/nga/Documents/20193/Project/request/Datas/rongbay.com"
    def __init__(self):
        for _,_, filenames in os.walk(self.path):
            for filename in filenames:
                print(filename)
                filename = "127.html"
                self.extract(BeautifulSoup(open(self.path+'/'+filename,'r').read(),'html.parser'), filename)
                return

    def saveFile(self, filename, info):
        json.dump(info, codecs.open('./'+filename.split('.')[0]+'.txt','w'),ensure_ascii=False)

    def extract(self, bs, filename):
        info = dict()

        info['type'] = bs.find("span", {"class":"nameScate"}).text.strip()
        info['title'] = bs.find("h1",{"class":"title font_28"}).text.strip()
        element = bs.find("p",{'class':"note info_item_popup cl_888 font_13"})
        key = None
        for item in element.find_all(text= lambda text: (not isinstance(text, bs4.element.Comment)) and (text.strip() != '') and (text.strip() != '-'), recursive=True):
            if key is None:
                info[item.strip()] = None
                key = item.strip()
            else:
                info[key] = item.strip()
                key = None
        # -----
        elements = bs.find("div",{'class':"box_infor_ct roboto_regular font_14 cl_333 seo1554192587"}).find_all("li")
        for items in elements:
            values = items.find_all(text= lambda text: (not isinstance(text, bs4.element.Comment)) and (text.strip() != ''), recursive=True)
            if len(values)==1:
                info[values[0].split(":")[0].strip()] = values[0].split(":")[1].strip()
            else:
                info[values[0].strip()] = " ".join([item.strip() for item in values[1:]])
        items = bs.find("p", {"class":"cl_666"}).find_all(text= lambda text: (not isinstance(text, bs4.element.Comment)) and (text.strip() != ''), recursive=True)
        info[items[0].strip()] = items[1].strip()
        # contact
        info['name(email)'] = bs.find("a", {"class":"name_store icon_bds"}).text.strip()
        info['phone'] = bs.find("p", {"class":"mobile_hide show_mobile roboto_regular"}).text.strip()
        element = bs.find("div", {"class":"info_text"}).find_all(text= lambda text: (not isinstance(text, bs4.element.Comment)) and (text.strip() != ''), recursive=True)
        info['description'] = "\n".join([item.strip() for item in element])
        print(info)
        return
        return self.saveFile(filename, info)

bds = Rongbay()


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

from tkinter import *
from PIL import ImageGrab,Image
import random,os
import numpy as np
import NeuralNet

class Paint(Frame):
    width = 200
    height = 200
    alphabet = ['А','Б','В','Г','Д','Е','Ё','Ж','З','И','К','Л','М','Н','О','П','Р','С','Т','У','Ф','Х','Ц','Ч','Ш','Щ','Ъ','Ь','Ы','Э','Ю','Я','0','1','2','3','4','5','6','7','8','9']
    # alphabet = ['0','1','2','3','4']
    learned = []

    def __init__(self, parent):
        Frame.__init__(self, parent)
        self.parent = parent
        self.hnodes = 400
        self.learning_rate = 0.0005
        self.nnet = NeuralNet.NeuralNet((self.width * self.height), self.hnodes,len(self.alphabet),self.learning_rate)
        # self.nnet = NeuralNet.NeuralNet(2, 2,2,self.learning_rate)

        self.setUI()
        self.brush_size = 8
        self.brush_color="black"

    def setUI(self):

        self.parent.title("Handwriting recognition")
        self.pack(side=RIGHT,fill=BOTH)
        self.canv = Canvas(self, bg="white", width=self.width, height=self.height)
        self.canv.grid(row=0, rowspan=10,columnspan=5)
        self.canv.bind("<B1-Motion>", self.draw)
        self.canv.bind("<Button-3>", self.clear)

        learn_label = Label(self, text="-" * 5 + " Add training examples " + "-" * 5)
        learn_label.grid(row=0, column=5, columnspan=5)

        learn_first = Label(self, text="1. Write a letter       ")
        learn_first.grid(row=1, column=5,columnspan=2)

        learn_second = Label(self, text="2. Select the letter   ")
        learn_second.grid(row=2, column=5,columnspan=2)
        self.var = StringVar(self)
        learn_second_input = OptionMenu(self, self.var, *self.alphabet,command=self.get_value)
        learn_second_input.grid(row=2, column=7,columnspan=3)

        learn_third_label = Label(self, text="3. Push the button  ")
        learn_third_label.grid(row=3, column=5,columnspan=2)
        learn_third_btn = Button(self, text="Add", width=8, command=lambda: self.add_btn(self))
        learn_third_btn.grid(row=3, column=7, padx=5,columnspan=3)

        separator_label = Label(self, text="-" * 34)
        separator_label.grid(row=4, column=5, columnspan=5)

        learn_btn = Button(self, text="Learn!", width=8, command=lambda: self.learn())
        learn_btn.grid(row=5, column=5, padx=5,columnspan=2)

        recognize_second_btn = Button(self, text="Recognize", width=8,command=lambda: self.recognize(self))
        recognize_second_btn.grid(row=5, column=7, padx=5,columnspan=3)

        load_btn = Button(self, text="Load", width=8, command=lambda: self.load())
        load_btn.grid(row=6, column=5, padx=5,columnspan=2)

        save_btn = Button(self, text="Save", width=8,command=lambda: self.save())
        save_btn.grid(row=6, column=7, padx=5,columnspan=3)

        learned_label = Label(self, text="-" * 32 + " Learned " + "-" * 32)
        learned_label.grid(row=11, column=0, columnspan=10)

        result_label = Label(self, text="-" * 33 + " Result " + "-" * 33)
        result_label.grid(row=20, column=0, columnspan=10)

        # col = 0
        # row = 0
        # for i in range(len(self.alphabet)):
        #     if i % 6 == 0 and i != 0:
        #         col+=1
        #         row=0
        #     Label(self, text="%s:%1.3f" % (self.alphabet[i],0)).grid(row=21+row, column=col+1,sticky=W)
        #     row+=1

    def draw(self,event):
        self.canv.create_oval(event.x - self.brush_size,event.y - self.brush_size,
                              event.x + self.brush_size,event.y + self.brush_size,
                              fill=self.brush_color, outline=self.brush_color)

    def clear(self, event):
        self.canv.delete("all")

    def add_btn(self, widget):
        x = self.winfo_rootx()
        y = self.winfo_rooty()
        x1 = x + self.width
        y1 = y + self.height
        im = ImageGrab.grab().crop((x, y, x1, y1))
        numimage = np.dot(np.array(im),[0.299, 0.587, 0.114]).ravel()
        # im.save(os.getcwd()+"\\img\\" + str(random.randint(1, 1000000000))+".JPG")
        scaled_image = (numimage / 255 * 0.98) + 0.01
        a_str = ','.join(str(x) for x in scaled_image)
        output = [0.99 if self.curval == x else 0.01 for x in self.alphabet ]
        o_str = ','.join(str(x) for x in output)

        f = open(os.getcwd()+"\\data.txt",'a')
        f.write(self.curval+','+o_str+','+a_str+'\n')
        f.close()

        if self.curval not in self.learned:
            self.learned.append(self.curval)

        r = 12
        c = 0
        for i in range(len(self.learned)):
            if i % 10 == 0 and i != 0:
                r += 1
                c = 0
            Label(self, text="%s " % self.learned[i]).grid(row=r, column=c)
            c+=1
        self.clear(None)

    def learn(self):
        # try:
            for i in range(10):
                f = open(os.getcwd()+"\\data.txt",'r')
                for line in f:
                    label = line.split(',')[0]
                    output = list(map(float,line.split(',')[1:len(self.alphabet)+1]))
                    data = list(map(float,line.split(',')[len(self.alphabet) + 1:]))
                    self.nnet.train(data,output)
                print(i)
        # except:
        #     print("Не удалось открыть файл")
        # finally:
        #     f.close()

    def recognize(self,widget):
        x = self.winfo_rootx()
        y = self.winfo_rooty()
        x1 = x + self.width
        y1 = y + self.height
        im = ImageGrab.grab().crop((x, y, x1, y1))
        numimage = np.dot(np.array(im),[0.299, 0.587, 0.114]).ravel()
        scaled_image = (numimage / 255 * 0.98) + 0.01
        result = self.nnet.work(scaled_image)

        col = 0
        row = 0
        for i in range(len(result)):
            if i % 6 == 0 and i != 0:
                col+=1
                row=0
            Label(self, text="%s:%3.2f " % (self.alphabet[i],result[i]*100)).grid(row=21+row, column=col+1,sticky=W)
            row+=1

    def save(self):
        f = open(os.getcwd()+"\\weights.txt",'w')
        # wih = '\n'.join([','.join(str(number) for number in line) for line in self.nnet.wih])
        # who = '\n'.join([','.join(str(number) for number in line) for line in self.nnet.who])
        wih =  ','.join(str(x) for x in self.nnet.wih.ravel())
        who =  ','.join(str(x) for x in self.nnet.who.ravel())
        f.write(wih+','+who)
        print(self.nnet.wih)
        print(self.nnet.who)
        print(self.nnet.who.shape)
        f.close()

    def load(self):
        # try:
            f = open(os.getcwd()+"\\weights.txt",'r')
            weights = f.readline().split(',')
            self.nnet.wih = np.array(weights[ : self.nnet.wih.shape[0] * self.nnet.wih.shape[1]]).reshape(self.nnet.wih.shape)
            self.nnet.who = np.array(weights[self.nnet.who.shape[0] * self.nnet.who.shape[1]: ]).reshape(self.nnet.who.shape)

        # except:
        #     print("Не удалось открыть файл")
        # finally:
        #     f.close()
    def get_value(self,value):
        self.curval = value

def main():
    root = Tk()
    root.geometry("390x475")
    root.resizable(width=FALSE, height=FALSE)
    app = Paint(root)
    root.mainloop()

if __name__ == "__main__":
    main()


from django.shortcuts import render,redirect
from django.views.generic import TemplateView
from django.contrib.auth.mixins import LoginRequiredMixin
from .forms import *
from .models import *
from django.core.paginator import Paginator
#####---------------------------------------------------------------------------------------------------#####
#-------------------------------------categories-ADMIN-VIEWS--------------------------------------------------#
#####---------------------------------------------------------------------------------------------------#####

class CategorieAdmin(LoginRequiredMixin,TemplateView):
    def get(self,request):
        categories_list=Categorie.objects.all()
        paginator = Paginator(categories_list, 25) # Show 25 contacts per page

        page = request.GET.get('page')
        categories = paginator.get_page(page)
        return render(request,'categories/BackOffice/categories/home_categorie.html', {'search':filter,'categories':categories,'categories_all_count':categories_list.count()})

class NewCategorieAdmin(LoginRequiredMixin,TemplateView):
    def get(self,request):
        return render(request,'categories/BackOffice/categories/add_categorie.html', {'form':CategorieForm()})

    def post(self,request):
        postt = request.POST.copy()
        postt.update({ 'user': request.user.id })
        form=CategorieForm(postt,request.FILES,request.user)
        if form.is_valid():
            form.save()
            return redirect('HomeCategorieAdmin')
        else:
            return render(request,'categories/BackOffice/categories/add_categorie.html', {'form':CategorieForm()})

class UpdateCategorieAdmin(LoginRequiredMixin,TemplateView):
    def get(self,request,id=0):
        categorie=Categorie.objects.get(id=id)
        if request.user.is_superuser :
            form=CategorieFormUpdate(instance=categorie)
            return render(request,'categories/BackOffice/categories/update_categorie.html', {'form':form})
        else:
            return redirect('profile')

    def post(self,request,id=0):
        categorie=Categorie.objects.get(id=id)
        # categorieImageOld=categorie.image.path
        if request.user.is_superuser :
            form=CategorieFormUpdate(request.POST,request.FILES,instance=categorie)
            if form.is_valid():
                # if request.FILES:
                #     try:
                #         os.remove(categorieImageOld)
                #     except:
                #         pass
                # else:
                #     print('none')
                form.save()
                return redirect('HomeCategorieAdmin')
            else:
                return render(request,'categories/update_categories.html', {'form':form})
        else:
            return redirect('HomeCategorieAdmin')

class DeleteCategorieAdmin(LoginRequiredMixin,TemplateView):
    def get(self,request):
        if request.GET.get('elements'):
            produits=Categorie.objects.filter(id__in=request.GET.get('elements').split(','))
            for produit in produits:
                if request.user.is_superuser :
                    produit.delete()

            return redirect('HomeCategorieAdmin')

                            #------END-categories-ADMIN-VIEWS-----------#


# project euler 218: Perfect right-angled triangles

"""
perfect right angled triangle:
    primitive
    hypotenuse is a perfect square

super-perfect right angled triangle:
    perfect
    area is multiple of 6, 28

!!! can be proved that no such triangle exists

https://luckytoilet.wordpress.com/2010/06/20/on-some-number-theoretic-properties-of-right-triangles-project-euler-218/

a**2 + b**2 = c**2

a = m**2 - n**2
b = 2*m*n
c = m**2 + n**2

m = p**2 - q**2
n = 2*p*q
c = (p**2 + q**2) ** 2
b = 4*p*q*(p**2 - q**2)
a = p**4 + q**4 - 6 * p**2 * q**2
"""

from fractions import gcd

N = 10 ** 4
SIZE = 10 ** 16

def main():
    count = 0
    for i in range(1, N):
        # i, j of different parity
        for j in range(i + 1, N, 2):
            if gcd(i, j) == 1:
                c = (i**2 + j**2) ** 2
                if c > SIZE:
                    break

                b = 4 * i * j * (j**2 - i**2)
                a = i**4 + j**4 - 6 * i**2 * j**2

                if (a * b) % 168 != 0:
                    print(a, b, c)
                    count += 1

    print(count)

if __name__ == '__main__':
    main()


import numpy as np
import argparse
import sys
import os
import random
import scipy
import colorsys

from itertools import islice
import matplotlib.pyplot as plt

sys.path.extend([
    os.path.join(os.getcwd(), 'pddlstream'), # Important to use absolute path when doing chdir
    os.path.join(os.getcwd(), 'ss-pybullet'),
])

from plan_tools.planner import PlanningWorld
from pybullet_tools.utils import set_pose, wait_for_user, set_camera_pose, draw_pose, get_pose, load_pybullet, \
    add_data_path, remove_body, get_bodies, set_color, apply_alpha, BLACK, RED, GREEN, spaced_colors, clip, multiply, \
    Pose, Euler, invert, unit_pose, get_image, get_camera, take_picture, save_image, wait_for_duration, read_pickle, \
    clip, create_box, create_cylinder, set_point, Point, add_line, tform_point, LockRenderer, approximate_as_prism, \
    WHITE, draw_point, GREY, randomize, INF, create_plane, safe_zip, point_from_pose, remove_handles
from plan_tools.samplers.pour import get_pour_feature, pour_path_from_parameter, is_valid_pour, get_bowl_from_pivot
from learn_tools.run_active import TRAIN_DATASETS, ACTIVE_DATASETS, TEST_DATASETS, BEST_DATASETS
from learn_tools.active_learner import BEST, STRADDLE, REJECTION, tail_confidence
from learn_tools.active_gp import get_parameters
from learn_tools.learner import FAILURE, SUCCESS, DEFAULT_INTERVAL, rescale, inclusive_range
from learn_tools.learnable_skill import read_data
#from learn_tools.collect_simulation import SKILL_COLLECTORS
from learn_tools.collectors.collect_pour import compute_fraction_filled, pour_score
from dimensions.common import load_cup_bowl

#BACKGROUND_COLOR = WHITE
BACKGROUND_COLOR = [0.9, 0.9, 1.0] # 229, 229, 255
GROUND_COLOR = 0.8*np.ones(3)

def create_world():
    world = PlanningWorld(task=None, use_robot=False, visualize=True, color=BACKGROUND_COLOR, shadows=False)
    add_data_path()
    environment = [
        #load_pybullet('models/short_floor.urdf'),
        #load_pybullet('plane.urdf', fixed_base=True),
        create_plane(color=GROUND_COLOR),
    ]
    set_camera_pose(camera_point=0.4*np.array([0, -1.0, 0.5]))
    #draw_pose(unit_pose(), length=1)
    return world

##################################################

def interpolate_hue(fraction, min_hue=0./3, max_hue=2./3):
    hue = (1 - fraction) * min_hue + fraction * max_hue
    return colorsys.hsv_to_rgb(hue, s=1, v=1)

def visualize_collected_pours(paths, num=6, save=True):
    result_from_bowl = {}
    for path in randomize(paths):
        results = read_data(path)
        print(path, len(results))
        for result in results:
            result_from_bowl.setdefault(result['feature']['bowl_type'], []).append(result)

    world = create_world()
    environment = get_bodies()
    #collector = SKILL_COLLECTORS['pour']
    print(get_camera())

    for bowl_type in sorted(result_from_bowl):
        # TODO: visualize same
        for body in get_bodies():
            if body not in environment:
                remove_body(body)
        print('Bowl type:', bowl_type)
        bowl_body = load_cup_bowl(bowl_type)
        bowl_pose = get_pose(bowl_body)

        results = result_from_bowl[bowl_type]
        results = randomize(results)

        score_cup_pose = []
        for i, result in enumerate(results):
            if num <= len(score_cup_pose):
                break
            feature = result['feature']
            parameter = result['parameter']
            score = result['score']
            if (score is None) or not result['execution'] or result['annotation']:
                continue
            cup_body = load_cup_bowl(feature['cup_type'])
            world.bodies[feature['bowl_name']] = bowl_body
            world.bodies[feature['cup_name']] = cup_body
            fraction = compute_fraction_filled(score)
            #value = collector.score_fn(feature, parameter, score)
            value = pour_score(feature, parameter, score)
            print(i, feature['cup_type'], fraction, value)
            path, _ = pour_path_from_parameter(world, feature, parameter)
            sort = fraction
            #sort = parameter['pitch']
            #sort = parameter['axis_in_bowl_z']
            score_cup_pose.append((sort, fraction, value, cup_body, path[0]))

        #order = score_cup_pose
        order = randomize(score_cup_pose)
        #order = sorted(score_cup_pose)
        angles = np.linspace(0, 2*np.pi, num=len(score_cup_pose), endpoint=False) # Halton
        for angle, (_, fraction, value, cup_body, pose) in zip(angles, order):
            #fraction = clip(fraction, min_value=0, max_value=1)
            value = clip(value, *DEFAULT_INTERVAL)
            fraction = rescale(value, DEFAULT_INTERVAL, new_interval=(0, 1))
            #color = (1 - fraction) * np.array(RED) + fraction * np.array(GREEN)
            color = interpolate_hue(fraction)
            set_color(cup_body, apply_alpha(color, alpha=0.25))
            #angle = random.uniform(-np.pi, np.pi)
            #angle = 0
            rotate_bowl = Pose(euler=Euler(yaw=angle))
            cup_pose = multiply(bowl_pose, invert(rotate_bowl), pose)
            set_pose(cup_body, cup_pose)
            #wait_for_user()
            #remove_body(cup_body)

        if save:
            #filename = os.path.join('workspace', '{}.png'.format(bowl_type))
            #save_image(filename, take_picture())  # [0, 255]
            #wait_for_duration(duration=0.5)
            # TODO: get window location
            #os.system("screencapture -R {},{},{},{} {}".format(
            #    275, 275, 500, 500, filename)) # -R<x,y,w,h> capture screen rect
            wait_for_user()
        remove_body(bowl_body)

##################################################

def add_obstacles():
    ceiling = create_box(w=0.2, l=0.2, h=0.01, color=RED)
    set_point(ceiling, Point(z=0.2))
    pole = create_cylinder(radius=0.025, height=0.1, color=RED)
    set_point(pole, Point(z=0.15))

def visualize_learned_pours(learner, bowl_type='blue_bowl', cup_type='red_cup', num_steps=None):
    learner.query_type = REJECTION # BEST | REJECTION | STRADDLE
    learner.validity_learner = None

    world = create_world()
    #add_obstacles()
    #environment = get_bodies()

    world.bodies[bowl_type] = load_cup_bowl(bowl_type)
    world.bodies[cup_type] = load_cup_bowl(cup_type)
    feature = get_pour_feature(world, bowl_type, cup_type)
    draw_pose(get_pose(world.get_body(bowl_type)), length=0.2)
    # TODO: sample different sizes

    print('Feature:', feature)
    for parameter in learner.parameter_generator(world, feature, valid=False):
        handles = []
        print('Parameter:', parameter)
        valid = is_valid_pour(world, feature, parameter)
        score = learner.score(feature, parameter)
        x = learner.func.x_from_feature_parameter(feature, parameter)
        [prediction] = learner.predict_x(np.array([x]), noise=True)
        print('Valid: {} | Mean: {:.3f} | Var: {:.3f} | Score: {:.3f}'.format(
            valid, prediction['mean'], prediction['variance'], score))

        #fraction = rescale(clip(prediction['mean'], *DEFAULT_INTERVAL), DEFAULT_INTERVAL, new_interval=(0, 1))
        #color = (1 - fraction) * np.array(RED) + fraction * np.array(GREEN)
        #set_color(world.get_body(cup_type), apply_alpha(color, alpha=0.25))
        # TODO: overlay many cups at different poses
        bowl_from_pivot = get_bowl_from_pivot(world, feature, parameter)
        #handles.extend(draw_pose(bowl_from_pivot))
        handles.extend(draw_point(point_from_pose(bowl_from_pivot), color=BLACK))

        # TODO: could label bowl, cup dimensions
        path, _ = pour_path_from_parameter(world, feature, parameter, cup_yaw=0)
        for p1, p2 in safe_zip(path[:-1], path[1:]):
            handles.append(add_line(point_from_pose(p1), point_from_pose(p2), color=RED))

        if num_steps is not None:
            path = path[:num_steps]
        for i, pose in enumerate(path[::-1]):
            #step_handles = draw_pose(pose, length=0.1)
            #step_handles = draw_point(point_from_pose(pose), color=BLACK)
            set_pose(world.get_body(cup_type), pose)
            print('Step {}/{}'.format(i+1, len(path)))
            wait_for_user()
            #remove_handles(step_handles)
        remove_handles(handles)

##################################################

def visualize_vector_field(learner, bowl_type='blue_bowl', cup_type='red_cup',
                           num=500, delta=False, alpha=0.5):
    print(learner, len(learner.results))
    xx, yy, ww = learner.xx, learner.yy, learner.weights
    learner.hyperparameters = get_parameters(learner.model)
    print(learner.hyperparameters)
    learner.query_type = REJECTION  # BEST | REJECTION | STRADDLE

    world = create_world()
    world.bodies[bowl_type] = load_cup_bowl(bowl_type)
    world.bodies[cup_type] = load_cup_bowl(cup_type)
    feature = get_pour_feature(world, bowl_type, cup_type)
    set_point(world.bodies[cup_type], np.array([0.2, 0, 0]))

    # TODO: visualize training data as well
    # TODO: artificially limit training data to make a low-confidence region
    # TODO: visualize mean and var at the same time using intensity and hue
    print('Feature:', feature)
    with LockRenderer():
        #for parameter in islice(learner.parameter_generator(world, feature, valid=True), num):
        parameters = []
        while len(parameters) < num:
            parameter = learner.sample_parameter()
            # TODO: special color if invalid
            if is_valid_pour(world, feature, parameter):
                parameters.append(parameter)

    center, _ = approximate_as_prism(world.get_body(cup_type))
    bodies = []
    with LockRenderer():
        for parameter in parameters:
            path, _ = pour_path_from_parameter(world, feature, parameter)
            pose = path[0]
            body = create_cylinder(radius=0.001, height=0.02, color=apply_alpha(GREY, alpha))
            set_pose(body, multiply(pose, Pose(point=center)))
            bodies.append(body)

    #train_sizes = inclusive_range(10, 100, 1)
    #train_sizes = inclusive_range(10, 100, 10)
    #train_sizes = inclusive_range(100, 1000, 100)
    #train_sizes = [1000]
    train_sizes = [None]

    # training_poses = []
    # for result in learner.results[:train_sizes[-1]]:
    #     path, _ = pour_path_from_parameter(world, feature, result['parameter'])
    #     pose = path[0]
    #     training_poses.append(pose)

    # TODO: draw the example as well
    scores_per_size = []
    for train_size in train_sizes:
        if train_size is not None:
            learner.xx, learner.yy, learner.weights = xx[:train_size], yy[:train_size],  ww[:train_size]
            learner.retrain()
        X = np.array([learner.func.x_from_feature_parameter(feature, parameter) for parameter in parameters])
        predictions = learner.predict_x(X, noise=False) # Noise is just a constant
        scores_per_size.append([prediction['mean'] for prediction in predictions]) # mean | variance
        #scores_per_size.append([1./np.sqrt(prediction['variance']) for prediction in predictions])
        #scores_per_size.append([prediction['mean'] / np.sqrt(prediction['variance']) for prediction in predictions])
        #plt.hist(scores_per_size[-1])
        #plt.show()
        #scores_per_size.append([scipy.stats.norm.interval(alpha=0.95, loc=prediction['mean'],
        #                                                  scale=np.sqrt(prediction['variance']))[0]
        #                         for prediction in predictions]) # mean | variance
        # score = learner.score(feature, parameter)

    if delta:
        scores_per_size = [np.array(s2) - np.array(s1) for s1, s2 in zip(scores_per_size, scores_per_size[1:])]
        train_sizes = train_sizes[1:]

    all_scores = [score for scores in scores_per_size for score in scores]
    #interval = (min(all_scores), max(all_scores))
    interval = scipy.stats.norm.interval(alpha=0.95, loc=np.mean(all_scores), scale=np.std(all_scores))
    #interval = DEFAULT_INTERVAL
    print('Interval:', interval)
    print('Mean: {:.3f} | Stdev: {:.3f}'.format(np.mean(all_scores), np.std(all_scores)))

    #train_body = create_cylinder(radius=0.002, height=0.02, color=apply_alpha(BLACK, 1.0))
    for i, (train_size, scores) in enumerate(zip(train_sizes, scores_per_size)):
        print('Train size: {} | Average: {:.3f} | Min: {:.3f} | Max: {:.3f}'.format(
            train_size, np.mean(scores), min(scores), max(scores)))
        handles = []
        # TODO: visualize delta
        with LockRenderer():
            #if train_size is None:
            #    train_size = len(learner.results)
            #set_pose(train_body, multiply(training_poses[train_size-1], Pose(point=center)))
            #set_pose(world.bodies[cup_type], training_poses[train_size-1])
            for score, body in zip(scores, bodies):
                score = clip(score, *interval)
                fraction = rescale(score, interval, new_interval=(0, 1))
                color = interpolate_hue(fraction)
                #color = (1 - fraction) * np.array(RED) + fraction * np.array(GREEN) # TODO: convex combination
                set_color(body, apply_alpha(color, alpha))
                #set_pose(world.bodies[cup_type], pose)
                #draw_pose(pose, length=0.05)
                #handles.extend(draw_point(tform_point(pose, center), color=color))
                #extent = np.array([0, 0, 0.01])
                #start = tform_point(pose, center - extent/2)
                #end = tform_point(pose, center + extent/2)
                #handles.append(add_line(start, end, color=color, width=1))
        wait_for_duration(0.5)
        # TODO: test on boundaries
    wait_for_user()

##################################################

# python3 -m learn_tools.visualize_pours data/pour_19-11-22_13-08-55/gp_batch_k\=mlp_v\=true.pk3
# data/scoop_19-12-07_16-02-40/gp_batch_k=mlp_v=true.pk3

def main():
    parser = argparse.ArgumentParser()
    parser.add_argument('paths', nargs='*', help='Paths to the data.')
    args = parser.parse_args()

    if not args.paths:
        visualize_collected_pours(paths=BEST_DATASETS) # TRAIN_DATASETS | ACTIVE_DATASETS | TEST_DATASETS | BEST_DATASETS
    else:
        [path] = args.paths
        learner = read_pickle(path)
        visualize_learned_pours(learner)
        #visualize_vector_field(learner)

if __name__ == '__main__':
    main()


import torch
import clip

def text_encode(classnames, templates, model):
    with torch.no_grad():
        text_feat = []
        for classname in classnames:
            texts = [template.format(classname) for template in templates]  # format with class
            texts = clip.tokenize(texts).cuda()  # tokenize
            class_embeddings = model.encode_text(texts)  # embed with text encoder
            class_embeddings /= class_embeddings.norm(dim=-1, keepdim=True)
            class_embedding = class_embeddings.mean(dim=0)
            class_embedding /= class_embedding.norm()
            text_feat.append(class_embedding)
        text_feat = torch.stack(text_feat, dim=1).cuda()
    return text_feat

def accuracy(output, label, n, topk=(1, 5)):
    pred = output.topk(max(topk), 1, True, True)[1].t()
    correct = pred.eq(label.view(1, -1).expand_as(pred))
    return (100 * float(correct[:k].reshape(-1).float().sum(0, keepdim=True).cpu().numpy()) / n for k in topk)


from utils import *

import jieba, sys

nltk.download('stopwords')
from nltk.corpus import stopwords

en_stopwords = stopwords.words('english')
non_bmp_map = dict.fromkeys(range(0x10000, sys.maxunicode + 1), ' ')

def preprocess_text(text,
                    filters='!"#$%&()*+,-.。 /:;<=>?@[\\]^_`{|}~\t\n',
                    split=' ',
                    lower=True,
                    remove_stopwords=False,
                    remove_non_ascii=False,
                    remove_emoji=False):
    if lower:
        text = text.lower()
    if remove_non_ascii:
        pattern = re.compile('[^(?u)\w\s]+')
        text = re.sub(pattern, '', text)
    if remove_stopwords:
        text = ' '.join([w for w in text.split() if not w in en_stopwords])
    if remove_emoji:
        text = text.translate(non_bmp_map)

    maketrans = str.maketrans
    translate_map = maketrans(filters, split * len(filters))
    text = text.translate(translate_map)
    text = text.replace("'", " ' ")

    if len(text) == 0:
        text = ' '

    # split
    kanji = re.compile('[一-龥]')
    if re.search(kanji, text):
        return [word for word in jieba.cut(text) if word != ' ']
    else:
        return text.split(split)

def w2v(train_corpus, w2v_params):
    model = word2vec.Word2Vec(train_corpus, **w2v_params)
    model.save("model.model")

    result = []
    for text in train_corpus:
        n_skip = 0
        for n_w, word in enumerate(text):
            try:
                vec_ = model.wv[word]
            except:
                n_skip += 1
                continue
            if n_w == 0:
                vec = vec_
            else:
                vec = vec + vec_
        vec = vec / (n_w - n_skip + 1)
        result.append(vec)

    w2v_cols = ["wv{}".format(i) for i in range(1, w2v_params["size"] + 1)]
    result = pd.DataFrame(result)
    result.columns = w2v_cols

    return result

if __name__ == '__main__':
    w2v_params = {
        "size": 300,
        "iter": 5,
        "seed": 0,
        "min_count": 1,
        "workers": 1
    }
    train["Description_Emb2"] = train["Description"].apply(preprocess_text)
    result = w2v(train["Description_Emb2"], w2v_params)
    result.to_feather("../feature/w2v_v2.feather")


import sys
import time
import thop
import torch
import torch.nn as nn

__all__ = ['LightNet']

NET_CONFIG = {  # k, inc, ouc, s, act, res
    "blocks2": [[3, 32, 64, 1, 0, 0]],  # 112
    "blocks3": [[3, 64, 128, 2, 1, 0], [3, 128, 128, 1, 0, 1]],  # 56
    "blocks4": [[3, 128, 256, 2, 1, 0], [3, 256, 256, 1, 0, 1]],  # 28
    "blocks5": [
        [3, 256, 512, 2, 1, 0],
        [5, 512, 512, 1, 1, 1],
        [5, 512, 512, 1, 1, 1],
        [5, 512, 512, 1, 1, 1],
        [5, 512, 512, 1, 1, 1],
        [5, 512, 512, 1, 0, 1],
    ],  # 14
    "blocks6": [[5, 512, 1024, 2, 1, 0], [5, 1024, 1024, 1, 1, 1]],  # 7
}

def make_divisible(v, divisor=8, min_value=None):
    if min_value is None:
        min_value = divisor
    new_v = max(min_value, int(v + divisor / 2) // divisor * divisor)
    if new_v < 0.9 * v:
        new_v += divisor
    return new_v

class ConvBN(nn.Module):
    def __init__(self, inc, ouc, kernel=3, stride=1, groups=1):
        super().__init__()
        padding = (kernel - 1) // 2
        self.conv = nn.Conv2d(inc, ouc, kernel, stride, padding, 1, groups, False)
        self.norm = nn.BatchNorm2d(ouc)

    def forward(self, x):
        x = self.conv(x)
        x = self.norm(x)
        return x

class DepthwiseSeparable(nn.Module):
    def __init__(self, inc, ouc, kernel=3, stride=1, need_act=1, residual=0):
        super().__init__()
        self.dwconv = ConvBN(inc, inc, kernel, stride, inc)
        self.pwconv = ConvBN(inc, ouc, 1)
        self.act = nn.ReLU6(False)
        self.scale = 5
        self.residual = residual
        self.need_act = need_act

    def forward(self, x):
        if self.residual == 1:
            shortcut = x
        x = self.dwconv(x)
        x = self.act(x / self.scale) * self.scale
        x = self.pwconv(x)
        if self.residual:
            x += shortcut
        if self.need_act == 1:
            x = self.act(x)
        return x

class LightNet(nn.Module):
    def __init__(
        self,
        r=1.0,
        with_act=True,
        in_chans=1,
        out_chans=8,
        need_residual=True,
        drop_rate=0.0,
        expansion=2,
        m=make_divisible,
    ):
        super().__init__()

        self.stem = ConvBN(in_chans, m(32 * r), 3, 2)

        for blk in range(2, 7):
            blocks = nn.Sequential(
                *[
                    DepthwiseSeparable(
                        m(inc * r), m(ouc * r), k, s, a, p if need_residual else 0
                    )
                    for i, (k, inc, ouc, s, a, p) in enumerate(
                        NET_CONFIG[f"blocks{blk}"]
                    )
                ]
            )
            setattr(self, f"blocks{blk}", blocks)

        self.act = nn.ReLU()
        self.with_act = with_act

        ch1 = NET_CONFIG["blocks6"][-1][2]
        ch1 = m(ch1 * r)
        ch2 = m(ch1 * expansion)
        self.head = nn.Sequential(
            nn.AdaptiveAvgPool2d(1),
            nn.Conv2d(ch1, ch2, 1),
            nn.ReLU(True),
            nn.Dropout(drop_rate),
            nn.Conv2d(ch2, out_chans, 1),
            nn.Flatten(1),
        )

    def forward(self, x):
        x = self.stem(x)
        for blk in range(2, 7):
            blocks = getattr(self, f"blocks{blk}")
            x = blocks(self.act(x))
        x = self.head(x)
        x = x.reshape(-1, 4, 2)
        return x

def get_model(params):
    ratio = params.channel_ratio
    in_ch = params.in_channel
    out_ch = params.out_channel
    drop_rate = params.drop_rate
    expansion = params.expansion

    output = LightNet(ratio, True, in_ch, out_ch, True, drop_rate, expansion)

    return output


import torch
import torch.nn as nn
import copy

def fuse_conv_and_bn(conv, bn):
    # Fuse convolution and batchnorm layers https://tehnokv.com/posts/fusing-batchnorm-and-conv/
    conv = copy.deepcopy(conv)
    bn = copy.deepcopy(bn)

    fusedconv = nn.Conv2d(conv.in_channels,
                          conv.out_channels,
                          kernel_size=conv.kernel_size,
                          stride=conv.stride,
                          padding=conv.padding,
                          groups=conv.groups,
                          bias=True).requires_grad_(False).to(conv.weight.device)

    conv_weight = conv.weight #shape: [out_c, in_c, h, w]
    conv_bias = torch.zeros(conv.weight.shape[0], device=conv.weight.device) if conv.bias is None else conv.bias #shape: [out_c]
    bn_affine_weight = torch.ones(conv.weight.shape[0], device=conv.weight.device) if bn.weight is None else bn.weight #shape: [out_c]
    bn_affine_bias = torch.zeros(conv.weight.shape[0], device=conv.weight.device) if bn.bias is None else bn.bias #shape: [out_c]
    bn_running_mean = bn.running_mean #shape: [out_c]
    bn_running_std = torch.sqrt(bn.eps + bn.running_var) #shape: [out_c]

    fusedconv.weight.copy_((conv_weight * bn_affine_weight.view(-1, 1, 1, 1)) / bn_running_std.view(-1, 1, 1, 1))
    fusedconv.bias.copy_(((conv_bias-bn_running_mean)/bn_running_std) + bn_affine_bias)
    return fusedconv

class ConvBatchNorm2d(nn.Module):
    def __init__(
        self,
    ):
        super().__init__()

        self.conv = nn.Conv2d(
            3,
            64,
            kernel_size=3,
            stride=1,
            padding=1,
            groups=1
        )
        self.bn = nn.BatchNorm2d(64)

        self.bn.running_mean.copy_(torch.randn_like(self.bn.running_mean))
        self.bn.running_var.copy_(abs(torch.randn_like(self.bn.running_var))+1e-6)

    def forward(self, x):
        x = self.conv(x)
        x = self.bn(x)
        return x

    def fuse_forward(self, x):
        fused_conv = fuse_conv_and_bn(self.conv, self.bn)
        fused_conv.eval()

        return fused_conv(x)

model = ConvBatchNorm2d()
input = torch.randn((1, 3, 224, 224))
model.eval()

with torch.no_grad():
    print(model(input).sum())
    print(model.fuse_forward(input).sum())

# class RepConv2d(nn.Module):
#     def __init__(
#         self,
#         in_channels,
#         out_channels,
#         kernel_size=3,
#         stride=1,
#         padding=1,
#         groups=1,
#         bias=False,
#     ):
#         super().__init__()
#         # same padding

#         self.conv = nn.Conv2d(
#             in_channels,
#             out_channels,
#             kernel_size=kernel_size,
#             stride=stride,
#             padding=padding,
#             groups=groups,
#             bias=bias,
#         )
#         self.conv_bn = nn.BatchNorm2d(out_channels)

#         self.pointwise_conv = nn.Conv2d(
#             in_channels,
#             out_channels,
#             kernel_size=1,
#             stride=stride,
#             padding=padding-kernel_size//2,
#             groups=groups,
#             bias=bias,
#         )
#         self.pointwise_conv_bn = nn.BatchNorm2d(out_channels)

#         self.identity = nn.BatchNorm2d(in_channels) if stride == 1 and in_channels == out_channels else False
#         self.re_parameterized = False

#     def identity2conv(self, identity):
#         assert isinstance(identity, nn.BatchNorm2d)
#         identity_conv = nn.Conv2d(
#             self.conv.in_channels,
#             self.conv.out_channels,
#             kernel_size=self.conv.kernel_size,
#             stride=self.conv.stride,
#             padding=self.conv.padding,
#             groups=self.conv.groups,
#             bias=True,
#         )

#         kernel_h, kernel_w = identity_conv.kernel_size
#         weight = torch.zeros_like(identity_conv.weight)
#         in_channels_per_group = self.conv.in_channels // self.conv.groups
#         for i in range(self.conv.in_channels):
#             weight[i, i % in_channels_per_group, 1, 1] = 1

#         print(weight[:, :, kernel_h//2, kernel_w//2].shape)

#         # weight = torch.zeros_like(identity_conv.weight)
#         # weight[:, :, kernel_h//2, kernel_w//2] = conv.weight.flatten(1)

#         identity_conv = fuse_conv_and_bn(identity_conv, identity)
#         return identity_conv

#     def pad_pointwise_conv(self, conv):
#         assert conv.kernel_size == (1, 1)
#         assert self.conv.kernel_size[0] % 2 == 1 and self.conv.kernel_size[1] % 2 == 1

#         padded_pointwise_conv = nn.Conv2d(
#             self.conv.in_channels,
#             self.conv.out_channels,
#             kernel_size=self.conv.kernel_size,
#             stride=self.conv.stride,
#             padding=self.conv.padding,
#             groups=self.conv.groups,
#             bias=False if conv.bias is None else True,
#         ).to(self.conv.weight.device)

#         kernel_h, kernel_w = padded_pointwise_conv.kernel_size

#         padded_weight = torch.zeros_like(padded_pointwise_conv.weight)
#         padded_weight[:, :, kernel_h//2, kernel_w//2] = conv.weight.flatten(1)

#         padded_pointwise_conv.weight.copy_(padded_weight)
#         padded_pointwise_conv.bias.copy_(conv.bias)
#         return padded_pointwise_conv

#     @torch.no_grad()
#     def re_parameterization(self):
#         bn_fused_conv = fuse_conv_and_bn(self.conv, self.conv_bn)
#         bn_fused_pointwise_conv = fuse_conv_and_bn(self.pointwise_conv, self.pointwise_conv_bn)
#         bn_fused_pointwise_conv = self.pad_pointwise_conv(bn_fused_pointwise_conv)

#         if self.identity:
#             identity_conv = self.identity2conv(self.identity)

#             pass

#         self.re_parameterized = True

#     def forward(self, x):
#         if self.training:
#             y_0 = self.conv_bn(self.conv(x))
#             y_1 = self.pointwise_conv_bn(self.pointwise_conv(x))
#             y = y_0 + y_1
#             if self.identity:
#                 y += self.identity(x)
#             return y

#         if not self.re_parameterized:
#             self.re_parameterization()
#         y = self.conv(x)
#         return y

# model = RepConv2d(in_channels=64, out_channels=64, groups=2)
# model.re_parameterization()


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

#data = pd.read_csv('CLEAN_House1.csv',usecols=['Time','Aggregate'])     #data is stored as comma-separated-value so 'read_csv' method is used
data = pd.read_csv('Filtered_output_myriad',usecols=['Time','Aggregate'])     #data is stored as comma-separated-value so 'read_csv' method is used
#read_csv automatically assumes separation using ',' so dont need to add
#usecols singles out specific columns

time = data['Time']     #set time column to time
power = data['Aggregate']       #set aggregate column to power

frequency = 50
plt.plot(time,power)
#plt.xticks(np.arange(0,len(time),frequency))      #change tick frequency, starting from 0, to length of array, taking every 'frequency' steps
plt.ylabel("Power",fontsize=20)
#plt.xlabel("Time",fontsize=20)
#plt.grid()
plt.title('Household load profile',fontsize=20)

plt.show()


import torch
import torchvision
import copy
import time

def train_model(model, criterion, optimizer, scheduler, dataloaders, dataset_sizes, device, num_epochs=25):
    since = time.time()
    best_model_wts = copy.deepcopy(model.state_dict())
    best_acc = 0.0

    for epoch in range(num_epochs):
        print("Epoch {}/{}".format(epoch, num_epochs - 1))
        print('-' * 10)

        for phase in ['train', 'val']:
            model.train() if phase == "train" else model.eval()
            running_loss, running_corrects = 0.0, 0.0

            for inputs, labels in dataloaders[phase]:
                inputs, labels = inputs.to(device), labels.to(device)
                optimizer.zero_grad()

                with torch.set_grad_enabled(phase == 'train'):
                    outputs = model(inputs)
                    _, preds = torch.max(outputs, 1)
                    loss = criterion(outputs, labels)

                    if phase == 'train':
                        loss.backward()
                        optimizer.step()

                running_loss += loss.item() * inputs.size(0)
                running_corrects += torch.sum(preds == labels)

            if phase == 'train':
                scheduler.step()

            epoch_loss = running_loss / dataset_sizes[phase]
            epoch_acc = running_corrects.double() / dataset_sizes[phase]

            print('{} Loss: {:.4f} Acc: {:.4f}'.format(phase, epoch_loss, epoch_acc))

            if phase == 'val' and epoch_acc > best_acc:
                best_acc = epoch_acc
                best_model_wts = copy.deepcopy(model.state_dict())

        print()

    time_elapsed = time.time() - since
    print('Training complete in {:.0f}m {:.0f}s'.format(time_elapsed // 60, time_elapsed % 60))
    print('Best val Acc: {:.4f}'.format(best_acc))

    model.load_state_dict(best_model_wts)
    return model


from typing import TypeVar, Type, Any, Generic, Callable, get_origin, get_args
from abc import abstractmethod
from inspect import getfullargspec

from tools import Status, status, StatusMeta

T = TypeVar("T")
AnyFunc = Callable[..., T]

# Interface for data input
# Generic argument is used in `Calculator`
# CONTAINS:
#   - data
#   - data type
#   - data state (has data or not)
class Input(Generic[T], Status):

    # QUERIES

    # Get data type
    @abstractmethod
    def get_type(self) -> Type[T]:
        assert False

    # Get data state
    @abstractmethod
    def has_data(self) -> bool:
        assert False

    # Get data
    # PRE: has data
    @abstractmethod
    @status("OK", "NO_DATA")
    def get(self) -> T:
        assert False

# Interface for data output
# Generic argument is used in `Calculator`
# CONTAINS:
#   - data
#   - data type
#   - data state (has data or not)
class Output(Generic[T], Status):

    # COMMANDS

    # Set data
    # PRE: `value` type fits data type
    # POST: data is `value`
    @abstractmethod
    @status("OK", "INVALID_TYPE")
    def set(self, value: T) -> None:
        assert False

    # Delete data
    # POST: no data
    @abstractmethod
    def clear(self) -> None:
        assert False

    # QUERIES

    # Get data type
    @abstractmethod
    def get_type(self) -> Type[T]:
        assert False

DataSource = Input[Any]
DataDest = Output[Any]

# Slot containing data
# CONTAINS:
#   - data
#   - data type
#   - data state (has data or not)
class Slot(DataSource, DataDest):

    __type: type
    __value: Any
    __has_data: bool

    # CONSTRUCTOR
    # POST: data type is `data_type`
    # POST: no data
    def __init__(self, data_type: type) -> None:
        super().__init__()
        self.__type = data_type
        self.__has_data = False

    # COMMANDS

    # Set data
    # PRE: `value` type fits data type
    # POST: data is `value`
    @status("OK", "INVALID_TYPE")
    def set(self, value: Any) -> None:
        if not _type_fits(type(value), self.__type):
            self._set_status("set", "INVALID_TYPE")
            return
        self._set_status("set", "OK")
        self.__has_data = True
        self.__value = value

    # Delete data
    # POST: no data
    def clear(self) -> None:
        self.__has_data = False

    # QUERIES

    # Get data type
    def get_type(self) -> Type[Any]:
        return self.__type

    # Get data state
    def has_data(self) -> bool:
        return self.__has_data

    # Get data
    # PRE: has data
    @status("OK", "NO_DATA")
    def get(self) -> Any:
        if not self.__has_data:
            self._set_status("get", "NO_DATA")
            return None
        self._set_status("get", "OK")
        return self.__value

# Procedure interface
# CONTAINS:
#   - input slots
#   - output slots
#   - calculations to run
class Procedure(Status):

    # COMMANDS

    # Run calculations
    # PRE: all inputs have data
    # PRE: input data lead to successfull calculation
    # POST: all outputs have data
    @abstractmethod
    @status("OK", "INVALID_INPUT", "INTERNAL_ERROR")
    def run(self) -> None:
        assert False

    # QUERIES

    # Get IDs of input slots
    @abstractmethod
    def get_input_ids(self) -> set[str]:
        assert False

    # Get IDs of output slots
    @abstractmethod
    def get_output_ids(self) -> set[str]:
        assert False

    # Get input slot
    # PRE: `id` is valid input slot ID
    @abstractmethod
    @status("OK", "INVALID_ID")
    def get_input(self, id: str) -> DataDest:
        assert False

    # Get output slot
    # PRE: `id` is valid output slot ID
    @abstractmethod
    @status("OK", "INVALID_ID")
    def get_output(self, id: str) -> DataSource:
        assert False

class CalculatorMeta(StatusMeta):

    def __new__(cls, class_name: str, bases: tuple[type, ...],
            namespace: dict[str, Any], **kwargs: Any) -> type:
        namespace["__inputs"] = cls.__get_fields(class_name, namespace, Input)
        namespace["__outputs"] = cls.__get_fields(class_name, namespace, Output)
        return super().__new__(cls, class_name, bases, namespace, **kwargs)

    @staticmethod
    def __get_fields(class_name: str, namespace: dict[str, Any],
            required_type: type) -> list[tuple[str, str, type]]:
        if "__annotations__" not in namespace:
            return list()
        fields = list[tuple[str, str, type]]()
        private_prefix = f"_{class_name}__"
        for field_name, field_type in namespace["__annotations__"].items():
            if get_origin(field_type) is not required_type:
                continue
            if field_name.startswith(private_prefix):
                slot_name = field_name[len(private_prefix):]
            elif field_name.startswith("_"):
                slot_name = field_name[1:]
            else:
                slot_name = field_name
            field_args = get_args(field_type)
            assert(len(field_args) == 1)
            data_type = field_args[0]
            fields.append((slot_name, field_name, data_type))
        return fields

# Procedure that automatically detects input and output slots from fields
# and performs calculations using custom algorithm
class Calculator(Procedure, metaclass=CalculatorMeta):

    __input_fields: dict[str, str]
    __output_fields: dict[str, str]

    # CONSTRUCTOR
    # POST: all `Input` and `Output` fields contain slots of specified types
    def __init__(self) -> None:
        super().__init__()
        self.__input_fields = dict()
        self.__output_fields = dict()
        for slot_name, field_name, data_type in getattr(self, "__inputs"):
            setattr(self, field_name, Slot(data_type))
            self.__input_fields[slot_name] = field_name
        for slot_name, field_name, data_type in getattr(self, "__outputs"):
            setattr(self, field_name, Slot(data_type))
            self.__output_fields[slot_name] = field_name

    # COMMANDS

    # Run calculations
    # Checks inputs then calls `calculate` method and then checks outputs
    # PRE: all inputs have data
    # PRE: input data lead to successfull calculation
    # POST: all outputs have data
    @status("OK", "INVALID_INPUT", "INTERNAL_ERROR")
    def run(self) -> None:
        for field_name in self.__input_fields.values():
            slot: Slot = getattr(self, field_name)
            if not slot.has_data():
                self._set_status("run", "INVALID_INPUT")
                return
        self.calculate()
        if not self.is_status("calculate", "OK"):
            self._set_status("run", "INTERNAL_ERROR")
            return
        for field_name in self.__output_fields.values():
            slot: Slot = getattr(self, field_name)
            if not slot.has_data():
                self._set_status("run", "INTERNAL_ERROR")
                return
        self._set_status("run", "OK")

    # Run calculations
    # To be implemented in child classes
    # PRE: input data lead to successfull calculation
    # POST: all outputs have data
    @abstractmethod
    @status("OK", "ERROR")
    def calculate(self) -> None:
        assert False

    # QUERIES

    # Get IDs of input slots
    def get_input_ids(self) -> set[str]:
        return set(self.__input_fields.keys())

    # Get IDs of output slots
    def get_output_ids(self) -> set[str]:
        return set(self.__output_fields.keys())

    # Get input slot
    # PRE: `id` is valid input slot ID
    @status("OK", "INVALID_ID")
    def get_input(self, id: str) -> DataDest:
        if id not in self.__input_fields:
            self._set_status("get_input", "INVALID_ID")
            return Slot(object)
        self._set_status("get_input", "OK")
        return getattr(self, self.__input_fields[id])

    # Get output slot
    # PRE: `id` is valid output slot ID
    @status("OK", "INVALID_ID")
    def get_output(self, id: str) -> DataSource:
        if id not in self.__output_fields:
            self._set_status("get_output", "INVALID_ID")
            return Slot(object)
        self._set_status("get_output", "OK")
        return getattr(self, self.__output_fields[id])

# Procedure that wraps a function giving names to the returned tuple elements
class Wrapper(Procedure):

    __func: AnyFunc[Any]
    __input_ids: list[str]
    __output_ids: list[str]
    __inputs: dict[str, Slot]
    __outputs: dict[str, Slot]

    # CONSTRUCTOR
    def __init__(self, func: AnyFunc[T], output_ids: list[str]) -> None:
        super().__init__()
        self.__func = func
        arg_spec = getfullargspec(func)
        self.__input_ids = arg_spec.args
        self.__output_ids = output_ids
        self.__inputs = dict()
        self.__outputs = dict()
        for id in self.__input_ids:
            self.__inputs[id] = Slot(arg_spec.annotations[id])
        if "return" not in arg_spec.annotations:
            assert len(output_ids) == 0
            return
        return_type = arg_spec.annotations["return"]
        if get_origin(return_type) is not tuple:
            assert len(output_ids) == 1
            self.__outputs[output_ids[0]] = Slot(return_type)
            return
        output_types = get_args(return_type)
        for i in range(len(self.__output_ids)):
            id = self.__output_ids[i]
            data_type = output_types[i]
            self.__outputs[id] = Slot(data_type)

    # COMMANDS

    # Run calculations
    # PRE: all inputs have data
    # PRE: input data lead to successfull calculation
    # POST: all outputs have data
    @status("OK", "INVALID_INPUT", "INTERNAL_ERROR")
    def run(self) -> None:
        args = list[Any]()
        for id in self.__input_ids:
            input = self.__inputs[id]
            if not input.has_data():
                self._set_status("run", "INVALID_INPUT")
                return
            args.append(input.get())
        try:
            result = self.__func(*args)
        except:
            self._set_status("run", "INTERNAL_ERROR")
            return
        if len(self.__output_ids) == 1:
            result = tuple(result,)
        for i in range(len(self.__output_ids)):
            value = result[i]
            id = self.__output_ids[i]
            output = self.__outputs[id]
            if not _type_fits(type(value), output.get_type()):
                self._set_status("run", "INTERNAL_ERROR")
                return
            output.set(value)
        self._set_status("run", "OK")

    # QUERIES

    # Get IDs of input slots
    def get_input_ids(self) -> set[str]:
        return set(self.__input_ids)

    # Get IDs of output slots
    def get_output_ids(self) -> set[str]:
        return set(self.__output_ids)

    # Get input slot
    # PRE: `id` is valid input slot ID
    @status("OK", "INVALID_ID")
    def get_input(self, id: str) -> DataDest:
        if id not in self.__inputs:
            self._set_status("get_input", "INVALID_ID")
            return Slot(object)
        self._set_status("get_input", "OK")
        return self.__inputs[id]

    # Get output slot
    # PRE: `id` is valid output slot ID
    @status("OK", "INVALID_ID")
    def get_output(self, id: str) -> DataSource:
        if id not in self.__outputs:
            self._set_status("get_output", "INVALID_ID")
            return Slot(object)
        self._set_status("get_output", "OK")
        return self.__outputs[id]

# Procedure that holds a composition of linked procedures
# and make them run using lazy dataflow
class Block(Procedure):

    __inputs: dict[str, Slot]
    __outputs: dict[str, Slot]

    # CONSTRUCTOR
    def __init__(self, pattern: list[Any]) -> None:
        super().__init__()

    # COMMANDS

    # Run calculations
    # PRE: all inputs have data
    # PRE: input data lead to successfull calculation
    # POST: all outputs have data
    @status("OK", "INVALID_INPUT", "INTERNAL_ERROR")
    def run(self) -> None:
        assert False

    # QUERIES

    # Get IDs of input slots
    def get_input_ids(self) -> set[str]:
        assert False

    # Get IDs of output slots
    def get_output_ids(self) -> set[str]:
        assert False

    # Get input slot
    # PRE: `id` is valid input slot ID
    @status("OK", "INVALID_ID")
    def get_input(self, id: str) -> DataDest:
        assert False

    # Get output slot
    # PRE: `id` is valid output slot ID
    @status("OK", "INVALID_ID")
    def get_output(self, id: str) -> DataSource:
        assert False

def _type_fits(t: type, required: type) -> bool:
    if issubclass(t, required):
        return True
    if required is complex:
        return t is int or t is float
    if required is float:
        return t is int
    return False


from __future__ import print_function, absolute_import, division
import rpc
import os
import sys
import numpy as np
import torch
from torchvision import models, transforms
from torch.autograd import Variable
from PIL import Image
import logging
from datetime import datetime

logging.basicConfig(format='%(asctime)s %(levelname)-8s [%(filename)s:%(lineno)d] %(message)s',
                    datefmt='%y-%m-%d:%H:%M:%S',
                    level=logging.INFO)

logger = logging.getLogger(__name__)

model_map = {
    "alexnet": models.alexnet,
    "vgg11": models.vgg11,
    "vgg13": models.vgg13,
    "vgg16": models.vgg16,
    "vgg19": models.vgg19,
    "res18": models.resnet18,
    "res34": models.resnet34,
    "res50": models.resnet50,
    "res101": models.resnet101,
    "res152": models.resnet152,
    "squeezenet10": models.squeezenet1_0,
    "squeezenet11": models.squeezenet1_1,
    "densenet121": models.densenet121,
    "densenet169": models.densenet169,
    "densenet161": models.densenet161,
    "densenet201": models.densenet201,
    "inceptionv3": models.inception_v3
}

class TorchContainer(rpc.ModelContainerBase):
    def __init__(self, model_arch):
        logger.info("Using Pytorch {}".format(model_arch))
        self.model = model_map[model_arch](pretrained=True)
        # if model_arch == "alexnet":
        #     logger.info("Using PyTorch Alexnet")
        #     self.model = models.alexnet(pretrained=True)
        # # elif model_arch == "squeezenet":
        # #     logger.info("Using PyTorch Squeezenet 1.1")
        # #     self.model = models.squeezenet1_1(pretrained=True)
        # elif model_arch == "res50":
        #     logger.info("Using PyTorch Resnet 50")
        #     self.model = models.resnet50(pretrained=True)
        # elif model_arch == "res18":
        #     logger.info("Using PyTorch Resnet 18")
        #     self.model = models.resnet18(pretrained=True)
        # elif model_arch == "res152":
        #     logger.info("Using PyTorch Resnet 152")
        #     self.model = models.resnet152(pretrained=True)
        # # elif model_arch == "inception":
        # #     logger.info("Using PyTorch Inception v3")
        # #     self.model = models.inception_v3(pretrained=True)
        # else:
        #     logger.error("{} is not currently supported".format(model_arch))
        #     sys.exit(1)

        if torch.cuda.is_available():
            logger.error("Using CUDA")
            self.model.cuda()
        else:
            logger.error("Not Using CUDA!")

        self.model.eval()
        # self.height = 299
        # self.width = 299

        # normalize = transforms.Normalize(
        #     mean=[0.485, 0.456, 0.406],
        #     std=[0.229, 0.224, 0.225]
        # )
        #
        # self.preprocess = transforms.Compose([
        #     transforms.Scale(256),
        #     transforms.CenterCrop(224),
        #     transforms.ToTensor(),
        #     normalize
        # ])

    def predict_doubles(self, inputs):
        # start = datetime.now()
        input_arrs = []
        for t in inputs:
            i = torch.from_numpy(t.reshape(3, 224, 224).astype(np.float32))
            input_arrs.append(i)
        input_batch = Variable(torch.stack(input_arrs, dim=0))
        if torch.cuda.is_available():
            input_batch = input_batch.cuda()
        logits = self.model(input_batch)
        maxes, arg_maxes = torch.max(logits, dim=1)
        pred_classes = arg_maxes.squeeze().data.cpu().numpy()
        if pred_classes.shape == ():
            outputs = [str(pred_classes)]
        else:
            outputs = [str(l) for l in pred_classes]
        end = datetime.now()
        return outputs

    def predict_floats(self, inputs):
        return self.predict_doubles(inputs)

    # def _predict_raw(self, input_arrs):
    #     inputs = []
    #     for i in input_arrs:
    #         img = Image.fromarray(i, mode="RGB")
    #         inputs.append(self.preprocess(img))
    #     input_batch = Variable(torch.stack(inputs, dim=0))
    #     if torch.cuda.is_available():
    #         input_batch = input_batch.cuda()
    #     logits = self.model(input_batch)
    #     maxes, arg_maxes = torch.max(logits, dim=1)
    #     pred_classes = arg_maxes.squeeze().data.cpu().numpy()
    #     return pred_classes

if __name__ == "__main__":
    start_time = datetime.now()
    try:
        model_name = os.environ["CLIPPER_MODEL_NAME"]
    except KeyError:
        print(
            "ERROR: CLIPPER_MODEL_NAME environment variable must be set",
            file=sys.stdout)
        sys.exit(1)
    try:
        model_version = os.environ["CLIPPER_MODEL_VERSION"]
    except KeyError:
        print(
            "ERROR: CLIPPER_MODEL_VERSION environment variable must be set",
            file=sys.stdout)
        sys.exit(1)

    ip = "127.0.0.1"
    if "CLIPPER_IP" in os.environ:
        ip = os.environ["CLIPPER_IP"]
    else:
        print("Connecting to Clipper on localhost")

    input_type = "bytes"
    if "CLIPPER_INPUT_TYPE" in os.environ:
        input_type = os.environ["CLIPPER_INPUT_TYPE"]

    mpath = os.environ["CLIPPER_MODEL_PATH"]
    with open(mpath, "rb") as f:
        model_arch = f.read().strip()
    model = TorchContainer(model_arch)
    rpc_service = rpc.RPCService()
    rpc_service.start(model, ip, model_name, model_version, input_type, start_time)


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

================================================================================
Fitting benchmark - Performance comparison between different fitting algorithms
================================================================================

Now with the new fitting module implemented as :py:func:`~radis.tools.new_fitting.fit_spectrum`
function on September 6 2022, we can experience an easier-to-use, robust and complete spectrum
fitting.

This example features performance comparison between 17 different fitting methods and algorithms
of LMFIT, introduced in `LMFIT method list <https://lmfit.github.io/lmfit-py/fitting.html#choosing-different-fitting-methods>`.
There are 2 variables to be compared: the last residual, and total number of loops required for
the fitting process. A good fitting method should have small number of loops (robustness) and has
low residual (fitting accuracy).

"""

import matplotlib.pyplot as plt

from radis import load_spec
from radis.test.utils import getTestFile
from radis.tools.new_fitting import fit_spectrum

# ========================================== PREPARE FITTING PROCESS ========================================== #

spec_file = getTestFile("synth-NH3-1-500-2000cm-P10-mf0.01-p1.spec")
s_experimental = load_spec(spec_file)

# Experimental conditions which will be used for spectrum modeling. Basically, these are known ground-truths.
experimental_conditions = {
    "molecule": "NH3",  # Molecule ID
    "isotope": "1",  # Isotope ID, can have multiple at once
    "wmin": 1000,  # Starting wavelength/wavenumber to be cropped out from the original experimental spectrum.
    "wmax": 1050,  # Ending wavelength/wavenumber for the cropping range.
    "wunit": "cm-1",  # Accompanying unit of those 2 wavelengths/wavenumbers above.
    "mole_fraction": 0.01,  # Species mole fraction, from 0 to 1.
    "pressure": 10,  # Total pressure of gas, in "bar" unit by default, but you can also use Astropy units.
    "path_length": 1,  # Experimental path length, in "cm" unit by default, but you can also use Astropy units.
    "slit": "1 nm",  # Experimental slit, must be a blank space separating slit amount and unit.
    "offset": "-0.2 nm",  # Experimental offset, must be a blank space separating offset amount and unit.
    "wstep": 0.003,  # Resolution of wavenumber grid, in cm-1.
    "databank": "hitran",  # Databank used for the spectrum calculation. Must be stated.
}

# List of parameters to be fitted, accompanied by their initial values.
fit_parameters = {
    "Tgas": 700,  # Gas temperature, in K.
}

# List of bounding ranges applied for those fit parameters above.
bounding_ranges = {
    "Tgas": [
        500,
        2000,
    ],
}

# Fitting pipeline setups.
fit_properties = {
    "fit_var": "radiance",  # Spectral quantity to be extracted for fitting process, such as "radiance", "absorbance", etc.
    "normalize": False,  # Either applying normalization on both spectra or not.
    "max_loop": 150,  # Max number of loops allowed. By default, 200.
    "tol": 1e-15,  # Fitting tolerance, only applicable for "lbfgsb" method.
}

# ============================================ BENCHMARKING PROCESS ============================================ #

list_methods = [
    "leastsq",  # Levenberg-Marquardt
    "least_squares",  # Least-Squares minimization, using Trust Region Reflective method
    "differential_evolution",  # Differential evolution
    "brute",  # Brute force
    "basinhopping",  # Basin-hopping
    "ampgo",  # Adaptive Memory Programming for Global Optimization
    "nelder",  # Nelder-Mead method
    "lbfgsb",  # Limited-memory Broyden–Fletcher–Goldfarb–Shanno (L-BFGS-B)
    "powell",  # Powell method
    "cg",  # Conjugate-gradient
    "cobyla",  # Cobyla method
    "bfgs",  # Broyden–Fletcher–Goldfarb–Shanno (BFGS)
    "tnc",  # Truncated Newton
    "trust-constr",  # Trust-region for constrained optimization
    "slsqp",  # Sequential Linear Squares Programming
    "shgo",  # Simplicial Homology Global Optimization
    "dual_annealing",  # Dual Annealing optimization
]

# Commence fitting processes of all above fitting methods

list_residual = []  # Store last residual
list_loops = []  # Store number of fitting loops

for method in list_methods:

    fit_properties["method"] = method

    # Commence fitting process for this particular method
    _, result, log = fit_spectrum(
        s_exp=s_experimental,  # Experimental spectrum.
        fit_params=fit_parameters,  # Fit parameters.
        bounds=bounding_ranges,  # Bounding ranges for those fit parameters.
        model=experimental_conditions,  # Experimental ground-truths conditions.
        pipeline=fit_properties,  # Fitting pipeline references.
        verbose=False,  # If you want a clean result, stay False. If you want to see more about each loop, True.
        show_plot=False,  # Since benchmarking involves multiple fitting processes, turn off plot showing.
    )

    # Store the performance result
    list_residual.append(log["residual"][-1])
    list_loops.append(result.nfev)

# Print the result as a table

print("\n======================== BENCHMARKING RESULT ========================\n")
print("||           METHOD          ||          RESIDUAL         || LOOPS ||")
print("||---------------------------||---------------------------||-------||")

for i in range(len(list_methods)):
    print(
        "|| {0: <25} || {1: <25} || {2: <5} ||".format(
            list_methods[i], list_residual[i], list_loops[i]
        )
    )

print("||---------------------------||---------------------------||-------||\n")

# Plot the benchmarking result as 2D scatter plot

fig, ax = plt.subplots()

# Scatter the data points
ax.scatter(list_loops, list_residual)
# Mark method name for each corresponding data point
for i in range(len(list_methods)):
    ax.annotate(list_methods[i], (list_loops[i], list_residual[i]))
# Add axis labels
ax.set_xlabel("Number of loops")
ax.set_ylabel("Residual")

ax.grid(True)
plt.show()

"""

As seen from the data above, we have several methods like basinhopping, ampgo, dual_annealing and
differential_evolution jumping out of the loop limit of 150, making them unstable fitting methods.
Then, in this particular case, if you zoom into clustered section below, you can see several good
methods with excellently low number of loops and residual. They are leastsq, lbfgsb, tnc, bfgs,
nelder and trust-constr.

Further benchmarking attempts with different other spectra have shown that, leastsq and lbfgsb
have consistently good performance. Generally speaking, while leastsq tends to have lower residual
and thus greater accuracy, lbfgsb on the other hand often requires less fitting loops and also
less time in total. As the discrepancy between these 2 methods are not really significant, I decided
to set leastsq as default fitting method, but users are encouraged to switch to lbfgsb as one of
first attempts in case the fitting result is unsatisfied.

"""


# -*- coding: utf-8 -*-
from collective.mailchimp.browser.newsletter import NewsletterSubscriberForm
from collective.mailchimp.testing import (
    COLLECTIVE_MAILCHIMP_INTEGRATION_TESTING,
)
from plone.app.testing import SITE_OWNER_NAME
from plone.app.testing import SITE_OWNER_PASSWORD
from plone.testing.z2 import Browser
from plone.z3cform.fieldsets import extensible
from zope import schema
from zope.component import adapts
from zope.component import provideAdapter
from zope.interface import Interface
from zope.interface import Invalid

import unittest

def validate(value):
    if value is not True:
        raise Invalid(u'Required test field not checked')
    return True

class ITestExtenderSchema(Interface):

    accept = schema.Bool(title=u'accept', required=True, constraint=validate)

class TestExtender(extensible.FormExtender):
    adapts(Interface, Interface, NewsletterSubscriberForm)

    def update(self):
        self.add(ITestExtenderSchema)

class TestNewsletterView(unittest.TestCase):

    layer = COLLECTIVE_MAILCHIMP_INTEGRATION_TESTING

    def setUp(self):
        app = self.layer['app']
        self.portal = self.layer['portal']
        self.request = self.layer['request']
        self.portal_url = self.portal.absolute_url()
        self.browser = Browser(app)
        self.browser.handleErrors = False
        self.browser.addHeader(
            'Authorization',
            'Basic %s:%s' % (SITE_OWNER_NAME, SITE_OWNER_PASSWORD),
        )

    def test_empty_form(self):
        self.browser.open("%s/newsletter" % self.portal_url)
        self.browser.getControl(name="form.buttons.subscribe").click()
        self.assertTrue("Required input is missing." in self.browser.contents)

    def test_form_with_invalid_email_address(self):
        self.browser.open("%s/newsletter" % self.portal_url)
        self.browser.getControl(
            name="form.widgets.email"
        ).value = "Not an email address"
        self.browser.getControl(name="form.buttons.subscribe").click()
        self.assertTrue("Invalid email address" in self.browser.contents)

class TestNewsletterExtender(unittest.TestCase):

    layer = COLLECTIVE_MAILCHIMP_INTEGRATION_TESTING

    def setUp(self):
        app = self.layer['app']
        self.portal = self.layer['portal']
        self.request = self.layer['request']
        self.portal_url = self.portal.absolute_url()
        self.browser = Browser(app)
        self.browser.handleErrors = False
        self.browser.addHeader(
            'Authorization',
            'Basic %s:%s' % (SITE_OWNER_NAME, SITE_OWNER_PASSWORD),
        )
        provideAdapter(factory=TestExtender)

    def test_newsletter_extender(self):
        self.browser.open("%s/newsletter" % self.portal_url)
        self.browser.getControl(
            name="form.widgets.email"
        ).value = "valid@email.com"
        self.browser.getControl(name="form.buttons.subscribe").click()
        self.assertTrue(
            "Required test field not checked" in self.browser.contents
        )

#    def test_form_with_valid_email_address(self):
#        self.browser.open("%s/newsletter" % self.portal_url)
#        self.browser.getControl(name="form.widgets.email").value = \
#            "john@doe.com"
#        self.browser.getControl(name="form.buttons.subscribe").click()

#    def test_form_with_already_subscribed_email_address(self):
#        # Subscribe once
#        self.browser.open("%s/newsletter" % self.portal_url)
#        self.browser.getControl(name="form.widgets.email").value = \
#            "john@doe.com"
#        self.browser.getControl(name="form.buttons.subscribe").click()
#        # Subscribe twice
#        self.browser.open("%s/newsletter" % self.portal_url)
#        self.browser.getControl(name="form.widgets.email").value = \
#            "john@doe.com"
#        self.browser.getControl(name="form.buttons.subscribe").click()

#    def test_form_with_banned_email_address(self):
#        self.browser.open("%s/newsletter" % self.portal_url)
#        self.browser.getControl(name="form.widgets.email").value = \
#            "john@doe.com"
#        self.browser.getControl(name="form.buttons.subscribe").click()

class TestUnsubscribeNewsletterView(unittest.TestCase):

    layer = COLLECTIVE_MAILCHIMP_INTEGRATION_TESTING

    def setUp(self):
        app = self.layer['app']
        self.portal = self.layer['portal']
        self.request = self.layer['request']
        self.portal_url = self.portal.absolute_url()
        self.browser = Browser(app)
        self.browser.handleErrors = False
        self.browser.addHeader(
            'Authorization',
            'Basic %s:%s' % (SITE_OWNER_NAME, SITE_OWNER_PASSWORD),
        )

    def test_empty_form(self):
        self.browser.open("%s/unsubscribe-newsletter" % self.portal_url)
        self.browser.getControl(name="form.buttons.unsubscribe").click()
        self.assertTrue("Required input is missing." in self.browser.contents)

    def test_form_with_email_address(self):
        self.browser.open("%s/unsubscribe-newsletter" % self.portal_url)
        self.browser.getControl(
            name="form.widgets.email"
        ).value = "freddy@freddiesjokes.com"
        unsub_all_checkbox = self.browser.getControl(
            name='form.widgets.unsubscribe:list', index=0
        )
        unsub_all_checkbox.value = ['selected']
        self.browser.getControl(name="form.buttons.unsubscribe").click()

        from collective.mailchimp.locator import requests

        self.assertEqual(
            requests.last_call,
            {
                'endpoint': (
                    u'lists/f6257645gs/members/'
                    u'06f12badc3b5fffc57576822131ded7c'
                ),
                'data': {u'status': u'unsubscribed'},
            },
        )

def test_suite():
    return unittest.defaultTestLoader.loadTestsFromName(__name__)


#! /usr/bin/env python3

from enum      import Enum, unique
from itertools import starmap
from operator  import le
from random    import choice, random, randrange
import numpy

@unique
class Tetra1(Enum):
	MAJOR    = (2, 2, 1)
	MINOR    = (2, 1, 2)
	PHRYGIAN = (1, 2, 2)
	HARMONIC = (1, 3, 1)
	MISC1    = (3, 1, 1)
	MISC2    = (1, 1, 3)
@unique
class Tetra2(Enum):
	MISC1    = (3, 2, 1)
	MISC2    = (3, 1, 2)
	MISC3    = (2, 2, 2)
	MISC4    = (1, 3, 2)
	MISC5    = (2, 1, 3)
	MISC6    = (1, 2, 3)
TETRA_EPSILON1 = 0.5
TETRA_EPSILON2 = 0.5
def random_tetrachords():
	if   random() < TETRA_EPSILON1: ltetra, utetra = Tetra1, Tetra1
	elif random() < TETRA_EPSILON2: ltetra, utetra = Tetra1, Tetra2
	else:                           ltetra, utetra = Tetra2, Tetra1
	ltetra = choice(list(ltetra))
	utetra = choice(list(utetra))
	print("lower tetra: %s" % (ltetra,))
	print("upper tetra: %s" % (utetra,))
	ltetra = ltetra.value
	utetra = utetra.value
	print("lower tetra: %s" % (ltetra,))
	print("upper tetra: %s" % (utetra,))
	assert len(ltetra) == 3
	assert len(utetra) == 3
	return ltetra, utetra

from itertools import accumulate

#scale  = list(accumulate((0,) + scale))
#assert scale[-1] == len(chroma)
#scale  = scale[:-1]
#print("scale: %s" % (scale,))

class Scale:
	def __init__(self, chroma, scale, mode):
		#assert sum(scale) == len(key.chroma)
		assert sum(scale) == len(chroma)
		assert 0 <= mode and mode < len(scale)
		self.chroma = chroma
		#self.orig   = scale
		scale       = scale[mode:] + scale[:mode]
		scale       = list(accumulate((0,) + scale))
		#assert scale[-1] == len(key.chroma)
		assert scale[-1] == len(chroma)
		scale       = tuple(scale[:-1])
		print("scale: %s" % (scale,))
		self.scale  = scale
		self.mode   = mode
	def __len__(self): return len(self.scale)
	#def ratio(self, i):
	def __getitem__(self, i):
		a = self.scale[i % len(self.scale)]
		#a = self.chroma.ratio(a)
		a = self.chroma[a]
		b =        int(i / len(self.scale))
		#print("scale a: %s, b: %s" % (a, b,))
		return a * 2**b

def random_scale(key, ltetra=None, utetra=None):
    if ltetra is None and utetra is None: ltetra, utetra = random_tetrachords()
    if ltetra is None:                    ltetra         = random_tetrachord (utetra)
    if utetra is None:                    utetra         = random_tetrachord (ltetra)

    step  = len(key.chroma) - sum(ltetra) - sum(utetra)
    print("step: %s" % (step,))
    scale = (*ltetra, step, *utetra)
    assert len(scale) == len(ltetra) + 1 + len(utetra)

    mode   = randrange(0, len(scale))
    print("mode: %s" % (mode,))

    scale = Scale(key, scale, mode)
    return scale
#ratios = tuple((scale.ratio(i) for i in range(0, len(scale.scale))))

#for i in range(0, len(scale.scale)):
#    print("i=%s: %s" % (i, scale.ratio(i),))

#ratios = [chroma[(scale[(i + mode) % len(scale)]) % len(chroma)] for i in range(0, len(scale))]

#ratios = tuple((scale[i] for i in range(0, len(scale))))
#print("ratios: %s" % (ratios,))


from interaction_learning.algorithms.interaction_framework.particle_interaction_agent import ParticleInteractionAgent
from interaction_learning.core.evaluation import evaluate, DummyAgent
from gym_cooking.environment.cooking_env import parallel_env
from interaction_learning.core.util import make_deterministic
from time import sleep
import torch
import tqdm

make_deterministic(1)

device = torch.device("cpu")

# 0 is do nothing 1 is move right 2 is down 3 is left 4 is up

n_agents = 1
num_humans = 0
max_steps = 100
render = True
obs_spaces = None
action_scheme = "scheme1"
ghost_agents = 0
manual_control = False

level = 'open_room_salad2'
seed = 1
record = False
max_num_timesteps = 1000
recipe = "TomatoSalad"
recipes = [recipe]

env = parallel_env(level=level, num_agents=n_agents, record=record, max_steps=max_num_timesteps, recipes=recipes,
                   obs_spaces=obs_spaces, action_scheme=action_scheme, ghost_agents=ghost_agents, render=render)

obs_dim = env.observation_spaces["player_0"].shape[0]
obs_dim_impact = 8
n_actions = env.action_spaces["player_0"].n
task_alpha = 0.04
impact_alpha = 0.04
batch_size = 32
gamma = 0.50
target_update_interval = 1000
memory_size = 50000
action_alignment = False

num_epochs = 400

interaction_agent = ParticleInteractionAgent(obs_dim, obs_dim_impact, n_actions, task_alpha, impact_alpha,
                                             action_alignment, batch_size, gamma, target_update_interval,
                                             memory_size, device)

dummy_agent = DummyAgent()
ep_rews = {}
eval_scores = {}
ep_length_stats = {}

env = parallel_env(level=level, num_agents=n_agents, record=record, max_steps=max_num_timesteps, recipes=recipes,
                   obs_spaces=obs_spaces, action_scheme=action_scheme, ghost_agents=ghost_agents, render=render)

interaction_agent.add_task(recipe)
interaction_agent.switch_active_tasks([recipe])

episode_reward = 0
num_eval_runs = 10

action_distribution = {n: 0 for n in range(env.action_spaces["player_0"].n)}
action_dists = []

episode_rewards = []
episode_lengths = []
average_q = []

action_bag = []

evaluation_scores = [evaluate(env, 10, [interaction_agent])]

for epoch in tqdm.tqdm(range(num_epochs)):
    state = env.reset()
    episode_reward = 0
    ep_length = 0
    for time_steps in range(max_steps):
        action = interaction_agent.select_action(state["player_0"])
        actions = {"player_0": action, "player_1": 0}
        next_state, reward, done, truncation, info = env.step(actions)
        episode_reward += reward["player_0"]

        interaction_agent.add_transition(state["player_0"], next_state["player_0"], action,
                                         reward["player_0"], done["player_0"])

        action_distribution[action] += 1
        ep_length += 1

        if time_steps % 4 == 0:
            interaction_agent.learn_step()

        if all(done.values()):
            break

        state = next_state

        if render:
            env.render()
            sleep(0.1)
    # if episode_reward > 1000:
    #     print(f"Reward higher than anticipated: {episode_reward}")
    action_bag = []
    episode_rewards.append(episode_reward)
    episode_lengths.append(ep_length)
    print(episode_rewards)
    # print(f"Episode Rewards: {episode_reward} || Episode Length: {ep_length}")
    if epoch % 10 == 0:
        evaluation_scores.append(evaluate(env, 10, [interaction_agent, dummy_agent]))
        # torch.save(onlineQNetwork.state_dict(), f'agent/sql{epoch}policy_y0dimpact')
        # print('Epoch {}\tMoving average score: {:.2f}\t'.format(epoch, episode_reward))