"""
 This module contains the Distribution class which defines a standard
 interface for distributions It also provides several implemented
 distributions, which inherit from Distribution Any user-specified
 distributions should inherit from Distribution
"""
import numpy as np
from .utils import overrides, package_path
import os
from scipy import stats
import pickle

class Distribution(object):
    """
    Interface/abstract class for distributions.
    Any user-specified distributions should be defined by inheriting from this class and
     overriding the appropriate methods.
    """

    def __init__(self, ndims=2, nbatch=100):
        """ Creates a Distribution object

        :param ndims: the dimension of the state space for this distribution
        :param nbatch: the number of sampling particles to run simultaneously
        :returns: a Distribution object
        :rtype: Distribution

        """

        # distribution dimensions
        self.ndims = ndims
        # number of sampling particles to use
        self.nbatch = nbatch

        # TensorflowDistributions require some special treatment
        # this attribute is to be used instead of isinstance, as that would require
        # tensorflow to be imported globally
        if not hasattr(self, 'backend'):
            self.backend = 'numpy'

        # true iff being sampled with a jump process
        self.mjhmc = None

        # number of times energy op has been called
        self.E_count = 0

        # number of times gradient op has been called
        self.dEdX_count = 0

        # only set to true when I have a bias initialization and am being burned in
        # to generate and cache a fair initialization for continuous samplers
        self.generation_instance = False

        # so some distributions may modify the default
        if not hasattr(self, 'max_n_particles'):
            self.max_n_particles = None

        # set the state fairly. calls out to a cache
        self.init_X()

    def E(self, X):
        self.E_count += X.shape[1]
        return self.E_val(X)

    def E_val(self, X):
        """
        Subclasses should implement this with the correct energy function
        """
        raise NotImplementedError()

    def dEdX(self, X):
        self.dEdX_count += X.shape[1]
        return self.dEdX_val(X)

    def dEdX_val(self, X):
        """
        Subclasses should implement this with the correct energy gradient function
        """
        raise NotImplementedError()

    def __hash__(self):
        """ Subclasses should implement this as the hash of the tuple of all parameters
        that effect the distribution, including ndims. This is very important!!
        nbatch should not be part of the hash!! Including it will break everything

        As an example, see how this is implemented in Gaussian

        :returns: a hash of the relevant parameters of self
        :rtype: int
        """
        raise NotImplementedError()

    def init_X(self):
        """
        Sets self.Xinit to a good initial value
        """
        # TODO: make production ready by adding global flag to disable
        #  research options like this
        self.cached_init_X()

    def cached_init_X(self):
        """ Sets self.Xinit to cached (serialized) initial states for continuous-time samplers, generated by burn in
        *For use with continuous-time samplers only*

        :returns: None
        :rtype: none
        """
        distr_name = type(self).__name__
        distr_hash = hash(self)
        file_name = '{}_{}.pickle'.format(distr_name, distr_hash)
        file_prefix = '{}/initializations'.format(package_path())
        if file_name in os.listdir(file_prefix):
            with open('{}/{}'.format(file_prefix, file_name), 'rb') as cache_file:
                mjhmc_endpt, _, _, control_endpt  = pickle.load(cache_file)
                if self.mjhmc:
                    self.Xinit = mjhmc_endpt[:, :self.nbatch]
                else:
                    self.Xinit = control_endpt[:, :self.nbatch]
        else:
            from mjhmc.misc.gen_mj_init import MAX_N_PARTICLES, cache_initialization
            # modify this object so it can be used by gen_mj_init
            old_nbatch = self.nbatch
            self.nbatch = self.max_n_particles or MAX_N_PARTICLES
            self.generation_instance = True

            # must rebuild now that nbatch is changed back
            if self.backend == 'tensorflow':
                self.build_graph()

            # start with biased initializations
            # changes self.nbatch
            try:
                self.gen_init_X()
            except NotImplementedError:
                # completely arbitrary choice
                self.Xinit = np.random.randn(self.ndims, self.nbatch)

            #generate and cache fair initialization
            cache_initialization(self)
            # reconstruct this object using fair initialization
            self.nbatch = old_nbatch
            self.generation_instance = False
            # must rebuild now that nbatch is changed back
            if self.backend == 'tensorflow':
                 self.build_graph()
            self.cached_init_X()

    def gen_init_X(self):
        """ Sets self.Xinit to generated initial states for the sampling particles
        *For use with discrete-time samplers only*

        :returns: None
        :rtype: None
        """
        raise NotImplementedError()

    def reset(self):
        """
        resets the object. returns self for convenience
        """
        self.E_count = 0
        self.dEdX_count = 0
        if not self.generation_instance:
            self.init_X()
        return self

    def __call__(self, X):
        """
        Convenience method for NUTS compatibility
        returns -E, -dEdX
        """
        rshp_X = X.reshape(len(X), 1)
        E = float(self.E(rshp_X))
        dEdX = self.dEdX(rshp_X).T[0]
        return -E, -dEdX

    def load_cache(self):
        """ Loads and returns the cached fair initializations and
         estimated variances associated with this
         distribution. Throws an error if the cache does not exist

        :returns: the loaded cache: (fair_initialization, emc_var_estimate, true_var_estimate)
        :rtype: (np.ndarray, float, float)
        """
        distr_name = type(self).__name__
        distr_hash = hash(self)
        file_name = '{}_{}.pickle'.format(distr_name, distr_hash)
        file_prefix = '{}/initializations'.format(package_path())
        with open('{}/{}'.format(file_prefix, file_name)) as cache_file:
            return pickle.load(cache_file)

class LambdaDistribution(Distribution):
    """ An `anonymous' distribution object for quick
    experimentation. Due to the initialization time that is required
    at first run it, one shouldn't use this object in the
    long-term. Rather create your own distribution class that inherits
    from Distribution.

    You should give your LambdaDistribution objects a name. Use a
    descriptive name, and use the same for functionally equivalent
    LambdaDistributions - the hash of the name is used to label the
    initialization information which is generated at first run time of
    a new distribution. This requirement is a side effect of the
    unfortunate fact that there is no computable hash function which
    assigns functionally identical programs to the same number.
    """

    #pylint: disable=too-many-arguments
    def __init__(self, energy_func=None, energy_grad_func=None, init=None, name=None):
        """ Creates an anonymous distribution object.

        :param ndims: the dimension of the state space for this distribution
        :param nbatch: the number of sampling particles to run simultaneously
        :param energy_func: function specifying the energy
        :param energy_grad_func: function specifying gradient of the energy
        :param name: name of this distribution. use the same name for
          functionally identical distributions
        :param init: fair initialization for this distribution. array of shape (ndims, nbatch)
        :returns: an anonymous distribution object
        :rtype: LambdaDistribution

        """
        self.energy_func = energy_func
        self.energy_grad_func = energy_grad_func
        self.init = init
        # TODO: raise warning if name is not passed
        self.name = name or str(np.random())
        super(LambdaDistribution, self).__init__(ndims=init.shape[0], nbatch=init.shape[1])

    @overrides(Distribution)
    def E_val(self, X):
        return np.sum(X*np.dot(self.J,X), axis=0).reshape((1,-1))/2.

    @overrides(Distribution)
    def dEdX_val(self, X):
        return np.dot(self.J,X)/2. + np.dot(self.J.T,X)/2.

    @overrides(Distribution)
    def gen_init_X(self):
        self.Xinit = self.init

    @overrides(Distribution)
    def __hash__(self):
        return hash((self.ndims, self.nbatch, self.name))

class Gaussian(Distribution):
    def __init__(self, ndims=2, nbatch=100, log_conditioning=6):
        """
        Energy function, gradient, and hyperparameters for the "ill
        conditioned Gaussian" example from the LAHMC paper.
        """
        self.conditioning = 10**np.linspace(-log_conditioning, 0, ndims)
        self.J = np.diag(self.conditioning)
        self.description = '%dD Anisotropic Gaussian, %g self.conditioning'%(ndims, 10**log_conditioning)
        super(Gaussian, self).__init__(ndims, nbatch)

    @overrides(Distribution)
    def E_val(self, X):
        return np.sum(X*np.dot(self.J,X), axis=0).reshape((1,-1))/2.

    @overrides(Distribution)
    def dEdX_val(self, X):
        return np.dot(self.J,X)/2. + np.dot(self.J.T,X)/2.

    @overrides(Distribution)
    def gen_init_X(self):
        self.Xinit = (1./np.sqrt(self.conditioning).reshape((-1,1))) * np.random.randn(self.ndims,self.nbatch)

    @overrides(Distribution)
    def __hash__(self):
        return hash((self.ndims, hash(tuple(self.conditioning))))

class RoughWell(Distribution):
    def __init__(self, ndims=2, nbatch=100, scale1=100, scale2=4):
        """
        Energy function, gradient, and hyperparameters for the "rough well"
        example from the LAHMC paper.
        """
        self.scale1 = scale1
        self.scale2 = scale2
        self.description = '{} Rough Well'.format(ndims)
        super(RoughWell, self).__init__(ndims, nbatch)

    @overrides(Distribution)
    def E_val(self, X):
        cosX = np.cos(X*2*np.pi/self.scale2)
        E = np.sum((X**2) / (2*self.scale1**2) + cosX, axis=0).reshape((1,-1))
        return E

    @overrides(Distribution)
    def dEdX_val(self, X):
        sinX = np.sin(X*2*np.pi/self.scale2)
        dEdX = X/self.scale1**2 + -sinX*2*np.pi/self.scale2
        return dEdX

    @overrides(Distribution)
    def gen_init_X(self):
        self.Xinit = self.scale1 * np.random.randn(self.ndims, self.nbatch)

    @overrides(Distribution)
    def __hash__(self):
        return hash((self.ndims, self.scale1, self.scale2))

class MultimodalGaussian(Distribution):
    def __init__(self, ndims=2, nbatch=100, separation=3):
        self.sep_vec = np.array([separation] * nbatch +
                                [0] * (ndims - 1) * nbatch).reshape(ndims, nbatch)
        # separated along first axis
        self.sep_vec[0] += separation
        super(MultimodalGaussian, self).__init__(ndims, nbatch)

    @overrides(Distribution)
    def E_val(self, X):
        trim_sep_vec = self.sep_vec[:, :X.shape[1]]
        return -np.log(np.exp(-np.sum((X + trim_sep_vec)**2, axis=0)) +
                       np.exp(-np.sum((X - trim_sep_vec)**2, axis=0)))

    @overrides(Distribution)
    def dEdX_val(self, X):
        # allows for partial batch size
        trim_sep_vec = self.sep_vec[:, :X.shape[1]]
        common_exp = np.exp(np.sum(4 * trim_sep_vec * X, axis=0))
        # floating point hax
        return ((2 * ((X - trim_sep_vec) * common_exp + trim_sep_vec + X)) /
                (common_exp + 1))

    @overrides(Distribution)
    def init_X(self):
        # okay, this is pointless... sep vecs cancel
        self.Xinit = ((np.random.randn(self.ndims, self.nbatch) + self.sep_vec) +
                (np.random.randn(self.ndims, self.nbatch) - self.sep_vec))

    @overrides(Distribution)
    def __hash__(self):
        return hash((self.ndims, self.separation))

class TestGaussian(Distribution):

    def __init__(self, ndims=2, nbatch=100, sigma=1.):
        """Simple default unit variance gaussian for testing samplers
        """
        self.sigma = sigma
        super(TestGaussian, self).__init__(ndims, nbatch)

    @overrides(Distribution)
    def E_val(self, X):
        return np.sum(X**2, axis=0).reshape((1, -1)) / (2. * self.sigma ** 2)

    @overrides(Distribution)
    def dEdX_val(self, X):
        return X/self.sigma**2

    @overrides(Distribution)
    def gen_init_X(self):
        self.Xinit = np.random.randn(self.ndims, self.nbatch)

    @overrides(Distribution)
    def __hash__(self):
        return hash((self.ndims, self.sigma))

#pylint: disable=too-many-instance-attributes
class ProductOfT(Distribution):
    """ Provides the product of T experts distribution
    """

    #pylint: disable=too-many-arguments
    def __init__(self, ndims=36, nbasis=36, nbatch=100, lognu=None, W=None, b=None):
        """ Product of T experts, assumes a fixed W that is sparse and alpha that is
        """
        # awkward hack to import theano in poe only
        try:
            import theano.tensor as T
            import theano
            self.theano = theano
            self.T = T
        except:
            raise ImportError("Theano could not be imported")

        if ndims != nbasis:
            raise NotImplementedError("Initializer only works for ndims == nbasis")
        self.ndims = ndims
        self.nbasis = nbasis
        self.nbatch = nbatch
        if W is None:
            W = np.eye(ndims, nbasis)
        self.weights = self.theano.shared(np.array(W, dtype='float32'), 'W')
        if lognu is None:
            pre_nu = np.random.rand(nbasis,) * 2 + 2.1
        else:
            pre_nu = np.exp(lognu)
        self.nu = self.theano.shared(np.array(pre_nu, dtype='float32'), 'nu')
        if b is None:
            b = np.zeros((nbasis,))
        self.bias = self.theano.shared(np.array(b, dtype='float32'), 'b')

        state = T.matrix()
        energy = self.E_def(state)
        gradient = T.grad(T.sum(energy), state)

        #@overrides(Distribution)
        self.E_val = self.theano.function([state], energy, allow_input_downcast=True)
        #@overrides(Distribution)
        self.dEdX_val = self.theano.function([state], gradient, allow_input_downcast=True)

        super(ProductOfT,self).__init__(ndims,nbatch)
        self.backend = 'theano'

    def E_def(self,X):
        """
        energy for a POE with student's-t expert in terms of:
                samples [# dimensions]x[# samples] X
                receptive fields [# dimensions]x[# experts] W
                biases [# experts] b
                degrees of freedom [# experts] nu
        """
        rshp_b = self.bias.reshape((1,-1))
        rshp_nu = self.nu.reshape((1, -1))
        alpha = (rshp_nu + 1.)/2.
        energy_per_expert = alpha * self.T.log(1 + ((self.T.dot(X.T, self.weights) + rshp_b) / rshp_nu) ** 2)
        energy = self.T.sum(energy_per_expert, axis=1).reshape((1, -1))
        return energy

    @overrides(Distribution)
    def gen_init_X(self):
        #hack to remap samples from a generic product of experts to
        #the model we are actually going to generate samples from
        Zinit = np.zeros((self.ndims, self.nbatch))
        for ii in xrange(self.ndims):
            Zinit[ii] = stats.t.rvs(self.nu.get_value()[ii], size=self.nbatch)

        Yinit = Zinit - self.bias.get_value().reshape((-1, 1))
        self.Xinit = np.dot(np.linalg.inv(self.weights.get_value()), Yinit)

    @overrides(Distribution)
    def __hash__(self):
        return hash((self.ndims,
                     self.nbasis,
                     hash(tuple(self.nu.get_value())),
                     hash(tuple(self.weights.get_value().ravel())),
                     hash(tuple(self.bias.get_value().ravel()))))


from io import BytesIO
from PIL import Image
from uuid import uuid4

from django.core.files import File

JPEG_IMAGE_QUALITY = 100

def crop_image(image):
    im = Image.open(image)

    (height, width) = (im.height, im.width)
    shortest_side = min(height, width)
    dimensions = (0, 0, shortest_side, shortest_side)
    image_name = _generate_random_file_name()

    im = im.convert("RGB")
    im = im.crop(dimensions)

    _bytes = BytesIO()

    im.save(_bytes, "JPEG", quality=JPEG_IMAGE_QUALITY)

    return File(_bytes, image_name)

def _generate_random_file_name():
    return str(uuid4()) + ".jpg"


from typing import List, Tuple, Union

import lightgbm as lgb
import numpy as np
import pandas as pd

import mlrun.errors

from .._ml_common import AlgorithmFunctionality, MLTypes, MLUtils

class LGBMTypes(MLTypes):
    """
    Typing hints for the LightGBM framework.
    """

    # A union of all LightGBM model base classes:
    ModelType = Union[lgb.LGBMModel, lgb.Booster]

    # A type for all the supported dataset types:
    DatasetType = Union[MLTypes.DatasetType, lgb.Dataset]

    # An evaluation result as packaged by the training in LightGBM:
    EvaluationResultType = Union[
        Tuple[str, str, float, bool],  # As packaged in `lightgbm.train`
        Tuple[str, str, float, bool, float],  # As packaged in `lightgbm.cv`
    ]

    # Detailed type for the named tuple `CallbackEnv` passed during LightGBM's training for the callbacks:
    CallbackEnvType = Tuple[
        lgb.Booster, dict, int, int, int, List[EvaluationResultType]
    ]

class LGBMUtils(MLUtils):
    """
    Utilities functions for the LightGBM framework.
    """

    @staticmethod
    def to_array(dataset: LGBMTypes.DatasetType) -> np.ndarray:
        """
        Convert the given dataset to np.ndarray.

        :param dataset: The dataset to convert. Must be one of {lgb.Dataset, pd.DataFrame, pd.Series,
                        scipy.sparse.base.spmatrix, list, tuple, dict}.

        :return: The dataset as a ndarray.

        :raise MLRunInvalidArgumentError: If the dataset type is not supported.
        """
        if isinstance(dataset, lgb.Dataset):
            x = LGBMUtils.to_array(dataset=dataset.data)
            if dataset.label is None:
                return x
            y = LGBMUtils.to_array(dataset=dataset.label)
            return LGBMUtils.to_array(LGBMUtils.concatenate_x_y(x=x, y=y)[0])
        try:
            return MLUtils.to_array(dataset=dataset)
        except mlrun.errors.MLRunInvalidArgumentError:
            raise mlrun.errors.MLRunInvalidArgumentError(
                f"Could not convert the given dataset into a numpy ndarray. Supporting conversion from: "
                f"{LGBMUtils.get_union_typehint_string(LGBMTypes.DatasetType)}. The given dataset was of type: "
                f"'{type(dataset)}'"
            )

    @staticmethod
    def to_dataframe(dataset: LGBMTypes.DatasetType) -> pd.DataFrame:
        """
        Convert the given dataset to pd.DataFrame.

        :param dataset: The dataset to convert. Must be one of {lgb.Dataset, np.ndarray, pd.Series,
                        scipy.sparse.base.spmatrix, list, tuple, dict}.

        :return: The dataset as a DataFrame.

        :raise MLRunInvalidArgumentError: If the dataset type is not supported.
        """
        if isinstance(dataset, lgb.Dataset):
            x = LGBMUtils.to_dataframe(dataset=dataset.data)
            if dataset.label is None:
                return x
            y = LGBMUtils.to_dataframe(dataset=dataset.label)
            return LGBMUtils.concatenate_x_y(x=x, y=y)[0]
        try:
            return MLUtils.to_dataframe(dataset=dataset)
        except mlrun.errors.MLRunInvalidArgumentError:
            raise mlrun.errors.MLRunInvalidArgumentError(
                f"Could not convert the given dataset into a pandas DataFrame. Supporting conversion from: "
                f"{LGBMUtils.get_union_typehint_string(LGBMTypes.DatasetType)}. The given dataset was of type: "
                f"'{type(dataset)}'"
            )

    @staticmethod
    def get_algorithm_functionality(
        model: MLTypes.ModelType = None,
        y: MLTypes.DatasetType = None,
        objective: str = None,
    ) -> AlgorithmFunctionality:
        """
        Get the algorithm functionality of the LightGBM model. If SciKit-Learn API is used, pass the LGBBMModel and a y
        sample. Otherwise, training API is used, so pass the objective of the params dictionary.

        The objectives here are taken from the official docs of LightGBBM at:
        https://lightgbm.readthedocs.io/en/latest/Parameters.html#core-parameters

        :param model:     The model to check if its a regression model or a classification model (SciKit-Learn API).
        :param y:         The ground truth values to check if its multiclass and / or multi output (SciKit-Learn API).
        :param objective: The objective string (Training API).

        :return: The objective's algorithm functionality.
        """
        # Check if LightGBM is being used with SciKit-Learn API:
        if objective is None:
            return super().get_algorithm_functionality(model=model, y=y)

        # Declare the conversion map according to the LightGBM docs:
        objective_to_algorithm_functionality_map = {
            # regression application:
            "regression": AlgorithmFunctionality.REGRESSION,
            "regression_l2": AlgorithmFunctionality.REGRESSION,
            "l2": AlgorithmFunctionality.REGRESSION,
            "mean_squared_error": AlgorithmFunctionality.REGRESSION,
            "mse": AlgorithmFunctionality.REGRESSION,
            "l2_root": AlgorithmFunctionality.REGRESSION,
            "root_mean_squared_error": AlgorithmFunctionality.REGRESSION,
            "rmse": AlgorithmFunctionality.REGRESSION,
            "regression_l1": AlgorithmFunctionality.REGRESSION,
            "l1": AlgorithmFunctionality.REGRESSION,
            "mean_absolute_error": AlgorithmFunctionality.REGRESSION,
            "mae": AlgorithmFunctionality.REGRESSION,
            "huber": AlgorithmFunctionality.REGRESSION,
            "fair": AlgorithmFunctionality.REGRESSION,
            "poisson": AlgorithmFunctionality.REGRESSION,
            "quantile": AlgorithmFunctionality.REGRESSION,
            "mape": AlgorithmFunctionality.REGRESSION,
            "mean_absolute_percentage_error": AlgorithmFunctionality.REGRESSION,
            "gamma": AlgorithmFunctionality.REGRESSION,
            "tweedie": AlgorithmFunctionality.REGRESSION,
            # binary classification application:
            "binary": AlgorithmFunctionality.BINARY_CLASSIFICATION,
            # multi-class classification application:
            "multiclass": AlgorithmFunctionality.MULTICLASS_CLASSIFICATION,
            "softmax": AlgorithmFunctionality.MULTICLASS_CLASSIFICATION,
            "multiclassova": AlgorithmFunctionality.MULTICLASS_CLASSIFICATION,
            "multiclass_ova": AlgorithmFunctionality.MULTICLASS_CLASSIFICATION,
            "ova": AlgorithmFunctionality.MULTICLASS_CLASSIFICATION,
            "ovr": AlgorithmFunctionality.MULTICLASS_CLASSIFICATION,
            # cross-entropy application
            "cross_entropy": AlgorithmFunctionality.BINARY_CLASSIFICATION,
            "xentropy": AlgorithmFunctionality.BINARY_CLASSIFICATION,
            "cross_entropy_lambda": AlgorithmFunctionality.BINARY_CLASSIFICATION,
            "xentlambda": AlgorithmFunctionality.BINARY_CLASSIFICATION,
            # ranking application
            "lambdarank": AlgorithmFunctionality.MULTICLASS_CLASSIFICATION,
            "rank_xendcg": AlgorithmFunctionality.MULTICLASS_CLASSIFICATION,
            "xendcg": AlgorithmFunctionality.MULTICLASS_CLASSIFICATION,
            "xe_ndcg": AlgorithmFunctionality.MULTICLASS_CLASSIFICATION,
            "xe_ndcg_mart": AlgorithmFunctionality.MULTICLASS_CLASSIFICATION,
            "xendcg_mart": AlgorithmFunctionality.MULTICLASS_CLASSIFICATION,
        }

        # Return unknown if the objective is not in the map and otherwise return its functionality:
        if objective not in objective_to_algorithm_functionality_map:
            raise AlgorithmFunctionality.UNKNOWN
        return objective_to_algorithm_functionality_map[objective]


import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import fsolve

a = 2
#b = 1

def plotter(b):
	func = lambda x : a - b*x - np.exp(-x)
	guess = a/b
	max_x = fsolve(func, guess)
	x = np.arange(0.0, max_x*1.05, 0.01)
	y1 = a - b*x
	y2 = np.exp(-x)
	y3 = y1 - y2
	null = 0*x
	plt.figure()
	plt.fill_between(x, y1, y2)
	plt.plot(x, y3, color="yellow", label="difference")
	plt.plot(x, null, "--b")
	plt.title('b = ' + str(b))
	plt.legend(loc="best")
	plt.xlabel('time')
	plt.ylabel('P(t)')
	plt.savefig("fig/SIR_b_" + str(b) + ".pdf")

def stability_plotter(b):
	func = lambda x : a - b*x - np.exp(-x)
	guess = a/b
	max_x = fsolve(func, guess)
	x = np.arange(0.0, max_x*1.2, 0.01)
	y = a - b*x - np.exp(-x)
	null = 0*x
	# plt.figure()
	# plt.fill_between(x, y1, y2)
	plt.plot(x, y, label='b = ' + str(b))
	plt.plot(x, null, "--b")

b_list = [0.2, 0.5, 0.8, 1, 1.2, 1.5, 2] #, 5, 10]
for b in b_list:
	plotter(b)

plt.figure()
for b in b_list:
	stability_plotter(b)
plt.title("Stability")
plt.legend(loc="best")
plt.xlabel('$u(\\tau)$')
plt.ylabel('$du/d\\tau$')
# plt.xscale("log")
plt.savefig("fig/SIR_stability.pdf")

# plot u* as func of b
u_list = []
b_max = 2
b_list = np.arange(0.01, b_max, 0.01)
for b in b_list:
	func = lambda x : a - b*x - np.exp(-x)
	guess = a/b
	u_eq = fsolve(func, guess)
	u_list.append(u_eq)
plt.figure()
plt.plot(b_list, u_list)
plt.title("equilibrium of u, parametrized by 'b'")
plt.legend(loc="best")
plt.xlabel('$b$')
plt.ylabel('$u*$')
# plt.xscale("log")
plt.savefig("fig/SIR_equilibrium_u_b.pdf")


from torch.utils.data import Dataset, DataLoader
from torchvision import transforms
import os
import random
import matplotlib.pyplot as plt
from PIL import Image
import torch
from torchvision.models import resnet18, ResNet18_Weights
import torch.nn as nn
import numpy as np

class Mydata(Dataset):
    def __init__(self, data_path, transform, label):
        super(Mydata, self).__init__()
        self.transform = transform
        self.label = label
        self.data_path = data_path

    def __len__(self):
        return len(self.data_path)

    def __getitem__(self, idx):
        img = Image.fromarray(plt.imread(self.data_path[idx]))
        img = self.transform(img)
        label = self.label[idx]
        return img, label

def visualize(labels, data_path):
    fig = plt.figure(figsize=(9, 9))
    rows, cols = 4, 4

    d_label = {
        0: 'Cat',
        1: 'Dog',
    }

    for i in range(1, rows * cols + 1):
        random_idx = random.randint(0, len(data_path) - 1)
        img = plt.imread(data_path[random_idx])
        label = d_label[labels[random_idx]]
        fig.add_subplot(rows, cols, i)
        plt.imshow(img)
        plt.title(label)
        plt.axis(False)
    plt.show()

def get_model():
    model = resnet18(weights=ResNet18_Weights.IMAGENET1K_V1)
    model.fc = nn.Sequential(
        nn.Linear(512, 1),
        nn.Sigmoid()
    )
    for name, param in model.named_parameters():
        if 'fc' not in name:
            param.requires_grad = False
    return model

def get_data():
    train_path = [os.path.join("train", name) for name in os.listdir("train")]
    cat_train_path = [x for x in train_path if 'cat' in x]
    dog_train_path = [x for x in train_path if 'dog' in x]
    train_data_path = cat_train_path[:int(len(cat_train_path) * 0.8)] + dog_train_path[:int(len(dog_train_path) * 0.8)]
    val_data_path = cat_train_path[int(len(cat_train_path) * 0.8):] + dog_train_path[int(len(dog_train_path) * 0.8):]
    random.shuffle(train_data_path)
    random.shuffle(val_data_path)
    labels_train = [0 if 'cat' in name else 1 for name in train_data_path]
    labels_val = [0 if 'cat' in name else 1 for name in val_data_path]
    return train_data_path, val_data_path, labels_train, labels_val

def get_dataloader():
    train_data_path, val_data_path, labels_train, labels_val = get_data()
    transform = transforms.Compose([
        transforms.Resize(size=(224, 224)),
        transforms.ToTensor(),
        transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
    ])

    train_data = Mydata(train_data_path, transform=transform, label=labels_train)
    val_data = Mydata(val_data_path, transform=transform, label=labels_val)
    train_loader = DataLoader(train_data, shuffle=True, batch_size=16, num_workers=2)
    val_loader = DataLoader(val_data, shuffle=True, batch_size=16, num_workers=2)
    return train_loader, val_loader

def train_step(model,
               dataloader,
               loss_fn,
               optimizer,
               device):
    model.train()

    train_loss, train_acc = 0, 0

    for batch, (X, y) in enumerate(dataloader):
        X, y = X.to(device), y.to(device)
        y_pred = model(X)
        y_pred = y_pred.squeeze(-1)

        loss = loss_fn(y_pred, y.float())
        train_loss += loss.item()
        optimizer.zero_grad()

        loss.backward()

        optimizer.step()

        y_pred_class = torch.tensor([0 if x < 0.5 else 1 for x in y_pred]).to(device)
        train_acc += (y_pred_class == y).sum().item() / len(y_pred)
        if batch % 100 == 0:
            print(y_pred.squeeze(-1), y)
            print()
            print(
                "Batch {} Loss {:.4f} Train Acc {}".format(batch, loss, (y_pred_class == y).sum().item() / len(y_pred)))
    train_loss = train_loss / len(dataloader)
    train_acc = train_acc / len(dataloader)
    return train_loss, train_acc

def val_step(model,
             dataloader,
             loss_fn: torch.nn.Module,
             device: torch.device):
    model.eval()

    test_loss, test_acc = 0, 0

    with torch.no_grad():
        for batch, (X, y) in enumerate(dataloader):
            X, y = X.to(device), y.to(device)

            test_pred_logits = model(X)
            test_pred_logits = test_pred_logits.squeeze(-1)
            loss = loss_fn(test_pred_logits, y.float())
            test_loss += loss.item()

            test_pred_labels = torch.tensor([0 if x < 0.5 else 1 for x in test_pred_logits]).to(device)
            test_acc += ((test_pred_labels == y).sum().item() / len(test_pred_labels))

    test_loss = test_loss / len(dataloader)
    test_acc = test_acc / len(dataloader)
    return test_loss, test_acc

if __name__ == '__main__':
    # Config
    model = get_model()
    device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
    num_epoches = 10
    optimizer = torch.optim.Adam(model.parameters(), lr=0.001, weight_decay=3e-5)
    loss = nn.BCELoss()
    model = model.to(device)
    # Data
    train_loader, val_loader = get_dataloader()
    train_losses = []
    train_accs = []
    test_losses = []
    test_accs = []
    for epoch in range(num_epoches):
        train_loss, train_acc = train_step(model, train_loader, loss, optimizer, device)
        print("Epoch {} Train_loss {} Train_acc {}".format(epoch, train_loss, train_acc))
        train_losses.append(train_loss)
        train_accs.append(train_acc)
        test_loss, test_acc = val_step(model, val_loader, loss, device)
        print("Epoch {} Test_loss {} Test_acc {}".format(epoch, test_loss, test_acc))
        test_losses.append(test_loss)
        test_accs.append(test_acc)
        print("Done Epoch {}".format(epoch))
        # print("________________________________________________________________")
    torch.save(model.state_dict(), "Weight/model.pt")
    np.save('Accuracy/train_losses.npy', np.array(train_losses))
    np.save('Accuracy/train_accs.npy', np.array(train_accs))
    np.save('Accuracy/test_losses.npy', np.array(test_losses))
    np.save('Accuracy/test_accs.npy', np.array(test_accs))


import cv2 as cv
import pandas as pd

df = pd.read_csv("Set03_video01.csv")
cam = cv.VideoCapture("Set03_video01.h264")
frame = 0
# initBB = (int(df.iloc[0]['x']),int(df.iloc[0]['y']),int(df.iloc[0]['w']),int(df.iloc[0]['h']))
# tracker = cv.TrackerCSRT_create()
# tracking = False
# while True:
#     _ret, img = cam.read()
#     if(_ret == False):
#         break
#     if(frame == df.iloc[0]['frame_start']):
#         tracker.init(img, initBB)
#         tracking = True
#     if tracking:
#         (success, box) = tracker.update(img)
#         if success:
#             (x, y, w, h) = [int(v) for v in box]
#             img = cv.rectangle(img, (x, y), (x + w, y + h),(0, 255, 0), 2)
#     cv.imshow("f",img)
#     cv.waitKey(5)
#     frame+=1
#     if(frame>100):
#         break

trackers = []
to_delete = []
boxes = []
while True:
    _ret, img = cam.read()
    if(_ret == False):
        break
    temp_df = df[(df.frame_start == frame)]
    if(temp_df is not None):
        for index,row in temp_df.iterrows():
            x = int(row['x'])
            if(x > 200):
                x = int(row['x'])-200
            y = int(row['y'])-400
            w = 3*int(row['w'])
            h = 6*int(row['h'])
            print(x,y+400)
            bbox = cv.selectROI(img, False)
            print("selected")
            initBB = (x,y,w,h)
            speed = float(row['speed'])
            tracker = cv.TrackerCSRT_create()
            tracker.init(img, bbox)
            failure_rate = 0
            trackers.append([tracker,int(row['frame_end'])-10,speed,failure_rate])
    if(len(trackers) > 0):
        for idx, item in enumerate(trackers):
            if(frame > item[1] or item[3]>4):
                to_delete.append(idx)
    if(len(to_delete) > 0):
        for item in to_delete:
            trackers.pop(item)
        to_delete = []
    for tracker_ in trackers:
        (success, box) = tracker_[0].update(img)
        if success:
            (x, y, w, h) = [int(v) for v in box]
            speed = tracker_[2]
            boxes.append([frame,x,y,w,h,speed])
            # img = cv.rectangle(img, (x, y), (x + w, y + h),(0, 255, 0), 2)
        else:
            tracker_[3] +=1
    # cv.imshow('f',img)
    # cv.waitKey(5)
    frame += 1
    if(frame%200==0):
        print(frame)
        # print("kength of trackers" + str(len(trackers)))
        # print("kength of to_delete" + str(len(to_delete)))

df = pd.DataFrame(boxes)
df.to_csv("boxes_data_3.csv",index=False,header=['frame','x','y','w','h','speed'])


from .method import get_json_ret, json_response_zh

class AuthMiddleware(object):
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request, *args, **kwargs):
        if "application/json" in request.headers.get("Content-Type"):
            import json
            request.json = json.loads(request.body)
        else:
            request.json = request.POST

        if not request.path.startswith('/negotiate_key'):
            shared_secret = request.session.get("shared_secret")
            if shared_secret is None:
                return json_response_zh(get_json_ret(42, msg="请先协商密钥"))
            from Crypto.Util.number import long_to_bytes
            request.DH_key = long_to_bytes(shared_secret)[:16].ljust(16, b'\x00')

        if not request.path.startswith('/negotiate_key') and not request.path == '/dynamicauth_api3/':
            request.data = request.json.get("data")
            if request.data is None:
                return json_response_zh(get_json_ret(40, msg="请传递 data 参数"))

        user_name = request.session.get("user_name")
        if user_name:
            from UserModel.models import UserModel
            request.user = UserModel.objects.get(user_name=user_name)
            if request.user is None:
                request.session["user_name"] = None

        response = self.get_response(request)
        return response


from raspberry_pi.adapter import Adapter
from raspberry_pi.human_sensor import HumanSensor
from raspberry_pi.humidity_sensor import HumiditySensor
from raspberry_pi.target import Target
from raspberry_pi.temperature_sensor import TemperatureSensor
import json

# 根据不同的key，获取需要被适配的类
def get_adaptee_class(key):
    adaptees = {
        'target': Target(),
        'temperature_sensor': TemperatureSensor(),
        'humidity_sensor': HumiditySensor(),
        'human_sensor': HumanSensor()
    }
    return adaptees.get(key, None)

if __name__ == "__main__":
    # 选择传感器类型
    adaptee_type = 'human_sensor'
    # 适配器进行适配
    adapter = Adapter(get_adaptee_class(adaptee_type))
    # 适配后的采集数据操作
    data = adapter.request()
    # 格式化为json格式
    json_data = json.dumps(data)
    print(json_data)
    # 传输到kafka


import pandas as pd
from simulation_model.create_params_grid import default_params, path_experiment_table
from utilities.small_functions import mkDir_if_not
import os
import seaborn as sns
import matplotlib.pyplot as plt
import numpy as np
from simulation_model.gather_results import bilinear_name

def get_results_experiment_name_path(experiment_name):
    results_dir = mkDir_if_not("simulation_model/results")
    return os.path.join(results_dir, "{}_results_cv.pkl".format(experiment_name))

def min_of_gt_0(x):
    return np.min(x[x > 0])

acronyms_dict = {"optirank": "optirank",
                 "logistic_regression_on_ranks": "rank-lr",
                 "logistic_regression": "lr"}

def acronym(serie):
    """df is a dataframe with column classifier_name. Add column classifier_acronym"""
    fun = lambda x: acronyms_dict[x]
    return serie.apply(fun)

dict_param_name_to_latex = {"d": "$d$",  # correspondences to the paper
                            "n_perturbing": "$d_{P}$",
                            "n_samples": "$n_{samples}$",
                            "tau": "$\\tau$",
                            "sigma": "$\sigma$"}

if __name__ == "__main__":
    experiment_table = pd.read_pickle(path_experiment_table)
    sns.set(font_scale=1.5)  # was 1.5
    sns.set_style("whitegrid")

    for i_row, row in experiment_table.iterrows():

        experiment_name = row["experiment_name"]
        n_params = row["n_params"]
        params_grid = list(row["param_grid"])
        results = pd.read_pickle(get_results_experiment_name_path(experiment_name))
        results["classifier"] = acronym(results["classifier_name"])

        for with_legend in [True, False]:
            output_dir = "simulation_model/results"
            if with_legend:
                legend = "full"
                location = "upper left"
                output_dir_plots = mkDir_if_not(os.path.join(output_dir, "plots", "legend_on"))

            else:
                legend = False
                location = "best"  # should have no effect
                output_dir_plots = mkDir_if_not(os.path.join(output_dir, "plots", "legend_off"))

            plt.figure()
            p = sns.lineplot(data=results.reset_index(), x="param_value", y="test_balanced_accuracy", hue="classifier",
                             legend=legend)
            lgd = plt.legend(loc='upper center', bbox_to_anchor=(0.5, -0.2), fancybox=True, shadow=False, ncol=3)
            p.axes.set_xlabel(dict_param_name_to_latex[pd.unique(results["param_name"]).item()], )
            p.axes.set_ylabel("test balanced accuracy (%)")
            # if pd.unique(results["param_name"]).item() == "sigma_m":
            #     p.axes.set_xscale("symlog", linthresh=min_of_gt_0(results[["param_value"]].values))
            figpath = os.path.join(output_dir_plots, "results_{}.pdf".format(experiment_name))
            plt.tight_layout()
            if with_legend:
                lgd.set_visible(True)
            else:
                lgd.set_visible(False)
            plt.savefig(figpath)
            plt.close()

            # overlap figure
            plt.figure()
            results_of_interest = results.loc[results.classifier_name == bilinear_name]
            p = sns.lineplot(data=results_of_interest.reset_index(), x="param_value", y="overlap", legend=legend)

            lgd = plt.legend(loc='upper center', bbox_to_anchor=(0.5, -0.2), fancybox=True, shadow=False, ncol=3)
            p.axes.set_xlabel(dict_param_name_to_latex[pd.unique(results["param_name"]).item()], )
            p.axes.set_ylabel("overlap")
            p.axes.set_ylim([0, 1])
            # if pd.unique(results["param_name"]).item() == "sigma_m":
            #     p.axes.set_xscale("symlog", linthresh=min_of_gt_0(results[["param_value"]].values))
            figpath = os.path.join(output_dir_plots, "results_overlap_{}.pdf".format(
                experiment_name))
            plt.tight_layout()
            if with_legend:
                lgd.set_visible(True)
            else:
                lgd.set_visible(False)
            plt.savefig(figpath)
            plt.close()

    # results for default parameters (taken from the experiment on d for instance)
    experiment_name = "different_d"
    results = pd.read_pickle(get_results_experiment_name_path(experiment_name))
    results_per_classifier = results.loc[
        results.param_value == default_params["d"], ["test_balanced_accuracy", "classifier_name", "overlap"]].groupby(
        ["classifier_name"]).agg(['mean', 'sem'])
    results_per_classifier.to_csv("simulation_model/results/results_default_parameters.csv")

    #output file for markdown
    outfile_md = "simulation_model/results/results_default_parameters.md"
    out_md = (100*results_per_classifier[("test_balanced_accuracy", "mean")]).map('{:,.0f}'.format) + " ± " + (100*results_per_classifier[("test_balanced_accuracy", "sem")]).map('{:,.0f}'.format)
    out_md = out_md.to_frame(name="test balanced accuracy")
    markdown_table = out_md.to_markdown()
    f = open(outfile_md, "w")
    f.write(markdown_table)
    f.close()


import pytest

from src.guess_number_higher_or_lower import Solution

@pytest.mark.parametrize(
    "n,pick,expected",
    [
        (10, 6, 6),
        (1, 1, 1),
        (2, 1, 1),
    ],
)
def test_solution(n, pick, expected, monkeypatch):
    monkeypatch.setenv("SECRET", str(pick))
    assert Solution().guessNumber(n) == expected


#!/usr/bin/env python3
import argparse
import os
import subprocess
import sys
from utilities import functional_annotation
from utilities import toGFF3
from utilities import clustering
from utilities import mapping
from utilities import mergeAll_to_gff
def main():
    parser = argparse.ArgumentParser()
    parser.add_argument("-p", "--faa", required = True, help="The location of the directory containing all .faa files")
    parser.add_argument("-n", "--fna", required = True, help = "The location of the directory containing all .fna files")
    parser.add_argument("-a", "--asem", required = True, help="The location of the directory containing all assembly files")
    parser.add_argument("-oa", "--annotationFolder",required = True, help="name of output containing annotation results")
    parser.add_argument("-og", "--gffFolder",required = True, help="name of output containing  gff files of each annotation tool")
    parser.add_argument("-om", "--mergeFolder",required = True, help="name of output containing all merged gff files")
    args = parser.parse_args()

    """ In order to run DOOR2 and VFDB, we need a database, so have to check if the database is provided in current working folder """
    operonDB="operonDB.fasta"
    vfdbDB="VFDBdb.fasta"
    if not os.path.exists(operonDB) or not os.path.exists(vfdbDB):
        raise SystemExit("missing database for DOOR2 or VFDB, cannot running DOOR2/VFDB.Exit")
    """ if there is no missing required files, then run annotation """
    faa_dir=args.faa
    """ cluster for eggNOG """
    mergeFile_faa="merge.txt"     #file that merge all 50 genomes
    clusterFolder="cluster_CDHIT"
    clusterFile="clusterFile.txt"
    if not os.path.exists(clusterFolder):
        clustering.main(faa_dir,mergeFile_faa,clusterFolder,clusterFile,0.95)
    """ name the output folder for each tool """
    outputFolder=args.annotationFolder
    tmhmm_out="{}/tmhmm_result".format(outputFolder)
    singalP_out="{}/signalP_result".format(outputFolder)
    door2_out="{}/door2_result".format(outputFolder)
    vfdb_out ="{}/VFDB_result".format(outputFolder)
    card_out="{}/card_result".format(outputFolder)
    piler_seq="{}/pilercr_seq".format(outputFolder)
    piler_out="{}/pilercr_result".format(outputFolder)
    eggNOG_out="{}/eggnog_result_oneFile".format(outputFolder) #contain 1 output , from annotated 1 cluster with eggnog
    eggNOG_map_out="{}/eggnog_result_allFiles".format(outputFolder)

    if not outputFolder in os.listdir():
        subprocess.call(["mkdir",outputFolder])
    try:
        if not door2_out.split("/")[1] in os.listdir(outputFolder):
            #functional_annotation.door2blast_local_all50(faa_dir,operonDB,"blast_operon_db",door2_out)
            functional_annotation.door2blast_server_all50(faa_dir,operonDB,"blast_operon_db",door2_out)
        if not vfdb_out.split("/")[1] in os.listdir(outputFolder):
            #functional_annotation.vfdbblast_local_all50(args.fna,vfdbDB,"blast_vfdb_db",vfdb_out)
            functional_annotation.vfdbblast_server_all50(args.fna,vfdbDB,"blast_vfdb_db",vfdb_out)
        if not card_out.split("/")[1] in os.listdir(outputFolder):
            functional_annotation.rgi_all50(faa_dir,card_out)
        if not tmhmm_out.split("/")[1] in os.listdir(outputFolder):
            functional_annotation.tmhmm_all50(faa_dir,tmhmm_out)
        if not singalP_out.split("/")[1] in os.listdir(outputFolder):
            functional_annotation.signalP_all50(faa_dir,singalP_out)
        if not piler_out.split("/")[1] in os.listdir(outputFolder):
            functional_annotation.piler_all50(args.asem,piler_seq,piler_out)
        if not eggNOG_out.split("/")[1] in os.listdir(outputFolder):
            subprocess.call(["python2","utilities/eggnog.py",eggNOG_out])
    except Exception as e:
        print(e)
        raise SystemExit("please fix the error. Pipeline is terminated")

    """ EGGNOG mapping: from one output of EGGNOG, map to 50 output files for 50 faa files"""
    try:
        if not eggNOG_map_out.split("/")[1] in os.listdir(outputFolder):
            names_file="allfiles.txt" #contains 50 id number of each genome
            id_file_faa="title.txt" # contains header line in merge file that concatenate all 50 genomes faa/fna
            mapping.main(faa_dir,names_file,id_file_faa,mergeFile_faa,eggNOG_out,clusterFolder,eggNOG_map_out)
    except Exception as e:
        print(e)
        sys.exit(-1)

    """ after annotation all 50 files, then convert to gff for each annotation tool"""
    try:
        gff_out_folder=args.gffFolder #folder containing gff files for each annotation tool
        if not gff_out_folder in os.listdir():
            subprocess.call(["mkdir",gff_out_folder])

        tmhmm_gff,signalP_gff,piler_gff,card_gff,door2_gff,vfdb_gff,eggnog_gff="{}/tmhmm_to_gff".format(gff_out_folder),"{}/signalP_to_gff".format(gff_out_folder)\
            ,"{}/pilercf_to_gff".format(gff_out_folder),"{}/card_to_gff".format(gff_out_folder),\
             "{}/door2_to_gff".format(gff_out_folder),"{}/vfdb_to_gff".format(gff_out_folder),"{}/eggnog_to_gff".format(gff_out_folder)

        toGFF3.main(tmhmm_out,tmhmm_gff,singalP_out,signalP_gff,door2_out,door2_gff,vfdb_out,vfdb_gff,card_out,card_gff,piler_out,piler_gff,eggNOG_map_out,eggnog_gff,outputFolder,gff_out_folder)
    except Exception as e:
        print(e)
        sys.exit(-1)

    #if there is exist all gff folders for all tool, we can begin to merge
    try:
        all_gff_faa=args.mergeFolder
        if all_gff_faa not in os.listdir():
            mergeAll_to_gff.main(tmhmm_gff,signalP_gff,piler_gff,card_gff,door2_gff,vfdb_gff,eggnog_gff,all_gff_faa)
    except Exception as e:
        print(e)
        sys.exit(-1)
if __name__ == "__main__":
    main()


#!/usr/bin/env python3

import asyncio
import unittest

from click.testing import CliRunner

from base_cli import _handle_debug, async_main, main

class TestCLI(unittest.TestCase):
    def test_async_main(self) -> None:
        self.assertEqual(0, asyncio.run(async_main(True)))

    def test_debug_output(self) -> None:
        self.assertTrue(_handle_debug(None, None, True))

    def test_help(self) -> None:
        runner = CliRunner()
        result = runner.invoke(main, ["--help"])
        assert result.exit_code == 0

if __name__ == "__main__":  # pragma: no cover
    unittest.main()


from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions
from selenium.webdriver.support.wait import WebDriverWait
from selenium.common import NoSuchElementException
from pages.utils import write_file

class BasePage:

    def __init__(self, driver):
        self.driver = driver
        self.waiter = WebDriverWait(driver=driver, timeout=5)

    def wait_until_displayed(self, by, xpath):
        """Waits until element displayed and return it, else raise an exception"""
        return self.waiter.until(
            method=expected_conditions.visibility_of_element_located(
                (by, xpath)
            )
        )

    def wait_until_clickable(self, by, xpath):
        """Waits until element clickable and return it, else raise an exception"""
        return self.waiter.until(
            method=expected_conditions.element_to_be_clickable((by, xpath)))

    def is_element_exist(self, xpath):
        """Waits until element exist, else raise an exception"""
        try:
            self.driver.find_element(by=By.XPATH, value=xpath)
            return True
        except (TimeoutError, NoSuchElementException):
            return False

    def is_element_visible(self, xpath):
        """Waits until element exist, else raise an exception"""
        try:
            self.wait_until_displayed(by=By.XPATH, xpath=xpath)
            return True
        except (TimeoutError, NoSuchElementException):
            return False

    def fill_field(self, xpath, value):
        """Fill field using provided value"""
        element = self.wait_until_clickable(by=By.XPATH, xpath=xpath)
        element.clear()
        element.send_keys(value)

    def fill_field_with_submit(self, xpath, value):
        """Fill field using provided value"""
        element = self.wait_until_clickable(by=By.XPATH, xpath=xpath)
        element.clear()
        element.send_keys(value)
        element.submit()

    def click(self, xpath):
        """Find and click on the element by providing xpath"""
        # self.wait_until_displayed(by=By.XPATH, xpath=xpath).click()
        self.driver.find_element(by=By.XPATH, value=xpath).click()

    def move_mouse_on_element(self, xpath):
        """Moves mouse on provided element"""
        try:
            action = webdriver.ActionChains(self.driver)
            element = self.driver.find_element(by=By.XPATH, value=xpath)
            action.move_to_element(element)
            action.perform()
        except (BaseException, Exception) as ex:
            write_file('move_mouse_on_element() Exception = ', ex)

    def switch_to_alert(self, alert_accept_dismiss):
        """Moves focus to Alert window"""
        self.waiter.until(
            method=expected_conditions.alert_is_present()
        )
        if alert_accept_dismiss:
            self.driver.switch_to.alert.accept()
        else:
            self.driver.switch_to.alert.dismiss()

    def get_element_value(self, xpath):
        """Get element attribute value"""
        if self.is_element_exist(xpath=xpath):
            element = self.driver.find_element(By.XPATH, xpath).get_attribute('value')
            return element

    def compare_element_text(self, text, xpath):
        """Compare element's text with provided text """
        element = self.wait_until_displayed(by=By.XPATH, xpath=xpath)
        return element.text == text


#  -*-coding:utf8-*-

################################################################################
#
#
#
################################################################################
"""
模块用法说明:达人推荐详情页

Authors: Turinblueice
Date: 2016/9/10
"""

from base import base_frame_view
from util import log

from gui_widgets.basic_widgets import frame_layout
from gui_widgets.basic_widgets import image_view
from gui_widgets.basic_widgets import text_view
from gui_widgets.basic_widgets import linear_layout
from gui_widgets.basic_widgets import recycler_view
from gui_widgets.basic_widgets import radio_button

from appium.webdriver import WebElement
from appium.webdriver.common import touch_action
from selenium.webdriver.common.touch_actions import TouchActions
from activities import activities

from appium.webdriver.common.mobileby import MobileBy
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import TimeoutException
import time

class TalentRecommendActivity(base_frame_view.BaseFrameView):

    """
        Summary:
            达人推荐页面

        Attributes:

    """
    name = '.module.square.activity.EretarActivity'  # 裁剪图片activity名称

    def __init__(self, parent):
        super(TalentRecommendActivity, self).__init__(parent)

        #  等待初始化
        self.wait_for_element_present(self.base_parent, id='com.jiuyan.infashion:id/login_tv_title')

        self._scroll_view = recycler_view.RecyclerView(self.parent, id='com.jiuyan.infashion:id/square_rv_tag')

    @property
    def talent_recommend(self):
        """
            Summary:
                达人推荐按钮
        """
        id_ = 'com.jiuyan.infashion:id/login_tv_title'
        return text_view.TextView(self.parent, id=id_)

    @property
    def talent_apply(self):
        """
            Summary:
                申请达人按钮
        """
        id_ = 'com.jiuyan.infashion:id/login_tv_title_right'
        return text_view.TextView(self.parent, id=id_)

    @property
    def back_button(self):
        """
            Summary:
                返回按钮
        """
        id_ = 'com.jiuyan.infashion:id/login_tv_title_left'
        return text_view.TextView(self.parent, id=id_)

    @property
    def talent_list(self):
        """
            Summary:
                达人列表
        :return:
        """
        xpath_ = '//android.support.v7.widget.RecyclerView[@resource-id="com.jiuyan.infashion:id/square_rv_tag"]/' \
                 'android.widget.LinearLayout'
        return TalentContainerList(self.base_parent, xpath=xpath_).item_list

    @property
    def category_list(self):
        """
            Summary:
                种类列表
        :return:
        """
        xpath_ = '//android.widget.ListView[@resource-id="com.jiuyan.infashion:id/square_rv_menu"]/' \
                 'android.widget.LinearLayout'
        return TalentCategoryList(self.base_parent, xpath=xpath_).item_list

    # **************************操作方法*****************************
    def wait_for_talent(self, timeout=10):
        """
            Summary:
                显示等待达人加载完毕
        :return:
        """
        xpath_ = '//android.support.v7.widget.RecyclerView[@resource-id="com.jiuyan.infashion:id/square_rv_tag"]/' \
                 'android.widget.LinearLayout'
        if self.wait_for_element_present(self.base_parent, timeout=timeout, xpath=xpath_):
            log.logger.info("达人列表已记载")
            return True
        log.logger.error("达人列表加载失败")
        return False

    def tap_back_button(self):
        """
            Summary:
                点击返回按钮
        """
        log.logger.info("开始点击返回按钮")
        self.back_button.tap()
        log.logger.info("完成返回按钮点击")
        if self.wait_activity(activities.ActivityNames.SQUARE_CATEGORY, 10):
            log.logger.info("成功返回到话题分类页面")
            return True
        log.logger.error("返回失败")
        return False

class TalentContainer(base_frame_view.BaseFrameView):
    """
        Summary:
            达人推荐
    """
    def __init__(self, parent, item=None, index=None, **kwargs):
        super(TalentContainer, self).__init__(parent)
        self._layout_view = item if isinstance(item, WebElement) else self.find_element(**kwargs)
        self._index = index
        self._xpath = '//android.support.v7.widget.RecyclerView[@resource-id="com.jiuyan.infashion:id/square_rv_tag"]/' \
                      'android.widget.LinearLayout[{}]'.format(self._index+1)

    @property
    def talent_name(self):
        """
            Summary:
                达人姓名
        """
        id_ = 'com.jiuyan.infashion:id/square_tv_name'
        return text_view.TextView(self._layout_view, id=id_).text

    @property
    def talent_avatar(self):
        """
            Summary:
                达人头像
        """
        id_ = 'com.jiuyan.infashion:id/transition_avatar_id'
        return image_view.ImageView(self._layout_view, id=id_)

    @property
    def follow_button(self):
        """
            Summary:
                关注按钮
        """
        id_ = 'com.jiuyan.infashion:id/square_tv_attention'
        return radio_button.RadioButton(self._layout_view, id=id_)

    @property
    def image_list(self):
        """
            Summary:
                图片列表
        """
        return image_view.ImageViewList(self._layout_view, id='com.jiuyan.infashion:id/login_iv_pic').image_list

    # ********************操作方法*************************

    def tap_avatar(self):
        """
            Summary:
                点击头像

        """
        curr_name = self.talent_name
        log.logger.info("开始点击\"{}\"的头像".format(curr_name))
        self.talent_avatar.tap()
        log.logger.info("点击完毕")
        if self.base_parent.wait_activity(activities.ActivityNames.DIARY_INFO, 10):
            log.logger.info("成功进入好友in记页面")
            return True
        log.logger.error("进入好友in记页面失败")
        return False

    def tap_image(self, index):
        """
            点击图片
        Args:
            index:
                图片序号
        Returns:

        """
        log.logger.info("点击第{}张".format(index+1))
        self.image_list[index].tap()
        log.logger.info("完成点击")
        if self.base_parent.wait_activity(activities.ActivityNames.FRIEND_PHOTO_DETAIL, 10):
            log.logger.info("成功进入好友照片页面")
            return True
        log.logger.error("进入好友照片页面失败")
        return False

    def tap_follow_button(self):
        """
            Summary:
                点击关注按钮
        """
        log.logger.info("开始点击关注")
        self.follow_button.tap()
        time.sleep(2)
        log.logger.info("点击完毕")
        try:
            WebDriverWait(self.base_parent, 10).until(
                EC.element_located_to_be_selected(
                    (MobileBy.XPATH, self._xpath+'/android.widget.RelativeLayout[1]/android.widget.RadioButton[1]')
                )
            )
            return True
        except:
            return False

class TalentContainerList(base_frame_view.BaseFrameView):

    def __init__(self, parent, **kwargs):
        super(TalentContainerList, self).__init__(parent)
        self.__list = self.find_elements(**kwargs)

    @property
    def item_list(self):

        if self.__list:
            return [TalentContainer(item.parent, item, index) for index, item in enumerate(self.__list)]
        return None

class TalentCategory(base_frame_view.BaseFrameView):
    """
        Summary:
            达人分类
    """
    def __init__(self, parent, item=None, **kwargs):
        super(TalentCategory, self).__init__(parent)
        self._layout_view = item if isinstance(item, WebElement) else self.find_element(**kwargs)

    @property
    def title(self):
        """
            Summary:
                达人分类的名称
        """
        id_ = 'com.jiuyan.infashion:id/square_tv_tag_menu'
        return text_view.TextView(self._layout_view, id=id_).text

    # ********************操作方法*************************

    def tap(self):
        """
            Summary:
                点击分类
        """
        title_ = self.title
        log.logger.info("开始点击\"{}\"".format(title_))
        self._layout_view.click()
        xpath_ = '//android.support.v7.widget.RecyclerView[@resource-id="com.jiuyan.infashion:id/square_rv_tag"]/' \
                 'android.widget.LinearLayout'
        if self.wait_for_element_present(self.base_parent, xpath=xpath_):
            # 点击左侧不同的达人类别后,等待右侧达人初始化加载
            log.logger.info("\"{}\"的达人已加载成功".format(title_))
            return True
        log.logger.error("达人列表初始化失败")
        return False

class TalentCategoryList(base_frame_view.BaseFrameView):

    def __init__(self, parent, **kwargs):
        super(TalentCategoryList, self).__init__(parent)
        self.__list = self.find_elements(**kwargs)

    @property
    def item_list(self):

        if self.__list:
            return [TalentCategory(item.parent, item) for item in self.__list]
        return None


import numpy as np
import matplotlib
#matplotlib.use('TkAgg')
import matplotlib.pyplot as plt

from skimage import data, img_as_float
from skimage.metrics import structural_similarity as ssim
from skimage.metrics import mean_squared_error

from skimage.transform import rescale, resize, downscale_local_mean
from skimage import io

im1 = io.imread('frac3_run2_sp5_map16.pgm')
im2 = io.imread('frac2_run4_sp5_map2.pgm')
im3 = io.imread('frac1A_run2_map5.pgm')
badim = io.imread('frac9A_run1_map2.pgm')
imfull = io.imread('frac3A_run2_map8.pgm')

plt.imshow(im1, plt.cm.gray)
plt.savefig("mygraph.png")

rows, cols = im1.shape
print("Size of im1: ",rows,cols)
print("Size of im2: ", im2.shape)
print("Size of im3: ", im3.shape)

img1 = resize(im1, (520, 500) )
img2 = resize(im2, (520, 500) )
img3 = resize(im3, (520, 500) )
imgFull = resize(imfull, (520, 500) )
badimg = resize(badim, (520, 500) )

plt.imshow(img1, plt.cm.gray)
plt.savefig("mygraph1.png")

plt.imshow(img2, plt.cm.gray)
plt.savefig("mygraph2.png")

plt.imshow(img3, plt.cm.gray)
plt.savefig("mygraph3.png")

plt.imshow(imgFull, plt.cm.gray)
plt.savefig("mygraph.png")

mse_11 = mean_squared_error(img1,img1)
mse_12 = mean_squared_error(img1,img2)
mse_22 = mean_squared_error(img2,img2)
mse_23 = mean_squared_error(img2, img3)
mse_13 = mean_squared_error(img1, img3)

print("MSE Error 11: %f, 12: %f, 22: %f, 23: %f, 13: %f"%(mse_11, mse_12, mse_22, mse_23, mse_13))

ssim11 = ssim(img1, img1, data_range=img1.max()-img1.min())
ssim22 = ssim(img2, img2, data_range=img2.max()-img2.min())
ssim12 = ssim(img1, img2, data_range=img1.max()-img1.min())
ssim23 = ssim(img2, img3, data_range=img1.max()-img1.min())
ssim13 = ssim(img1, img3, data_range=img1.max()-img1.min())
print("SSIM 12: ", ssim12, "ssim11 %f, ssim22 %f, ssim23 %f, ssim13: %f"%(ssim11, ssim22, ssim23, ssim13) )

print("Comparing with FULL map:")
print("MSE 1full: %f, 2full: %f, 3full: %f, badimg-full: %f"%(mean_squared_error(img1, imgFull) ,mean_squared_error(img2, imgFull) ,mean_squared_error(img3, imgFull), mean_squared_error(badimg, imgFull) ) )
print("SSIM with full: 1f: %f, 2f: %f, 3f: %f, badimg-full %f"%( ssim(img1, imgFull, data_range=imgFull.max()-imgFull.min()), ssim(img2, imgFull, data_range=imgFull.max()-imgFull.min()), ssim(img3, imgFull, data_range=imgFull.max()-imgFull.min()), ssim(badimg, imgFull, data_range=imgFull.max()-imgFull.min()) ) )


import random
from collections import defaultdict

import torch
from ltp import LTP

from .base_func import BaseFunc

class NerFunc(BaseFunc):
    def __init__(self, config):
        super(NerFunc, self).__init__(config)
        self.augment_num = config.ner_func.augment_num
        self.combine_dict = self.load_ner_files()
        self.model = LTP(config.ner_func.ner_tool_name)
        if torch.cuda.is_available():
            self.model.to("cuda")

    @staticmethod
    def load_ner_files():
        combine_dict = defaultdict(set)
        # Nh file
        for line in open("files/ner/people_name.txt", "r", encoding="utf-8"):
            combine_dict["Nh"].add(line.strip())
        # Ns file
        for line in open("files/ner/place_name.txt", "r", encoding="utf-8"):
            combine_dict["Ns"].add(line.strip())
        # Ni file
        for line in open("files/ner/company_name.txt", "r", encoding="utf-8"):
            combine_dict["Ni"].add(line.strip())
        return combine_dict

    def process(self, sentence):
        final_augment_sentence = []
        seg_list = self.cut_words(sentence)
        result = self.model.pipeline(seg_list, tasks=["ner"])
        if len(result.ner) == 0:
            return final_augment_sentence
        for _ in range(self.augment_num):
            n, word = random.choice(result.ner)
            if n in self.combine_dict.keys():
                new_word = random.choice(list(self.combine_dict[n]))
                old_index = seg_list.index(word)
                seg_list[old_index] = new_word
                new_sentence = ''.join(seg_list)
                final_augment_sentence.append(new_sentence)

        return final_augment_sentence


#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Python version: 3.6
import os
import copy
import time
import pickle
import numpy as np
from tqdm import tqdm
import yaml
import argparse

import torch
from tensorboardX import SummaryWriter

# from src.options import args_parser
from update import LocalUpdate
from utils import get_dataset, average_weights, exp_details
from deepctr_torch.inputs import get_feature_names
from sklearn.metrics import log_loss, roc_auc_score

from deepctr_torch.models import DeepFM

if __name__ == '__main__':
    start_time = time.time()

    # define paths
    path_project = os.path.abspath('..')
    logger = SummaryWriter('../logs')

    parser = argparse.ArgumentParser(description='PPDL')
    parser.add_argument('--params', dest='params', default='utils/params.yaml')
    params = parser.parse_args()

    with open(f'../{params.params}', 'r') as f:
        args = yaml.load(f)

    exp_details(args)
    exit(0)
    if args['gpu']:
        torch.cuda.set_device(args['gpu'])
    device = 'cuda' if args['gpu'] else 'cpu'

    # load dataset and user groups  # prepare feature for model
    (train_dataset, test_dataset, user_groups),fixlen_feature_columns = get_dataset(args)

    # count #unique features for each sparse field,and record dense feature field name

    dnn_feature_columns = fixlen_feature_columns
    linear_feature_columns = fixlen_feature_columns

    feature_names = get_feature_names(linear_feature_columns + dnn_feature_columns)

    # BUILD MODEL
    if args['model'].lower() == 'deepfm':
        # 4.Define Model,train,predict and evaluate
        global_model = DeepFM(linear_feature_columns, dnn_feature_columns, task='binary')
        global_model.compile("adam", "binary_crossentropy",
                             metrics=['binary_crossentropy'], )
    else:
        exit('Error: unrecognized model')

    # # Set the model to train and send it to device.
    # global_model.to(device)
    # global_model.train() # torch claim

    # print(global_model)

    # copy weights
    global_weights = global_model.state_dict()
    # print(global_weights.keys())

    # Training
    # train_loss, train_accuracy = [], []
    # val_acc_list, net_list = [], []
    # cv_loss, cv_acc = [], []
    # print_every = 2
    # val_loss_pre, counter = 0, 0

    # temp test data

    test_model_input = {name: test_dataset[name] for name in feature_names}

    # for comparsion
    # best_model = copy.deepcopy(global_model)
    min_loss = 1000.0
    max_auc = -1.0

    for epoch in tqdm(range(args['epochs'])):
        local_weights= [] #, local_losses , []
        print(f'\n | Global Training Round : {epoch+1} |\n')
        # frac default 0.1; num_users default 100
        m = max(int(args['frac'] * args['num_users']), 1)
        # 100 randomly select 10 as training client
        idxs_users = np.random.choice(range(args['num_users']), m, replace=False)

        for idx in idxs_users: # 10 random users
            local_model = LocalUpdate(args=args, dataset=train_dataset,
                                      idxs=user_groups[idx], logger=logger)
            w = local_model.update_weights(
                model=copy.deepcopy(global_model), features=feature_names)

            local_weights.append(copy.deepcopy(w))
            # local_losses.append(copy.deepcopy(loss))

        # update global weights
        global_weights = average_weights(local_weights)
        global_model.load_state_dict(global_weights)

        # temp test
        pred_ans = global_model.predict(test_model_input, batch_size=256)
        logloss = log_loss(test_dataset['label'].values, pred_ans)
        aucscore = roc_auc_score(test_dataset['label'].values, pred_ans)
        print("test LogLoss", round(logloss, 4))
        print("test AUC", round(aucscore, 4))
        if aucscore > max_auc:
            # best_model = copy.deepcopy(global_model)
            min_loss = logloss
            max_auc = aucscore

    print("|---- Min log loss: {:.4f}%".format(min_loss))
    print("|---- Best AUC: {:.4f}%".format(max_auc))
    print("test done")


import openpyxl
from openpyxl.utils import cell

def read(config):
    try:
        workbook = openpyxl.load_workbook(config.source_path, data_only=True)
        datasets = {}
        for source_tab_name in config.source_tabs:
            datasets[source_tab_name] = extract_dataset(workbook, source_tab_name, config)
        return datasets
    finally:
        workbook.close()

def extract_dataset(workbook, source_tab_name, config):
    worksheet = workbook.get_sheet_by_name(source_tab_name)
    last_data_row = find_last_data_row(worksheet, config.start_search_row, config.column_range_start)
    copy_range_start = "{}{}".format(config.column_range_start, config.start_search_row)
    copy_range_end   = "{}{}".format(config.column_range_stop, last_data_row)
    return worksheet[copy_range_start:copy_range_end]

def find_last_data_row(worksheet, start_row, column_range_start):
    column_index = cell.column_index_from_string(column_range_start)
    for current_row in range(start_row, worksheet.max_row):
        val = worksheet.cell(row = current_row, column = column_index).value
        if worksheet.cell(row = current_row, column = column_index).value == None:
            return current_row - 1
    return worksheet.max_row


# -*- coding: utf-8  -*-
import yaml

from . import instructions
from . import value_containers
from .exceptions import ParserException
from .method import Method

def _process_func(method, func):
    if not func or not isinstance(func, dict):
        raise ParserException('"func" not defined')
    method.function_name = func['name']
    method.return_type = value_containers.types[func['type'].lower()]()
    _process_vars(method, func['args'], inc_arg_count=True)

def _add_label(method, label, index):
    if not isinstance(label, str):
        raise ParserException('label %s needs to be a string' % label)
    if label is None:
        raise ParserException('every local variable needs a label')
    if label in method.labels:
        raise ParserException('labels has to be unique: duplicate %s' % label)
    method.labels[label] = index

def _process_vars(method, args, inc_arg_count=False):
    for arg in args:
        t = arg['type'].lower()
        if inc_arg_count:
            method.argument_count += 1
        method.variables.append(value_containers.types[t]())
        l = arg.get('label', None)
        _add_label(method, l, len(method.variables) - 1)

def _process_ins(method, ins):
    # pass1: collect labels
    offset = 0
    label = None
    label_current = None
    for i in ins:
        if isinstance(i, dict):
            if not len(i) == 1:
                raise ParserException('bad syntax for data %s' % i)
            label_current = i.get('label', None)
            if label and label_current:
                raise ParserException('label cannot follow label: %s, %s' % (label, label_current))
            label = label_current
        else:
            label = None
            label_current = None
        if label:
            _add_label(method, label, offset)
        else:
            offset += 1
    else:
        if label:
            raise ParserException('label cannot be as last instruction %s' % i)

    # pass2: use labels and collect instructions
    offset = 0
    for i in ins:
        if isinstance(i, dict) and i.get('label', None):
            continue
        else:
            offset += 1
        if isinstance(i, str):
            inst = instructions.keywords[i.lower()]
            if not issubclass(inst, instructions.InsNoArgument):
                raise ParserException('instruction %s requires argument' % i)
            method.code.append(inst())
        elif isinstance(i, dict):
            kw, value = i.popitem()
            inst = instructions.keywords[kw.lower()]
            if issubclass(inst, instructions.InsNoArgument):
                raise ParserException('instruction %s takes no argument' % i)
            if issubclass(inst, instructions.InsArgILabel):
                if isinstance(value, str):
                    try:
                        value = method.labels[value]
                    except KeyError:
                        raise ParserException('label %s is not defined' % value)
                else:
                    raise ParserException('instruction %s requires label as argument' % i)
            elif issubclass(inst, instructions.InsArgInteger):
                if value != int(value):
                    raise ParserException('instruction %s requires integer argument' % i)
            elif issubclass(inst, instructions.InsArgFloat):
                if value != float(value):
                    raise ParserException('instruction %s requires float argument' % i)

            method.code.append(inst(instructions.contain_value(inst, value)))
        else:
            raise ParserException('unknown instruction format %s' % i)

def process_yaml(structure):
    m = Method()
    _process_func(m, structure.get('func', {}))
    _process_vars(m, structure.get('lvars', []))
    _process_ins(m, structure.get('ins', []))
    return m

def parse_string(data):
    structure = yaml.safe_load(data)
    return process_yaml(structure)

def parse_file(fname):
    with open(fname, 'r') as f:
        return parse_string(f)


import argparse
import os
from distutils.util import strtobool
import random
import time

import numpy as np
import torch
import gym
import torch.nn as nn
import torch.nn.functional as F
from torch.utils.tensorboard import SummaryWriter
import torch.optim as optim
from stable_baselines3.common.buffers import ReplayBuffer

def parse_args():
    parser = argparse.ArgumentParser()
    # Experiment settings
    parser.add_argument("--exp-name", type=str, default=os.path.basename(__file__).rstrip(".py"),
                        help="the name of this experiment")
    parser.add_argument("--seed", type=int, default=1,
                        help="seed of the experiment")
    parser.add_argument("--torch-deterministic", type=lambda x: bool(strtobool(x)), default=True, nargs="?", const=True,
                        help="if toggled, `torch.backends.cudnn.deterministic=False`")
    parser.add_argument("--cuda", type=lambda x: bool(strtobool(x)), default=True, nargs="?", const=True,
                        help="if toggled, cuda will be enabled by default")
    parser.add_argument("--track", type=lambda x: bool(strtobool(x)), default=False, nargs="?", const=True,
                        help="if toggled, this experiment will be tracked with Weights and Biases")
    parser.add_argument("--wandb-project-name", type=str, default="cfrl",
                        help="the wandb's project name")
    parser.add_argument("--wandb-entity", type=str, default=None,
                        help="the entity (team) of wandb's project")
    parser.add_argument("--capture-video", type=lambda x: bool(strtobool(x)), default=False, nargs="?", const=True,
                        help="whether to capture videos of the agent performances (check out `videos` folder)")
    parser.add_argument("--env-id", type=str, default="CartPole-v1",
                        help="the id of the environment")
    # Hyperparameters
    parser.add_argument("--v-max", type=float, default=100,
                        help="the number of atoms")
    parser.add_argument("--v-min", type=float, default=-100,
                        help="the number of atoms")
    parser.add_argument("--n-atoms", type=int, default=101,
                        help="the number of atoms")
    parser.add_argument("--replay-memory-size", type=int, default=1000000,
                        help="SGD updates are sampled from this number of most recent frames")
    parser.add_argument("--agent-history-length", type=int, default=4,
                        help="The number of most recent frames experienced by the agent that are given as input to the Q network")
    parser.add_argument("--action-repeat", type=int, default=4,
                        help="repeate each action selected by the agent this many times")
    parser.add_argument("--minibatch-size", type=int, default=128,
                        help="the batch size of sample from the reply memory")
    parser.add_argument("--update-frequency", type=int, default=4,
                        help="the number of actions selected by the agent between successive SGD updates")
    parser.add_argument("--gamma", type=float, default=0.99,
                        help="the discount factor gamma")
    parser.add_argument("--learning-rate", type=float, default=2.5e-4,
                        help="the learning rate of the optimizer")
    parser.add_argument("--target-network-frequency", type=int, default=10000,
                        help="the timesteps it takes to update the target network")
    parser.add_argument("--initial-exploration", type=float, default=1,
                        help="the starting epsilon for exploration")
    parser.add_argument("--final-exploration", type=float, default=0.1,
                        help="the ending epsilon for exploration")
    parser.add_argument("--final-exploration-frame", type=int, default=1000000,
                        help="the number of frames over which the initial value of epsilon is linearly annealed to its final value")
    parser.add_argument("--replay-start-size", type=int, default=50000,
                        help="A uniform random  policy is run for this number of frames before learning starts and the resulting experience is used to populate the replay memory")
    parser.add_argument("--noop-max", type=int, default=30,
                        help="maximum number of doing nothing action to be performed by the agent at the start of an episode")
    parser.add_argument("--total-timesteps", type=int, default=10000000,
                        help="total timesteps of the experiments")

    args = parser.parse_args()
    # fmt: on
    return args

def make_env(env_id, seed, idx, capture_video, run_name):
    def thunk():
        env = gym.make(env_id)
        env = gym.wrappers.RecordEpisodeStatistics(env)
        if capture_video:
            if idx == 0:
                env = gym.wrappers.RecordVideo(env, f"videos/{run_name}")
        env.seed(seed)
        env.action_space.seed(seed)
        env.observation_space.seed(seed)
        return env

    return thunk

class QNetwork(nn.Module):
    def __init__(self, env, n_atoms=101, v_min=-100, v_max=100):
        super().__init__()
        self.env = env
        self.n_atoms = n_atoms
        self.register_buffer("atoms", torch.linspace(
            v_min, v_max, steps=n_atoms))
        self.n = env.single_action_space.n
        self.network = nn.Sequential(
            nn.Linear(np.array(env.single_observation_space.shape).prod(), 120),
            nn.ReLU(),
            nn.Linear(120, 84),
            nn.ReLU(),
            nn.Linear(84, self.n * n_atoms),
        )

    def forward(self, x):
        return self.network(x)

    def get_action(self, x, action=None):
        logits = self.network(x)
        # probability mass function for each action
        pmfs = torch.softmax(logits.view(len(x), self.n, self.n_atoms), dim=2)
        q_values = (pmfs * self.atoms).sum(2)
        if action is None:
            action = torch.argmax(q_values, 1)
        return action, pmfs[torch.arange(len(x)), action]

def linear_schedule(start_e: float, end_e: float, duration: int, t: int):
    slope = (end_e - start_e) / duration
    return max(slope * t + start_e, end_e)

if __name__ == "__main__":
    args = parse_args()
    run_name = f"{args.env_id}__{args.exp_name}__{args.seed}__{int(time.time())}"
    if args.track:
        import wandb

        wandb.init(
            project=args.wandb_project_name,
            entity=args.wandb_entity,
            sync_tensorboard=True,
            config=vars(args),
            name=run_name,
            monitor_gym=True,
            save_code=True,
        )
    writer = SummaryWriter(f"runs/{run_name}")
    writer.add_text(
        "hyperparameters",
        "|param|value|\n|-|-|\n%s" % (
            "\n".join([f"|{key}|{value}|" for key, value in vars(args).items()])),
    )

    # TRY NOT TO MODIFY: seeding
    random.seed(args.seed)
    np.random.seed(args.seed)
    torch.manual_seed(args.seed)
    torch.backends.cudnn.deterministic = args.torch_deterministic

    device = torch.device(
        "cuda" if torch.cuda.is_available() and args.cuda else "cpu")

    # env setup
    envs = gym.vector.SyncVectorEnv(
        [make_env(args.env_id, args.seed, 0, args.capture_video, run_name)])
    assert isinstance(envs.single_action_space,
                      gym.spaces.Discrete), "only discrete action space is supported"

    q_network = QNetwork(envs, n_atoms=args.n_atoms,
                         v_min=args.v_min, v_max=args.v_max).to(device)
    optimizer = optim.Adam(q_network.parameters(
    ), lr=args.learning_rate, eps=0.01 / args.minibatch_size)
    target_network = QNetwork(envs).to(device)
    target_network.load_state_dict(q_network.state_dict())

    rb = ReplayBuffer(
        args.replay_memory_size,
        envs.single_observation_space,
        envs.single_action_space,
        device,
        optimize_memory_usage=True,
        handle_timeout_termination=True,
    )
    start_time = time.time()

    # TRY NOT TO MODIFY: start the game
    obs = envs.reset()
    for global_step in range(args.total_timesteps):
        # ALGO LOGIC: put action logic here
        epsilon = linear_schedule(
            args.initial_exploration, args.final_exploration, args.final_exploration_frame, global_step)
        if random.random() < epsilon:
            actions = np.array([envs.single_action_space.sample()
                               for _ in range(envs.num_envs)])
        else:
            actions, pmf = q_network.get_action(torch.Tensor(obs).to(device))
            actions = actions.cpu().numpy()

        # TRY NOT TO MODIFY: execute the game and log data.
        next_obs, rewards, dones, infos = envs.step(actions)

        # TRY NOT TO MODIFY: record rewards for plotting purposes
        for info in infos:
            if "episode" in info.keys():
                print(
                    f"global_step={global_step}, episodic_return={info['episode']['r']}")
                writer.add_scalar("charts/episodic_return",
                                  info["episode"]["r"], global_step)
                writer.add_scalar("charts/episodic_length",
                                  info["episode"]["l"], global_step)
                writer.add_scalar("charts/epsilon", epsilon, global_step)
                break

        # TRY NOT TO MODIFY: save data to reply buffer; handle `terminal_observation`
        real_next_obs = next_obs.copy()
        for idx, d in enumerate(dones):
            if d:
                real_next_obs[idx] = infos[idx]["terminal_observation"]
        rb.add(obs, real_next_obs, actions, rewards, dones, infos)

        # TRY NOT TO MODIFY: CRUCIAL step easy to overlook
        obs = next_obs

        # ALGO LOGIC: training.
        if global_step > args.replay_start_size and global_step % args.update_frequency == 0:
            data = rb.sample(args.minibatch_size)
            with torch.no_grad():
                _, next_pmfs = target_network.get_action(
                    data.next_observations)
                next_atoms = data.rewards + args.gamma * \
                    target_network.atoms * (1 - data.dones)
                # projection
                delta_z = target_network.atoms[1] - target_network.atoms[0]
                tz = next_atoms.clamp(args.v_min, args.v_max)

                b = (tz - args.v_min) / delta_z
                l = b.floor().clamp(0, args.n_atoms - 1)
                u = b.ceil().clamp(0, args.n_atoms - 1)
                # (l == u).float() handles the case where bj is exactly an integer
                # example bj = 1, then the upper ceiling should be uj= 2, and lj= 1
                d_m_l = (u + (l == u).float() - b) * next_pmfs
                d_m_u = (b - l) * next_pmfs
                target_pmfs = torch.zeros_like(next_pmfs)
                for i in range(target_pmfs.size(0)):
                    target_pmfs[i].index_add_(0, l[i].long(), d_m_l[i])
                    target_pmfs[i].index_add_(0, u[i].long(), d_m_u[i])

            _, old_pmfs = q_network.get_action(
                data.observations, data.actions.flatten())
            loss = (-(target_pmfs * old_pmfs.clamp(min=1e-5,
                    max=1 - 1e-5).log()).sum(-1)).mean()

            if global_step % 100 == 0:
                writer.add_scalar("losses/td_loss", loss, global_step)
                old_val = (old_pmfs * q_network.atoms).sum(1)
                writer.add_scalar("losses/q_values",
                                  old_val.mean().item(), global_step)
                print("SPS:", int(global_step / (time.time() - start_time)))
                writer.add_scalar(
                    "charts/SPS", int(global_step / (time.time() - start_time)), global_step)

            # optimize the model
            optimizer.zero_grad()
            loss.backward()
            optimizer.step()

            # update the target network
            if global_step % args.target_network_frequency == 0:
                target_network.load_state_dict(q_network.state_dict())

    envs.close()
    writer.close()


from array import *
import os
from PIL import Image

Buttons=[0x300fd40bf,0x300fdc03f,0x300fd20df,0x300fda05f,0x300fd609f,0x300fde01f,0x300fd10ef,0x300fd906f,0x300fd50af,0x300fd30cf,0x300fdb24d,0x300fd728d,0x300fdf20d,0x300fd8877,0x300fd48b7]
ButtonsNames=["One","Two","Three","Four","Five","Six","Seven","Eight","Nine","Zero","MENU","TITLE","L/R","Left_key","Right_key"]
distantToFolderMappings = {
    12: "C:/Users/rakib/OneDrive/Desktop/Optoshi/12 Feet",
    10: "C:/Users/rakib/OneDrive/Desktop/Optoshi/10 Feet",
    8: "C:/Users/rakib/OneDrive/Desktop/Optoshi/8 Feet"
}
menuMapping = {
    "One": "e",
    "Two": "pediatric"
}
distanceMapping = {
    "Eight": 8,
    "Ten": 10
}
def convertHex(binaryValue):
	tmpB2 =int(str(binaryValue),2)
	return hex(tmpB2)

class Folder:
    menuId = ""
    images = []
    name = ""
    currentIndex = 0
    def __init__(self, distance, menuId):
        self.name = menuMapping[menuId]
        distancePath = distantToFolderMappings[distance]
        actualFolderPath = distancePath+"/"+self.name
        folderImages = self.get_files_in_directory(actualFolderPath)
        self.images = [file for file in folderImages if file.endswith(".png") or file.endswith(".jpg")]

        self.currentIndex = 0
        self.menuId = menuId
        self.openImage()
        #print(self.images)
    def openImage(self):
        print("Opening from Folder "+self.name+" with index "+str(self.currentIndex))
        img = Image.open(self.images[self.currentIndex])
        self.currentImage = img
        img.show()
    def moveIndexToLeft(self):
         if self.currentIndex-1<0:
            self.currentIndex = len(self.images)-1
         else:
            self.currentIndex = self.currentIndex-1
         print("Processing done")
    def moveIndexToRight(self):
         if self.currentIndex+1<len(self.images):
            self.currentIndex = self.currentIndex + 1
         else:
            self.currentIndex = 0

    def processIrSignal(self, signal):
        status = 1
        if signal == "Left":
           self.moveIndexToLeft()
           print(signal + " Pressed on " + self.name)
        elif signal == "Right":
            self.moveIndexToRight()
            print(signal + " Pressed on " + self.name)
        elif signal == self.findKey(menuMapping, self.name):
            print("Menu button pressed. Opening Next image")
            self.moveIndexToRight()
        else:
            status = -1
            print("Command not found inside class and status is -1")
        if status == 1:
            self.openImage()
        return status
    def getImagePath(self, distance):
        return distantToFolderMappings[distance]+self.name
    def get_files_in_directory(self, directory):
        return [os.path.join(directory, f) for f in os.listdir(directory) if os.path.isfile(os.path.join(directory, f))]

    def findKey(self, dictionary, value):
        for dict_key, dict_value in dictionary.items():
            if dict_value == value:
                return dict_key
        return None

currentFolder = None
def createFolder(distance, value):
    if value in menuMapping:
        return Folder(distance, value)
    else:
        return None
def process_signal(distance, value):
    selButton = value
    global currentFolder
    if currentFolder is not None:
        status = currentFolder.processIrSignal(selButton)
        if status == -1:
            print("Status found -1 from processSignal")
            newFolder = createFolder(distance, value)
            if newFolder is not None:
                currentFolder = newFolder
    else:
        currentFolder = createFolder(distance, value)
        if currentFolder is not None:
            print(menuMapping[value]+" Menu Selected")
        else:
            print("Command not found")

while True:
    distance = 10
    while True:
        inData = input("Enter a string: ")
        if inData == "Exit" and currentFolder is None:
            print("Exiting to select distance")
            currentFolder = None
            break
        elif inData == "Exit" and currentFolder is not None:
            print("Exiting to Select folder")
            currentFolder = None
        elif inData in distanceMapping:
            distance = distanceMapping[inData]
            print(str(distance) + " Selected")
            currentFolder = None
        else:
            process_signal(distance, inData)
            print("Action taken with "+str(distance)+" feet distance")


# Cemantix game solver
import logging
import os
import yaml

from src import *

os.chdir(os.path.abspath(os.path.dirname(__file__)))
with open("config.yaml", "r") as config_file:
    config = yaml.load(config_file, Loader=yaml.FullLoader)
logging.basicConfig(filename=f"./logs/cemantix_{dt.datetime.now().strftime(format='%Y-%m-%d_%Hh%M')}.log", level=logging.INFO)

def main():
    model = load_model(embedding=config['word2vec'])
    game = CemantixGame(executable_path="./artifacts/chromedriver.exe",game_url=config["game_url"])
    if config["agent_type"] == "bandit":
        agent = CemantixBandit(model=model, **config["agent_params"])
    elif config["agent_type"] == "gangster":
        agent = CemantixGangster(model=model, **config["agent_params"])
    else:
        raise ValueError("Unknown agent_type")
    game.play_strategy(agent, max_iter=config["max_iter"])
    game.save_result()
    game.end()

main()


import torch
from torch import nn
from .strategy import Strategy
from .utils import ner_predict, re_predict

class EntropySampling(Strategy):
    def __init__(self, annotator_config_name, pool_size, setting: str='knn', engine: str='gpt-35-turbo-0301',
                 reduction: str='mean'):
        super().__init__(annotator_config_name, pool_size, setting, engine)
        assert reduction in ['mean', 'sum', 'max']
        self.reduction = reduction

    def query(self, args, k: int, model: nn.Module, features):
        pool_indices = self._get_pool_indices()
        pool_features = [features[i] for i in pool_indices]
        if self.task_type == 'ner':
            pred_logits = ner_predict(args, pool_features, model)
            uncertainties = []
            for logit in pred_logits:
                prob = torch.softmax(logit, dim=-1)
                entropy = torch.special.entr(prob).sum(dim=-1) # entropy over each token
                if self.reduction == 'mean':
                    uncertainties.append(entropy.mean())
                elif self.reduction == 'sum':
                    uncertainties.append(entropy.sum())
                elif self.reduction == 'max':
                    uncertainties.append(entropy.max())
            uncertainties = torch.stack(uncertainties)
        elif self.task_type == 're':
            pred_logits = re_predict(args, pool_features, model)
            prob = torch.softmax(pred_logits, dim=-1)
            entropy = torch.special.entr(prob).sum(dim=-1)
            uncertainties = entropy
        else:
            raise ValueError('tbd.')
        lab_indices = torch.topk(uncertainties, k=k)[1]
        lab_indices = [pool_indices[i] for i in lab_indices]
        return lab_indices


from replit import db
from util import str_to_arr
from person import Person

def matches_to_string(matches):
	string = "List of matches:\n"
	for match in matches:
		string += match + "\n"
	return string

async def make_connections(message, person_calling):
	matches = []

	person_1 = Person.str_to_person(db[f"{person_calling}"])
	person_1_interests = str_to_arr(person_1.interests)
	person_1_courses = str_to_arr(person_1.courses)

	for person in db.keys():
		similarities = 0

		if str(person) == str(person_calling):
			continue

		person_2 = Person.str_to_person(db[f"{person}"])
		person_2_interests = str_to_arr(person_2.interests)
		person_2_courses = str_to_arr(person_2.courses)

		if person_1.year == person_2.year:
			similarities += 1

		for interest in person_1_interests:
			if interest in person_2_interests:
				similarities += 1

		for course in person_1_courses:
			if course in person_2_courses:
				similarities += 1

		if int(similarities/2) >= person_1.fit:
			matches.append(person_2.user)

	await message.author.send(matches_to_string(matches))
	return