from numpy import sqrt, pi, exp
import numpy as np

class muedge(object):
    def _init_(self,x,y,e=1):
        '''
        generic __init__ for all mufunction classes
        x, y, e are numpy arrays
        e is always defined,
        either provided by the caller
        or default to np.ones(x.shape[0])
        '''
        if x.shape[0]!=y.shape[0]:
            raise ValueError('x, y have different lengths')
        else:
            self.x = x
            self.y = y
        try:
            if e==1:
                self.e = np.ones(x.shape[0])
        except:
            if e.shape[0]!=x.shape[0]:
                raise ValueError('x, e have different lengths')
            else:
                self.e = e
    # ---- end generic __init__

    def f(self,t, t00, N, D):
        '''
        fit function for ISIS t0 fit on edge
        see mulab function f=mathedge(t,N,Delta,TauPi,TauMu,t0)
        obtained from MuonBeamSimple.nb in Mathematica
        here time in musec (vs mulab in ns)
        '''
        tp = 0.026003 # mus
        t0 = 0.82*tp-t00
        tm = 2.197 # mus
        f = (6*N/(D**3*(tm-tp)))*\
             exp(-(((t+t0+D/2)*tm+(t+t0+D/2)*tp)/(tm*\
                                               tp)))*\
             ((-2*exp(((t+t0+D/2)*tm+D*tp)/(tm*tp))*\
             tm**2*(-D/2+tm)+\
             2*exp((D*tm+(t+t0+D/2)*tp)/(tm*tp))*\
             tp**2*(-(D/2)+tp)+\
             exp(((t+t0+D/2)*(tm+tp))/(tm*tp))*(tm-tp)*\
             (t**2+t0**2+2*t0*(t-tm-tp)-2*t*(tm+tp)+\
             2*(-(D**2/8)+tm**2+tm*tp+tp**2)))*\
             ((t+t0-D/2)>0)+\
             (2*exp((t+t0+D/2)/tp)*tm**2*(D/2+tm)-\
              2*exp((t+t0+D/2)/tm)*tp**2*(D/2+tp)-\
              exp(((t+t0+D/2)*(tm+tp))/(tm*tp))*(tm-tp)*\
              (t**2+t0**2+2*t0*(t-tm-tp)-2*t*(tm+tp)+\
              2*(-(D**2/8)+tm**2+tm*tp+tp**2)))*((t+t0+D/2)>0))
        return f

    def __call__(self,t00, N, D):
        '''
        chisquare, normalized if self.e was provided, unnormalized otherwise
        '''
        return sum(((self.f(self.x, t00, N, D)-self.y)/self.e)**2)


import numpy as np
from scipy.integrate import solve_ivp
import matplotlib.pyplot as plt
from numpy.random import randint, rand
from sir import *

def SIR_continuous_reinfected(b,k,time,ii,r):
    """
    Simulates continuous SIR model
    ii = initial percentage of infected
    time = Days of simulation
    b = probability that people getting infectious
    k = probability that people getting recovered
    r = reinfected probability

    returns sol from solve_ivp
    """
    def SIR(t, X):

        #The main set of equations
        Y = np.zeros((3))
        Y[0] = -b * X[0] * X[2]
        Y[1] = k * X[2] - r * X[1]
        Y[2] = b * X[0] * X[2] - (k * X[2]) + r * X[1]
        return Y
    t_eval = np.linspace(0, time, time)
    sol1 = solve_ivp(SIR, [0, time], [1-ii, 0, ii], method='RK45', t_eval=t_eval)    # solve the equation
    return sol1

## Discrete

class Person_reinfection(Person):
    """
    An agent representing a person.

    By default, a person is susceptible but not infectious. They can become infectious by exposing with disease method.

    Status: 0 = susceptible        1 = infected          2 = removed
    """

    def __init__(self,startpos=None):
        self.status = 0
        if startpos==None:
            self.pos = np.random.rand(2)
        else:
            self.pos = np.array(startpos)
        self.reinfection=1

    def reinfectionrate(self):
        return self.reinfection
    def immunization(self,p):
        q=self.reinfection-p
        if q<0:
            q=0
        self.reinfection=q

def count_susceptible(pop):
    """
    counts number of susceptible
    """
    return sum(p.is_susceptible() for p in pop)

def count_infected(pop):
    """
    counts number of infected
    """
    return sum(p.is_infected() for p in pop)

def count_removed(pop):
    """
    counts number of removed
    """
    return sum(p.is_removed() for p in pop)

def SIR_discrete_reinfection(N,ii,b,T,k):
    """
    Simulates discrete SIR model
    N = Total number of people
    ii = initial percentage of infected
    b = number of contacts per day
    T = Days of simulation
    k = probability that people getting recovered

    returns list of s,i,r
    """
    pop = [Person_reinfection() for i in range(N)]
    initial_infection = randint(N,size=np.int(N*ii))
    for i in initial_infection:
        pop[i].infection()

    counts_susceptible = [count_susceptible(pop)]
    counts_infected = [count_infected(pop)]
    counts_removed = [count_removed(pop)]

    for t in range(T):
        # update the population
        for i in range(N):
            if pop[i].is_infected():
                # person i infected all their contacts
                contacts = randint(N, size=b)
                for j in contacts:
                    if not pop[j].is_removed():
                        pop[j].infection()
                        #if rand() < p:
                        #    pop[j].infection()
                    if pop[j].is_removed():
                        if rand()<pop[j].reinfectionrate():
                            pop[j].infection()
                if rand()< k:
                    pop[i].remove()
                    pop[i].immunization(rand())

        # add to our counts
        counts_susceptible.append(count_susceptible(pop))
        counts_infected.append(count_infected(pop))
        counts_removed.append(count_removed(pop))
    return np.array([counts_susceptible,counts_infected,counts_removed])


import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt

'''
QUESTION 2
'''
'''
Read the data.
'''
data = np.loadtxt('data/hands_aligned_train.txt',skiprows=1)
'''
Caclulate mu
'''
mu=np.mean(data,axis=1)
'''
Calculate W = w-mu
'''
W = data - mu.reshape(-1,1)
'''
Caclulate covariance matrix W.Wt
'''
cov = np.cov(W)
'''
Find the eigen vectors and eigen value of covariance matrix
'''
eigen_value,eigen_vectors=np.linalg.eig(cov)
'''
Take the first 5 eigen vectors and find phi and sigma square.
'''
K=5
sigma_square = 1/(112-K)*(np.sum(eigen_value[K+1:]))
L2 = np.diag(eigen_value)
second = L2[:K,:K] - sigma_square*np.eye(K,K)
phi = np.dot(eigen_vectors[:,:K],np.sqrt(second))
'''
Calculate wi with the weights given in the sheet.
'''
wi=(mu + np.dot(phi,np.array([-0.4,-0.2,0,0.2,0.4])).real)
'''
Plot the wi.
'''
plt.suptitle("Statistical Shape Model , K = 5 and W = [-0.4,-0.2,0,0.2,0.4]")
plt.plot(wi[:56],wi[56:])
plt.show()
plt.pause(2)
plt.close()

'''
QUESTION 3
'''

'''
Read the test shape
'''
test_shape = np.loadtxt('data/hands_aligned_test.txt',skiprows=1)
h = np.array([0,0,0,0,0]) #Assume random h
h_org = np.copy(h)
count_iter=0
while(True):
    w = mu + np.dot(phi,h) # w
    w=w.real
    '''
    Shape changes to calculate psuedo inverse equation components
    '''
    w_xy = np.array([np.split(w,2)[0],np.split(w,2)[1]]).T
    A= []
    for point in w_xy:
        A = A + [[point[0], point[1], 0, 0, 1, 0]]
        A = A + [[0, 0, point[0], point[1], 0, 1]]
    A = np.array(A)
    test_split = np.split(test_shape, 2)
    y = np.array([test_split[0], test_split[1]]).T.flatten()
    '''
    Calculate transformation vector psi
    '''
    psi = np.dot(np.dot(np.linalg.inv(np.dot(A.T, A)), A.T), y)
    psi_1 = psi[:4].reshape(2,2)
    psi_2 = psi[4:]
    '''
    Transform points according to psi
    '''
    w = np.dot(A,psi).real
    '''
    Caclulate parts of the h update equation
    '''
    second_term = y - (np.dot(A,psi))
    second_term=np.array(np.split(second_term, 56)).T.flatten()
    second_sum = np.zeros((5,1))
    for n in range(56):
        phi_n=np.array([phi[n, :], phi[n+ 56, :]])
        first_n = np.dot(psi_1,phi_n).T.real
        second_n = np.array([second_term[n],second_term[n+56]]).reshape(-1,1)
        second_sum = second_sum + np.dot(first_n,second_n)

    sum_first = np.zeros((5,5))
    for n in range(56):
        phi_n = np.array([phi[n, :], phi[n + 56, :]])
        sum_first += phi_n.T.dot(psi_1.T).dot(psi_1).dot(phi_n).real
    sum_first = sum_first+ sigma_square*np.eye(5,5)
    sum_first_inv = np.linalg.inv(sum_first)
    '''
    Evalulate h
    '''
    h = np.dot(sum_first_inv,second_sum).real.flatten()

    print(h)
    '''
    Check if h has converged
    '''
    if count_iter>15:
        plt.plot(np.array(np.split(w, 56))[:, 0], np.array(np.split(w, 56))[:, 1], c='r')
        plt.show()
        break
    else:
        h_org=h
    count_iter+=1
    '''
    Calculate RMS error
    '''
    RMS = np.sqrt(np.sum(np.square(w-y)))
    plt.suptitle("Inference Step - RMS" + str(RMS))
    plt.plot(np.array(np.split(w,56))[:,0],np.array(np.split(w,56))[:,1],c='r')
    plt.plot(test_shape[:56],test_shape[56:])
    plt.pause(0.2)
    plt.cla()


import numpy as np
import matplotlib.pyplot as plt
from matplotlib.ticker import NullFormatter
import pandas as pd
import matplotlib.ticker as ticker
from sklearn import preprocessing
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
from sklearn import metrics
from sklearn.model_selection import KFold
from sklearn.cross_validation import cross_val_score, cross_val_predict

df = pd.read_csv('biopsy.csv')

# x = df['Class'].value_counts()
# print(x)

X = df[['ClumpThickness', 'UniformityOfCellSize', 'UniformityOfCellShape', 'MarginalAdhesion', 'EpithelialCellSize', 'BareNuclei', 'BlandChromatin', 'NormalNucleoli', 'Mitoses']]
# print(X[0:5])

X = preprocessing.StandardScaler().fit(X).transform(X.astype(float))
# print(X[0:5])

Y = df['Class'].values
# print(Y[0:9])

X_train, X_test, Y_train, Y_test = train_test_split( X, Y, test_size=0.2, random_state=42)
# print ('Train set:', X_train.shape,  Y_train.shape)
# print ('Test set:', X_test.shape,  Y_test.shape)

nCount = range(1,20,1)
training_accuracy = []
test_accuracy = []

for i  in nCount:

    # Train Model and Predict
    neigh = KNeighborsClassifier(n_neighbors = i).fit(X_train,Y_train)
    # print(neigh)

    Yhat = neigh.predict(X_test)
    # print(Yhat[0:5])

    # Accuracy Evaluation
    training_accuracy.append(metrics.accuracy_score(Y_train, neigh.predict(X_train)))
    # print("Train set Accuracy: ",training_accuracy)
    # plt.plot(i,metrics.accuracy_score(Y_train, neigh.predict(X_train)), color = 'black')
    test_accuracy.append(metrics.accuracy_score(Y_test, Yhat))
    # print("Test set Accuracy: ", test_accuracy)

plt.plot(nCount,training_accuracy, label ='Training Accuracy')
plt.plot(nCount, test_accuracy, label ='Testing Accuracy')
plt.xlabel('Number of Neighbours')
plt.ylabel('Accuracy')
plt.title('Convergence of Train and Test Graph')
plt.legend()
# plt.show()

neigh = KNeighborsClassifier(n_neighbors = 9).fit(X_train,Y_train)
Yhat = neigh.predict(X_test)

# Accuracy Evaluation
print('\n')
print('Train set Accuracy: ', metrics.accuracy_score(Y_train, neigh.predict(X_train)))
print('Test set Accuracy: ', metrics.accuracy_score(Y_test, Yhat))

# K-Fold Implementation

kf = KFold(3, True)
kf.get_n_splits(X)
# print(kf)

for train_index, test_index in kf.split(X):
    # print('Train: ', train_index, 'Test: ', test_index)
    X_train, X_test = X[train_index], X[test_index]
    Y_train, Y_test = Y[train_index], Y[test_index]

# Cross Validation

kNN = KNeighborsClassifier(n_neighbors = 9)
score = cross_val_score(kNN, X, Y, cv=7)
print('\n')
print('Cross Validation Mean Score: ',score.mean())


import datetime as dt
import logging
import sys
import time
import warnings

import numpy as np
from scipy.integrate import solve_ivp
from scipy.optimize import minimize

from .utils import (mse, rmse, msle, mae)
from .utils import (normalize, restore)

__all__ = ['SEIRModel', 'SEIRDModel']

def _SEIR(t, y, beta, delta, alpha, gamma):
    """Return the SEIR compartmental system values. For details check:
    https://en.wikipedia.org/wiki/Compartmental_models_in_epidemiology

    Parameters
    ----------
    t : numpy.ndarray
        Discrete time points.
    y : list or tuple
        Values of S, E, I and R.
    beta : float
        Transition (infectious) rate controls the rate of spread.
    delta : float
        Direct transition rate between S and E individual.
    alpha : float
        Incubation rate, the reciprocal value of the incubation period.
    gamma : float
        Recovery (or mortality) rate.

    Returns
    -------
    list
        Values of the SEIR compartmental model free parameters.
    """
    S, E, I, R = y
    N = S + E + I + R
    return [
        -beta*S*I/N - delta*S*E,
        beta*S*I/N - alpha*E + delta*S*E,
        alpha*E - gamma*I,
        gamma*I,
    ]

def _SEIRD(t, y, beta, alpha, gamma, mu):
    """Return the SEIRD compartmental system values. For details check:
    https://en.wikipedia.org/wiki/Compartmental_models_in_epidemiology

    Parameters
    ----------
    t : numpy.ndarray
        Discrete time points.
    y : list or tuple
        Values of S, E, I and R.
    beta : float
        Transition (infectious) rate controls the rate of spread.
    alpha : float
        Incubation rate, the reciprocal value of the incubation period.
    gamma : float
        Recovery rate.
    mu : float
        Mortality rate.

    Returns
    -------
    list
        Values of the SEIRD compartmental model free parameters.
    """
    S, E, I, R, D = y
    N = S + E + I + R + D
    return [
        -beta*S*I/N,
        beta*S*I/N - alpha*E,
        alpha*E - gamma*I - mu*I,
        gamma*I,
        mu*I,
    ]

class CompartmentalModel(object):
    """General compartmental model class."""

    def __init__(self, loss_fun, ode_system, calc_ci, **kwargs):
        """Constructor.

        Parameters
        ----------
        loss_fun: str, optional
            Estimator that measures distance between true and predicted
            values. Loss function is set to `mean_squared_error`.
        ode_system : callable(t, y, ...), optional
            User defined SEIR-based compartmental model.
            The first parameter is a sequence of time points for which
            to solve for `y`. This sequence must be monotonically
            increasing sequence and repeated values are not allowed.
        calc_ci: bool, optional
            Calculate confidence interval by providing additional
            keyword arguments specified below.
        kwargs : dict, optional
            If `calc_ci` is set to True, and the following keyword
            arguments are specified, `CompartmenalModel.simulate` will
            have its output bounded in confidence intervals. Keyword
            are as follows: `pcr_sens` (float), measure of the
            proportion of positives that are correctly identified (e.g.
            the percentage of sick people who are correctly identified
            as being infected), `pcr_spec` (float), the measure of the
            proportion of negatives that are correctly identified (e.g.
            the percentage of healthy people who are correctly
            identified as not infected), `daily_tests` (numpy.ndarray),
            time series of new daily completed tests.
        """
        if loss_fun in ['mean_squared_error', 'mse', 'MSE']:
            self.loss_fun = mse
        elif loss_fun in ['root_mean_squared_error', 'rmse', 'RMSE']:
            self.loss_fun = rmse
        elif loss_fun in ['mean_squared_logarithmic_error', 'msle', 'MSLE']:
            self.loss_fun = msle
        elif loss_fun in ['mean_absolute_error', 'mae', 'MAE']:
            self.loss_fun = mae
        else:
            raise ValueError('Given loss function is not supported.')
        self.calc_ci = calc_ci
        if self.calc_ci:
            if kwargs:
                for kw in kwargs.keys():
                    if kw == 'pcr_sens':
                        assert isinstance(kwargs[kw], (float, )), \
                            '`pcr_sens` has to be a floating point number.'
                        self.pcr_sens = kwargs[kw]
                    elif kw == 'pcr_spec':
                        assert isinstance(kwargs[kw], (float, )), \
                            '`pcr_spec` has to be a floating point number.'
                        self.pcr_spec = kwargs[kw]
                    elif kw == 'daily_tests':
                        assert isinstance(kwargs[kw], np.ndarray), \
                            '`daily_test` has to be of `numpy.ndarray` type.'
                        self.daily_tests = kwargs[kw]
                    else:
                        raise KeyError('Optional keyword argument invalid.')
            else:
                self.calc_ci = False
                warnings.warn(
                    'Confidence interval will not be calculated'
                    'Optional `kwargs` are not specified.')
        self.params = None

    @staticmethod
    def calculate_ci(pcr_sens, pcr_spec, daily_positive, cum_removed,
        daily_tests):
        """Return two arrays: the lower confidence interval bound, the
        second row is the upper confidence interval bound.

        Parameters
        ----------
        pcr_sens : float or numpy.ndarray
            PCR-test sensitivity, the measure of the proportion of
            `daily_positives` that are correctly classified.
        pcr_spec : float or numpy.ndarray
            PCR-test pecificity, the measure of the proportion of
            the correctly classified negative tests.
        daily_positive : numpy.ndarray
            Daily number of newly confirmed positive infections.
        cum_removed : numpy.ndarray
            Cumulative number of confirmed recoveries and deaths.
        daily_tests : numpy.ndarray
            Daily number of tests.

        Returns
        -------
        tuple
            Tuple with 4 arrays: lower 95% CI bound, lower CI bound,
            upper CI bound and upper 95% CI bound.
        """
        # LOWER BOUND
        # lower bound CI
        tp_lb = pcr_sens * daily_positive
        active_lb = np.cumsum(tp_lb) - cum_removed
        active_lb[np.where(active_lb<0)] = 0
        # lower bound 95% CI
        std_sens_err = np.sqrt(np.divide(
            (1 - pcr_sens) * pcr_sens,
            daily_positive,
            out=np.zeros(daily_positive.shape, dtype=float),
            where=daily_positive!=0,))
        sens_lb_ci = pcr_sens - 1.96 * std_sens_err
        tp_lb_ci = sens_lb_ci * daily_positive
        active_lb_ci = np.cumsum(tp_lb_ci) - cum_removed
        active_lb_ci[np.where(active_lb_ci<0)] = 0
        # UPPER BOUND
        # yesterday's test gives today's result
        daily_tests = np.concatenate((np.array([daily_tests[0]]),
            daily_tests[:-1]))
        daily_negative = daily_tests - daily_positive
        # upper bound CI
        tn = pcr_spec * daily_negative
        fn = daily_negative - tn
        tp_ub = fn + daily_positive
        active_ub = np.cumsum(tp_ub) - cum_removed
        # upper bound 95% CI
        std_spec_err = np.sqrt(np.divide(
            (1 - pcr_spec) * pcr_spec,
            daily_negative,
            out=np.zeros(daily_negative.shape, dtype=float),
            where=daily_negative!=0,))
        spec_ub_ci = pcr_spec - 1.96 * std_spec_err
        tn_ci = spec_ub_ci * daily_negative
        fn_ci = daily_negative - tn_ci
        tp_ub_ci = fn_ci + daily_positive
        active_ub_ci = np.cumsum(tp_ub_ci) - cum_removed
        return (active_lb_ci, active_lb, active_ub, active_ub_ci)

class SEIRModel(CompartmentalModel):
    """SEIR model class."""

    def __init__(self, loss_fun='mse', ode_system=None, calc_ci=False,
        **kwargs):
        super().__init__(loss_fun, ode_system, calc_ci, **kwargs)
        if callable(ode_system):
            self.ode_system = ode_system
        else:
            self.ode_system = _SEIR

    def fit(self, cum_positives, cum_recovered, cum_deceased, IC,
        guess=[0.1, 0.1, 0.1, 0.1]):
        """Fit SEIR model.

        Parameters
        ----------
        cum_positives : numpy.ndarray
            Cumaltive number of confirmed positive infections.
        cum_recovered : numpy.ndarray
            Cumulative number of confirmed recoveries.
        cum_deceased : numpy.ndarray
            Cumulative number of deaths.
        IC : list
            Initial values of S, E, I and R at the first day.
        guess : list, optional
            Initial guess for parameters to be fitted. Size of the list
            depends on the number of free parameters in `ode_system`.
            For default `_SEIR`, there should be 4 unknown parameters:
            transition or infectious rate, recovery rate, incubation
            rate and direct transition rate, respectively.

        Returns
        -------
        tuple
            Fitted epidemiological parameters.
        list
            Loss values during the optimization procedure.
        """
        assert isinstance(cum_positives, np.ndarray), \
            '`cum_positives` has to be of `numpy.ndarray` type.'
        assert isinstance(cum_recovered, np.ndarray), \
            '`cum_recovered` has to be of `numpy.ndarray` type.'
        assert isinstance(cum_deceased, np.ndarray), \
            '`cum_deceased` has to be of `numpy.ndarray` type.'
        self.daily_positive = np.concatenate((
            np.array([cum_positives[0]]), np.diff(cum_positives)))
        self.active = cum_positives - cum_recovered - cum_deceased
        self.removed = cum_recovered + cum_deceased
        self.IC = IC
        loss_values = []
        def _print_loss(p):
            """Optimizer callback."""
            loss_values.append(
                SEIRModel._loss(p, self.active, self.removed, self.IC,
                    self.loss_fun, self.ode_system))
        # optimization procedure
        logging.basicConfig(stream=sys.stdout, level=logging.INFO)
        logging.info(f'L-BFGS-B optimization started: {dt.datetime.now()}')
        start_stopwatch = time.time()
        opt = minimize(
            fun=SEIRModel._loss,
            x0=guess,
            args=(self.active, self.removed, self.IC, self.loss_fun,
                self.ode_system),
            method='L-BFGS-B',
            bounds=[(1e-5, 1.0)] * len(guess),
            options={'maxiter': 1000, 'disp': True},
            callback=_print_loss,
            )
        elapsed = time.time() - start_stopwatch
        logging.info(f'Elapsed time: {round(elapsed, 4)}s')
        self.params = opt.x
        return self.params, loss_values

    def simulate(self):
        """Simulate S, E, I and R based on the fitted epidemiological
        parameters.

        Returns
        -------
        tuple
            S, E, I and R simulated values.
        """
        sol = solve_ivp(
            fun=self.ode_system,
            t_span=(0, self.active.size),
            y0=self.IC,
            args=tuple(self.params),
            method='RK45',
            t_eval=np.arange(0, self.active.size, 1),
            vectorized=True,
            )
        if self.calc_ci:
            (active_lb_ci, active_lb, active_ub, active_ub_ci) = \
                self.calculate_ci(self.pcr_sens, self.pcr_spec,
                    self.daily_positive, self.removed, self.daily_tests)
            I_ci = np.r_[
                active_lb_ci.reshape(1, -1),
                active_lb.reshape(1, -1),
                sol.y[2].reshape(1, -1),
                active_ub.reshape(1, -1),
                active_ub_ci.reshape(1, -1)]
            return (sol.y[0], sol.y[1], I_ci, sol.y[3])
        return (sol.y[0], sol.y[1], sol.y[2], sol.y[3])

    def forecast(self, n_days):
        """Predict S, E, I and R based on the fitted epidemiological
        parameters.

        Parameters
        ----------
        n_days : int
            Number of days to forecast S, E, I and R in the future.

        Returns
        -------
        tuple
            S, E, I and R predicted values.
        """
        assert isinstance(n_days, (int, )), '`n_days` must be an integer.'
        eff_idx = self.active.size
        sol = solve_ivp(
            fun=self.ode_system,
            t_span=(0, eff_idx + n_days),
            y0=self.IC,
            args=tuple(self.params),
            method='RK45',
            t_eval=np.arange(0, eff_idx + n_days, 1),
            vectorized=True,
            )
        return (sol.y[0][eff_idx:], sol.y[1][eff_idx:], sol.y[2][eff_idx:],
            sol.y[3][eff_idx:],)

    def __str__(self):
        return('SEIR model class')

    def __repr__(self):
        return self.__str__()

    @property
    def get_params(self):
        if self.params is None:
            raise ValueError('No fitted parameters. Call `fit` method first.')
        return (self.params)

    @staticmethod
    def _loss(params, active, cum_removed, IC, loss_fun, ode_system):
        """Calculate and return the loss function between the actual
        and predicted values.

        Parameters
        ----------
        params : list
            Epidemiological parameters.
        active: numpy.ndarray
            Time series of currently active infected individuals.
        cum_removed : numpy.ndarray
            Cumulative number of confirmed recoveries and deaths.
        IC: list
            Initial conditions.
        loss_fun : str
            Estimator.
        ode_system : callable(t, y, ...)
            User defined SEIR-based compartmental model.

        Returns
        -------
        float
            Loss between the actual and predicted value of confirmed
            and recovered individuals.
        """
        size = active.size
        sol = solve_ivp(
            fun=ode_system,
            t_span=(0, size),
            y0=IC,
            args=params,
            method='RK45',
            t_eval=np.arange(0, size, 1),
            vectorized=True,
        )
        return loss_fun(sol.y[2], active)\
               + loss_fun(sol.y[3], cum_removed)

class SEIRDModel(CompartmentalModel):
    """SEIRD model class."""

    def __init__(self, loss_fun='mse', ode_system=None, calc_ci=False,
        **kwargs):
        super(SEIRDModel, self).__init__(loss_fun, ode_system, calc_ci,
            **kwargs)
        if callable(ode_system):
            self.ode_system = ode_system
        else:
            self.ode_system = _SEIRD

    def fit(self, cum_positives, cum_recovered, cum_deceased, IC,
        guess=[0.1, 0.1, 0.01, 0.01]):
        """Fit SEIRD model.

        Parameters
        ----------
        cum_positives : numpy.ndarray
            Cumaltive number of confirmed positive infections.
        cum_recovered : numpy.ndarray
            Cumulative number of confirmed recoveries.
        cum_deceased : numpy.ndarray
            Cumulative number of deaths.
        IC : list
            Initial values of S, E, I, R and D at the first day.
        guess : list, optional
            Initial guess for parameters to be fitted. Size of the list
            depends on the number of free parameters in `ode_system`.
            For default `_SEIRD`, there should be 4 unknown parameters:
            transition or infectious rate, recovery rate, incubation
            rate and mortality rate, respectively.

        Returns
        -------
        tuple
            Fitted epidemiological parameters.
        list
            Loss values during the optimization procedure.
        """
        assert isinstance(cum_positives, np.ndarray), \
            '`cum_positives` has to be of `numpy.ndarray` type.'
        assert isinstance(cum_recovered, np.ndarray), \
            '`cum_recovered` has to be of `numpy.ndarray` type.'
        assert isinstance(cum_deceased, np.ndarray), \
            '`cum_deceased` has to be of `numpy.ndarray` type.'
        self.daily_positive = np.concatenate((
            np.array([cum_positives[0]]), np.diff(cum_positives)))
        self.active = cum_positives - cum_recovered - cum_deceased
        self.removed = cum_recovered + cum_deceased
        self.IC = IC
        loss_values = []
        def _print_loss(p):
            """Optimizer callback."""
            loss_values.append(
                SEIRDModel._loss(p, self.active, cum_recovered, cum_deceased,
                    self.IC, self.loss_fun, self.ode_system))
        # optimization procedure
        logging.basicConfig(stream=sys.stdout, level=logging.INFO)
        logging.info(f'L-BFGS-B optimization started: {dt.datetime.now()}')
        start_stopwatch = time.time()
        opt = minimize(
            fun=SEIRDModel._loss,
            x0=guess,
            args=(self.active, cum_recovered, cum_deceased, self.IC,
                self.loss_fun, self.ode_system),
            method='L-BFGS-B',
            bounds=[(1e-5, 1.0)] * len(guess),
            options={'maxiter': 1000, 'disp': True},
            callback=_print_loss,
            )
        elapsed = time.time() - start_stopwatch
        logging.info(f'Elapsed time: {round(elapsed, 4)}s')
        self.params = opt.x
        return self.params, loss_values

    def simulate(self):
        """Simulate S, E, I, R and D based on the fitted
        epidemiological parameters.

        Returns
        -------
        tuple
            S, E, I, R and D simulated values.
        """
        sol = solve_ivp(
            fun=self.ode_system,
            t_span=(0, self.active.size),
            y0=self.IC,
            args=tuple(self.params),
            method='RK45',
            t_eval=np.arange(0, self.active.size, 1),
            vectorized=True,
            )
        if self.calc_ci:
            (active_lb_ci, active_lb, active_ub, active_ub_ci) = \
                self.calculate_ci(self.pcr_sens, self.pcr_spec,
                    self.daily_positive, self.removed, self.daily_tests)
            I_ci = np.r_[
                active_lb_ci.reshape(1, -1),
                active_lb.reshape(1, -1),
                sol.y[2].reshape(1, -1),
                active_ub.reshape(1, -1),
                active_ub_ci.reshape(1, -1)]
            return (sol.y[0], sol.y[1], I_ci, sol.y[3], sol.y[4])
        return (sol.y[0], sol.y[1], sol.y[2], sol.y[3], sol.y[4])

    def forecast(self, n_days):
        """Predict S, E, I, R and D based on the fitted
        epidemiological parameters.

        Parameters
        ----------
        n_days : int
            Number of days to forecast S, E, I, R and D in the future.

        Returns
        -------
        tuple
            S, E, I, R and D predicted values.
        """
        assert isinstance(n_days, (int, )), '`n_days` must be an integer.'
        eff_idx = self.active.size
        sol = solve_ivp(
            fun=self.ode_system,
            t_span=(0, eff_idx + n_days),
            y0=self.IC,
            args=tuple(self.params),
            method='RK45',
            t_eval=np.arange(0, eff_idx + n_days, 1),
            vectorized=True,
        )
        return (sol.y[0][eff_idx:], sol.y[1][eff_idx:], sol.y[2][eff_idx:],
            sol.y[3][eff_idx:], sol.y[4][eff_idx:])

    def __str__(self):
        return('SEIRD model class')

    def __repr__(self):
        return self.__str__()

    @property
    def get_params(self):
        if self.params is None:
            raise ValueError('No fitted parameters. Call `fit` method first.')
        return (self.params)

    @staticmethod
    def _loss(params, active, cum_recovered, cum_deceased, IC, loss_fun,
        ode_system):
        """Calculate and return the loss function between actual and
        predicted values.

        Parameters
        ----------
        params : list
            Epidemiological parameters
        active: numpy.ndarray
            Time series of currently active infected individuals.
        cum_recovered : numpy.ndarray
            Cumulative number of confirmed recoveries.
        cum_deceased : numpy.ndarray
            Cumulative number of confirmed recoveries.
        IC : list
            Initial conditions.
        loss_fun : str
            Estimator.
        ode_system : callable(t, y, ...)
            User defined SEIR-based compartmental model.

        Returns
        -------
        float
            Loss between the actual and predicted value of confirmed,
            recovered and deceased individuals.
        """
        size = active.size
        sol = solve_ivp(
            fun=ode_system,
            t_span=(0, size),
            y0=IC,
            args=params,
            method='RK45',
            t_eval=np.arange(0, size, 1),
            vectorized=True,
        )
        return loss_fun(sol.y[2], active) \
               + loss_fun(sol.y[3], cum_recovered) \
               + loss_fun(sol.y[4], cum_deceased)


"""
helper function to plot gaussian contour in 2D
"""

import matplotlib.pyplot as plt  # set plt as alias for matplotlib.pyplot
import numpy as np
from scipy.stats import multivariate_normal
from mpl_toolkits.mplot3d import Axes3D

from sympy import *
from sympy.solvers import solve
from sympy import Symbol

x1=Symbol('x1')
x2=Symbol('x2')

def plot2dGaussianCont(w1_mu,w1_sigma,w2_mu,w2_sigma,w3_mu,w3_sigma,eq1,eq2,eq3):
    #Create grid and multivariate normal
    x = np.linspace(-40,40,500)
    y = np.linspace(-40,40,500)
    X, Y = np.meshgrid(x,y)
    pos = np.empty(X.shape + (2,))
    pos[:, :, 0] = X; pos[:, :, 1] = Y

    rv1 = multivariate_normal(w1_mu,w1_sigma)
    rv2 = multivariate_normal(w2_mu,w2_sigma)
    rv3 = multivariate_normal(w3_mu,w3_sigma)

    plt.clf()
    plt.contour(X, Y, rv1.pdf(pos))
    plt.contour(X, Y, rv2.pdf(pos))
    plt.contour(X, Y, rv3.pdf(pos))
    plt.xlabel("x1")
    plt.ylabel("x2")
    plt.title("Distribution of Data in feature space")
    plt.annotate('W1',(-5,-7))
    plt.annotate('W2',(3,15))
    plt.annotate('W3',(10,-12))

    # plotting the eigen vectors for w1_sigma
    evals, evecs = np.linalg.eig(w1_sigma)
    # print(evecs)
    eig_vec1 = evecs[:,0]
    eig_vec2 = evecs[:,1]
    print("Eigen vector 1 (w1_sigma): ",eig_vec1)
    print("Eigen vector 2 (w1_sigma): ",eig_vec2)
    plt.quiver(*w1_mu, *eig_vec1, color=['r'], scale=10)
    plt.quiver(*w1_mu, *eig_vec2, color=['b'], scale=10)

    # plotting the eigen vectors for w2_sigma
    evals, evecs = np.linalg.eig(w2_sigma)
    # print(evecs)
    eig_vec1 = evecs[:,0]
    eig_vec2 = evecs[:,1]
    print("Eigen vector 1 (w2_sigma): ",eig_vec1)
    print("Eigen vector 2 (w2_sigma): ",eig_vec2)
    plt.quiver(*w2_mu, *eig_vec1, color=['r'], scale=10)
    plt.quiver(*w2_mu, *eig_vec2, color=['b'], scale=10)

    # plotting the eigen vectors for w3_sigma
    evals, evecs = np.linalg.eig(w3_sigma)
    # print(evecs)
    eig_vec1 = evecs[:,0]
    eig_vec2 = evecs[:,1]
    print("Eigen vector 1 (w3_sigma): ",eig_vec1)
    print("Eigen vector 2 (w3_sigma): ",eig_vec2)
    plt.quiver(*w3_mu, *eig_vec1, color=['r'], scale=10)
    plt.quiver(*w3_mu, *eig_vec2, color=['b'], scale=10)

    # # Case 1
    # # print(eq1)
    # # print(solve(eq1,x2))
    # db1 = solve(eq1,x2)
    # lam_x = lambdify(x1, db1, modules=['numpy'])
    # x=np.linspace(-40,7.107,500)
    # y_vals = np.array(lam_x(x)).reshape((500,1))
    # plt.plot(x, y_vals[:],"black")
    #
    # db2 = solve(eq2,x2)
    # lam_x = lambdify(x1, db2, modules=['numpy'])
    # x=np.linspace(7.107,40,500)
    # y_vals = np.array(lam_x(x)).reshape((500,1))
    # plt.plot(x, y_vals[:],"black")
    #
    # db3 = solve(eq3,x2)
    # lam_x = lambdify(x1, db3, modules=['numpy'])
    # x=np.linspace(-40,7.107,500)
    # y_vals = np.array(lam_x(x)).reshape((500,1))
    # plt.plot(x, y_vals[:],"black")
    # # print(db1,db2,db3)
    # # print("intersection point",solve(db1[0]-db2[0],x1))

    ## case2
    # print(eq1)
    # print(solve(eq1,x2))
    # # print(x.shape)
    # db1 = solve(eq2,x2)
    # lam_x = lambdify(x1, db1, modules=['numpy'])
    # # x=np.linspace(-40,7.107,500)
    # y_vals = np.array(lam_x(x)).reshape((500,2))
    # plt.plot(x, y_vals[:,1],"black")

    plt.xlim((-40,40))
    plt.ylim((-40,40))
    plt.show()


# Correlation for devolatilization time accounting for moisture content within
# a wood particle. Refer to Equations 2-7 in the paper for more details.
# -- Reference (Diego2003) --
# De Diego, L. F., et al. "Effect of moisture content on devolatilization times
# of pine wood particles in a fluidized bed." Energy & fuels 17.2 (2003): 285-290.

import numpy as np
import matplotlib.pyplot as py

py.close('all')

# Parameters
# -----------------------------------------------------------------------------

d = 2       # diameter of particle, mm
n = 1.5     # exponent n
b = 1.7e-2  # factor for moisture content

# coefficient a at various temperatures
a1 = 1.69   # for 923 K
a2 = 1.38   # for 1023 K
a3 = 1.03   # for 1123 K
a4 = 0.89   # for 1223 K

# Calculate devolatilization time
#------------------------------------------------------------------------------

# range of moisture content from 0-50%
mc = np.linspace(0, 50) # moisture content, %

# general equation for devolatilization time tv = a*(d)^n per Eq. 2
# tv = tvo*(1+b*mc) per Eq. 5
tv1 = a1*d**n*(1 + b*mc)
tv2 = a2*d**n*(1 + b*mc)
tv3 = a3*d**n*(1 + b*mc)
tv4 = a4*d**n*(1 + b*mc)

# Plot results
#------------------------------------------------------------------------------

# this figure should have similar trends as Fig. 4 in Diego2003
py.figure(1)
py.plot(mc, tv1, lw=2, label='923 K')
py.plot(mc, tv2, lw=2, label='1023 K')
py.plot(mc, tv3, lw=2, label='1123 K')
py.plot(mc, tv4, lw=2, label='1223 K')
py.xlabel('Moisture Content (%)')
py.ylabel('tv (s)')
py.legend(loc='best', numpoints=1, fontsize=12)
py.title('d = {} mm particle size'.format(d))
py.grid()
py.show()

# plot the (a) coefficients vs temperature
T = [923, 1023, 1123, 1223]
a = [a1, a2 ,a3 ,a4]

py.figure(2)
py.scatter(T, a, s=60, c='red')
py.ylabel('Coefficient  a')
py.xlabel('Temperature (K)')
py.title('d = {} mm particle size'.format(d))
py.grid()
py.show()


import numpy as np

def sigmoid(Z):
    A = 1/(1+np.exp(-Z))
    cache = Z

    return A, cache

def relu(Z):
    A = np.maximum(0,Z)

    assert(A.shape == Z.shape)

    cache = Z
    return A, cache

def relu_backward(dA, cache):
    Z = cache
    dZ = np.array(dA, copy=True)

    dZ[Z <= 0] = 0

    assert(dZ.shape == Z.shape)

    return dZ

def sigmoid_backward(dA, cache):
    Z = cache

    s = 1/(1+np.exp(-Z))
    dZ = dA*s*(1-s)

    assert(dZ.shape == Z.shape)

    return dZ


import numpy as np
from decimal import *

epsilon_ = 1e-10

class Hyperplane():
    def __init__(self, dim, point, n):
        """
        initial a hyperplane
        :param point: a point on the boundary of hyperplane
        :param n: a normal vector point to the outside of hyperplane
        """
        self.dim = dim
        self.point = point
        self.n = n

    def signed_dist(self, pt):
        """
        cal the sign-distance between the boundary and point, <0: in hyperplane
        :param pt: a point
        :return: distance
        """
        return Decimal(self.n.dot(pt - self.point))

    def dist(self, pt):
        """
        cal the distance between the boundary and point
        :param pt: a point
        :return: distance
        """
        return abs(self.signed_dist(pt))

class Polygon():
    def __init__(self, dim, hps):
        """
        initial a polygon
        :param hps: a list of hyperplanes
        """
        self.dim = dim
        self.hps = hps

    def inside(self, pt):
        """
        judge whether a point is in polygon
        :param pt: a point
        :return: whether pt is in the polygon
        """
        for hp in self.hps:
            if hp.signed_dist(pt) > epsilon_:
                return False
        return True

    def add(self, hp):
        self.hps.append(hp)

    def points_inside(self, pts):
        """
        figure out all point which is in the polygon
        :param pts: [np.array(), np.array()]  a list contain many point
        :return: a list contain point which is inside the polygon
        """
        result = []
        for point in pts:
            if self.inside(point):
                result.append(point)
        return result

    def cal_normals(self):
        """
        used for visualization
        :return:
        """
        result = []
        for item in self.hps:
            result.append([item.point, item.n])
        return result

    def prunePolygon(self):
        """
        delete surplus hyperplane, calc all intersection
        :return:
        """
        pass

class LinearConstraint():
    def __init__(self, dim, A, b):
        """
        Ax < b
        :param A:
        :param b:
        """
        self.dim = dim
        self.A = A
        self.b = b
        pass

    def inside(self, pt):
        """
        judge whether a point is satisfy the linear constraints
        :param pt: a point
        :return: whether pt is satisfy the linear constraints
        """
        d = self.A * pt - self.b
        for i in range(len(d)):
            if d[i] > 0:
                return False
        return True

def get_linear_constraints(dim, point, hps):
    length = len(hps)
    A = np.zeros((length, len(point)))
    b = np.zeros((length, ))
    for i in range(length):
        n_norm = hps.n
        c = Decimal(hps[i].point.dot(n_norm))
        if n_norm.dot(point) - c > 0:
            n_norm = -n_norm
            c = -c
        A[i] = n_norm
        b[i] = c
    return LinearConstraint(dim, A, b)


from scipy import misc
import matplotlib.pyplot as plt
import numpy as np
from numpy import linalg

img = misc.face()
img_array = img / 255
img_array_transposed = np.transpose(img_array, (2, 0, 1))

U, s, Vt = linalg.svd(img_array_transposed)

Sigma = np.zeros((3, 768, 1024))
for j in range(3):
    np.fill_diagonal(Sigma[j, :, :], s[j, :])

reconstructed = U @ Sigma @ Vt
plt.imshow(np.transpose(reconstructed, (1, 2, 0)))


import numpy as np
import matplotlib.pyplot as plt

### Standard Time Series Line Chart ##

# Create a 1D array with 1000 evenly spaced points between 0 and 20
x = np.linspace(0 , 20, 1000)
y = np.sin(x) + 0.2 * x

plt.plot(x,y)
plt.xlabel('input')
plt.ylabel('output')
plt.title('my plot')
plt.show()

## Scatter Plot ##
# 100 datapoints with dimensionality of 2
x = np.random.randn(100, 2)
plt.scatter(x[:,0], x[:,1])
plt.show()

# Do some classification
x = np.random.randn(200, 2)
# Since randr draws from the standard normal, they all tend to be near zero. Center some of the datapoints at 3 instead.
x[:50] += 3
 # Create and array of zeros for labels
y = np.zeros(200)
# Set first half of the values to 1; some will be centered at zero, and others will be at 1
y[:50] = 1
plt.scatter(x[:,0], x[:,1], c=y)
plt.show()

## Histogram ##
# Create 10,000 random numbers
x = np.random.randn(10000)
plt.hist(x)
plt.show()

plt.hist(x, bins=50) # create more bars to help view
plt.show()

# uses the “continuous uniform” distribution instead
x = np.random.random(10000)
plt.hist(x, bins=50)
plt.show()


import sys
from sympy import sympify, lambdify
from sympy.abc import x
EPS = sys.float_info.epsilon

def bisect(f, x1, x2, eps, maxn):
	assert f(x1)*f(x2) < 0, \
	"We cannot find a root if the function does not change signs"
	xl = x1
	xu = x2
	xr = 0
	fl = f(xl)
	err = 1000
	for i in range(maxn):
		r = (xl + xu)/2
		print(r)
		fr = f(r)
		if not abs(r - 0) <  EPS:
			err = abs((r-xr)/r) * 100
		if err < eps:
			print("Error =" + str(err))
			break
		v = fl * fr
		if v < 0:
			xu = r
		if v > 0:
			xl = r
			fl = fr
		else:
			err = 0
		xr = r
	return r

eq = input("Please enter a function of x: ")
eq = sympify(eq)
f = lambdify(x, eq, 'math')
ll = float(input("Please enter the lower limit: "))
ul = float(input("Please enter the upper limit: "))
print("Calculating...")
root = bisect(f, ll, ul, 0.001, 100)
print("The root in the given range seems to be:" + str(root))


import numpy as np

def mcl(M, expand_factor = 2, inflate_factor = 2, max_loop = 100, mult_factor = 1, eps = 1e-5):
    '''Markov Cluster Algorithm (MCL).
    MCL is a very simple clustering algorithm on graph, which intuitively believe a random walk is more likely to be trapped in a cluster. Note that in MCL a single node could be in different clusters.

    Parameters
    -----
        M, ndarray.
            The input graph adjacency matrix.
        expand_factor, int, default 2.
            The expand factor. random walk step num, larger number tends to fewer clusters.
        inflate_factor, float, default 2.
            The inflate factor. larger number tends to more clusters.
        max_loop, int, defalut 100.
            The max  number of loops to stop the algorithm.
        mult_factor, float, default 1.
            The number adds to the diagnal of matrix. tends to make algorithm stable.
        eps, float, default 1e-5.
            A small number used to check convergence.
    Returns
    -----
        clusters, dict
    Reference
    -----
        Van Dongen, S. (2000) Graph Clustering by Flow Simulation. PhD Thesis, University of Utrecht, The Netherlands.
    '''
    assert type(M) is np.ndarray and M.ndim==2
    M = M + mult_factor * np.identity(M.shape[0])
    M = M / M.sum(0)

    for i in range(max_loop):
        _M = M
        # inflate step
        M = np.power(M, inflate_factor)
        M = M / M.sum(0)
        # expend step
        M = np.linalg.matrix_power(M, expand_factor)
        # Convergence Check
        if np.abs(M - _M).max() < eps:
            break

    # get_cluster
    clusters = []
    for i, r in enumerate((M>0).tolist()):
        if r[i]:
            clusters.append(M[i,:]>0)

    clust_map  ={}
    for cn , c in enumerate(clusters):
        for x in  [ i for i, x in enumerate(c) if x ]:
            clust_map[cn] = clust_map.get(cn, [])  + [x]

    return M, clust_map


import numpy as np

def radius_of_curvature(pixels_x, pixels_y, mx, my):
    if pixels_y is None or pixels_x is None:
        return 0

    y_eval = np.max(pixels_y) * my
    fit = np.polyfit(pixels_y * my, pixels_x * mx, 2)
    curvature = ((1 + (2 * fit[0] * y_eval + fit[1]) ** 2) ** 1.5) / np.absolute(2 * fit[0])
    return curvature

# calcualte curvature in meters, based on parabola
def measure_curvature_real(left_fit, right_fit, ploty, xm, ym):
    '''
    Calculates the curvature of polynomial functions in meters.
    '''
    # Define conversions in x and y from pixels space to meters
    left_curvature_real = radius_of_curvature(left_fit, ploty, xm, ym)
    right_curvature_real = radius_of_curvature(right_fit, ploty, xm, ym)

    return left_curvature_real, right_curvature_real

def calulate_position(left_fit, right_fit, xm):
    if left_fit is None or right_fit is None:
        return 0
    left_pos = left_fit[-1]
    right_pos = right_fit[-1]

    center_px = left_pos + (right_pos - left_pos) / 2
    diff = center_px - 640
    diff_in_m = diff * xm
    return diff_in_m

def calulate_lane_size(left_fit, right_fit, xm):
    if left_fit is None or right_fit is None:
        return 0, 0, 0, 0

    lane_width_m = (right_fit - left_fit) * xm

    lane_width = lane_width_m[-1]
    lane_width_min = np.min(lane_width_m)
    lane_width_max = np.max(lane_width_m)

    return lane_width, lane_width_min, lane_width_max


r"""
Symbolic Equations and Inequalities

Sage can solve symbolic equations and inequalities. For
example, we derive the quadratic formula as follows::

    sage: a,b,c = var('a,b,c')
    sage: qe = (a*x^2 + b*x + c == 0)
    sage: qe
    a*x^2 + b*x + c == 0
    sage: print solve(qe, x)
    [
    x == -1/2*(b + sqrt(b^2 - 4*a*c))/a,
    x == -1/2*(b - sqrt(b^2 - 4*a*c))/a
    ]

The operator, left hand side, and right hand side
--------------------------------------------------

Operators::

    sage: eqn = x^3 + 2/3 >= x - pi
    sage: eqn.operator()
    <built-in function ge>
    sage: (x^3 + 2/3 < x - pi).operator()
    <built-in function lt>
    sage: (x^3 + 2/3 == x - pi).operator()
    <built-in function eq>

Left hand side::

    sage: eqn = x^3 + 2/3 >= x - pi
    sage: eqn.lhs()
    x^3 + 2/3
    sage: eqn.left()
    x^3 + 2/3
    sage: eqn.left_hand_side()
    x^3 + 2/3

Right hand side::

    sage: (x + sqrt(2) >= sqrt(3) + 5/2).right()
    sqrt(3) + 5/2
    sage: (x + sqrt(2) >= sqrt(3) + 5/2).rhs()
    sqrt(3) + 5/2
    sage: (x + sqrt(2) >= sqrt(3) + 5/2).right_hand_side()
    sqrt(3) + 5/2

Arithmetic
----------
Add two symbolic equations::

    sage: var('a,b')
    (a, b)
    sage: m = 144 == -10 * a + b
    sage: n = 136 == 10 * a + b
    sage: m + n
    280 == 2*b
    sage: int(-144) + m
    0 == -10*a + b - 144

Subtract two symbolic equations::

    sage: var('a,b')
    (a, b)
    sage: m = 144 == 20 * a + b
    sage: n = 136 == 10 * a + b
    sage: m - n
    8 == 10*a
    sage: int(144) - m
    0 == -20*a - b + 144

Multiply two symbolic equations::

    sage: x = var('x')
    sage: m = x == 5*x + 1
    sage: n = sin(x) == sin(x+2*pi)
    sage: m * n
    x*sin(x) == (5*x + 1)*sin(2*pi + x)
    sage: m = 2*x == 3*x^2 - 5
    sage: int(-1) * m
    -2*x == -3*x^2 + 5

Divide two symbolic equations::

    sage: x = var('x')
    sage: m = x == 5*x + 1
    sage: n = sin(x) == sin(x+2*pi)
    sage: m/n
    x/sin(x) == (5*x + 1)/sin(2*pi + x)
    sage: m = x != 5*x + 1
    sage: n = sin(x) != sin(x+2*pi)
    sage: m/n
    x/sin(x) != (5*x + 1)/sin(2*pi + x)

Substitution
------------

Substitution into relations::

    sage: x, a = var('x, a')
    sage: eq = (x^3 + a == sin(x/a)); eq
    x^3 + a == sin(x/a)
    sage: eq.substitute(x=5*x)
    125*x^3 + a == sin(5*x/a)
    sage: eq.substitute(a=1)
    x^3 + 1 == sin(x)
    sage: eq.substitute(a=x)
    x^3 + x == sin(1)
    sage: eq.substitute(a=x, x=1)
    x + 1 == sin(1/x)
    sage: eq.substitute({a:x, x:1})
    x + 1 == sin(1/x)

Solving
-------

We can solve equations::

    sage: x = var('x')
    sage: S = solve(x^3 - 1 == 0, x)
    sage: S
    [x == 1/2*I*sqrt(3) - 1/2, x == -1/2*I*sqrt(3) - 1/2, x == 1]
    sage: S[0]
    x == 1/2*I*sqrt(3) - 1/2
    sage: S[0].right()
    1/2*I*sqrt(3) - 1/2
    sage: S = solve(x^3 - 1 == 0, x, solution_dict=True)
    sage: S
    [{x: 1/2*I*sqrt(3) - 1/2}, {x: -1/2*I*sqrt(3) - 1/2}, {x: 1}]
    sage: z = 5
    sage: solve(z^2 == sqrt(3),z)
    Traceback (most recent call last):
    ...
    TypeError: 5 is not a valid variable.

We illustrate finding multiplicities of solutions::

    sage: f = (x-1)^5*(x^2+1)
    sage: solve(f == 0, x)
    [x == -I, x == I, x == 1]
    sage: solve(f == 0, x, multiplicities=True)
    ([x == -I, x == I, x == 1], [1, 1, 5])

We can also solve many inequalities::

    sage: solve(1/(x-1)<=8,x)
    [[x < 1], [x >= (9/8)]]

We can numerically find roots of equations::

    sage: (x == sin(x)).find_root(-2,2)
    0.0
    sage: (x^5 + 3*x + 2 == 0).find_root(-2,2,x)
    -0.6328345202421523
    sage: (cos(x) == sin(x)).find_root(10,20)
    19.634954084936208

We illustrate some valid error conditions::

    sage: (cos(x) != sin(x)).find_root(10,20)
    Traceback (most recent call last):
    ...
    ValueError: Symbolic equation must be an equality.
    sage: (SR(3)==SR(2)).find_root(-1,1)
    Traceback (most recent call last):
    ...
    RuntimeError: no zero in the interval, since constant expression is not 0.

There must be at most one variable::

    sage: x, y = var('x,y')
    sage: (x == y).find_root(-2,2)
    Traceback (most recent call last):
    ...
    NotImplementedError: root finding currently only implemented in 1 dimension.

Assumptions
-----------

Forgetting assumptions::

    sage: var('x,y')
    (x, y)
    sage: forget() #Clear assumptions
    sage: assume(x>0, y < 2)
    sage: assumptions()
    [x > 0, y < 2]
    sage: (y < 2).forget()
    sage: assumptions()
    [x > 0]
    sage: forget()
    sage: assumptions()
    []

Miscellaneous
-------------

Conversion to Maxima::

    sage: x = var('x')
    sage: eq = (x^(3/5) >= pi^2 + e^i)
    sage: eq._maxima_init_()
    '(x)^(3/5) >= ((%pi)^(2))+(exp(0+%i*1))'
    sage: e1 = x^3 + x == sin(2*x)
    sage: z = e1._maxima_()
    sage: z.parent() is sage.calculus.calculus.maxima
    True
    sage: z = e1._maxima_(maxima)
    sage: z.parent() is maxima
    True
    sage: z = maxima(e1)
    sage: z.parent() is maxima
    True

Conversion to Maple::

    sage: x = var('x')
    sage: eq = (x == 2)
    sage: eq._maple_init_()
    'x = 2'

Comparison::

    sage: x = var('x')
    sage: (x>0) == (x>0)
    True
    sage: (x>0) == (x>1)
    False
    sage: (x>0) != (x>1)
    True

Variables appearing in the relation::

    sage: var('x,y,z,w')
    (x, y, z, w)
    sage: f =  (x+y+w) == (x^2 - y^2 - z^3);   f
    w + x + y == -z^3 + x^2 - y^2
    sage: f.variables()
    (w, x, y, z)

LaTeX output::

    sage: latex(x^(3/5) >= pi)
    x^{\frac{3}{5}} \geq \pi

When working with the symbolic complex number `I`, notice that comparison do not
automatically simplifies even in trivial situations::

    sage: I^2 == -1
    -1 == -1
    sage: I^2 < 0
    -1 < 0
    sage: (I+1)^4 > 0
    -4 > 0

Nevertheless, if you force the comparison, you get the right answer (:trac:`7160`)::

    sage: bool(I^2 == -1)
    True
    sage: bool(I^2 < 0)
    True
    sage: bool((I+1)^4 > 0)
    False

More Examples
-------------

::

    sage: x,y,a = var('x,y,a')
    sage: f = x^2 + y^2 == 1
    sage: f.solve(x)
    [x == -sqrt(-y^2 + 1), x == sqrt(-y^2 + 1)]

::

    sage: f = x^5 + a
    sage: solve(f==0,x)
    [x == (-a)^(1/5)*e^(2/5*I*pi), x == (-a)^(1/5)*e^(4/5*I*pi), x == (-a)^(1/5)*e^(-4/5*I*pi), x == (-a)^(1/5)*e^(-2/5*I*pi), x == (-a)^(1/5)]

You can also do arithmetic with inequalities, as illustrated
below::

    sage: var('x y')
    (x, y)
    sage: f = x + 3 == y - 2
    sage: f
    x + 3 == y - 2
    sage: g = f - 3; g
    x == y - 5
    sage: h =  x^3 + sqrt(2) == x*y*sin(x)
    sage: h
    x^3 + sqrt(2) == x*y*sin(x)
    sage: h - sqrt(2)
    x^3 == x*y*sin(x) - sqrt(2)
    sage: h + f
    x^3 + x + sqrt(2) + 3 == x*y*sin(x) + y - 2
    sage: f = x + 3 < y - 2
    sage: g = 2 < x+10
    sage: f - g
    x + 1 < -x + y - 12
    sage: f + g
    x + 5 < x + y + 8
    sage: f*(-1)
    -x - 3 < -y + 2

TESTS:

We test serializing symbolic equations::

    sage: eqn = x^3 + 2/3 >= x
    sage: loads(dumps(eqn))
    x^3 + 2/3 >= x
    sage: loads(dumps(eqn)) == eqn
    True

AUTHORS:

- Bobby Moretti: initial version (based on a trick that Robert
  Bradshaw suggested).

- William Stein: second version

- William Stein (2007-07-16): added arithmetic with symbolic equations

"""
import operator
from sage.calculus.calculus import maxima

def test_relation_maxima(relation):
    """
    Return True if this (in)equality is definitely true. Return False
    if it is false or the algorithm for testing (in)equality is
    inconclusive.

    EXAMPLES::

        sage: from sage.symbolic.relation import test_relation_maxima
        sage: k = var('k')
        sage: pol = 1/(k-1) - 1/k -1/k/(k-1);
        sage: test_relation_maxima(pol == 0)
        True
        sage: f = sin(x)^2 + cos(x)^2 - 1
        sage: test_relation_maxima(f == 0)
        True
        sage: test_relation_maxima( x == x )
        True
        sage: test_relation_maxima( x != x )
        False
        sage: test_relation_maxima( x > x )
        False
        sage: test_relation_maxima( x^2 > x )
        False
        sage: test_relation_maxima( x + 2 > x )
        True
        sage: test_relation_maxima( x - 2 > x )
        False

    Here are some examples involving assumptions::

        sage: x, y, z = var('x, y, z')
        sage: assume(x>=y,y>=z,z>=x)
        sage: test_relation_maxima(x==z)
        True
        sage: test_relation_maxima(z<x)
        False
        sage: test_relation_maxima(z>y)
        False
        sage: test_relation_maxima(y==z)
        True
        sage: forget()
        sage: assume(x>=1,x<=1)
        sage: test_relation_maxima(x==1)
        True
        sage: test_relation_maxima(x>1)
        False
        sage: test_relation_maxima(x>=1)
        True
        sage: test_relation_maxima(x!=1)
        False
        sage: forget()
        sage: assume(x>0)
        sage: test_relation_maxima(x==0)
        False
        sage: test_relation_maxima(x>-1)
        True
        sage: test_relation_maxima(x!=0)
        True
        sage: test_relation_maxima(x!=1)
        False
        sage: forget()
    """
    m = relation._maxima_()

    #Handle some basic cases first
    if repr(m) in ['0=0']:
        return True
    elif repr(m) in ['0#0', '1#1']:
        return False

    if relation.operator() == operator.eq: # operator is equality
        try:
            s = m.parent()._eval_line('is (equal(%s,%s))'%(repr(m.lhs()),repr(m.rhs())))
        except TypeError, msg:
            raise ValueError, "unable to evaluate the predicate '%s'"%repr(relation)

    elif relation.operator() == operator.ne: # operator is not equal
        try:
            s = m.parent()._eval_line('is (notequal(%s,%s))'%(repr(m.lhs()),repr(m.rhs())))
        except TypeError, msg:
            raise ValueError, "unable to evaluate the predicate '%s'"%repr(relation)

    else: # operator is < or > or <= or >=, which Maxima handles fine
        try:
            s = m.parent()._eval_line('is (%s)'%repr(m))
        except TypeError, msg:
            raise ValueError, "unable to evaluate the predicate '%s'"%repr(relation)

    if s == 'true':
        return True
    elif s == 'false':
        return False # if neither of these, s=='unknown' and we try a few other tricks

    if relation.operator() != operator.eq:
        return False

    difference = relation.lhs() - relation.rhs()
    if repr(difference) == '0':
        return True

    #Try to apply some simplifications to see if left - right == 0
    simp_list = [difference.simplify_log, difference.simplify_rational, difference.simplify_exp,difference.simplify_radical,difference.simplify_trig]
    for f in simp_list:
        try:
            if repr( f() ).strip() == "0":
                return True
                break
        except Exception:
            pass
    return False

def string_to_list_of_solutions(s):
    r"""
    Used internally by the symbolic solve command to convert the output
    of Maxima's solve command to a list of solutions in Sage's symbolic
    package.

    EXAMPLES:

    We derive the (monic) quadratic formula::

        sage: var('x,a,b')
        (x, a, b)
        sage: solve(x^2 + a*x + b == 0, x)
        [x == -1/2*a - 1/2*sqrt(a^2 - 4*b), x == -1/2*a + 1/2*sqrt(a^2 - 4*b)]

    Behind the scenes when the above is evaluated the function
    :func:`string_to_list_of_solutions` is called with input the
    string `s` below::

        sage: s = '[x=-(sqrt(a^2-4*b)+a)/2,x=(sqrt(a^2-4*b)-a)/2]'
        sage: sage.symbolic.relation.string_to_list_of_solutions(s)
         [x == -1/2*a - 1/2*sqrt(a^2 - 4*b), x == -1/2*a + 1/2*sqrt(a^2 - 4*b)]
    """
    from sage.categories.all import Objects
    from sage.structure.sequence import Sequence
    from sage.calculus.calculus import symbolic_expression_from_maxima_string
    v = symbolic_expression_from_maxima_string(s, equals_sub=True)
    return Sequence(v, universe=Objects(), cr_str=True)

###########
# Solving #
###########

def solve(f, *args, **kwds):
    r"""
    Algebraically solve an equation or system of equations (over the
    complex numbers) for given variables. Inequalities and systems
    of inequalities are also supported.

    INPUT:

    -  ``f`` - equation or system of equations (given by a
       list or tuple)

    -  ``*args`` - variables to solve for.

    -  ``solution_dict`` - bool (default: False); if True or non-zero,
       return a list of dictionaries containing the solutions. If there
       are no solutions, return an empty list (rather than a list containing
       an empty dictionary). Likewise, if there's only a single solution,
       return a list containing one dictionary with that solution.

    There are a few optional keywords if you are trying to solve a single
    equation.  They may only be used in that context.

    -  ``multiplicities`` - bool (default: False); if True,
       return corresponding multiplicities.  This keyword is
       incompatible with ``to_poly_solve=True`` and does not make
       any sense when solving inequalities.

    -  ``explicit_solutions`` - bool (default: False); require that
       all roots be explicit rather than implicit. Not used
       when solving inequalities.

    -  ``to_poly_solve`` - bool (default: False) or string; use
       Maxima's ``to_poly_solver`` package to search for more possible
       solutions, but possibly encounter approximate solutions.
       This keyword is incompatible with ``multiplicities=True``
       and is not used when solving inequalities. Setting ``to_poly_solve``
       to 'force' (string) omits Maxima's solve command (useful when
       some solutions of trigonometric equations are lost).

    EXAMPLES::

        sage: x, y = var('x, y')
        sage: solve([x+y==6, x-y==4], x, y)
        [[x == 5, y == 1]]
        sage: solve([x^2+y^2 == 1, y^2 == x^3 + x + 1], x, y)
        [[x == -1/2*I*sqrt(3) - 1/2, y == -sqrt(-1/2*I*sqrt(3) + 3/2)],
         [x == -1/2*I*sqrt(3) - 1/2, y == sqrt(-1/2*I*sqrt(3) + 3/2)],
         [x == 1/2*I*sqrt(3) - 1/2, y == -sqrt(1/2*I*sqrt(3) + 3/2)],
         [x == 1/2*I*sqrt(3) - 1/2, y == sqrt(1/2*I*sqrt(3) + 3/2)],
         [x == 0, y == -1],
         [x == 0, y == 1]]
        sage: solve([sqrt(x) + sqrt(y) == 5, x + y == 10], x, y)
        [[x == -5/2*I*sqrt(5) + 5, y == 5/2*I*sqrt(5) + 5], [x == 5/2*I*sqrt(5) + 5, y == -5/2*I*sqrt(5) + 5]]
        sage: solutions=solve([x^2+y^2 == 1, y^2 == x^3 + x + 1], x, y, solution_dict=True)
        sage: for solution in solutions: print solution[x].n(digits=3), ",", solution[y].n(digits=3)
        -0.500 - 0.866*I , -1.27 + 0.341*I
        -0.500 - 0.866*I , 1.27 - 0.341*I
        -0.500 + 0.866*I , -1.27 - 0.341*I
        -0.500 + 0.866*I , 1.27 + 0.341*I
        0.000 , -1.00
        0.000 , 1.00

    Whenever possible, answers will be symbolic, but with systems of
    equations, at times approximations will be given, due to the
    underlying algorithm in Maxima::

        sage: sols = solve([x^3==y,y^2==x],[x,y]); sols[-1], sols[0]
        ([x == 0, y == 0], [x == (0.309016994375 + 0.951056516295*I),  y == (-0.809016994375 - 0.587785252292*I)])
        sage: sols[0][0].rhs().pyobject().parent()
        Complex Double Field

    If ``f`` is only one equation or expression, we use the solve method
    for symbolic expressions, which defaults to exact answers only::

        sage: solve([y^6==y],y)
        [y == e^(2/5*I*pi), y == e^(4/5*I*pi), y == e^(-4/5*I*pi), y == e^(-2/5*I*pi), y == 1, y == 0]
        sage: solve( [y^6 == y], y)==solve( y^6 == y, y)
        True

    Here we demonstrate very basic use of the optional keywords for
    a single expression to be solved::

        sage: ((x^2-1)^2).solve(x)
        [x == -1, x == 1]
        sage: ((x^2-1)^2).solve(x,multiplicities=True)
        ([x == -1, x == 1], [2, 2])
        sage: solve(sin(x)==x,x)
        [x == sin(x)]
        sage: solve(sin(x)==x,x,explicit_solutions=True)
        []
        sage: solve(abs(1-abs(1-x)) == 10, x)
        [abs(abs(x - 1) - 1) == 10]
        sage: solve(abs(1-abs(1-x)) == 10, x, to_poly_solve=True)
        [x == -10, x == 12]

    .. note::

        For more details about solving a single equation, see
        the documentation for the single-expression
        :meth:`~sage.symbolic.expression.Expression.solve`.

    ::

        sage: from sage.symbolic.expression import Expression
        sage: Expression.solve(x^2==1,x)
        [x == -1, x == 1]

    We must solve with respect to actual variables::

        sage: z = 5
        sage: solve([8*z + y == 3, -z +7*y == 0],y,z)
        Traceback (most recent call last):
        ...
        TypeError: 5 is not a valid variable.

    If we ask for dictionaries containing the solutions, we get them::

        sage: solve([x^2-1],x,solution_dict=True)
        [{x: -1}, {x: 1}]
        sage: solve([x^2-4*x+4],x,solution_dict=True)
        [{x: 2}]
        sage: res = solve([x^2 == y, y == 4],x,y,solution_dict=True)
        sage: for soln in res: print "x: %s, y: %s"%(soln[x], soln[y])
        x: 2, y: 4
        x: -2, y: 4

    If there is a parameter in the answer, that will show up as
    a new variable.  In the following example, ``r1`` is a real free
    variable (because of the ``r``)::

        sage: solve([x+y == 3, 2*x+2*y == 6],x,y)
        [[x == -r1 + 3, y == r1]]

    Especially with trigonometric functions, the dummy variable may
    be implicitly an integer (hence the ``z``)::

        sage: solve([cos(x)*sin(x) == 1/2, x+y == 0],x,y)
        [[x == 1/4*pi + pi*z78, y == -1/4*pi - pi*z78]]

    Expressions which are not equations are assumed to be set equal
    to zero, as with `x` in the following example::

        sage: solve([x, y == 2],x,y)
        [[x == 0, y == 2]]

    If ``True`` appears in the list of equations it is
    ignored, and if ``False`` appears in the list then no
    solutions are returned. E.g., note that the first
    ``3==3`` evaluates to ``True``, not to a
    symbolic equation.

    ::

        sage: solve([3==3, 1.00000000000000*x^3 == 0], x)
        [x == 0]
        sage: solve([1.00000000000000*x^3 == 0], x)
        [x == 0]

    Here, the first equation evaluates to ``False``, so
    there are no solutions::

        sage: solve([1==3, 1.00000000000000*x^3 == 0], x)
        []

    Completely symbolic solutions are supported::

        sage: var('s,j,b,m,g')
        (s, j, b, m, g)
        sage: sys = [ m*(1-s) - b*s*j, b*s*j-g*j ];
        sage: solve(sys,s,j)
        [[s == 1, j == 0], [s == g/b, j == (b - g)*m/(b*g)]]
        sage: solve(sys,(s,j))
        [[s == 1, j == 0], [s == g/b, j == (b - g)*m/(b*g)]]
        sage: solve(sys,[s,j])
        [[s == 1, j == 0], [s == g/b, j == (b - g)*m/(b*g)]]

    Inequalities can be also solved::

        sage: solve(x^2>8,x)
        [[x < -2*sqrt(2)], [x > 2*sqrt(2)]]

    We use ``use_grobner`` in Maxima if no solution is obtained from
    Maxima's ``to_poly_solve``::

       sage: x,y=var('x y'); c1(x,y)=(x-5)^2+y^2-16; c2(x,y)=(y-3)^2+x^2-9
       sage: solve([c1(x,y),c2(x,y)],[x,y])
       [[x == -9/68*sqrt(55) + 135/68, y == -15/68*sqrt(11)*sqrt(5) + 123/68], [x == 9/68*sqrt(55) + 135/68, y == 15/68*sqrt(11)*sqrt(5) + 123/68]]

    TESTS::

        sage: solve([sin(x)==x,y^2==x],x,y)
        [sin(x) == x, y^2 == x]
        sage: solve(0==1,x)
        Traceback (most recent call last):
        ...
        TypeError:  The first argument must be a symbolic expression or a list of symbolic expressions.

    Test if the empty list is returned, too, when (a list of)
    dictionaries (is) are requested (#8553)::

        sage: solve([SR(0)==1],x)
        []
        sage: solve([SR(0)==1],x,solution_dict=True)
        []
        sage: solve([x==1,x==-1],x)
        []
        sage: solve([x==1,x==-1],x,solution_dict=True)
        []
        sage: solve((x==1,x==-1),x,solution_dict=0)
        []

    Relaxed form, suggested by Mike Hansen (#8553)::

        sage: solve([x^2-1],x,solution_dict=-1)
        [{x: -1}, {x: 1}]
        sage: solve([x^2-1],x,solution_dict=1)
        [{x: -1}, {x: 1}]
        sage: solve((x==1,x==-1),x,solution_dict=-1)
        []
        sage: solve((x==1,x==-1),x,solution_dict=1)
        []

    This inequality holds for any real ``x`` (trac #8078)::

        sage: solve(x^4+2>0,x)
        [x < +Infinity]

    Test for user friendly input handling :trac:`13645`::

        sage: poly.<a,b> = PolynomialRing(RR)
        sage: solve([a+b+a*b == 1], a)
        Traceback (most recent call last):
        ...
        TypeError: The first argument to solve() should be a symbolic expression or a list of symbolic expressions, cannot handle <type 'bool'>
        sage: solve([a, b], (1, a))
        Traceback (most recent call last):
        ...
        TypeError: 1 is not a valid variable.
        sage: solve([x == 1], (1, a))
        Traceback (most recent call last):
        ...
        TypeError: 1 is not a valid variable.
    """
    from sage.symbolic.expression import is_Expression
    if is_Expression(f): # f is a single expression
        ans = f.solve(*args,**kwds)
        return ans

    if not isinstance(f, (list, tuple)):
        raise TypeError("The first argument must be a symbolic expression or a list of symbolic expressions.")

    if len(f)==1:
        # f is a list with a single element
        if is_Expression(f[0]):
            # if its a symbolic expression call solve method of this expression
            return f[0].solve(*args,**kwds)
        # otherwise complain
        raise TypeError("The first argument to solve() should be a symbolic "
                        "expression or a list of symbolic expressions, "
                        "cannot handle %s"%repr(type(f[0])))

    # f is a list of such expressions or equations
    from sage.symbolic.ring import is_SymbolicVariable

    if len(args)==0:
        raise TypeError("Please input variables to solve for.")
    if is_SymbolicVariable(args[0]):
        variables = args
    else:
        variables = tuple(args[0])

    for v in variables:
        if not is_SymbolicVariable(v):
            raise TypeError("%s is not a valid variable."%repr(v))

    try:
        f = [s for s in f if s is not True]
    except TypeError:
        raise ValueError, "Unable to solve %s for %s"%(f, args)

    if any(s is False for s in f):
        return []

    from sage.calculus.calculus import maxima
    m = maxima(f)

    try:
        s = m.solve(variables)
    except Exception: # if Maxima gave an error, try its to_poly_solve
        try:
            s = m.to_poly_solve(variables)
        except TypeError, mess: # if that gives an error, raise an error.
            if "Error executing code in Maxima" in str(mess):
                raise ValueError, "Sage is unable to determine whether the system %s can be solved for %s"%(f,args)
            else:
                raise

    if len(s)==0: # if Maxima's solve gave no solutions, try its to_poly_solve
        try:
            s = m.to_poly_solve(variables)
        except Exception: # if that gives an error, stick with no solutions
            s = []

    if len(s)==0: # if to_poly_solve gave no solutions, try use_grobner
        try:
            s = m.to_poly_solve(variables,'use_grobner=true')
        except Exception: # if that gives an error, stick with no solutions
            s = []

    sol_list = string_to_list_of_solutions(repr(s))

    # Relaxed form suggested by Mike Hansen (#8553):
    if kwds.get('solution_dict', False):
        if len(sol_list)==0: # fixes IndexError on empty solution list (#8553)
            return []
        if isinstance(sol_list[0], list):
            sol_dict=[dict([[eq.left(),eq.right()] for eq in solution])
                    for solution in sol_list]
        else:
            sol_dict=[{eq.left():eq.right()} for eq in sol_list]

        return sol_dict
    else:
        return sol_list

def solve_mod(eqns, modulus, solution_dict = False):
    r"""
    Return all solutions to an equation or list of equations modulo the
    given integer modulus. Each equation must involve only polynomials
    in 1 or many variables.

    By default the solutions are returned as `n`-tuples, where `n`
    is the number of variables appearing anywhere in the given
    equations. The variables are in alphabetical order.

    INPUT:

    -  ``eqns`` - equation or list of equations

    -  ``modulus`` - an integer

    -  ``solution_dict`` - bool (default: False); if True or non-zero,
       return a list of dictionaries containing the solutions. If there
       are no solutions, return an empty list (rather than a list containing
       an empty dictionary). Likewise, if there's only a single solution,
       return a list containing one dictionary with that solution.

    EXAMPLES::

        sage: var('x,y')
        (x, y)
        sage: solve_mod([x^2 + 2 == x, x^2 + y == y^2], 14)
        [(4, 2), (4, 6), (4, 9), (4, 13)]
        sage: solve_mod([x^2 == 1, 4*x  == 11], 15)
        [(14,)]

    Fermat's equation modulo 3 with exponent 5::

        sage: var('x,y,z')
        (x, y, z)
        sage: solve_mod([x^5 + y^5 == z^5], 3)
        [(0, 0, 0), (0, 1, 1), (0, 2, 2), (1, 0, 1), (1, 1, 2), (1, 2, 0), (2, 0, 2), (2, 1, 0), (2, 2, 1)]

    We can solve with respect to a bigger modulus if it consists only of small prime factors::

        sage: [d] = solve_mod([5*x + y == 3, 2*x - 3*y == 9], 3*5*7*11*19*23*29, solution_dict = True)
        sage: d[x]
        12915279
        sage: d[y]
        8610183

    For cases where there are relatively few solutions and the prime
    factors are small, this can be efficient even if the modulus itself
    is large::

        sage: sorted(solve_mod([x^2 == 41], 10^20))
        [(4538602480526452429,), (11445932736758703821,), (38554067263241296179,),
        (45461397519473547571,), (54538602480526452429,), (61445932736758703821,),
        (88554067263241296179,), (95461397519473547571,)]

    We solve a simple equation modulo 2::

        sage: x,y = var('x,y')
        sage: solve_mod([x == y], 2)
        [(0, 0), (1, 1)]

    .. warning::

       The current implementation splits the modulus into prime
       powers, then naively enumerates all possible solutions
       (starting modulo primes and then working up through prime
       powers), and finally combines the solution using the Chinese
       Remainder Theorem.  The interface is good, but the algorithm is
       very inefficient if the modulus has some larger prime factors! Sage
       *does* have the ability to do something much faster in certain
       cases at least by using Groebner basis, linear algebra
       techniques, etc. But for a lot of toy problems this function as
       is might be useful. At least it establishes an interface.

    TESTS:

    Make sure that we short-circuit in at least some cases::

        sage: solve_mod([2*x==1], 2*next_prime(10^50))
        []

    Try multi-equation cases::

        sage: x, y, z = var("x y z")
        sage: solve_mod([2*x^2 + x*y, -x*y+2*y^2+x-2*y, -2*x^2+2*x*y-y^2-x-y], 12)
        [(0, 0), (4, 4), (0, 3), (4, 7)]
        sage: eqs = [-y^2+z^2, -x^2+y^2-3*z^2-z-1, -y*z-z^2-x-y+2, -x^2-12*z^2-y+z]
        sage: solve_mod(eqs, 11)
        [(8, 5, 6)]

    Confirm that modulus 1 now behaves as it should::

        sage: x, y = var("x y")
        sage: solve_mod([x==1], 1)
        [(0,)]
        sage: solve_mod([2*x^2+x*y, -x*y+2*y^2+x-2*y, -2*x^2+2*x*y-y^2-x-y], 1)
        [(0, 0)]

    """
    from sage.rings.all import Integer, Integers, crt_basis
    from sage.symbolic.expression import is_Expression
    from sage.misc.all import cartesian_product_iterator
    from sage.modules.all import vector
    from sage.matrix.all import matrix

    if not isinstance(eqns, (list, tuple)):
        eqns = [eqns]
    eqns = [eq if is_Expression(eq) else (eq.lhs()-eq.rhs()) for eq in eqns]
    modulus = Integer(modulus)
    if modulus < 1:
        raise ValueError, "the modulus must be a positive integer"
    vars = list(set(sum([list(e.variables()) for e in eqns], [])))
    vars.sort(key=repr)

    if modulus == 1: # degenerate case
        ans = [tuple(Integers(1)(0) for v in vars)]
        return ans

    factors = modulus.factor()
    crt_basis = vector(Integers(modulus), crt_basis([p**i for p,i in factors]))
    solutions = []

    has_solution = True
    for p,i in factors:
        solution =_solve_mod_prime_power(eqns, p, i, vars)
        if len(solution) > 0:
            solutions.append(solution)
        else:
            has_solution = False
            break

    ans = []
    if has_solution:
        for solution in cartesian_product_iterator(solutions):
            solution_mat = matrix(Integers(modulus), solution)
            ans.append(tuple(c.dot_product(crt_basis) for c in solution_mat.columns()))

    # if solution_dict == True:
    # Relaxed form suggested by Mike Hansen (#8553):
    if solution_dict:
        sol_dict = [dict(zip(vars, solution)) for solution in ans]
        return sol_dict
    else:
        return ans

def _solve_mod_prime_power(eqns, p, m, vars):
    r"""
    Internal help function for solve_mod, does little checking since it expects
    solve_mod to do that

    Return all solutions to an equation or list of equations modulo p^m.
    Each equation must involve only polynomials
    in 1 or many variables.

    The solutions are returned as `n`-tuples, where `n`
    is the number of variables in vars.

    INPUT:

    -  ``eqns`` - equation or list of equations

    -  ``p`` - a prime

    -  ``i`` - an integer > 0

    -  ``vars`` - a list of variables to solve for

    EXAMPLES::

        sage: var('x,y')
        (x, y)
        sage: solve_mod([x^2 + 2 == x, x^2 + y == y^2], 14)
        [(4, 2), (4, 6), (4, 9), (4, 13)]
        sage: solve_mod([x^2 == 1, 4*x  == 11], 15)
        [(14,)]

    Fermat's equation modulo 3 with exponent 5::

        sage: var('x,y,z')
        (x, y, z)
        sage: solve_mod([x^5 + y^5 == z^5], 3)
        [(0, 0, 0), (0, 1, 1), (0, 2, 2), (1, 0, 1), (1, 1, 2), (1, 2, 0), (2, 0, 2), (2, 1, 0), (2, 2, 1)]

    We solve a simple equation modulo 2::

        sage: x,y = var('x,y')
        sage: solve_mod([x == y], 2)
        [(0, 0), (1, 1)]

    .. warning::

       Currently this constructs possible solutions by building up
       from the smallest prime factor of the modulus.  The interface
       is good, but the algorithm is horrible if the modulus isn't the
       product of many small primes! Sage *does* have the ability to
       do something much faster in certain cases at least by using the
       Chinese Remainder Theorem, Groebner basis, linear algebra
       techniques, etc. But for a lot of toy problems this function as
       is might be useful. At the very least, it establishes an
       interface.

    TESTS:

    Confirm we can reproduce the first few terms of OEIS A187719::

        sage: from sage.symbolic.relation import _solve_mod_prime_power
        sage: [sorted(_solve_mod_prime_power([x^2==41], 10, i, [x]))[0][0] for i in [1..13]]
        [1, 21, 71, 1179, 2429, 47571, 1296179, 8703821, 26452429, 526452429,
        13241296179, 19473547571, 2263241296179]

    """
    from sage.rings.all import Integers, PolynomialRing
    from sage.modules.all import vector
    from sage.misc.all import cartesian_product_iterator

    mrunning = 1
    ans = []
    for mi in xrange(m):
        mrunning *= p
        R = Integers(mrunning)
        S = PolynomialRing(R, len(vars), vars)
        eqns_mod = [S(eq) for eq in eqns]
        if mi == 0:
            possibles = cartesian_product_iterator([xrange(len(R)) for _ in xrange(len(vars))])
        else:
            shifts = cartesian_product_iterator([xrange(p) for _ in xrange(len(vars))])
            pairs = cartesian_product_iterator([shifts, ans])
            possibles = (tuple(vector(t)+vector(shift)*(mrunning//p)) for shift, t in pairs)
        ans = list(t for t in possibles if all(e(*t) == 0 for e in eqns_mod))
        if not ans: return ans

    return ans

def solve_ineq_univar(ineq):
    """
    Function solves rational inequality in one variable.

    INPUT:

    - ``ineq`` - inequality in one variable

    OUTPUT:

    - ``list`` -- output is list of solutions as a list of simple inequalities
      output [A,B,C] means (A or B or C) each A, B, C is again a list and
      if A=[a,b], then A means (a and b). The list is empty if there is no
      solution.

    EXAMPLES::

        sage: from sage.symbolic.relation import solve_ineq_univar
        sage: solve_ineq_univar(x-1/x>0)
        [[x > -1, x < 0], [x > 1]]

        sage: solve_ineq_univar(x^2-1/x>0)
        [[x < 0], [x > 1]]

        sage: solve_ineq_univar((x^3-1)*x<=0)
        [[x >= 0, x <= 1]]

    ALGORITHM:

    Calls Maxima command solve_rat_ineq

    AUTHORS:

    - Robert Marik (01-2010)
    """
    ineqvar = ineq.variables()
    if len(ineqvar) != 1:
        raise NotImplementedError, "The command solve_ineq_univar accepts univariate inequalities only. Your variables are ", ineqvar
    ineq0 = ineq._maxima_()
    ineq0.parent().eval("if solve_rat_ineq_loaded#true then (solve_rat_ineq_loaded:true,load(\"solve_rat_ineq.mac\")) ")
    sol = ineq0.solve_rat_ineq().sage()
    if repr(sol)=="all":
        from sage.rings.infinity import Infinity
        sol = [ineqvar[0]<Infinity]
    return sol

def solve_ineq_fourier(ineq,vars=None):
    """
    Solves sytem of inequalities using Maxima and fourier elimination

    Can be used for system of linear inequalities and for some types
    of nonlinear inequalities. For examples see the section EXAMPLES
    below and http://maxima.cvs.sourceforge.net/viewvc/maxima/maxima/share/contrib/fourier_elim/rtest_fourier_elim.mac

    INPUT:

    - ``ineq`` - list with system of inequalities

    - ``vars`` - optionally list with variables for fourier elimination.

    OUTPUT:

    - ``list`` - output is list of solutions as a list of simple inequalities
      output [A,B,C] means (A or B or C) each A, B, C is again a list and
      if A=[a,b], then A means (a and b). The list is empty if there is no
      solution.

    EXAMPLES::

        sage: from sage.symbolic.relation import solve_ineq_fourier
        sage: y=var('y')
        sage: solve_ineq_fourier([x+y<9,x-y>4],[x,y])
        [[y + 4 < x, x < -y + 9, y < (5/2)]]
        sage: solve_ineq_fourier([x+y<9,x-y>4],[y,x])
        [[y < min(x - 4, -x + 9)]]

        sage: solve_ineq_fourier([x^2>=0])
        [[x < +Infinity]]

        sage: solve_ineq_fourier([log(x)>log(y)],[x,y])
        [[y < x, 0 < y]]
        sage: solve_ineq_fourier([log(x)>log(y)],[y,x])
        [[0 < y, y < x, 0 < x]]

    Note that different systems will find default variables in different
    orders, so the following is not tested::

        sage: solve_ineq_fourier([log(x)>log(y)]) # not tested - one of the following appears
        [[0 < y, y < x, 0 < x]]
        [[y < x, 0 < y]]

    ALGORITHM:

    Calls Maxima command fourier_elim

    AUTHORS:

    - Robert Marik (01-2010)
    """
    if vars is None:
        setvars = set([])
        for i in (ineq):
            setvars = setvars.union(set(i.variables()))
            vars =[i for i in setvars]
    ineq0 = [i._maxima_() for i in ineq]
    ineq0[0].parent().eval("if fourier_elim_loaded#true then (fourier_elim_loaded:true,load(\"fourier_elim\"))")
    sol = ineq0[0].parent().fourier_elim(ineq0,vars)
    ineq0[0].parent().eval("or_to_list(x):=\
        if not atom(x) and op(x)=\"or\" then args(x) \
        else [x]")
    sol = sol.or_to_list().sage()
    if repr(sol) == "[emptyset]":
        sol = []
    if repr(sol) == "[universalset]":
        from sage.rings.infinity import Infinity
        sol = [[i<Infinity for i in vars]]
    return sol

def solve_ineq(ineq, vars=None):
    """
    Solves inequalities and systems of inequalities using Maxima.
    Switches between rational inequalities
    (sage.symbolic.relation.solve_ineq_rational)
    and fourier elimination (sage.symbolic.relation.solve_ineq_fouried).
    See the documentation of these functions for more details.

    INPUT:

    - ``ineq`` - one inequality or a list of inequalities

      Case1: If ``ineq`` is one equality, then it should be rational
      expression in one varible. This input is passed to
      sage.symbolic.relation.solve_ineq_univar function.

      Case2: If ``ineq`` is a list involving one or more
      inequalities, than the input is passed to
      sage.symbolic.relation.solve_ineq_fourier function. This
      function can be used for system of linear inequalities and
      for some types of nonlinear inequalities. See
      http://maxima.cvs.sourceforge.net/viewvc/maxima/maxima/share/contrib/fourier_elim/rtest_fourier_elim.mac
      for a big gallery of problems covered by this algorithm.

    - ``vars`` - optional parameter with list of variables. This list
      is used only if fourier elimination is used. If omitted or if
      rational inequality is solved, then variables are determined
      automatically.

    OUTPUT:

    - ``list`` -- output is list of solutions as a list of simple inequalities
      output [A,B,C] means (A or B or C) each A, B, C is again a list and
      if A=[a,b], then A means (a and b).

    EXAMPLES::

        sage: from sage.symbolic.relation import solve_ineq

    Inequalities in one variable. The variable is detected automatically::

        sage: solve_ineq(x^2-1>3)
        [[x < -2], [x > 2]]

        sage: solve_ineq(1/(x-1)<=8)
        [[x < 1], [x >= (9/8)]]

    System of inequalities with automatically detected inequalities::

        sage: y=var('y')
        sage: solve_ineq([x-y<0,x+y-3<0],[y,x])
        [[x < y, y < -x + 3, x < (3/2)]]
        sage: solve_ineq([x-y<0,x+y-3<0],[x,y])
        [[x < min(-y + 3, y)]]

    Note that although Sage will detect the variables automatically,
    the order it puts them in may depend on the system, so the following
    command is only guaranteed to give you one of the above answers::

        sage: solve_ineq([x-y<0,x+y-3<0]) # not tested - random
        [[x < y, y < -x + 3, x < (3/2)]]

    ALGORITHM:

    Calls solve_ineq_fourier if inequalities are list and
    solve_ineq_univar of the inequality is symbolic expression. See
    the description of these commands for more details related to the
    set of inequalities which can be solved. The list is empty if
    there is no solution.

    AUTHORS:

    - Robert Marik (01-2010)
    """
    if isinstance(ineq,list):
        return(solve_ineq_fourier(ineq, vars))
    else:
        return(solve_ineq_univar(ineq))


'''NAME
       Simp13
VERSION
        1.0
AUTHOR
        Elizabeth Marquez Gomez <elimqzg@lcg.unam.mx>
DESCRIPTION
        Implementation of Simpson 1/3 method to find an area between the limits of a function
CATEGORY
        Numeric methods
USAGE
        Simp13.py
'''
#IMPORT LIBRARIES
from sympy import *

#DEFINE SYMBOLS THAT WILL BE USE IN EQUATIONS
x = symbols('x')

print('\t\t-- Welcome to Simpson 1/3 method --')

#GET THE EQUATION OF THE FUNCTION
equation = sympify((input('-> Insert the integrand in terms of "x": ')).replace('e','E'))

#ASK FOR LIMITS AND NUMBER OF BANDS
correct = False
inf = N(input('-> Insert lower limit for the integral: '))
sup = N(input('-> Insert superior limit for the integral: '))
while not correct:
    bands = int(input('-> Insert number of bands: '))
    correct = True if bands%2 == 0 else False

#DEFINE START VALUES
h = (sup-inf)/bands
y0 = N(equation.subs(x, inf))
yn = N(equation.subs(x, inf + (bands * h)))
sum = y0 + yn

#START THE CYCLE
for i in range(1, bands):
    if i%2 == 0:
        sum += 2 * N(equation.subs(x, inf + (i * h)))
    else:
        sum += 4 * N(equation.subs(x, inf + (i * h)))

sum *= h/3
#CHECK IF THE USER WANTS TO ROUND THE RESULT
if input('-> Would you like to round the result? (Y/N) --> ').upper() == 'Y':
    rounder = int(input('\tInsert the decimals to round for the result: '))
    print(f'\tThe value of area is {round(sum, rounder)}')
else:
    print(f'\tThe value of the area is {sum}')
# ------VARIABLE'S DICTIONARY------
#             +----Main----+
# *NAME*         *TYPE*        *USAGE*
# x              string       Represents the symbol "x" that will be use in equations
# equation       sympify      It stores the general equation
# sup            float        Indicates the superior value for the integrand
# inf            float        Indicates the lower value for the integrand
# bands          int          It stores the number od band in wich the area will be divided
# h              float        Indicates h value
# y0             float        Indicates the value of "y" evaluated at initial value of "x"
# yn             float        Indicates the last value of "y"
# sum            float        Accumulates the sum of values to get the area
# i              int          Iterator variable
# correct        boolean      Checks if something is allowed to the method


import numpy as np
import math
from numba import jit, cuda, float32, int32

'''Create Global Variable that is the block size'''
TPB = 128
RAD = 1
'''Create Global Variable that is the length of the shared memory array'''
NSHARED = 130 # This value must agree with TPB + 2*RAD

@cuda.jit(device = True)
def parab(x0):
	return x0**2

@cuda.jit #Lazy compilation
#@cuda.jit('void(float32[:], float32[:])') #Eager compilation
def pKernel(d_f, d_x):
	i = cuda.grid(1)
	n = d_x.shape[0]
	if i < n:
		d_f[i] = parab(d_x[i])

def pArray(x):
	TPB = 32
	n = x.shape[0]
	d_x = cuda.to_device(x)
	d_f = cuda.device_array(n, dtype = np.float32) #need dtype spec for eager compilation
	pKernel[(n+TPB-1)/TPB, TPB](d_f, d_x)
	return d_f.copy_to_host()

#@cuda.jit("void(float32[:],float32[:],float32[:])")
@cuda.jit
def derivativeKernel(d_deriv, d_f, stencil):
	sh_f = cuda.shared.array(NSHARED, dtype = float32)
	i = cuda.grid(1)
	n = d_f.shape[0]
	if i>=n:
		return

	''' thread index and index into static shared output array'''
	tIdx = cuda.threadIdx.x
	''' index in shared input array'''
	shIdx = tIdx + RAD

	#Load regular cells
	sh_f[shIdx] = d_f[i]

	#Halo cells- Check that the entries to be loaded are within array bounds
	if tIdx < RAD:
		if i >= RAD:
			sh_f[shIdx - RAD] = d_f[i-RAD]
		if i + cuda.blockDim.x < n:
			sh_f[shIdx + cuda.blockDim.x] = d_f[i + cuda.blockDim.x]

	cuda.syncthreads()
	# Only write values where the full stencil is "in bounds"
	if i >= RAD and i < n-RAD:
		tmp =  sh_f[shIdx  ]*stencil[RAD]
		for d in range(1,RAD+1):
			tmp += sh_f[shIdx-d]*stencil[RAD-d] + sh_f[shIdx+d]*stencil[RAD+d]
		d_deriv[i] = tmp
		'''
		d_deriv[i]= sh_f[shIdx-1]*stencil[0] + \
					sh_f[shIdx  ]*stencil[1] + \
					sh_f[shIdx+1]*stencil[2]
					'''
	#RECODE THIS AS LOOP SUM FOR GENERAL STENCIL SIZE
def derivativeArray(f, order):
	n = f.shape[0]
	if order == 1:
		stencil =(n-1)/2. * np.array([-1., 0., 1.], dtype = np.float32)
	elif order == 2:
		stencil = (n-1)*(n-1)* np.array([1., -2., 1.], dtype = np.float32)
	d_f = cuda.to_device(f)
	d_deriv = cuda.device_array(n, dtype = np.float32)
	derivativeKernel[(n+TPB-1)/TPB, TPB](d_deriv, d_f, stencil)
	return d_deriv.copy_to_host()


import tensorflow as tf
import numpy as np
import logging

from tensorflow.contrib.learn.python.learn.utils import input_fn_utils

tf.logging.set_verbosity(tf.logging.INFO)

atributes = [
	[0, 0]
	, [0, 1]
	, [1, 0]
	, [1, 1]
]

labels = [
	0
	, 1
	, 1
	, 0
]

#data = np.array(atributes, 'float32') #data and target tranformed to int instead of float, because exception throws for metric operations
#target = np.array(labels, 'float32')
data = np.array(atributes, 'int64')
target = np.array(labels, 'int64')

feature_columns = [tf.contrib.layers.real_valued_column(""
							, dimension=len(atributes[0]) #attributes consist of two columns: x1 and x2.
							, dtype=tf.float32)]

learningRate = 0.1
epoch = 10000

#available metrics: https://www.tensorflow.org/api_docs/python/tf/metrics
validation_metrics = {
	"accuracy": tf.contrib.learn.MetricSpec(metric_fn = tf.contrib.metrics.streaming_accuracy
		, prediction_key = tf.contrib.learn.PredictionKey.CLASSES)
	, "precision": tf.contrib.learn.MetricSpec(metric_fn = tf.contrib.metrics.streaming_precision
		, prediction_key = tf.contrib.learn.PredictionKey.CLASSES)
	, "recall": tf.contrib.learn.MetricSpec(metric_fn = tf.contrib.metrics.streaming_recall
		, prediction_key = tf.contrib.learn.PredictionKey.CLASSES)
	, "mean_absolute_error": tf.contrib.learn.MetricSpec(metric_fn = tf.contrib.metrics.streaming_mean_absolute_error
		, prediction_key = tf.contrib.learn.PredictionKey.CLASSES)
	, "false_negatives": tf.contrib.learn.MetricSpec(metric_fn = tf.contrib.metrics.streaming_false_negatives
		, prediction_key = tf.contrib.learn.PredictionKey.CLASSES)
	, "false_positives": tf.contrib.learn.MetricSpec(metric_fn = tf.contrib.metrics.streaming_false_positives
		, prediction_key = tf.contrib.learn.PredictionKey.CLASSES)
	, "true_positives": tf.contrib.learn.MetricSpec(metric_fn = tf.contrib.metrics.streaming_true_positives
		, prediction_key = tf.contrib.learn.PredictionKey.CLASSES)
}

validation_monitor = tf.contrib.learn.monitors.ValidationMonitor(data, target, every_n_steps = 500
	, metrics = validation_metrics #normally tp, fp, fn are not traced. we can add these metrics by adding metrics param
)

classifier = tf.contrib.learn.DNNClassifier(
	feature_columns = feature_columns
	, hidden_units = [3]
	, activation_fn = tf.nn.sigmoid
	, optimizer = tf.train.GradientDescentOptimizer(learningRate)
	, model_dir = "model"
	, config = tf.contrib.learn.RunConfig(save_checkpoints_secs = 1)
)

classifier.fit(data, target, steps = epoch
	, monitors = [validation_monitor])

#print("params: ", classifier.get_variable_names())
print("total epoch: ", classifier.get_variable_value("global_step"))
print("weights from input layer to hidden layer\n", classifier.get_variable_value("dnn/hiddenlayer_0/weights"))
print("weights from hidden layer to output layer\n", classifier.get_variable_value("dnn/logits/weights"))

"""
#this block is deactivated because I would not exported saved model in external system like Java anymore
feature_spec = tf.contrib.layers.create_feature_spec_for_parsing(feature_columns)
serving_input_fn = input_fn_utils.build_parsing_serving_input_fn(feature_spec)
classifier.export_savedmodel(classifier.model_dir+"\export", serving_input_fn, as_text=True)
"""

def test_set():
	return np.array(atributes, np.float32)

predictions = classifier.predict_classes(input_fn = test_set)

#dumping predictions
index = 0
for i in predictions:

	print(data[index], " -> actual: ", target[index], ", predict: ", i)
	index  = index + 1

#--------------------------------

#dumping metrics
success_metrics = classifier.evaluate(data, target, metrics = validation_metrics)
print("FN: ", success_metrics["false_negatives"])
print("FP: ", success_metrics["false_positives"])
print("TP: ", success_metrics["true_positives"])
print("-----------------")
print("precision: ", success_metrics["precision"]) # TP / (FP + TP)
print("recall: ", success_metrics["recall"]) # TP / (FN + TP)
print("accuracy: ", success_metrics["accuracy"])
print("mae: ", success_metrics["mean_absolute_error"])


import numpy as np
import numpy.linalg as la

def periodic_sol(data, N, i, j):

    m = i
    n = j

    # x-periodic
    if i < 0:
        m = N+i
    elif i > N-1:
        m = i-N

    # y-periodic
    if j < 0:
        n = N+j
    elif j > N-1:
        n = j-N

    return data[m,n,:]

def structure_mean_point(data, i, j, stencil, p):
    #
    # Approximates mean field structure function for a single point in the spatial domain
    #
    N = data.shape[0]
    Smeanpt = 0

    istart = i - stencil
    iend = i + stencil

    jstart = j - stencil
    jend = j + stencil

    Uij = data[i, j, :]
    #print(Uij)
    for m in range(istart, iend+1):
        for n in range(jstart, jend+1):
            if m == i and n == j:
                continue
            Umn = periodic_sol(data, N, m, n)
            #Smeanpt += la.norm(Uij - Umn, p)**p
            Smeanpt += (np.abs(Uij[0] - Umn[0]))**p
            Smeanpt += (np.abs(Uij[1] - Umn[1]))**p

    # print('\n\n')
    return Smeanpt/(2*stencil+1)**2

def structure_mean_point_fast(data, i, j, stencil, p):
    # Approximates mean field structure function for a single point in the spatial domain
    # Computes only the outer shell of the stencil
    N = data.shape[0]
    Smeanpt = 0

    istart = i - stencil
    iend = i + stencil

    jstart = j - stencil
    jend = j + stencil

    Uij = data[i, j, :]
    #print(Uij)
    for m in [istart, iend]:
        for n in range(jstart, jend+1):
            # print(m,n)
            Umn = periodic_sol(data, N, m, n)
            Smeanpt += (np.abs(Uij[0] - Umn[0]))**p
            Smeanpt += (np.abs(Uij[1] - Umn[1]))**p

    for m in range(istart+1, iend):
        for n in [jstart, jend]:
            # print(m,n)
            Umn = periodic_sol(data, N, m, n)
            Smeanpt += (np.abs(Uij[0] - Umn[0]))**p
            Smeanpt += (np.abs(Uij[1] - Umn[1]))**p

    # print('\n\n')
    return Smeanpt

def structure_mean(data, stencil, p):
    """
    Approximates mean field structure function
    """
    N = data.shape[0]
    Smean = 0

    for i in range(0,N):
        for j in range(0,N):
            Smean += structure_mean_point(data, i, j, stencil, p)
            # print(i, j)

    return (Smean / N**2)**(1./p)

def structure_mean_fast(data, stencil, p):
    """
    Approximates mean field structure function
    """
    N = data.shape[0]

    # compute outer shells of stencil
    Smean_raw = np.zeros(stencil.size)
    for i in range(0,N):
        for j in range(0,N):
            for (ns,s) in enumerate(stencil):
                Smean_raw[ns] += structure_mean_point_fast(data, i, j, s, p)

    # post-process
    Smean = np.zeros(stencil.size)
    for ns in range(0, stencil.size):
        factor = 1./(2*stencil[ns]+1)**2/N**2
        Smean[ns] = (np.sum(Smean_raw[0:ns+1])*factor)**(1./p)

    return Smean


import numpy as np
from scipy import signal
import matplotlib.pyplot as plt

def bellshape(x, a, b, c):
    y = 1.0 + np.abs((x - c) / (a + EPS)) ** (2 * b)
    return 1.0 / y

def gauss(x, a, c):
    y = - np.square(x - c) / ((a + EPS) ** 2)
    y = np.exp(y)
    return y

if __name__ == '__main__':
    EPS = 1.0e-16
    N = 1
    Ns = 100

    x = np.linspace(0, N, Ns)

    a1 = 0.3
    b1 = 1.5
    c1 = 0.4
    y1 = bellshape(x, a=a1, b=b1, c=c1)

    a2 = 0.3
    b2 = 2
    c2 = 0.6
    y2 = bellshape(x, a=a2, b=b2, c=c2)

    plt.figure()
    plt.plot(x, y1, '-r')
    plt.plot(x, y2, '-b')
    plt.legend(['a=' + str(a1) + ', b=' + str(b1) + ', c=' + str(c1),
                'a=' + str(a2) + ', b=' + str(b2) + ', c=' + str(c2)])
    plt.hold()
    plt.grid()
    plt.title('bell shape membership function')
    plt.xlabel('x')
    plt.ylabel('f(x)')
    plt.show()

    a1 = 0.2
    c1 = 0.4
    y1 = gauss(x, a=a1, c=c1)

    a2 = 0.1
    c2 = 0.6
    y2 = gauss(x, a=a2, c=c2)

    plt.figure()
    plt.plot(x, y1, '-r')
    plt.plot(x, y2, '-b')
    plt.legend(['a=' + str(a1) + ', c=' + str(c1),
                'a=' + str(a2) + ', c=' + str(c2)])
    plt.hold()
    plt.grid()
    plt.title('gauss membership function')
    plt.xlabel('x')
    plt.ylabel('f(x)')
    plt.show()


import numpy as np

def secant(f, a, b, epsilon=10**-5):
    assert epsilon > 0
    assert f(a)*f(b) <= 0

    range_hist = [(a, b)]
    fc = epsilon + 1  # for first check

    while not np.abs(fc) <= epsilon:
        fa = f(a)
        fb = f(b)

        c = (a*fb - b*fa) / (fb-fa)
        fc = f(c)

        if fc * fa > 0:
            a = c
        else:
            b = c

        range_hist.append((a, b))

    hist = np.asarray(range_hist[:-1])
    return c, hist