from datetime import datetime
from collections import OrderedDict
from collections.abc import Mapping
import logging
# ontology
from owlready2 import And, Or, Not, FunctionalProperty, InverseFunctionalProperty, ReflexiveProperty, SymmetricProperty, AsymmetricProperty, IrreflexiveProperty, TransitiveProperty

# numpy
import numpy as np

# pytorch
import torch

# Gurobi
from gurobipy import GRB, Model

from regr.graph.concept import Concept
from regr.solver.ilpOntSolver import ilpOntSolver
from regr.solver.gurobiILPBooleanMethods import gurobiILPBooleanProcessor
from regr.solver.lcLossBooleanMethods import lcLossBooleanMethods
from regr.graph import LogicalConstrain, V
from pickle import FALSE

class gurobiILPOntSolver(ilpOntSolver):
    ilpSolver = 'Gurobi'

    def __init__(self, graph, ontologiesTuple, _ilpConfig) -> None:
        super().__init__(graph, ontologiesTuple, _ilpConfig)
        self.myIlpBooleanProcessor = gurobiILPBooleanProcessor()
        self.myLcLossBooleanMethods = lcLossBooleanMethods()

    def valueToBeSkipped(self, x):
        return (
                x != x or  # nan
                abs(x) == float('inf')  # inf
                )

    # Get Ground Truth for provided concept
    def __getLabel(self, dn, conceptRelation, fun=None, epsilon = None):
        value = dn.getAttribute(conceptRelation, 'label')

        return value

    # Get and calculate probability for provided concept
    def __getProbability(self, dn, conceptRelation, fun=None, epsilon = 0.00001):
        if not dn:
            valueI = None
        else:
            valueI = dn.getAttribute(conceptRelation, "local" , "softmax")

        if valueI is None: # No probability value - return negative probability
            return [float("nan"), float("nan")]

        if conceptRelation[2] is not None:
            value = torch.empty(2, dtype=torch.float)

            value[0] = 1 - valueI[conceptRelation[2]]
            value[1] = valueI[conceptRelation[2]]
        else:
            value = valueI

        # Process probability through function and apply epsilon
        if epsilon is not None:
            if value[0] > 1-epsilon:
                value[0] = 1-epsilon
            elif value[1] > 1-epsilon:
                value[1] = 1-epsilon

            if value[0] < epsilon:
                value[0] = epsilon
            elif value[1] < epsilon:
                value[1] = epsilon

            # Apply fun on probabilities
            if fun is not None:
                value = fun(value)

        return value # Return probability

    def createILPVariables(self, m, rootDn, *conceptsRelations, dnFun = None, fun=None, epsilon = 0.00001):
        x = {}
        Q = None

        # Create ILP variables
        for _conceptRelation in conceptsRelations:
            rootConcept = rootDn.findRootConceptOrRelation(_conceptRelation[0])
            dns = rootDn.findDatanodes(select = rootConcept)

            for dn in dns:
                currentProbability = dnFun(dn, _conceptRelation, fun=fun, epsilon=epsilon)

                if currentProbability == None or (torch.is_tensor(currentProbability) and currentProbability.dim() == 0) or len(currentProbability) < 2:
                    self.myLogger.warning("Probability not provided for variable concept %s in dataNode %s - skipping it"%(_conceptRelation[0].name,dn.getInstanceID()))

                    continue

                # Check if probability is NaN or if and has to be skipped
                if self.valueToBeSkipped(currentProbability[1]):
                    self.myLogger.info("Probability is %f for concept %s and dataNode %s - skipping it"%(currentProbability[1],_conceptRelation[1],dn.getInstanceID()))
                    continue

                # Create variable
                xVarName = "x_%s_is_%s"%(dn.getInstanceID(), _conceptRelation[1])
                xNew = m.addVar(vtype=GRB.BINARY,name=xVarName)
                xkey = '<' + _conceptRelation[0].name + '>/ILP/x'

                if xkey not in dn.attributes:
                    dn.attributes[xkey] = [None] * _conceptRelation[3]

                if _conceptRelation[2] is not None:
                    dn.attributes[xkey][_conceptRelation[2]] = xNew
                    x[_conceptRelation[0], _conceptRelation[1], dn.getInstanceID(), _conceptRelation[2]] = xNew
                else:
                    dn.attributes[xkey][0] = xNew
                    x[_conceptRelation[0], _conceptRelation[1], dn.getInstanceID(), 0] = xNew

                Q += currentProbability[1] * xNew

                # Check if probability is NaN or if and has to be created based on positive value
                if self.valueToBeSkipped(currentProbability[0]):
                    currentProbability[0] = 1 - currentProbability[1]
                    self.myLogger.info("No ILP negative variable for concept %s and dataNode %s - created based on positive value %f"%(dn.getInstanceID(), _conceptRelation[0].name, currentProbability[1]))

                # Create negative variable for binary concept
                if _conceptRelation[2] is None: # ilpOntSolver.__negVarTrashhold:
                    xNotNew = m.addVar(vtype=GRB.BINARY,name="x_%s_is_not_%s"%(dn.getInstanceID(),  _conceptRelation[1]))
                    notxkey = '<' + _conceptRelation[0].name + '>/ILP/notx'

                    if notxkey not in dn.attributes:
                        dn.attributes[notxkey] = [None] * _conceptRelation[3]

                    if _conceptRelation[2] is not None:
                        dn.attributes[notxkey][_conceptRelation[2]] = xNotNew
                        x[_conceptRelation[0], 'Not_'+_conceptRelation[1], dn.getInstanceID(), _conceptRelation[2]] = xNotNew
                    else:
                        dn.attributes[notxkey][0] = xNotNew
                        x[_conceptRelation[0], 'Not_'+_conceptRelation[1], dn.getInstanceID(), 0] = xNotNew

                    Q += currentProbability[0] * xNotNew

                else:
                    self.myLogger.info("No ILP negative variable for concept %s and dataNode %s created"%( _conceptRelation[1], dn.getInstanceID()))

        m.update()

        if len(x):
            self.myLogger.info("Created %i ILP variables"%(len(x)))
        else:
            self.myLogger.warning("No ILP variables created")

        return Q, x

    def addGraphConstrains(self, m, rootDn, *conceptsRelations):
        # Add constrain based on probability
        for _conceptRelation in conceptsRelations:
            rootConcept = rootDn.findRootConceptOrRelation(_conceptRelation[0])
            dns = rootDn.findDatanodes(select = rootConcept)

            conceptRelation = _conceptRelation[0].name

            xkey = '<' + conceptRelation + '>/ILP/x'
            notxkey = '<' + conceptRelation + '>/ILP/notx'

            for dn in dns:
                if notxkey not in dn.attributes:
                    continue

                if _conceptRelation[2] is None:
                    x = dn.getAttribute(xkey)[0]
                    notx = dn.getAttribute(notxkey)[0]
                else:
                    x = dn.getAttribute(xkey)[_conceptRelation[2]]
                    notx = dn.getAttribute(notxkey)[_conceptRelation[2]]

                currentConstrLinExpr = x + notx

                m.addConstr(currentConstrLinExpr == 1, name='Disjoint: %s and %s'%(_conceptRelation[1], 'Not_'+_conceptRelation[1]))
                self.myLogger.debug("Disjoint constrain between variable %s is  %s and variable %s is not - %s == %i"%(dn.getInstanceID(),_conceptRelation[1],dn.getInstanceID(),'Not_'+_conceptRelation[1],1))

        m.update()

        # Create subclass constraints
        for concept in conceptsRelations:

            # Skip if multiclass
            if concept[2] is not None:
                continue

            rootConcept = rootDn.findRootConceptOrRelation(concept[0])
            dns = rootDn.findDatanodes(select = rootConcept)

            for rel in concept[0].is_a():
                if  not rel.auto_constraint:
                    continue

                # A is_a B : if(A, B) : A(x) <= B(x)

                sxkey = '<' + rel.src.name + '>/ILP/x'
                dxkey = '<' + rel.dst.name + '>/ILP/x'

                for dn in dns:

                    if sxkey not in dn.attributes: # subclass (A)
                        continue

                    if dxkey not in dn.attributes: # superclass (B)
                        continue

                    self.myIlpBooleanProcessor.ifVar(m, dn.getAttribute(sxkey)[0], dn.getAttribute(dxkey)[0], onlyConstrains = True)
                    self.myLogger.info("Created - subclass - constraints between concept \"%s\" and concepts %s"%(rel.src.name,rel.dst.name))

        # Create disjoint constraints
        foundDisjoint = dict() # To eliminate duplicates
        for concept in conceptsRelations:

            # Skip if multiclass
            if concept[2] is not None:
                continue

            rootConcept = rootDn.findRootConceptOrRelation(concept[0])
            dns = rootDn.findDatanodes(select = rootConcept)

            for rel in concept[0].not_a():
                if  not rel.auto_constraint:
                    continue

                conceptName = concept[1]

                relDestFound = False
                for c in conceptsRelations:
                    if rel.dst == c[0]:
                        relDestFound = True
                        break
                if not relDestFound:
                    continue

                disjointConcept = rel.dst.name

                if conceptName in foundDisjoint:
                    if disjointConcept in foundDisjoint[conceptName]:
                        continue

                if disjointConcept in foundDisjoint:
                    if conceptName in foundDisjoint[disjointConcept]:
                        continue

                cxkey = '<' + conceptName + '>/ILP/x'
                dxkey = '<' + disjointConcept + '>/ILP/x'

                for dn in dns:
                    if cxkey not in dn.attributes:
                        continue

                    if dxkey not in dn.attributes:
                        continue

                    self.myIlpBooleanProcessor.countVar(m, dn.getAttribute(cxkey)[0], dn.getAttribute(dxkey)[0], onlyConstrains = True,  limitOp = '<=', limit = 1, logicMethodName = "atMostL")

                if not (conceptName in foundDisjoint):
                    foundDisjoint[conceptName] = {disjointConcept}
                else:
                    foundDisjoint[conceptName].add(disjointConcept)

            if concept[1] in foundDisjoint:
                self.myLogger.info("Created - disjoint - constraints between concept \"%s\" and concepts %s"%(conceptName,foundDisjoint[conceptName]))

        # Create relation links constraints
        for concept in conceptsRelations:

            # Skip if multiclass
            if concept[2] is not None:
                continue

            isConstrainCreated = False

            for arg_id, rel in enumerate(concept[0].has_a()):

                if  not rel.auto_constraint:
                    continue

                isConstrainCreated = True

                # TODO: need to include indirect ones like sp_tr is a tr while tr has a lm
                # A has_a B : A(x,y,...) <= B(x)
                #for xy in candidates[rel.src]:
                #x = xy[arg_id]

                relationName = rel.src.name
                rootRelation = rootDn.findRootConceptOrRelation(relationName)
                dnsR = rootDn.findDatanodes(select = rootRelation)
                _relAttr = rootDn.getRelationAttrNames(rootRelation)
                relAttr = list(_relAttr.keys())

                conceptName = rel.dst.name
                rootConcept = rootDn.findRootConceptOrRelation(conceptName)

                rxkey = '<' + relationName + '>/ILP/x'
                cxkey = '<' + conceptName + '>/ILP/x'

                for dn in dnsR:
                    if rxkey not in dn.attributes:
                        continue

                    cdn = dn.relationLinks[relAttr[arg_id]]

                    if not cdn or cxkey not in cdn[0].attributes:
                        continue

                    self.myIlpBooleanProcessor.ifVar(m,dn.getAttribute(rxkey)[0], cdn[0].getAttribute(cxkey)[0], onlyConstrains = True)

            if isConstrainCreated:
                self.myLogger.info("Created - doman/range constraints for concepts %s"%(concept[1]))

        m.update()

    def addOntologyConstrains(self, m, rootDn, *_conceptsRelations):

        if not hasattr(self, 'myOnto'):
            return

        conceptsRelations = [cr[0].name for cr in _conceptsRelations]

        # -- Add constraints based on concept disjoint statements in ontology - not(and(var1, var2)) = nand(var1, var2)
        foundDisjoint = dict() # too eliminate duplicates
        for conceptName in conceptsRelations:

            currentConcept = self.myOnto.search_one(iri = "*%s"%(conceptName))

            if currentConcept is None :
                continue

            rootConcept = rootDn.findRootConceptOrRelation(conceptName)
            dns = rootDn.findDatanodes(select = rootConcept)

            #self.myLogger.debug("Concept \"%s\" from data set mapped to \"%s\" concept in ontology"%(currentConcept.name, conceptName))

            for currentD in currentConcept.disjoints():
                currentDisjoints = []

                for e in currentD.entities:
                    if e != currentConcept:
                        currentDisjoints.append(e)

                for currentDisjoint in currentDisjoints:
                    extendedCurrentDisjoints = currentDisjoint.descendants()

                    for d in extendedCurrentDisjoints:
                        disjointConcept = d._name

                        if disjointConcept not in conceptsRelations:
                            continue

                        if conceptName in foundDisjoint:
                            if disjointConcept in foundDisjoint[conceptName]:
                                continue

                        if disjointConcept in foundDisjoint:
                            if conceptName in foundDisjoint[disjointConcept]:
                                continue

                        rootDisjointConcept = rootDn.findRootConceptOrRelation(disjointConcept)
                        dnsDC = rootDn.findDatanodes(select = rootDisjointConcept)

                        cxkey = '<' + conceptName + '>/ILP/x'
                        dxkey = '<' + disjointConcept + '>/ILP/x'

                        for dn in zip(dns,dnsDC):
                            if cxkey not in dn[0].attributes:
                                continue

                            if dxkey not in dn[1].attributes:
                                continue

                            self.myIlpBooleanProcessor.nandVar(m, dn[0].getAttribute(cxkey)[0], dn[1].getAttribute(dxkey)[0], onlyConstrains = True)

                        if not (conceptName in foundDisjoint):
                            foundDisjoint[conceptName] = {disjointConcept}
                        else:
                            foundDisjoint[conceptName].add(disjointConcept)

            if conceptName in foundDisjoint:
                self.myLogger.info("Created - disjoint - constraints between concept \"%s\" and concepts %s"%(conceptName,foundDisjoint[conceptName]))

        # -- Add constraints based on concept equivalent statements in ontology - and(var1, av2)
        foundEquivalent = dict() # too eliminate duplicates
        for conceptName in conceptsRelations:

            currentConcept = self.myOnto.search_one(iri = "*%s"%(conceptName))

            if currentConcept is None :
                continue

            for equivalentConcept in currentConcept.equivalent_to:
                equivalentConceptName = equivalentConcept.name
                if equivalentConceptName not in conceptsRelations:
                    continue

                if conceptName in foundEquivalent:
                    if equivalentConceptName in foundEquivalent[conceptName]:
                        continue

                if equivalentConceptName in foundEquivalent:
                    if conceptName in foundEquivalent[equivalentConceptName]:
                        continue

                rootEquivalentConcept = rootDn.findRootConceptOrRelation(equivalentConceptName)
                dnsEC = rootDn.findDatanodes(select = rootEquivalentConcept)

                cxkey = '<' + conceptName + '>/ILP/x'
                exkey = '<' + equivalentConceptName + '>/ILP/x'

                for dn in zip(dns,dnsEC):
                    if cxkey not in dn[0].attributes:
                        continue

                    if exkey not in dn[1].attributes:
                        continue

                    self.myIlpBooleanProcessor.andVar(m, dn[0].getAttribute(cxkey)[0], dn[1].getAttribute(exkey)[0], onlyConstrains = True)

                if not (conceptName in foundEquivalent):
                    foundEquivalent[conceptName] = {equivalentConceptName}
                else:
                    foundEquivalent[conceptName].add(equivalentConceptName)

            if conceptName in foundEquivalent:
                self.myLogger.info("Created - equivalent - constraints between concept \"%s\" and concepts %s"%(conceptName,foundEquivalent[conceptName]))

        # -- Add constraints based on concept subClassOf statements in ontology - var1 -> var2
        for conceptName in conceptsRelations:

            currentConcept = self.myOnto.search_one(iri = "*%s"%(conceptName))

            if currentConcept is None :
                continue

            for ancestorConcept in currentConcept.ancestors(include_self = False):
                ancestorConceptName = ancestorConcept.name
                if ancestorConceptName not in conceptsRelations:
                    continue

                rootAncestorConcept = rootDn.findRootConceptOrRelation(ancestorConceptName)
                dnsAC = rootDn.findDatanodes(select = rootAncestorConcept)

                cxkey = '<' + conceptName + '>/ILP/x'
                axkey = '<' + ancestorConceptName + '>/ILP/x'

                for dn in zip(dns,dnsAC):
                    if cxkey not in dn[0].attributes:
                        continue

                    if axkey not in dn[1].attributes:
                        continue

                    self.myIlpBooleanProcessor.ifVar(m,  dn[0].getAttribute(cxkey)[0], dn[1].getAttribute(axkey)[0], onlyConstrains = True)

                self.myLogger.info("Created - subClassOf - constraints between concept \"%s\" and concept \"%s\""%(conceptName,ancestorConceptName))

        # ---- ------No supported yet ontology concept constraints --------

        # -- Add constraints based on concept intersection statements in ontology - and(var1, var2, var3, ..)
        # -- Add constraints based on concept union statements in ontology -  or(var1, var2, var3, ..)
        # -- Add constraints based on concept objectComplementOf statements in ontology - xor(var1, var2)
        # -- Add constraints based on concept disjonitUnion statements in ontology -  Not supported by owlready2 yet
        # -- Add constraints based on property domain and range statements in ontology - P(x,y) -> D(x), R(y)
        # -- Add constraints based on concept oneOf statements in ontology - ?

        # -- Add constraints based on property domain and range statements in ontology - P(x,y) -> D(x), R(y)
        for relationName in conceptsRelations:
            currentRelation = self.myOnto.search_one(iri = "*%s"%(relationName))

            if currentRelation is None:
                continue

            try:
                currentRelationDomain = currentRelation.get_domain() # domains_indirect()
                currentRelationRange = currentRelation.get_range()
            except AttributeError:
                continue

            self.myLogger.debug("Relation \"%s\" from data set mapped to \"%s\" concept in ontology"%(currentRelation.name, relationName))

            rootRelation = rootDn.findRootConceptOrRelation(relationName)
            dnsR = rootDn.findDatanodes(select = rootRelation)
            _relAttr = rootDn.getRelationAttrNames(rootRelation)
            relAttr = list(_relAttr.keys())

            cxkey = '<' + currentRelation.name + '>/ILP/x'

            for rel_domain in currentRelationDomain:
                if rel_domain._name not in conceptsRelations:
                    continue

                dxkey = '<' + rel_domain._name + '>/ILP/x'

                for rel_range in currentRelationRange:
                    if rel_range.name not in conceptsRelations:
                        continue

                    rxkey = '<' + rel_range._name + '>/ILP/x'

                    for dn in dnsR:
                        if cxkey not in dn.attributes:
                            continue

                        ddn = dn.relationLinks[relAttr[0]]

                        if not ddn or dxkey not in ddn[0].attributes:
                            continue

                        self.myIlpBooleanProcessor.ifVar(m, dn.getAttribute(cxkey)[0], ddn[0].getAttribute(dxkey)[0], onlyConstrains = True)

                        rdn = dn.relationLinks[relAttr[1]]

                        if not rdn or rxkey not in rdn[0].attributes:
                            continue

                        self.myIlpBooleanProcessor.ifVar(m, dn.getAttribute(cxkey)[0], rdn[0].getAttribute(rxkey)[0], onlyConstrains = True)

                self.myLogger.info("Created - domain-range - constraints for relation \"%s\" for domain \"%s\" and range \"%s\""%(relationName,rel_domain._name,rel_range._name))

        m.update()

    def addLogicalConstrains(self, m, dn, lcs, p):
        self.myLogger.info('Starting method')

        key = "/ILP/xP" # to get ILP variable from datanodes

        for lc in lcs:

            if lc.active:
                self.myLogger.info('Processing Logical Constrain %s(%s) - %s'%(lc.lcName, lc, [str(e) for e in lc.e]))
            else:
                self.myLogger.info('Skipping not active Logical Constrain %s(%s) - %s'%(lc.lcName, lc, [str(e) for e in lc.e]))
                continue

            result = self.__constructLogicalConstrains(lc, self.myIlpBooleanProcessor, m, dn, p, key = key,  lcVariablesDns = {}, headLC = True)

            if result != None and isinstance(result, list):
                self.myLogger.info('Successfully added Logical Constrain %s'%(lc.lcName))
            else:
                self.myLogger.error('Failed to add Logical Constrain %s'%(lc.lcName))

    def __constructLogicalConstrains(self, lc, booleanProcesor, m, dn, p, key = "", lcVariablesDns = {}, headLC = False):
        lcVariables = {}
        vNo = 0
        firstV = True

        for eIndex, e in enumerate(lc.e):
            if  isinstance(e, V):
                continue # already processed in the previous Concept

            if isinstance(e, (Concept,  LogicalConstrain, tuple)):
                # Look one step ahead in the parsed logical constrain and get variables names (if present) after the current concept
                if eIndex + 1 < len(lc.e) and isinstance(lc.e[eIndex+1], V):
                    variable = lc.e[eIndex+1]
                else:
                    if isinstance(e, LogicalConstrain):
                        variable = V(name="_lc" + str(vNo))
                        vNo += 1
                    else:
                        if firstV:
                            variable = V(name="_x" )
                            firstV = False
                        else:
                            variable = V(name="_x" + str(vNo), v = ("_x",))
                            vNo += 1

                if variable.name:
                    variableName = variable.name
                else:
                    variableName = "V" + str(eIndex)

                if variableName in lcVariables:
                    newvVariableName = "_x" + str(vNo)
                    vNo += 1

                    lcVariablesDns[newvVariableName] = lcVariablesDns[variableName]
                    if None in lcVariablesDns:
                        pass

                    lcVariables[newvVariableName] = lcVariables[variableName]

                elif isinstance(e, (Concept, tuple)): # -- Concept
                    if isinstance(e, Concept):
                        conceptName = e.name
                    else:
                        conceptName = e[0].name

                    xPkey = '<' + conceptName + ">" + key

                    dnsList = [] # Stores lists of dataNode for each corresponding dataNode
                    vDns = [] # Stores ILP variables

                    if variable.v == None:
                        if variable.name == None:
                            self.myLogger.error('The element %s of logical constrain %s has no name for variable'%(conceptName, lc.lcName))
                            return None

                        rootConcept = dn.findRootConceptOrRelation(conceptName)
                        _dns = dn.findDatanodes(select = rootConcept)
                        dnsList = [[dn] for dn in _dns]
                    else:
                        if len(variable.v) == 0:
                            self.myLogger.error('The element %s of logical constrain %s has no empty part v of the variable'%(conceptName, lc.lcName))
                            return None

                        path = variable.v

                        paths = []
                        lo = None

                        if isinstance(path[0], str) and len(path) == 1:
                            paths.append(path)
                        elif isinstance(path[0], str) and not isinstance(path[1], tuple):
                            paths.append(path)
                        else:
                            for i, vE in enumerate(variable.v):
                                if i == 0 and isinstance(vE, str):
                                    lo = vE
                                    continue

                                paths.append(vE)

                        _dnsList = []
                        for i, v in enumerate(paths):
                            _dnsList.append([])
                            referredVariableName = v[0] # Get name of the referred variable already defined in the logical constrain from the v part

                            if referredVariableName not in lcVariablesDns:
                                self.myLogger.error('The element %s of logical constrain %s has v referring to undefined variable %s'%(conceptName, lc.lcName, referredVariableName))
                                return None

                            referredDns = lcVariablesDns[referredVariableName] # Get Datanodes for referred variables already defined in the logical constrain
                            for rDn in referredDns:
                                eDns = []
                                for _rDn in rDn:
                                    if _rDn is None:
                                        continue
                                    _eDns = _rDn.getEdgeDataNode(v[1:]) # Get Datanodes for the edge defined by the path part of the v

                                    if _eDns and _eDns[0]:
                                        eDns.extend(_eDns)
                                    else:
                                        self.myLogger.info('The element %s of logical constrain %s has not exiting path %s in the current graph'%(conceptName, lc.lcName, v[1:]))
                                        eDns.extend([None])

                                _dnsList[i].append(eDns)

                        dnsList = _dnsList[0]

                        for l in _dnsList[1:]:
                            # Intersection - use lo if defined to determine if different set operation
                            _d = [x if x in l else [None] for x in dnsList]
                            dnsList = _d

                    # Get ILP variables from collected Datanodes for the given element of logical constrain
                    for dns in dnsList:
                        _vDns = []
                        for _dn in dns:
                            if _dn == None:
                                _vDns.append(None)
                                continue

                            if _dn.ontologyNode.name == conceptName:
                                _vDns.append(1)
                                continue

                            if xPkey not in _dn.attributes:
                                _vDns.append(None)
                                continue

                            if isinstance(e, Concept):
                                ilpVs = _dn.getAttribute(xPkey) # Get ILP variable for the concept

                                if isinstance(ilpVs, Mapping) and p not in ilpVs:
                                    _vDns.append(None)
                                    continue

                                vDn = ilpVs[p]
                            else:
                                if p == 0:
                                    try:
                                        vDn = _dn.getAttribute(xPkey)[e[1]] # Get ILP variable for the concept
                                    except IndexError:
                                        vDn = None
                                else:
                                    vDn = _dn.getAttribute(xPkey)[p][e[2]] # Get ILP variable for the concept

                            if torch.is_tensor(vDn):
                                vDn = vDn.item()

                            _vDns.append(vDn)

                        vDns.append(_vDns)

                    lcVariablesDns[variableName] = dnsList

                    if None in lcVariablesDns:
                        pass

                    lcVariables[variableName] = vDns

                elif isinstance(e, LogicalConstrain): # LogicalConstrain - process recursively
                    self.myLogger.info('Processing Logical Constrain %s(%s) - %s'%(e.lcName, e, [str(e1) for e1 in e.e]))
                    vDns = self.__constructLogicalConstrains(e, booleanProcesor, m, dn, p, key = key, lcVariablesDns = lcVariablesDns, headLC = False)

                    if vDns == None:
                        self.myLogger.warning('Not found data for %s(%s) nested logical Constrain required to build Logical Constrain %s(%s) - skipping this constrain'%(e.lcName,e,lc.lcName,lc))
                        return None

                    lcVariables[variableName] = vDns
            # Int - limit
            elif isinstance(e, int):
                if eIndex == 0:
                    pass # if this lc using it
                else:
                    pass # error!
            elif isinstance(e, str):
                if eIndex == 2:
                    pass # if this lc using it
                else:
                    pass # error!
            else:
                self.myLogger.error('Logical Constrain %s has incorrect element %s'%(lc,e))
                return None

        return lc(m, booleanProcesor, lcVariables, headConstrain = headLC)

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

    # -- Main method of the solver - creating ILP constraints plus objective, invoking the ILP solver and returning the result of the ILP solver classification
    def calculateILPSelection(self, dn, *conceptsRelations, fun=None, epsilon = 0.00001, minimizeObjective = False, ignorePinLCs = False):
        if self.ilpSolver == None:
            self.myLogger.warning('ILP solver not provided - returning')
            return

        start = datetime.now()

        try:
            # Create a new Gurobi model
            self.myIlpBooleanProcessor.resetCaches()
            m = Model("decideOnClassificationResult" + str(start))
            m.params.outputflag = 0

            # Create ILP Variables for concepts and objective
            Q, x = self.createILPVariables(m, dn, *conceptsRelations, dnFun = self.__getProbability, fun=fun, epsilon = epsilon)

            # Add constraints based on ontology and graph definition
            self.addOntologyConstrains(m, dn, *conceptsRelations)
            self.addGraphConstrains(m, dn, *conceptsRelations)

            # ILP Model objective setup
            if Q is None:
                Q = 0
                self.myLogger.error("No data provided to create any ILP varibale - not ILP result reurn")

            if minimizeObjective:
                m.setObjective(Q, GRB.MINIMIZE)
            else:
                m.setObjective(Q, GRB.MAXIMIZE) # -------- Default

            m.update()

            # Collect head logical constraints
            _lcP = {}
            _lcP[100] = []
            for graph in self.myGraph:
                for _, lc in graph.logicalConstrains.items():
                    if lc.headLC:
                        if not ignorePinLCs:
                            lcP = lc.p
                        else:
                            lcP = 100

                        if lcP not in _lcP:
                            _lcP[lcP] = []

                        _lcP[lcP].append(lc) # Keep constrain with the same p in the list

            # Sort constraints according to their p
            lcP = OrderedDict(sorted(_lcP.items(), key=lambda t: t[0], reverse = True))
            for p in lcP:
                self.myLogger.info('Found %i logical constraints with p %i - %s'%(len(lcP[p]),p,lcP[p]))

            # Search through set of logical constraints for subset satisfying and the mmax/min calculated objective value
            lcRun = {} # Keeps information about subsequent model runs
            ps = [] # List with processed p
            for p in lcP:
                ps.append(p)
                mP = m.copy() # Copy model for this run

                # Map variables to the new copy model
                xP = {}
                for _x in x:
                    xP[_x] = mP.getVarByName(x[_x].VarName)

                    rootConcept = dn.findRootConceptOrRelation(_x[0])

                    dns = dn.findDatanodes(select = ((rootConcept,), ("instanceID", _x[2])))

                    if dns:
                        if _x[1].startswith('Not'):
                            xPkey = '<' + _x[0].name + '>/ILP/notxP'
                        else:
                            xPkey = '<' + _x[0].name + '>/ILP/xP'

                        if xPkey not in dns[0].attributes:
                            dns[0].attributes[xPkey] = {}

                        if p not in dns[0].attributes[xPkey]:
                            xkey = '<' + _x[0].name + '>/ILP/x'
                            if xkey not in dns[0].attributes:
                                continue

                            xLen = len(dns[0].attributes[xkey])
                            dns[0].attributes[xPkey][p] = [None] * xLen

                        dns[0].attributes[xPkey][p][_x[3]] = mP.getVarByName(x[_x].VarName)

                # Prepare set with logical constraints for this run
                lcs = []
                for _p in lcP:
                    lcs.extend(lcP[_p])

                    if _p == p:
                        break

                # Add LC constraints to the copy model
                self.addLogicalConstrains(mP, dn, lcs, p)
                self.myLogger.info('Optimizing model for logical constraints with probabilities %s with %i variables and %i constraints'%(p,mP.NumVars,mP.NumConstrs))

                startOptimize = datetime.now()

                # Run ILP model - Find solution
                mP.optimize()
                mP.update()

                #mP.display()

                endOptimize = datetime.now()
                elapsedOptimize = endOptimize - startOptimize

                # check model run result
                solved = False
                objValue = None
                if mP.status == GRB.Status.OPTIMAL:
                    self.myLogger.info('%s optimal solution was found for p - %i with value %f - solver time: %ims'%('Min' if minimizeObjective else 'Max', p, mP.ObjVal,elapsedOptimize.microseconds/1000))
                    solved = True
                    objValue = mP.ObjVal
                elif mP.status == GRB.Status.INFEASIBLE:
                    self.myLogger.warning('Model was proven to be infeasible for p - %i.'%(p))
                elif mP.status == GRB.Status.INF_OR_UNBD:
                    self.myLogger.warning('Model was proven to be infeasible or unbound for p - %i.'%(p))
                elif mP.status == GRB.Status.UNBOUNDED:
                    self.myLogger.warning('Model was proven to be unbound.')
                else:
                    self.myLogger.warning('Optimal solution not was found for p - %i - error code %i'%(p,mP.status))

                # Print ILP model to log file if model is not solved or logger level is DEBUG
                if not solved or self.myLogger.level <= logging.INFO:
                    import sys
                    so = sys.stdout
                    logFileName = self.myLogger.handlers[0].baseFilename
                    log = open(logFileName, "a")
                    sys.stdout = log
                    mP.display()
                    sys.stdout = so

                # Keep result of the model run
                lcRun[p] = {'p':p, 'solved':solved, 'objValue':objValue, 'lcs':lcs, 'mP':mP, 'xP':xP, 'elapsedOptimize':elapsedOptimize.microseconds/1000}

            # Select model run with the max/min objective value
            maxP = None
            for p in lcRun:
                if lcRun[p]['objValue']:
                    if maxP:
                        if minimizeObjective and lcRun[maxP]['objValue'] >= lcRun[p]['objValue']:
                            maxP = p
                        elif not minimizeObjective and lcRun[maxP]['objValue'] <= lcRun[p]['objValue']:
                            maxP = p
                    else:
                        maxP = p

            device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')

            # If found model - return best result
            if maxP:
                self.myLogger.info('Best  solution found for p - %i'%(maxP))

                lcRun[maxP]['mP'].update()

                for c in conceptsRelations:
                    if c[2] is None:
                        index = 0
                    else:
                        index = c[2] # multiclass

                    c_root = dn.findRootConceptOrRelation(c[0])
                    c_root_dns = dn.findDatanodes(select = c_root)

                    ILPkey = '<' + c[0].name + '>/ILP'
                    xkey = ILPkey + '/x'
                    xPkey = ILPkey + '/xP'
                    xNotPkey = ILPkey + '/notxP'

                    for cDn in c_root_dns:
                        dnAtt = cDn.getAttributes()
                        if xPkey not in dnAtt:
                            dnAtt[ILPkey] = torch.tensor([float("nan")], device=device)
                            self.myLogger.info('Not returning solutions for %s in %sit is nan'%(c[1], cDn))
                            continue

                        solution = dnAtt[xPkey][maxP][index].X
                        if solution == 0:
                            solution = 0
                        elif solution == 1:
                            solution = 1

                        if ILPkey not in dnAtt:
                            dnAtt[ILPkey] = torch.empty(c[3], dtype=torch.float)

                        if xkey not in dnAtt:
                            dnAtt[xkey] = torch.empty(c[3], dtype=torch.float)

                        dnAtt[ILPkey][index] = solution
                        dnAtt[xkey][index] = dnAtt[xPkey][maxP][index]
                        if xNotPkey in dnAtt:
                            dnAtt[xNotPkey][index] = dnAtt[xNotPkey][maxP][index]

                        ILPV = dnAtt[ILPkey][index]
                        if ILPV == 1:
                            self.myLogger.info('\"%s\" is \"%s\"'%(cDn,c[1]))

            else:
                pass

        except Exception as inst:
            self.myLogger.error('Error returning solutions -  %s'%(inst))
            raise

        end = datetime.now()
        elapsed = end - start
        self.myLogger.info('')
        self.myLogger.info('End - elapsed time: %ims'%(elapsed.microseconds/1000))

        # Return
        return

    # -- Calculated loss values for logical constraints
    def calculateLcLoss(self, dn):
        m = None

        p = 0
        key = "/local/softmax"
        #key = ""

        lcLosses = {}
        for graph in self.myGraph:
            for _, lc in graph.logicalConstrains.items():
                if not lc.headLC:
                    continue

                self.myLogger.info('Processing Logical Constrain %s(%s) - %s'%(lc.lcName, lc, [str(e) for e in lc.e]))
                lossList = self.__constructLogicalConstrains(lc, self.myLcLossBooleanMethods, m, dn, p, key = key, lcVariablesDns = {}, headLC = True)

                if not lossList:
                    continue

                lossTensor = torch.zeros(len(lossList))

                for i, l in enumerate(lossList):
                    l = l[0]
                    if l is not None:
                        lossTensor[i] = l
                    else:
                        lossTensor[i] = float("nan")

                lcLosses[lc.lcName] = {}

                lcLosses[lc.lcName]['lossTensor'] = lossTensor

        return lcLosses


#coding:utf-8
import xadmin
from sblog.models import Author,Tag,Blog
from django.conf import settings
from photologue.models import Gallery,Photo,PhotoEffect,PhotoSize,Watermark
from xadmin import views
from xadmin.layout import Col, Fieldset

from .models import Blog,Author,Tag,AccessRecord
from .forms import GalleryAdminForm,PhotoAdminForm,BlogForm
from threadedcomments.models import ThreadedComment

MULTISITE = getattr(settings, 'PHOTOLOGUE_MULTISITE', False)
# Register your models here.
class BaesSetting(object):
    enable_themes = True
    use_bootswatch = True

class GlobalSetting(object):
    site_title = u"喵黑小熊"
    site_footer = u"我的！窝哒！还是我大！"
    menu_style = 'accordion'
    global_search_models = [Blog,Gallery,ThreadedComment]
    global_models_icon = {
        Blog:'fa fa-x fa-laptop', Gallery:'fa fa-x fa-camera' ,
        ThreadedComment:'fa fa-comment',
    }

    apps_label_title = {
        'auth':u"权限管理",
        'threadedcomments':u'评论',
        'reversion':u'版本控制',
    }

    def get_site_menu(self):
        return (
            {'title':u'博客','perm':self.get_model_perm(Blog,'change'),'icon':'fa fa-x fa-laptop','menus':(
                {'title':u'文章','url':self.get_model_url(Blog,'changelist'),'icon':'fa fa-pencil-square-o'},
                {'title':u'作者','url':self.get_model_url(Author,'changelist'),'icon':'fa fa-user'},
                {'title':u'标签','url':self.get_model_url(Tag,'changelist'),'icon':'fa fa-tag'},
            )},
            {'title':u'相册&照片','perm':self.get_model_perm(Gallery,'change'),'icon':'fa fa-x fa-camera','menus':(
                {'title':u'画廊','url':self.get_model_url(Gallery,'changelist'),'icon':'fa fa-x fa-book'},
                {'title':u'照片','url':self.get_model_url(Photo,'changelist'),'icon':'fa fa-x fa-camera'},
                {'title':u'照片尺寸','url':self.get_model_url(PhotoSize,'changelist'),'icon':'fa fa-x fa-scissors'},
                {'title':u'照片效果','url':self.get_model_url(PhotoEffect,'changelist'),'icon':'fa fa-x fa-adjust'},
                {'title':u'水印','url':self.get_model_url(Watermark,'changelist'),'icon':'fa fa-x fa-moon-o'},
            )},
            {'title':u'系统日志','perm':self.get_model_perm(AccessRecord,'change'),'icon':'fa fa-x fa-desktop','menus':(
                {'title':u'访问记录','url':self.get_model_url(AccessRecord,'changelist')},
            )},
        )

class AuthorAdmin(object):
    """docstring for AuthorAdmin"""
    list_display = ('name','email','website')
    search_fields = ('name',)
    reversion_enable = True

class BlogAdmin(object):
    """docstring for BlogAdmin"""
    list_display = ('caption','id','tags_names','author','publish_time')
    list_filter = ('publish_time','author')
    date_hierarchy = 'publish_time'
    ordering = ('-publish_time',)
    filter_horizontal = ('tags',)
    form = BlogForm
    reversion_enable = True

class TagAdmin(object):
    list_display = ('tag_name','create_time')
    reversion_enable = True

class GalleryAdmin(object):
    list_display = ('title', 'date_added', 'photo_count', 'is_public')
    list_filter = ['date_added', 'is_public']
    if MULTISITE:
        list_filter.append('sites')
    date_hierarchy = 'date_added'
    prepopulated_fields = {'slug': ('title',)}
    form = GalleryAdminForm
    reversion_enable = True

class PhotoAdmin(object):
    list_display = ('title', 'date_taken', 'date_added',
                    'is_public', 'view_count', 'admin_thumbnail')
    list_filter = ['date_added', 'is_public']
    if MULTISITE:
        list_filter.append('sites')
    search_fields = ['title', 'slug', 'caption']
    list_per_page = 10
    prepopulated_fields = {'slug': ('title',)}
    readonly_fields = ('date_taken',)
    form = PhotoAdminForm
    reversion_enable = True

    if MULTISITE:
        filter_horizontal = ['sites']
    if MULTISITE:
        actions = ['add_photos_to_current_site', 'remove_photos_from_current_site']

class PhotoSizeAdmin(object):
    list_display = ('name', 'width', 'height', 'crop', 'pre_cache', 'effect', 'increment_count')
    fieldsets = (
        (None, {
            'fields': ('name', 'width', 'height', 'quality')
        }),
        ('Options', {
            'fields': ('upscale', 'crop', 'pre_cache', 'increment_count')
        }),
        ('Enhancements', {
            'fields': ('effect', 'watermark',)
        }),
    )
    reversion_enable = True

class PhotoEffectAdmin(object):
    list_display = ('name', 'description', 'color', 'brightness',
                    'contrast', 'sharpness', 'filters', 'admin_sample')
    fieldsets = (
        (None, {
            'fields': ('name', 'description')
        }),
        ('Adjustments', {
            'fields': ('color', 'brightness', 'contrast', 'sharpness')
        }),
        ('Filters', {
            'fields': ('filters',)
        }),
        ('Reflection', {
            'fields': ('reflection_size', 'reflection_strength', 'background_color')
        }),
        ('Transpose', {
            'fields': ('transpose_method',)
        }),
    )
    reversion_enable = True

class WatermarkAdmin(object):
    list_display = ('name', 'opacity', 'style')
    reversion_enable = True

class ThreadedCommentsAdmin(object):
    fieldsets = (
        (None,
           {'fields': ('content_type', 'object_pk', 'site')}
        ),
        (('Content'),
           {'fields': ('user', 'user_name', 'user_email', 'user_url', 'title', 'comment')}
        ),
        (('Hierarchy'),
           {'fields': ('parent',)}
        ),
        (('Metadata'),
           {'fields': ('submit_date', 'ip_address', 'is_public', 'is_removed')}
        ),
    )

    list_display = ('name', 'title', 'content_type', 'object_pk', 'parent',
                    'ip_address', 'submit_date', 'is_public', 'is_removed')
    search_fields = ('title', 'comment', 'user__username', 'user_name',
                     'user_email', 'user_url', 'ip_address')
    raw_id_fields = ("parent",)
    reversion_enable = True

class AccessRecordAdmin(object):
    def avg_count(self, instance):
        return int(instance.view_count / instance.user_count)

    avg_count.short_description = "Avg Count"
    avg_count.allow_tags = True
    avg_count.is_column = True

    list_display = ('date', 'user_count', 'view_count', 'avg_count')
    list_display_links = ('date',)

    list_filter = ['date', 'user_count', 'view_count']
    actions = None
    aggregate_fields = {"user_count": "sum", 'view_count': "sum"}

    refresh_times = (3, 5, 10)
    data_charts = {
        "user_count": {'title': u"User Report", "x-field": "date", "y-field": ("user_count", "view_count"),
                       "order": ('date',)},
        "avg_count": {'title': u"Avg Report", "x-field": "date", "y-field": ('avg_count',), "order": ('date',)},
        "per_month": {'title': u"Monthly Users", "x-field": "_chart_month", "y-field": ("user_count",),
                      "option": {
                          "series": {"bars": {"align": "center", "barWidth": 0.8, 'show': True}},
                          "xaxis": {"aggregate": "sum", "mode": "categories"},
                      },
                      },
    }

    def _chart_month(self, obj):
        return obj.date.strftime("%B")

xadmin.site.register(views.BaseAdminView,BaesSetting)
xadmin.site.register(views.CommAdminView,GlobalSetting)
xadmin.site.register(Author,AuthorAdmin)
xadmin.site.register(Blog,BlogAdmin)
xadmin.site.register(Tag,TagAdmin)

xadmin.site.register(Gallery,GalleryAdmin)
xadmin.site.register(Photo,PhotoAdmin)
xadmin.site.register(PhotoSize,PhotoSizeAdmin)
xadmin.site.register(PhotoEffect,PhotoEffectAdmin)
xadmin.site.register(Watermark,WatermarkAdmin)
xadmin.site.register(ThreadedComment,ThreadedCommentsAdmin)
xadmin.site.register(AccessRecord, AccessRecordAdmin)


from modules.entities import EntityTracker
from modules.bow import BoW_encoder
from  modules.lstm_net import LSTM_net
from modules.embed import UtteranceEmbed
from modules.actions import ActionTracker
from modules.data_utils import Data
# from modules.entity_tracker import GoogleEntityTracker
import modules.util as util

import numpy as np
import sys

class InteractiveSession():

    def __init__(self):

        et = EntityTracker()
        self.bow_enc = BoW_encoder()
        self.emb = UtteranceEmbed()
        at = ActionTracker(et)

        obs_size = self.emb.dim + self.bow_enc.vocab_size + et.num_features
        self.action_templates = at.get_action_templates()
        action_size = at.action_size
        nb_hidden = 128

        self.net = LSTM_net(obs_size=obs_size,
                       action_size=action_size,
                       nb_hidden=nb_hidden)

        # restore checkpoint
        self.net.restore()

    def interact(self):
        # create entity tracker
        et = EntityTracker()

        # neural entity tracker
        # g_et = GoogleEntityTracker()

        # create action tracker
        at = ActionTracker(et)

        # reset network
        self.net.reset_state()

        # begin interaction loop
        while True:

            # get input from user
            u = raw_input(':: ')

            # check if user wants to begin new session
            if u == 'clear' or u == 'reset' or u == 'restart':
                self.net.reset_state()
                et = EntityTracker()
                at = ActionTracker(et)
                print('')

            # check for exit command
            elif u == 'exit' or u == 'stop' or u == 'quit' or u == 'q':
                break

            else:
                # ENTER press : silence
                if not u:
                    u = '<SILENCE>'

                # encode
                u_ent, u_entities = et.extract_entities(u, is_test=True)
                # u_ent, u_entities = g_et.extract_entities(u)
                u_ent_features = et.context_features()
                u_emb = self.emb.encode(u_ent)
                u_bow = self.bow_enc.encode(u_ent)
                # concat features
                features = np.concatenate((u_ent_features, u_emb, u_bow), axis=0)
                # get action mask
                action_mask = at.action_mask()

                # forward
                prediction = self.net.forward(features, action_mask)
                # print('prediction: ', prediction)
                print(u_entities)
                if self.handle_api_call(prediction, u_ent_features):
                    if prediction == 1:
                        print('>> api_call ' + u_entities['<location>'])
                    if prediction == 2:
                        print('>> api_call ' + u_entities['<objectives>'] + ' ' + u_entities['<patient_name>'] + ' ' + u_entities['<address>'])
                    if prediction == 3:
                        print('>> api_call ' + u_entities['<objectives>'] + ' ' + u_entities['<patient_name>'] + ' ' + u_entities['<address>'] + ' ' + u_entities['<time>'])
                    if prediction == 4:
                        print('>> api_call ' + u_entities['<patient_name>'] + ' ' + u_entities['<address>'] + ' ' + u_entities['<time>'] + ' ' + u_entities['<dr_name>'])
                else:
                    if self.handle_response(prediction):
                        response = self.action_templates[prediction]
                        response = response.split(' ')
                        if prediction == 5 or prediction == 7 or prediction == 12:
                            response = [r.replace('<patient_name>', u_entities['<patient_name>']) for r in response]
                            print('>> ' + ' '.join(response))
                        else:
                            response = [r.replace('<patient_name>', u_entities['<patient_name>']) for r in response]
                            response = [r.replace('<objectives>', u_entities['<objectives>']) for r in response]
                            print('>> ' + ' '.join(response))
                    else:
                        # prediction = self.action_post_process(prediction, u_entities)
                        print('>> ' + self.action_templates[prediction])
                        # if all(u_ent_featur == 1 for u_ent_featur in u_ent_features) and (prediction == 10):
                        #     break

    def handle_api_call(self, prediction, u_ent_features):
        expected = [1,2,3,4]
        if prediction in expected:
            return True
        else:
            return False

    def handle_response(self, prediction):
        expected = [5,7,10,12]
        if prediction in expected:
            return True
        else:
            return False

    def action_post_process(self, prediction, u_entities):
        attr_mapping_dict = {
            4: '<patient_name>',
            6: '<patient_name>',
            9: ['<patient_name>', '<objectives>'],
            11: '<patient_name>'
        }

        # find exist and non-exist entity
        exist_ent_index = [key for key, value in u_entities.items() if value != None]
        non_exist_ent_index = [key for key, value in u_entities.items() if value == None]

        # if predicted key is already in exist entity index then find non exist entity index
        # and leads the user to input non exist entity.

        if prediction in attr_mapping_dict:
            pred_key = attr_mapping_dict[prediction]
            if pred_key in exist_ent_index:
                for key, value in attr_mapping_dict.items():
                    if value == non_exist_ent_index[0]:
                        return key
            else:
                return prediction
        else:
            return prediction

if __name__ == '__main__':
    # create interactive session
    isess = InteractiveSession()
    # begin interaction
    isess.interact()


import bottle
from objects import *

import types, inspect, functools
from bottle import request, response

from site_engine.plugins.template import *

from site_engine import bem
import bem_blocks

class MyBaseTemplate(TemplatePlugin):

	bem_context = injected_dependency(bem.Context)
	base_layout = bem.block_dependency(bem_context, bem_blocks.BaseLayout)
	quote_display = bem.block_dependency(bem_context, bem_blocks.QuoteDisplay)

	class plugin_instance_mixin_cls(Object):
		title = precalculated_attr(lambda:TitleZone())
		content = precalculated_attr(lambda:TemplateZone())
		js_head = precalculated_attr(lambda:JSZone())
		css_head = precalculated_attr(lambda:CSSZone())

class IndexTemplate(TemplatePlugin):
	filename = 'index.html'

	bem_context = injected_dependency(bem.Context)
	index_page = bem.block_dependency(bem_context, bem_blocks.IndexPage)
	shortener_form = bem.block_dependency(bem_context, bem_blocks.ShortenerForm)

class AppBase(bottle.Bottle):
	def _init(self):
		super()._init()
		self._init_routes()

	def _init_routes(self):
		pass

from sqlalchemy import create_engine
from sqlalchemy import Column, Integer, String, DateTime
from sqlalchemy.sql import func
from site_engine.plugins.sqlalchemy import ModelsMixin, DbPlugin

from random import choice, randrange, shuffle

class QuoteProvider(ModelsMixin, bottle.ComplexPlugin):

	def _models(self):

		class Quote:
			__tablename__ = 'quote'
			quote_id = Column(Integer, primary_key=True)
			quote = Column(String, nullable=False)
			quote_author = Column(String)

		return locals()

	@calculated_attr
	def plugin_instance_cls(self):

		class QuoteProviderInstance(Object):

			db_session = bottle.plugin_dependency(self.db_plugin)

			def add_quote(self_inst, quote, quote_author=None):
				quote = self.Quote(quote=quote, quote_author=quote_author)
				self_inst.db_session.add(quote)
				return quote

			def get_random(self_inst):
				db_session = self_inst.db_session
				Quote = self.Quote
				n = db_session.query(func.count(Quote.quote_id)).scalar()
				return db_session.query(Quote).offset(randrange(n)).limit(1).first()

			def get_random_sequence(self_inst):
				ids = [x[0] for x in self_inst.db_session.query(self.Quote.quote_id)]
				shuffle(ids)
				return ids

			def get(self_inst, _id):
				return self_inst.db_session.query(self.Quote).filter_by(quote_id=_id).first()

		return QuoteProviderInstance

class VisitLogger(ModelsMixin, bottle.ComplexPlugin):

	def _models(self):

		class Visit:
			__tablename__ = 'visit'
			visit_id = Column(Integer, primary_key=True)
			path = Column(String, nullable=False)
			query_string = Column(String)
			remote_addr = Column(String)
			user_agent = Column(String)
			referrer = Column(String)
			timestamp = Column(DateTime, default=func.now())
			extra = Column(Integer)
			sid = Column(String)

		return locals()

	@calculated_attr
	def plugin_instance_cls(self):

		class VisitLoggerInstance(Object):

			db_session = bottle.plugin_dependency(self.db_plugin)

			def log(self_inst, request=None, **kwargs):
				if request is None:
					request = bottle.request

				visit = self.Visit(\
						path=request.path, query_string=request.query_string,\
						remote_addr=request.remote_addr,\
						user_agent=request.get_header('User-Agent'),\
						referrer=request.get_header('Referer'))

				for k, v in kwargs.items():
					setattr(visit, k, v)

				self_inst.db_session.add(visit)

		return VisitLoggerInstance

class UrlShortener(ModelsMixin, bottle.ComplexPlugin):

	@precalculated_attr
	def alphabet():
		return ''.join(map(lambda x:chr(x) if type(x) is int else x,\
				list(range(ord('a'), ord('z')+1)) +\
				list(range(ord('A'), ord('Z')+1)) +\
				list(range(ord('0'), ord('9')+1)) +\
				list('')))

	min_id_length = 3
	max_id_length = 9

	def _models(self):

		class Url:
			__tablename__ = 'url'
			url_id = Column(String(self.max_id_length), primary_key=True)
			url = Column(String)
			visits = Column(Integer, default=0)

		return locals()

	def _random_char(self):
		return choice(self.alphabet)

	@calculated_attr
	def plugin_instance_cls(self):

		class UrlShortenerInstance(Object):

			db_session = bottle.plugin_dependency(self.db_plugin)

			def new_id(self_inst, url):
				db_session = self_inst.db_session
				Url = self.Url

				l = ''.join(map(lambda f:f(), [self._random_char] * self.min_id_length))
				while len(l) <= self.max_id_length:
					if db_session.query(Url).filter_by(url_id=l).count():
						continue

					db_session.add(Url(url=url, url_id=l))
					return l

				return None

			def get_url(self_inst, url_id):
				db_session = self_inst.db_session
				Url = self.Url

				return db_session.query(Url).filter_by(url_id=url_id).one_or_none()

		return UrlShortenerInstance

import re
from uuid import uuid4

class MainApp(AppBase):

	_significant_ = True

	@precalculated_attr
	def secret_seed():
		return uuid4().bytes

	def secret_hash(self, data):
		from hashlib import sha256 as hasher
		return hasher(self.secret_seed + data).digest()

	def sid(self):
		return uuid4().hex

	json_format = bottle.JSONPlugin()

	url_re = precalculated_attr(lambda:re.compile(r'^https?://.*'))

	url_shortener = predefined_dependency(Description(UrlShortener))
	quote_provider = predefined_dependency(Description(QuoteProvider))
	visit_logger = predefined_dependency(Description(VisitLogger))

	db_plugin = predefined_dependency(Description(DbPlugin, engine=create_engine('sqlite:////tmp/shortener.db', echo=True)))

	bem_context = predefined_dependency(bem.Context)

	base_template = associated_dependency(MyBaseTemplate, filename='base.html')
	index_template = associated_dependency(IndexTemplate, base_template=base_template)

	styles_template = TemplatePlugin(filename='styles.css')

	def _init_routes(self):

		@self.get('/')
		def index(*, tpl:self.index_template):
			return tpl.render(dict())

		@self.post('/new_url')
		@self.json_format
		def new_url(*, url_short:self.url_shortener):
			params = request.params
			url = params.get('url', '')

			if not self.url_re.match(url):
				return ('error', 'bad url')

			url_id = url_short.new_id(url)
			if url_id:
				return ('ok', dict(url_id=url_id))
			else:
				return ('retry', 'too long')

		@self.get('/<url_id:re:[%s]+>' % self.url_shortener.alphabet)
		def url_page(url_id, *, url_short:self.url_shortener, vl:self.visit_logger):
			url = url_short.get_url(url_id)
			if url is None:
				#~ response.status = 404
				raise(bottle.HTTPError(404, "Cannot resolve this URL."))

			url.visits += 1
			vl.log()

			response.set_cookie('go', {'url': url.url, 'url_id': url.url_id}, secret=self.secret_seed)
			return bottle.redirect(self.get_url('go', url=url.url))
			#~ return 'Redirecting to: {0}'.format(url.url)

		@self.get('/go', name='go')
		def go_to(*, vl:self.visit_logger):
			params = request.query
			url = params.get('url', '')
			if not self.url_re.match(url):
				raise(bottle.HTTPError(400, "Bad request."))

			url_verify = request.get_cookie('go', secret=self.secret_seed)
			response.delete_cookie('go')
			if url_verify and url_verify['url'] == url:
				vl.log(extra=1)
				#~ return 'Redirecting to: {0} (verified)'.format(url)
			else:
				vl.log(extra=2)
				#~ return 'Redirecting to: {0} (unverified)'.format(url)
			return bottle.redirect(url)

		@self.post('/!QuoteDisplay')
		@self.json_format
		def quote_display(*, qp:self.quote_provider):
			params = request.params
			qid = params.get('id')

			q = None
			seq = None
			if qid:
				q = qp.get(qid)
			else:
				seq = qp.get_random_sequence()
				print('seq', seq)
				if seq:
					q = qp.get(seq[0])

			if q:
				d = dict(quote=q.quote, quote_author=q.quote_author)
				if seq:
					d['sequence'] = seq
				return d
			else:
				return dict()

		@self.get('/css/styles.css')
		def styles_css(*, tpl:self.styles_template):
			bottle.response.content_type = 'text/css'
			#~ return tpl.render(dict(manager=self.style_manager))
			return bottle.template('styles.css', template_settings=dict(syntax='/*% %*/ @% %= =%'))

		@self.get('/js/bem_init.js')
		def bem_defs_js():
			bottle.response.content_type = 'text/javascript'
			bottle.response.set_header('Cache-Control', 'max-age=30')
			return bottle.template('bem_defs.tpl.js', template_settings=dict(syntax='/*% %*/ //% /*{ }*/'))

		import os.path
		@self.get('/<dir_part:re:img|css|js|font-awesome>/<path:path>')
		def static(dir_part, path):
			return bottle.static_file(os.path.join(dir_part, path), root='static')

r = registry(Description(MainApp))
application = r.lookup(MainApp)

#~ import pprint
#~ pprint.pprint(r._predefined)

if __name__ == '__main__':
	from bottle import run
	run(app=application, host='0.0.0.0', port=8080, debug=True, reloader=True)


# 问题：我们有一个元素序列，想知道序列中出现次数最多的元素是什么。

# 解决方案：collections 模块中的 Counter 类下辈 为此类问题所设计的。
# 它有个方法方法可以直接告诉我们  most_common()

words = [
    'look', 'into', 'my', 'eyes', 'look', 'into', 'my', 'eyes',
    'the', 'eyes', 'the', 'eyes', 'the', 'eyes', 'not', 'around', 'the',
    'eyes', "don't", 'look', 'around', 'the', 'eyes', 'look', 'into',
    'my', 'eyes', "you're", 'under'
]

from collections import Counter

word_counts = Counter(words)
top_three = word_counts.most_common(3)
print(top_three)        # [('eyes', 8), ('the', 5), ('look', 4)]

# tips：Counter 也可手动增加(更新)计数，还可以和各种数学运算操作结合使用。

1、数据结构和算法/1.12、找出序列中出现次数最多的元素.py

1.12、找出序列中出现次数最多的元素.py


"""
Contains all the machinery for dealing with threads.
"""
from __future__ import annotations

import threading
from typing import Any, Callable, Dict, Optional

import wx

class ThreadResult(wx.PyEvent):
    """Simple event to carry arbitrary result data."""

    def __init__(self, data: Any, event_type: int):
        """Initialises a result event for the thread.

        Args:
            data: The data to be passed to the main thread.
            event_type: The type of event.
        """
        wx.PyEvent.__init__(self)
        self.SetEventType(event_type)
        self.data = data

class WorkerThread(threading.Thread):
    """Worker Thread Class."""

    def __init__(
        self,
        target: Callable,
        on_abort: Optional[Callable] = None,
        on_complete: Optional[Callable] = None,
        on_error: Optional[Callable] = None,
        daemon: Optional[bool] = None,
    ):
        """Configures and start a new thread executing the target action.

        Args:
            target: The function to be executed
            on_abort: Function to be called if the thread aborts. Takes as only argument
                the output of the target function when that one aborts.
            on_complete: Function to be called if the thread finished normally. Takes as
                only argument the output of the target function when that one finishes.
            on_error: Function to be called if anything bad happens in the thread. Takes
                as only argument the exception catched.
        """
        super(WorkerThread, self).__init__(target=target, daemon=daemon)
        self._on_abort = on_abort
        self._on_complete = on_complete
        self._on_error = on_error
        self._event_on_abort = wx.NewEventType()
        self._event_on_complete = wx.NewEventType()
        self._event_on_error = wx.NewEventType()
        self.abort = False

    def connect_events(self, window: wx.Frame) -> None:
        """Connect the complete, abort and error events to the functions to execute.

        Args:
            window: The main window of the program.
        """
        window.Connect(-1, -1, self._event_on_abort, self.on_abort)
        window.Connect(-1, -1, self._event_on_complete, self.on_complete)
        window.Connect(-1, -1, self._event_on_error, self.on_error)

    def run(self):
        """Run worker thread and deals with the wrapping up accordingly."""
        try:
            result = self._target()

            if self.abort:
                ThreadPool().post_event(ThreadResult(result, self._event_on_abort))
            else:
                ThreadPool().post_event(ThreadResult(result, self._event_on_complete))

        except Exception as err:
            ThreadPool().post_event(ThreadResult(err.args[0], self._event_on_error))

    def on_abort(self, event: ThreadResult):
        """To execute when the thread execution is aborted.

        Args:
            event: The event object with the relevant output data
        """
        if self._on_abort is not None:
            self._on_abort(event.data)

    def on_complete(self, event: ThreadResult):
        """To execute when the thread execution is completed normally.

        Args:
            event: The event object with the relevant output data
        """
        if self._on_complete is not None:
            self._on_complete(event.data)

    def on_error(self, event: ThreadResult):
        """To execute when the thread ends with an error.

        Args:
            event: The exception raised
        """
        if self._on_error is not None:
            self._on_error(event.data)

class ThreadPool:

    _instance: Optional[ThreadPool] = None

    def __new__(cls, window: Optional[wx.Frame] = None):
        if window is None and cls._instance is None:
            raise ValueError(
                "The first time it is called, 'window' must be a wx.Frame object."
            )
        elif cls._instance is None:
            cls._instance = object.__new__(cls)
            cls._instance._window = window
            cls._instance._workers = {}
        return cls._instance

    def __init__(self, window: Optional[wx.Frame] = None):
        self._window: wx.Frame
        self._workers: Dict[int, WorkerThread]

    def run_thread(
        self,
        target: Callable,
        on_abort: Optional[Callable] = None,
        on_complete: Optional[Callable] = None,
        on_error: Optional[Callable] = None,
        daemon: Optional[bool] = None,
    ) -> int:
        """Runs a new thread executing the target callable on it.

        The thread will run until:
            - The 'target' function returns
            - An exception is raised

        The target function might finish because it has completed what it was doing. In
        that case, 'on_complete' is executed after returning from `target`.

        It might also finish because it is monitoring the value of
        `ThreadPool().query_abort()` and this is `True`. In that case, target should
        return with an appropriate value and `on_abort` is then executed.

        Finally, an exception might occur in the thread. In this case, the exception
        is caught and `on_error` is executed.

        `on_complete`, `on_abort` and `on_error` are all executed in the main thread.

        Args:
            target: The function to be executed in a separate thread.
            on_abort: The function to be executed when the target function is aborted.
                Takes as input the value returned by target.
            on_complete: The function to be executed when the target function is
                completed normally. Takes as input the value returned by target.
            on_error: The function to be executed when an exception is raised in the
                target. Takes as input the exception raised.
            daemon: If the function is a daemonic function.

        Returns:
            The id number for the thread, needed if it is to be aborted externally.
        """
        thread = WorkerThread(target, on_abort, on_complete, on_error, daemon)
        thread.connect_events(self._window)
        thread.start()
        self._workers[thread.ident] = thread
        return thread.ident

    def query_abort(self) -> bool:
        """Checks if the current thread is to be aborted."""
        return self._workers[threading.get_ident()].abort

    def abort_thread(self, ident: int) -> None:
        """Flags the thread with `ident` to be aborted.

        Args:
            ident: The id of the thread
        """
        self._workers[ident].abort = True

    def post_event(self, event: ThreadResult):
        """Adds an event to the event loop of the main thread."""
        wx.PostEvent(self._window, event)


# -*- coding: utf-8 -*-
#Nissan.py

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

from google.colab import files

uploaded = files.upload()

nissan= pd.read_csv(io.BytesIO(uploaded['Nissan.csv']))
nissan

namedum = pd.get_dummies(nissan.Name)
nissan = nissan.join(namedum)
colourdum = pd.get_dummies(nissan.Color,prefix="is")
nissan=nissan.join(colourdum)
enginedum = pd.get_dummies(nissan['Engine Type'])
nissan=nissan.join(enginedum)
Bodydum = pd.get_dummies(nissan['Body Type'])
nissan=nissan.join(Bodydum)
nissan.drop(['Name', 'Color', 'Engine Type', 'Body Type'], axis=1, inplace=True)

nissan['Anti lock Braking System']=nissan['Anti lock Braking System'].fillna(0)

nissan.shape

Y = nissan.Price

nissan.drop(['Price'], axis=1, inplace=True)
X=nissan

from sklearn.model_selection import train_test_split
X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size=0.2,random_state=0)

model = keras.models.Sequential()

model.add(keras.layers.Dense(128, activation='relu', input_shape=(149,)))
model.add(keras.layers.Dense(64, activation='relu'))
model.add(keras.layers.Dense(16, activation='relu'))
model.add(keras.layers.Dense(8, activation='relu'))
model.add(keras.layers.Dense(1, activation='linear'))

keras.optimizers.Adam(lr=0.00001, beta_1=0.9, beta_2=0.999, amsgrad=False)
model.compile(loss='mean_squared_error', optimizer='adam', metrics=['mean_absolute_percentage_error'])

keras.optimizers.Adam(lr=0.001, beta_1=0.9, beta_2=0.999, amsgrad=False)
model.compile(loss='mean_squared_error', optimizer='adam', metrics=['mse','mae','acc'])

model.fit(X, Y, epochs=4500,batch_size=35, callbacks=[keras.callbacks.EarlyStopping(patience=3)])

pred=model.predict(X_test, batch_size=1)

actual=pd.DataFrame(Y_test)
pred=pd.DataFrame(pred)

pred.columns=['Pred']

actual.shape

a=[]
for i in range (212):
  a.append(i)
actual.index=a

nissan=pd.concat([pred, actual], axis=1)

nissan

from sklearn import metrics
import sklearn.metrics as sm
print("Mean absolute error =", round(sm.mean_absolute_error(nissan.Pred, nissan.Price)))
print("Mean squared error =", round(sm.mean_squared_error(nissan.Pred, nissan.Price)))
print("Median absolute error =", round(sm.median_absolute_error(nissan.Pred, nissan.Price)))
print("Explain variance score =", (sm.explained_variance_score(nissan.Pred, nissan.Price)))
print("R2 score =", (sm.r2_score(nissan.Pred, nissan.Price)))

plt.figure(figsize=(100,200))
fig, ax = plt.subplots()
ax.plot(nissan.index,nissan['Pred'],color='blue',marker='o')
ax.plot(nissan.index,nissan['Price'], color='orange',marker='*')

pred=0
act=0
for i in range (212):
  pred=pred+nissan.Pred[i]
  act=act+nissan.Price[i]

import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_axes([0,0,1,1])
langs = ['Actual','Predicted']
students = [act,pred]
ax.bar(langs[0],students[0],color='black')
ax.bar(langs[1],students[1],color='blue')
plt.show()

import numpy as np
import matplotlib.pyplot as plt
import random
a=random.randrange(212)
b=random.randrange(212)
c=random.randrange(212)
d=random.randrange(212)
e=random.randrange(212)
f=random.randrange(212)
g=random.randrange(212)
h=random.randrange(212)
i=random.randrange(212)
j=random.randrange(212)
data= [[nissan.Pred[a],nissan.Pred[b],nissan.Pred[c],nissan.Pred[d],nissan.Pred[e],nissan.Pred[f],nissan.Pred[g],nissan.Pred[h],nissan.Pred[i],nissan.Pred[j]],
     [nissan.Price[a],nissan.Price[b],nissan.Price[c],nissan.Price[d],nissan.Price[e],nissan.Price[f],nissan.Price[g],nissan.Price[h],nissan.Price[i],nissan.Price[j]]]

fig = plt.figure()
ax = fig.add_axes([0,0,1,1])
X=np.arange(10)
ax.bar(X + 0.00, data[0], color = 'b', width = 0.25,)
ax.bar(X + 0.25, data[1], color = 'black', width = 0.25,)
ax.legend(labels=['Predicted', 'Actual'])


import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import scipy.signal as sig
import os
from statsmodels.tsa.seasonal import seasonal_decompose
from statsmodels.tsa.stattools import adfuller
from statsmodels.tsa.arima_model import ARIMA
from statsmodels.tsa.api import ExponentialSmoothing, SimpleExpSmoothing
from sklearn import preprocessing
from sklearn.linear_model import LinearRegression
from pandas.plotting import autocorrelation_plot

def left_to_right(origin, N):
    ret = origin.loc[N, range(1,21)]
    ret.index = pd.date_range('1975', periods=20, freq='AS')
    ret = pd.DataFrame(data=ret)
    ret.columns = ['values']
    return ret

def standard(origin):
    print("bon",origin)
    vals = origin.values
    min_max = preprocessing.MinMaxScaler()
    val_scaled = min_max.fit_transform(vals)
    ret = pd.DataFrame(val_scaled)
    ret.index = pd.date_range('1975', periods=20, freq='AS')
    ret.columns = ['values']
    return ret

def de_trend(origin):
    X = [i for i in range(0, len(origin))]
    X = np.reshape(X, (len(X), 1))
    y = observe.values
    model = LinearRegression()
    model.fit(X, y)
    trend = model.predict(X)

    ret = pd.DataFrame(data=sig.detrend(origin['values']))
    ret.index = pd.date_range('1975', periods=20, freq='AS')
    ret.columns = ['values']
    return ret, trend

def stationary_test(df):
    rolmean = df.rolling(5).mean()
    rolstd = df.rolling(5).std()
    orig = plt.plot(df, color='blue',label='Original')
    mean = plt.plot(rolmean, color='red', label='Rolling Mean')
    std = plt.plot(rolstd, color='black', label = 'Rolling Std')
    plt.legend(loc='best')
    plt.title('Rolling Mean & Standard Deviation')
    plt.show()

    result = adfuller(df['values'])
    print('ADF Statistic: {}'.format(result[0]))
    print('p-value: {}'.format(result[1]))
    print('Critical Values:')
    for key, value in result[4].items():
        print('\t{}: {}'.format(key, value))

path = os.path.abspath("M3TrainingSet.xlsx")
xls = pd.ExcelFile(path)
original_data = pd.read_excel(xls)

original_data = pd.read_excel(xls)#, 'M3Year')

#observe = original_data.loc[0:146,5]
#observe.index = original_data.loc[0:146,'Series'] These are for top to bottom

observe = left_to_right(original_data, 0) #select which series to investigate

print(observe.head()) #Checking that the right data is selected

scaled = standard(observe) #scaling the data

notrend, trend = de_trend(scaled) #detrending the data

print(trend)

stationary_test(observe) #checking if data is non-stationary (p-value>0.05)

#rolling_mean = observe.rolling(5).mean()
#df_log_minus_mean = observe - rolling_mean
#df_log_minus_mean.dropna(inplace=True)
#stationary_test(df_log_minus_mean)

stationary_test(notrend)

plt.plot(scaled, label='scaled')
plt.plot(notrend, label='detrended')
plt.title('Financial data starting in 1975')
plt.xlabel('Year', fontsize=10)
plt.legend()
plt.show()

autocorrelation_plot(notrend['values'])
plt.title('Autocorrelation plot')
plt.show()

result = seasonal_decompose(notrend, model='additive')
result.plot()
plt.show()

model = ARIMA(notrend, order=(2,1,0))
resultsar = model.fit(disp=-1)
plt.plot(notrend)
plt.plot(resultsar.fittedvalues, color='red')
plt.title('ARIMA fittedvalues')
plt.show()

resultsar.plot_predict(1,25)
plt.title('ARIMA predicted values')
plt.show()

print(resultsar.fittedvalues)


from django.conf.urls import url

from . import views

app_name='tutor4u'
urlpatterns = [
    # ex: /polls/
    url(r'^$', views.index, name='index'),
    url(r'^tutor$', views.tutor_signup, name='signup2'),
    # url(r'^signup_tutor$', views.tutorsignup, name='tutorsignup'),
    url(r'^signup_complete$', views.signup_complete, name='signup_complete'),
    # ex: /polls/5/
    url(r'^(?P<question_id>[0-9]+)/$', views.detail, name='detail'),
    # ex: /polls/5/results/
    #url(r'^(?P<question_id>[0-9]+)/results/$', views.results, name='results'),
    # ex: /polls/5/vote/
    url(r'^(?P<question_id>[0-9]+)/vote/$', views.vote, name='vote'),
    url(r'^form_upload$', views.form_upload, name='form_upload'),
    url(r'^signup$', views.tutee_signup, name='signup'),
    url(r'^signup_action$', views.signup_action, name='signup_action'),
    url(r'^result/(?P<user_id>[0-9]+)$',views.results, name='results'),
    url(r'^tutorresults/(?P<user_id>[0-9]+)$',views.tutorresults, name='tutorresults'),
    url(r'^user_login$', views.user_login, name='user_login'),
    url(r'^about$',views.about, name='about'),
    url(r'^locations$',views.locations, name='locations'),
    url(r'^subjects$',views.subjects, name='subjects'),
    url(r'^lv$',views.lv, name='lv')
]


import torch
import torch.nn as nn
import torch.nn.functional as F
import pdb
from models.EM import EM
from torch.distributions import Categorical
from models.initializer import initialize_weights

# constants
# number of vector signal (lstm gates)
N_VSIG = 3
# number of scalar signal (sigma)
N_SSIG = 1
# the ordering in the cache
scalar_signal_names = ['input strength']
vector_signal_names = ['f', 'i', 'o']
sigmoid = nn.Sigmoid()

class LCALSTM_after(nn.Module):

    def __init__(
            self, input_dim, output_dim, rnn_hidden_dim, dec_hidden_dim,
            kernel='cosine', dict_len=2, weight_init_scheme='ortho',
            cmpt=.8, em_gate=.3, add_penalty_dim=True
    ):
        super(LCALSTM_after, self).__init__()
        self.cmpt = cmpt
        self.em_gate = em_gate
        if add_penalty_dim:
            self.input_dim = input_dim + 1
        else:
            self.input_dim = input_dim
        # self.input_dim = input_dim + 1
        self.rnn_hidden_dim = rnn_hidden_dim
        self.n_hidden_total = (N_VSIG + 1) * rnn_hidden_dim + N_SSIG
        # rnn module
        self.i2h = nn.Linear(self.input_dim, self.n_hidden_total)
        self.h2h = nn.Linear(rnn_hidden_dim, self.n_hidden_total)
        # deicion module
        self.ih = nn.Linear(rnn_hidden_dim, dec_hidden_dim)
        self.actor = nn.Linear(dec_hidden_dim, output_dim)
        self.critic = nn.Linear(dec_hidden_dim, 1)
        # memory
        self.hpc = nn.Linear(
            rnn_hidden_dim + rnn_hidden_dim + dec_hidden_dim, N_SSIG
        )
        self.em = EM(dict_len, rnn_hidden_dim, kernel)
        # the RL mechanism
        self.weight_init_scheme = weight_init_scheme
        self.init_model()

    def init_model(self):
        # add name fields
        self.n_ssig = N_SSIG
        self.n_vsig = N_VSIG
        self.vsig_names = vector_signal_names
        self.ssig_names = scalar_signal_names
        # init params
        initialize_weights(self, self.weight_init_scheme)

    def get_init_states(self, scale=.1, device='cpu'):
        h_0_ = sample_random_vector(self.rnn_hidden_dim, scale)
        c_0_ = sample_random_vector(self.rnn_hidden_dim, scale)
        return (h_0_, c_0_)

    def forward(self, x_t, hc_prev, beta=1):
        # unpack activity
        (h_prev, c_prev) = hc_prev
        h_prev = h_prev.view(h_prev.size(1), -1)
        c_prev = c_prev.view(c_prev.size(1), -1)
        x_t = x_t.view(x_t.size(1), -1)
        # transform the input info
        preact = self.i2h(x_t) + self.h2h(h_prev)
        # get all gate values
        gates = preact[:, : N_VSIG * self.rnn_hidden_dim].sigmoid()
        c_t_new = preact[:, N_VSIG * self.rnn_hidden_dim + N_SSIG:].tanh()
        # split input(write) gate, forget gate, output(read) gate
        f_t = gates[:, :self.rnn_hidden_dim]
        o_t = gates[:, self.rnn_hidden_dim:2 * self.rnn_hidden_dim]
        i_t = gates[:, -self.rnn_hidden_dim:]
        # new cell state = gated(prev_c) + gated(new_stuff)
        c_t = torch.mul(c_prev, f_t) + torch.mul(i_t, c_t_new)
        # make 1st decision attempt
        h_t = torch.mul(o_t, c_t.tanh())
        dec_act_t = F.relu(self.ih(h_t))
        # recall / encode
        # hpc_input_t = torch.cat([c_t, dec_act_t], dim=1)
        # inps_t = sigmoid(self.hpc(hpc_input_t))
        # [inps_t, comp_t] = torch.squeeze(phi_t)
        m_t = self.recall(c_t, self.em_gate)
        hpc_input_t = torch.cat([m_t, c_t, dec_act_t], dim=1)
        em_g_t = sigmoid(self.hpc(hpc_input_t))
        cm_t = c_t + m_t * em_g_t
        self.encode(cm_t)
        # make final dec
        h_t = torch.mul(o_t, cm_t.tanh())
        dec_act_t = F.relu(self.ih(h_t))
        pi_a_t = _softmax(self.actor(dec_act_t), beta)
        value_t = self.critic(dec_act_t)
        # reshape data
        h_t = h_t.view(1, h_t.size(0), -1)
        cm_t = cm_t.view(1, cm_t.size(0), -1)
        # scache results
        scalar_signal = [em_g_t, 0, 0]
        vector_signal = [f_t, i_t, o_t]
        misc = [h_t, m_t, cm_t, dec_act_t, self.em.get_vals()]
        cache = [vector_signal, scalar_signal, misc]
        return pi_a_t, value_t, (h_t, cm_t), cache

    def recall(self, c_t, inps_t, comp_t=None):
        """run the "pattern completion" procedure

        Parameters
        ----------
        c_t : torch.tensor, vector
            cell state
        leak_t : torch.tensor, scalar
            LCA param, leak
        comp_t : torch.tensor, scalar
            LCA param, lateral inhibition
        inps_t : torch.tensor, scalar
            LCA param, input strength / feedforward weights

        Returns
        -------
        tensor, tensor
            updated cell state, recalled item

        """
        if comp_t is None:
            comp_t = self.cmpt

        if self.em.retrieval_off:
            m_t = torch.zeros_like(c_t)
        else:
            # retrieve memory
            m_t = self.em.get_memory(c_t, leak=0, comp=comp_t, w_input=inps_t)
        return m_t

    def encode(self, cm_t):
        if not self.em.encoding_off:
            self.em.save_memory(cm_t)

    def pick_action(self, action_distribution):
        """action selection by sampling from a multinomial.

        Parameters
        ----------
        action_distribution : 1d torch.tensor
            action distribution, pi(a|s)

        Returns
        -------
        torch.tensor(int), torch.tensor(float)
            sampled action, log_prob(sampled action)

        """
        m = Categorical(action_distribution)
        a_t = m.sample()
        log_prob_a_t = m.log_prob(a_t)
        return a_t, log_prob_a_t

    def init_em_config(self):
        self.flush_episodic_memory()
        self.encoding_off()
        self.retrieval_off()

    def flush_episodic_memory(self):
        self.em.flush()

    def encoding_off(self):
        self.em.encoding_off = True

    def retrieval_off(self):
        self.em.retrieval_off = True

    def encoding_on(self):
        self.em.encoding_off = False

    def retrieval_on(self):
        self.em.retrieval_off = False

def sample_random_vector(n_dim, scale=.1):
    return torch.randn(1, 1, n_dim) * scale

def _softmax(z, beta):
    """helper function, softmax with beta

    Parameters
    ----------
    z : torch tensor, has 1d underlying structure after torch.squeeze
        the raw logits
    beta : float, >0
        softmax temp, big value -> more "randomness"

    Returns
    -------
    1d torch tensor
        a probability distribution | beta

    """
    assert beta > 0
    # softmax the input to a valid PMF
    pi_a = F.softmax(torch.squeeze(z / beta), dim=0)
    # make sure the output is valid
    if torch.any(torch.isnan(pi_a)):
        raise ValueError(f'Softmax produced nan: {z} -> {pi_a}')
    return pi_a


# -*- coding: utf-8 -*-
import numpy as np
import math
import os
import json

def rssi2distance( x, p , height=1.6 ):

	w, b = p
	return ( 10**( ( x + 55 - b )/(-10*w) ) ) *height

def conver_distance( x ):

	if x < 1.6:
		return 0

	return (x**2 - 1.6**2) ** 0.5

def xy_dis( x1, y1, x2, y2):
	return ( math.pow( (x1-x2), 2 ) + math.pow( (y1-y2), 2 ) ) ** 0.5

def process_correct( circles , point , min_err = 3 , rate = 1.5 ):
	#circles 三个定位的圆心及半径
	#point初步定位点
	#初步定位点到最小的圆的圆心距离 > min_err 且是半径的rate倍 才启动修正
	circles = sorted( circles )

	r  = circles[0][0]
	cx = circles[0][1][0]
	cy = circles[0][1][1]

	px = point[0]
	py = point[1]

	dis_center = xy_dis( px,py, cx, cy )

	if dis_center > min_err and dis_center > rate*r :

		c = r/dis_center

		x01 = c*abs(px-cx)+cx
		y01 = c*abs(py-cy)+cy

		x02 = c*abs(px-cx)-cx
		y02 = c*abs(py-cy)-cy

		dis_p1 = xy_dis( px,py , x01, y01 )
		dis_p2 = xy_dis( px,py , x02, y02 )

		if dis_p1 > dis_p2:
			return [ x02, y02 ]
		else:
			return [ x01, y01 ]

	else:
		return point

def location( tensor ):

	A = []
	b = []

	for i in range( 0 , len(tensor) ):
		d1	  = tensor[0][0]
		x1,y1 = tensor[0][1]

		di	  = tensor[i][0]
		xi,yi = tensor[i][1]

		a1 = 2*(x1 - xi )
		a2 = 2*(y1 - yi )

		A.append( [a1,a2] )

		b1 = x1**2 - xi**2 + y1**2 - yi**2 + di**2 - d1**2

		b.append([b1])

	ma = np.mat(A)
	mb = np.mat(b)

	return (ma.T*ma).I*(ma.T)*mb

def find_center( p0, p1, p2, d0, d1, d2 ):

	l = [ [p0,d0] , [p1,d1],[p2,d2] ]
	l = sorted( l )

	mn  = l[0]
	mid = l[1]
	mx = l[2]

	a0 = mn[0] + mn[1]
	a2 = mx[0] - mx[1]

	#最中间的取2次值
	mid1 = mid[0] + mid[1]
	mid2 = mid[0] - mid[1]

	k1 = abs( mid1-a0) + abs(mid1-a2)
	k2 = abs( mid2-a0) + abs(mid2-a2)

	if k1 < k2:
		return ( a0 + a2 + mid1 ) / 3.0
	else:
		return ( a0 + a2 + mid2 ) / 3.0

def process_singular_matrix( mats ):

	p0 = mats[0]
	p1 = mats[1]
	p2 = mats[2]

	#beacon的位置
	p0x, p0y = p0[1]
	p1x, p1y = p1[1]
	p2x, p2y = p2[1]

	#到beacon的距离
	d0 = p0[0]
	d1 = p1[0]
	d2 = p2[0]

	#找到直线
	if abs( p0x - p1x) < 0.5 and abs( p1x - p2x ) < 0.5:
		x = p0x
		y = find_center( p0y, p1y, p2y, d0, d1, d2 )

	if abs( p0y - p1y ) < 0.5 and abs( p1y - p2y ) < 0.5:
		y = p0y
		x = find_center( p0x, p1x, p2x, d0, d1, d2 )

	return np.mat( [ x,y ] )

def load_3D_print( file_path  ):

		file_path += '_3D'

		if not os.path.exists( file_path ):
			return [],[],[],[]

		param = [0]*4

		file = open( file_path )

		count = 0
		for line in file.readlines():
			line = line.strip()

			if len(line) < 5:
				continue

			param[count] = json.loads(line)
			count += 1

		return param[0],param[1],param[2],param[3]

def process_3d(position, test_x , floor ):

		iBeaconX, iBeaconY, pp , floors  = load_3D_print( position )
		vec = []

		if position == 'B31':
			del test_x[33]
			del test_x[35]
			test_x[34] = -999.0

		rssis = np.array( test_x )

		for i in range(0,len(iBeaconX) ):

			index    = rssis.argmax()
			max_rssi = rssis.max()

			if floor != 0 and floors[index] != floor:
				rssis[index] = -999.0
				continue

			p = pp[index]

			d = conver_distance( rssi2distance( max_rssi,p ) )

			rssis[index] = -999.0

			vec.append( [d, [ iBeaconX[index], iBeaconY[index] ] ] )

			if len(vec) == 3:
				break

		try:

			if len(vec) < 3:
				return -3, "input data error"

			if ( abs( vec[0][1][0] - vec[1][1][0] ) < 0.5 and abs( vec[0][1][0] - vec[2][1][0] ) < 0.5 ) or \
		   		( abs( vec[0][1][1] - vec[1][1][1] ) < 0.5 and abs( vec[0][1][1] - vec[2][1][1] ) < 0.5 ) :
				find = process_singular_matrix( vec )

			else:
				find = location(vec)

		except Exception as e:
			find = process_singular_matrix( vec )

		loc_x = round( find.getA1()[0] ,3 )
		loc_y = round( find.getA1()[1] , 3)

		loc_x, loc_y = process_correct( vec, [loc_x, loc_y] )

		return loc_x, loc_y