Exemplo n.º 1
0
    def copy(self, deep=False):
        """
        Create a copy of the current species. If the 
        kw argument 'deep' is True, then a deep copy will be made of the 
        Molecule objects in self.molecule.

        For other complex attributes, a deep copy will always be made.
        """
        from copy import deepcopy

        cython.declare(other=Species)

        other = Species.__new__(Species)

        other.index = self.index

        other.label = self.label

        other.thermo = deepcopy(self.thermo)

        other.molecule = []
        for mol in self.molecule:
            other.molecule.append(mol.copy(deep=deep))

        other.conformer = deepcopy(self.conformer)

        other.transportData = deepcopy(self.transportData)

        other.molecularWeight = deepcopy(self.molecularWeight)
        other.energyTransferModel = deepcopy(self.energyTransferModel)
        other.reactive = self.reactive        
        other.props = deepcopy(self.props)

        return other
Exemplo n.º 2
0
def generate_minimum_resonance_isomer(mol):
    """
    Select the resonance isomer that is isomorphic to the parameter isomer, with the lowest unpaired
    electrons descriptor.

    First, we generate all isomorphic resonance isomers.
    Next, we return the candidate with the lowest unpaired electrons metric.

    The metric is a sorted list with indices of the atoms that bear an unpaired electron
    """

    cython.declare(
        candidates=list,
        sel=Molecule,
        cand=Molecule,
        metric_sel=list,
        metric_cand=list,
        )


    candidates = resonance.generate_isomorphic_isomers(mol)
    
    sel = candidates[0]
    metric_sel = get_unpaired_electrons(sel)
    for cand in candidates[1:]:
       metric_cand = get_unpaired_electrons(cand)
       if metric_cand < metric_sel:
            sel = cand
            metric_sel = metric_cand

    return sel
Exemplo n.º 3
0
def is_valid_combo(combo, mol, distances):
    """
    Check if the combination of atom indices refers to
    atoms that are adjacent in the molecule.
    """
    cython.declare(
        agglomerates=list,
        new_distances=list,
        orig_dist=dict,
        new_dist=dict,
        )

    # compute shortest path between atoms 
    agglomerates = agglomerate(combo)
    new_distances = compute_agglomerate_distance(agglomerates, mol)

    # combo is valid if the distance is equal to the parameter distance

    if len(distances) != len(new_distances): return False

    for orig_dist, new_dist in zip(distances, new_distances):
        # only compare the values of the dictionaries:
        if sorted(orig_dist.values()) != sorted(new_dist.values()):
            return False

    return True
Exemplo n.º 4
0
    def getDensityOfStates(self, Elist):
        """
        Return the density of states at the specified energies `Elist` in J/mol
        above the ground state. For the cosine potential, the formula is

        .. math:: \\rho(E) = \\frac{2 q_\\mathrm{1f}}{\\pi^{3/2} V_0^{1/2}} \\mathcal{K}(E / V_0) \\hspace{20pt} E < V_0

        and

        .. math:: \\rho(E) = \\frac{2 q_\\mathrm{1f}}{\\pi^{3/2} E^{1/2}} \\mathcal{K}(V_0 / E) \\hspace{20pt} E > V_0

        where

        .. math:: q_\\mathrm{1f} = \\frac{\\pi^{1/2}}{\\sigma} \\left( \\frac{8 \\pi^2 I}{h^2} \\right)^{1/2}

        :math:`E` is energy, :math:`V_0` is barrier height, and
        :math:`\\mathcal{K}(x)` is the complete elliptic integral of the first
        kind. There is currently no functionality for using the Fourier series
        potential.
        """
        cython.declare(rho=numpy.ndarray, q1f=cython.double, pre=cython.double, V0=cython.double, i=cython.int)
        rho = numpy.zeros_like(Elist)
        q1f = math.sqrt(8 * math.pi * math.pi * math.pi * self.inertia.value / constants.h / constants.h / constants.Na) / self.symmetry
        V0 = self.barrier.value
        pre = 2.0 * q1f / math.sqrt(math.pi * math.pi * math.pi * V0)
        # The following is only valid in the classical limit
        # Note that cellipk(1) = infinity, so we must skip that value
        for i in range(len(Elist)):
            if Elist[i] / V0 < 1:
                rho[i] = pre * cellipk(Elist[i] / V0)
            elif Elist[i] / V0 > 1:
                rho[i] = pre * math.sqrt(V0 / Elist[i]) * cellipk(V0 / Elist[i])
        return rho
Exemplo n.º 5
0
 def getDensityOfStates(self, Elist):
     """
     Return the value of the density of states in mol/J at the specified
     energies `Elist` in J/mol above the ground state. An active K-rotor is
     automatically included if there are no translational or external 
     rotational modes.
     """
     cython.declare(rho=numpy.ndarray, i=cython.int, E=cython.double)
     rho = numpy.zeros_like(Elist)
     # Active K-rotor
     # Only include this if there is no translation or rotation data
     translators = [mode for mode in self.modes if isinstance(mode, Translation)]
     rotors = [mode for mode in self.modes if isinstance(mode, RigidRotor)]
     if len(translators) == 0 and len(rotors) == 0:
         rho0 = numpy.zeros_like(Elist)
         for i, E in enumerate(Elist):
             if E > 0: rho0[i] = 1.0 / math.sqrt(1.0 * E)
         rho = convolve(rho, rho0, Elist)
     # Other non-vibrational modes
     for mode in self.modes:
         if not isinstance(mode, HarmonicOscillator):
             rho = convolve(rho, mode.getDensityOfStates(Elist), Elist)
     # Vibrational modes
     for mode in self.modes:
         if isinstance(mode, HarmonicOscillator):
             rho = mode.getDensityOfStates(Elist, rho)
     return rho * self.spinMultiplicity
Exemplo n.º 6
0
    def getDensityOfStates(self, Elist):
        """
        Return the density of states at the specified energies `Elist` in J/mol
        above the ground state in mol/J. The formula is

        .. math:: \\rho(E) = \\frac{8 \\pi^2 I}{\\sigma h^2}

        for linear rotors and

        .. math:: \\rho(E) = \\frac{\\sqrt{\\pi}}{\\sigma} \\left( \\frac{8 \\pi^2}{h^2} \\right)^{3/2} \\sqrt{I_\\mathrm{A} I_\\mathrm{B} I_\\mathrm{C}} \\frac{E^{1/2}}{\\frac{1}{2}!}

        for nonlinear rotors. Above, :math:`E` is energy, :math:`\\sigma`
        is the symmetry number, :math:`I` is the moment of inertia,
        :math:`k_\\mathrm{B}` is the Boltzmann constant, and :math:`h` is the
        Planck constant.
        """
        cython.declare(theta=cython.double, inertia=cython.double)
        if self.linear:
            inertia = self.inertia.value
            theta = constants.h * constants.h / (8 * constants.pi * constants.pi * inertia) * constants.Na
            return numpy.ones_like(Elist) / theta / self.symmetry
        else:
            theta = 1.0
            for inertia in self.inertia.values:
                theta *= constants.h * constants.h / (8 * constants.pi * constants.pi * inertia) * constants.Na
            return 2.0 * numpy.sqrt(Elist / theta) / self.symmetry
Exemplo n.º 7
0
    def getEntropy(self, T):
        """
        Return the contribution to the heat capacity due to hindered rotation
        in J/mol*K at the specified temperature `T` in K. For the cosine
        potential, this is calculated numerically from the partition function.
        For the Fourier series potential, we solve the corresponding 1D
        Schrodinger equation to obtain the energy levels of the rotor and
        utilize the expression

        .. math:: S^\\mathrm{hind}(T) = R \\left( \\ln q_\\mathrm{hind}(T) + \\frac{\\sum_i E_i e^{-\\beta E_i}}{RT \\sum_i e^{-\\beta E_i}} \\right)

        to obtain the entropy.
        """
        if self.fourier is not None:
            cython.declare(S=cython.double, E=numpy.ndarray, e_kT=numpy.ndarray, i=cython.int)
            E = self.energies
            S = constants.R * numpy.log(self.getPartitionFunction(T))
            e_kT = numpy.exp(-E / constants.R / T)
            S += numpy.sum(E*e_kT) / (T * numpy.sum(e_kT))
            return S
        else:
            Tlow = T * 0.999
            Thigh = T * 1.001
            return (numpy.log(self.getPartitionFunction(Thigh)) +
                T * (numpy.log(self.getPartitionFunction(Thigh)) -
                numpy.log(self.getPartitionFunction(Tlow))) /
                (Thigh - Tlow)) * constants.R
Exemplo n.º 8
0
def convolve(rho1, rho2, Elist):
    """
    Convolutes two density of states arrays `rho1` and `rho2` with corresponding
    energies `Elist` together using the equation

    .. math:: \\rho(E) = \\int_0^E \\rho_1(x) \\rho_2(E-x) \\, dx

    The units of the parameters do not matter so long as they are consistent.
    """

    cython.declare(rho=numpy.ndarray, found1=cython.bint, found2=cython.bint)
    cython.declare(dE=cython.double, nE=cython.int, i=cython.int, j=cython.int)
    rho = numpy.zeros_like(Elist)

    found1 = rho1.any(); found2 = rho2.any()
    if not found1 and not found2:
        pass
    elif found1 and not found2:
        rho = rho1
    elif not found1 and found2:
        rho = rho2
    else:
        dE = Elist[1] - Elist[0]
        nE = len(Elist)
        for i in range(nE):
            for j in range(i+1):
                rho[i] += rho2[i-j] * rho1[i] * dE

    return rho
Exemplo n.º 9
0
    def getPartitionFunction(self, T):
        """
        Return the value of the partition function at the specified temperature
        `T` in K. The formula is

        .. math:: q_\\mathrm{rot}(T) = \\frac{8 \\pi^2 I k_\\mathrm{B} T}{\\sigma h^2}

        for linear rotors and

        .. math:: q_\\mathrm{rot}(T) = \\frac{\\sqrt{\\pi}}{\\sigma} \\left( \\frac{8 \\pi^2 k_\\mathrm{B} T}{h^2} \\right)^{3/2} \\sqrt{I_\\mathrm{A} I_\\mathrm{B} I_\\mathrm{C}}

        for nonlinear rotors. Above, :math:`T` is temperature, :math:`\\sigma`
        is the symmetry number, :math:`I` is the moment of inertia,
        :math:`k_\\mathrm{B}` is the Boltzmann constant, and :math:`h` is the
        Planck constant.
        """
        cython.declare(theta=cython.double, inertia=cython.double)
        if self.linear:
            inertia = self.inertia.value
            theta = constants.h * constants.h / (8 * constants.pi * constants.pi * inertia * constants.kB)
            return T / theta / self.symmetry
        else:
            theta = 1.0
            for inertia in self.inertia.values:
                theta *= constants.h * constants.h / (8 * constants.pi * constants.pi * inertia * constants.kB)
            return numpy.sqrt(constants.pi * T**len(self.inertia.values) / theta) / self.symmetry
Exemplo n.º 10
0
    def __exploreCyclesRecursively(self, chain, cycleList):
        """
        Finds cycles by spidering through a graph.
        Give it a chain of atoms that are connected, `chain`,
        and a list of cycles found so far `cycleList`.
        If `chain` is a cycle, it is appended to `cycleList`.
        Then chain is expanded by one atom (in each available direction)
        and the function is called again. This recursively spiders outwards
        from the starting chain, finding all the cycles.
        """
        vertex2 = cython.declare(Vertex)
        edge = cython.declare(Edge)

        # chainLabels = cython.declare(list)
        # chainLabels=[self.keys().index(v) for v in chain]
        # print "found %d so far. Chain=%s"%(len(cycleList),chainLabels)

        for vertex2, edge in self.edges[chain[-1]].iteritems():
            # vertex2 will loop through each of the atoms
            # that are bonded to the last atom in the chain.
            if vertex2 is chain[0] and len(chain) > 2:
                # it is the first atom in the chain - so the chain IS a cycle!
                cycleList.append(chain[:])
            elif vertex2 not in chain:
                # make the chain a little longer and explore again
                chain.append(vertex2)
                cycleList = self.__exploreCyclesRecursively(chain, cycleList)
                # any cycles down this path (-vertex2) have now been found,
                # so remove the vertex from the chain
                chain.pop(-1)
        return cycleList
Exemplo n.º 11
0
def _pickle_context(ctx):
    """
    returns a picklable tuple of args to be passed to _unpickle_context
    
    The context and all of its shifted contexts are pickled, along with
    and node states that exist for any of those contexts.
    """
    shifted_ctx = cython.declare(MDFContext)
    node = cython.declare(MDFNode)
    node_state = cython.declare(NodeState)

    all_ctx_ids = [ctx.get_id()]

    # get the shift sets for all shifted contexts
    shift_sets = []
    for shifted_ctx in ctx.get_shifted_contexts():
        shift_set = shifted_ctx.get_shift_set()
        shift_sets.append((shifted_ctx.get_id(), shift_set))
        all_ctx_ids.append(shifted_ctx.get_id())

    # get the cached values for all nodes in any of the contexts we're interested in
    node_states = []
    for node in _all_nodes.itervalues():
        for ctx_id, node_state in node._states.iteritems():
            if ctx_id in all_ctx_ids:
                node_states.append((ctx_id, node, NodeStateWrapper(node_state)))

    return (ctx.__class__,
            ctx.get_id(),
            ctx.get_date(),
            node_states,
            shift_sets)
Exemplo n.º 12
0
    def isSpecificCaseOf(self, other):
        """
        Returns ``True`` if `other` is the same as `self` or is a more
        specific case of `self`. Returns ``False`` if some of `self` is not
        included in `other` or they are mutually exclusive.
        """
        cython.declare(gb=GroupBond)
        if not isinstance(other, GroupBond):
            # Let the isSpecificCaseOf method of other handle it
            # We expect self to be a Bond object, but can't test for it here
            # because that would create an import cycle
            return other.isSpecificCaseOf(self)
        gb = other

        cython.declare(order1=str, order2=str)
        # Compare two bond groups for equivalence
        # Each atom type in self must have an equivalent in other
        for order1 in self.order:  # all these must match
            for order2 in gb.order:  # can match any of these
                if order1 == order2:
                    break
            else:
                return False
        # Otherwise self is in fact a specific case of other
        return True
Exemplo n.º 13
0
    def isSubgraphIsomorphic(self, other, initialMap=None):
        """
        Returns ``True`` if `other` is subgraph isomorphic and ``False``
        otherwise. In other words, return ``True`` if self is more specific than other.
        The `initialMap` attribute can be used to specify a required
        mapping from `self` to `other` (i.e. the atoms of `self` are the keys,
        while the atoms of `other` are the values). The `other` parameter must
        be a :class:`Group` object, or a :class:`TypeError` is raised.
        """
        cython.declare(group=Group)
        cython.declare(mult1=cython.short, mult2=cython.short)
        # It only makes sense to compare a Group to a Group for subgraph
        # isomorphism, so raise an exception if this is not what was requested
        if not isinstance(other, Group):
            raise TypeError(
                'Got a {0} object for parameter "other", when a Group object is required.'.format(other.__class__)
            )
        group = other

        if self.multiplicity:
            for mult1 in self.multiplicity:
                if group.multiplicity:
                    for mult2 in group.multiplicity:
                        if mult1 == mult2:
                            break
                    else:
                        return False
        else:
            if group.multiplicity:
                return False
        # Do the isomorphism comparison
        return Graph.isSubgraphIsomorphic(self, other, initialMap)
Exemplo n.º 14
0
def generateResonanceIsomers(mol):
    """
    Generate and return all of the resonance isomers of this molecule.
    """
    cython.declare(isomers=list, newIsomers=list, index=cython.int, atom=Atom)
    cython.declare(isomer=Molecule, newIsomer=Molecule, isom=Molecule)
        
    isomers = [mol]

    # Iterate over resonance isomers
    index = 0
    while index < len(isomers):
        isomer = isomers[index]
        
        newIsomers = []
        for algo in populate_resonance_generation_algorithm():
            newIsomers.extend(algo(isomer))

        for newIsomer in newIsomers:
            # Append to isomer list if unique
            for isom in isomers:
                if isom.isIsomorphic(newIsomer):
                    break
            else:
                isomers.append(newIsomer)
    
        # Move to next resonance isomer
        index += 1
    
    return isomers
Exemplo n.º 15
0
    def equivalent(self, other):
        """
        Returns ``True`` if `other` is equivalent to `self` or ``False`` if not,
        where `other` can be either an :class:`Bond` or an :class:`GroupBond`
        object.
        """
        cython.declare(gb=GroupBond)
        if not isinstance(other, GroupBond):
            # Let the equivalent method of other handle it
            # We expect self to be a Bond object, but can't test for it here
            # because that would create an import cycle
            return other.equivalent(self)
        gb = other

        cython.declare(order1=str, order2=str)
        # Compare two bond groups for equivalence
        # Each atom type in self must have an equivalent in other (and vice versa)
        for order1 in self.order:
            for order2 in gb.order:
                if order1 == order2:
                    break
            else:
                return False
        for order1 in gb.order:
            for order2 in self.order:
                if order1 == order2:
                    break
            else:
                return False
        # Otherwise the two bond groups are equivalent
        return True
Exemplo n.º 16
0
    def _shift(self, shift_set_, cache_context=True):
        """
        see shift - this is the implementation called from C, split
        out so other C functions can call this directly
        """
        # if there's nothing to shift return this context
        if not shift_set_:
            return self

        # check the type of shift_set_ and construct a new ShiftSet if required
        shift_set = cython.declare(ShiftSet)
        if not isinstance(shift_set_, ShiftSet):
            shift_set = ShiftSet(shift_set_)
        else:
            shift_set = shift_set_

        # if a context already exists for this shift set then return it.
        parent = cython.declare(MDFContext)
        parent = self._parent or self
        try:
            shift_key = shift_set._get_shift_key(self)
            return parent._shifted_cache[shift_key]
        except KeyError:
            pass

        # return a new shifted context
        return MDFContext(self._now,
                          _shift_parent=self,
                          _shift_set=shift_set,
                          _cache_shifted=cache_context)
Exemplo n.º 17
0
 def isBalanced(self):
     """
     Return ``True`` if the reaction has the same number of each atom on
     each side of the reaction equation, or ``False`` if not.
     """
     from rmgpy.molecule.element import elementList
     
     cython.declare(reactantElements=dict, productElements=dict, molecule=Molecule, atom=Atom, element=Element)
     
     reactantElements = {}; productElements = {}
     for element in elementList:
         reactantElements[element] = 0
         productElements[element] = 0
     
     for reactant in self.reactants:
         if isinstance(reactant, Species):
             molecule = reactant.molecule[0]
         elif isinstance(reactant, Molecule):
             molecule = reactant
         for atom in molecule.atoms:
             reactantElements[atom.element] += 1
     
     for product in self.products:
         if isinstance(product, Species):
             molecule = product.molecule[0]
         elif isinstance(product, Molecule):
             molecule = product
         for atom in molecule.atoms:
             productElements[atom.element] += 1
      
     for element in elementList:
         if reactantElements[element] != productElements[element]:
             return False
     
     return True
Exemplo n.º 18
0
 def fixBarrierHeight(self, forcePositive=False):
     """
     Turns the kinetics into Arrhenius (if they were ArrheniusEP)
     and ensures the activation energy is at least the endothermicity
     for endothermic reactions, and is not negative only as a result 
     of using Evans Polanyi with an exothermic reaction.
     If `forcePositive` is True, then all reactions
     are forced to have a non-negative barrier.
     """
     cython.declare(H0=cython.double, H298=cython.double, Ea=cython.double)
     H298 = self.getEnthalpyOfReaction(298)
     H0 = sum([spec.thermo.E0.value_si for spec in self.products]) - sum([spec.thermo.E0.value_si for spec in self.reactants])
     if isinstance(self.kinetics, ArrheniusEP):
         Ea = self.kinetics.E0.value_si # temporarily using Ea to store the intrinsic barrier height E0
         self.kinetics = self.kinetics.toArrhenius(H298)
         if Ea > 0 and self.kinetics.Ea.value_si < 0:
             self.kinetics.comment += "\nEa raised from {0:.1f} to 0 kJ/mol.".format(self.kinetics.Ea.value_si/1000)
             logging.info("For reaction {1!s} Ea raised from {0:.1f} to 0 kJ/mol.".format(self.kinetics.Ea.value_si/1000, self))
             self.kinetics.Ea.value_si = 0
     if isinstance(self.kinetics, Arrhenius):
         Ea = self.kinetics.Ea.value_si
         if H0 > 0 and Ea < H0:
             self.kinetics.Ea.value_si = H0
             self.kinetics.comment += "\nEa raised from {0:.1f} to {1:.1f} kJ/mol to match endothermicity of reaction.".format(Ea/1000,H0/1000)
             logging.info("For reaction {2!s}, Ea raised from {0:.1f} to {1:.1f} kJ/mol to match endothermicity of reaction.".format(Ea/1000, H0/1000, self))
     if forcePositive and isinstance(self.kinetics, Arrhenius) and self.kinetics.Ea.value_si < 0:
         self.kinetics.comment += "\nEa raised from {0:.1f} to 0 kJ/mol.".format(self.kinetics.Ea.value_si/1000)
         logging.info("For reaction {1!s} Ea raised from {0:.1f} to 0 kJ/mol.".format(self.kinetics.Ea.value_si/1000, self))
         self.kinetics.Ea.value_si = 0
Exemplo n.º 19
0
def integrate(exp,downer,upper):
        if(upper=='inf'):
            upper=100000
        if(exp.isnumeric()):
            return(int(exp)*(upper-downer))
        dx=(upper-downer)/1000
        val1=cython.declare(cython.float)
        val2=cython.declare(cython.float)
        arr=[0]
        if(upper==downer):
            sum=0.0
        elif(upper>downer):
            j=downer
            sum=0.0
            while(j<upper):
                arr[0]=j
                val1=extract.main(exp,arr)
                arr[0]=j+dx
                val2=extract.main(exp,arr)
                sum+=(val1+val2)/2*dx
                j=j+dx
        else:
            j=upper
            sum=0.0
            while(j<downer):
                arr[0]=j
                val1=extract.main(exp,arr)
                arr[0]=j-dx
                val2=extract.main(exp,arr)
                sum+=(val1+val2)/2*dx
                j=j-dx
        return sum
Exemplo n.º 20
0
def getFeatures(atom, bonds):
    """
    Returns a list of features needed to determine atomType for :class:'Atom'
    or :class:'GroupAtom' object 'atom and with local bond structure `bonds`,
    a ``dict`` containing atom-bond pairs.

    """
    cython.declare(single=cython.int, allDouble=cython.int, rDouble=cython.int,
                   sDouble=cython.int, oDouble=cython.int, triple=cython.int,
                   benzene=cython.int)
    cython.declare(features=cython.list)

    # Count numbers of each higher-order bond type
    single = 0; rDouble = 0; oDouble = 0; sDouble = 0; triple = 0; benzene = 0
    for atom2, bond12 in bonds.iteritems():
        if bond12.isSingle():
            single += 1
        elif bond12.isDouble():
            if atom2.isOxygen():
                oDouble += 1
            elif atom2.isSulfur():
                sDouble += 1
            else:
                # rDouble is for double bonds NOT to oxygen or Sulfur
                rDouble += 1
        elif bond12.isTriple(): triple += 1
        elif bond12.isBenzene(): benzene += 1

    # allDouble is for all double bonds, to anything
    allDouble = rDouble + oDouble + sDouble
    features = [single, allDouble, rDouble, oDouble, sDouble, triple, benzene, atom.lonePairs]

    return features
Exemplo n.º 21
0
 def getEquilibriumConstant(self, T, type='Kc'):
     """
     Return the equilibrium constant for the reaction at the specified
     temperature `T` in K. The `type` parameter lets	you specify the
     quantities used in the equilibrium constant: ``Ka`` for	activities,
     ``Kc`` for concentrations (default), or ``Kp`` for pressures. Note that
     this function currently assumes an ideal gas mixture.
     """
     cython.declare(dGrxn=cython.double, K=cython.double, C0=cython.double, P0=cython.double)
     # Use free energy of reaction to calculate Ka
     dGrxn = self.getFreeEnergyOfReaction(T)
     K = numpy.exp(-dGrxn / constants.R / T)
     # Convert Ka to Kc or Kp if specified
     P0 = 1e5
     if type == 'Kc':
         # Convert from Ka to Kc; C0 is the reference concentration
         C0 = P0 / constants.R / T
         K *= C0 ** (len(self.products) - len(self.reactants))
     elif type == 'Kp':
         # Convert from Ka to Kp; P0 is the reference pressure
         K *= P0 ** (len(self.products) - len(self.reactants))
     elif type != 'Ka' and type != '':
         raise ReactionError('Invalid type "%s" passed to Reaction.getEquilibriumConstant(); should be "Ka", "Kc", or "Kp".')
     if K == 0:
         raise ReactionError('Got equilibrium constant of 0')
     return K
Exemplo n.º 22
0
 def __cpp_calculate_phi(self, x, t):
     cython.declare(xx=cython.double[3])
     xx[0] = x[0]
     xx[1] = x[1]
     xx[2] = x[2]
     level = self.WT.mwl + self.WT.eta(xx,t)
     return x[self.vert_axis]-level
Exemplo n.º 23
0
def find_lone_pair_multiple_bond_paths(atom1):
    """
    Find all the delocalization paths between lone electron pair and multiple bond in a 3-atom system
    `atom1` indicates the localized lone pair site. Currently carbenes are excluded from this path.

    Examples:

    - N2O (N#[N+][O-] <-> [N-]=[N+]=O)
    - Azide (N#[N+][NH-] <-> [N-]=[N+]=N <-> [N-2][N+]#[NH+])
    - N#N group on sulfur (O[S-](O)[N+]#N <-> OS(O)=[N+]=[N-] <-> O[S+](O)#[N+][N-2])
    - N[N+]([O-])=O <=> N[N+](=O)[O-], these structures are isomorphic but not identical, this transition is
      important for correct degeneracy calculations
    """
    cython.declare(paths=list, atom2=Atom, atom3=Atom, bond12=Bond, bond23=Bond)

    # No paths if atom1 has no lone pairs, or cannot lose them, or is a carbon atom
    if atom1.lonePairs <= 0 or not is_atom_able_to_lose_lone_pair(atom1) or atom1.isCarbon():
        return []

    paths = []
    for atom2, bond12 in atom1.edges.items():
        # Bond must be capable of gaining an order
        if bond12.isSingle() or bond12.isDouble():
            for atom3, bond23 in atom2.edges.items():
                # Bond must be capable of losing an order without breaking, atom3 must be able to gain a lone pair
                if atom1 is not atom3 and (bond23.isDouble() or bond23.isTriple())\
                        and (atom3.isCarbon() or is_atom_able_to_gain_lone_pair(atom3)):
                    paths.append([atom1, atom2, atom3, bond12, bond23])
    return paths
Exemplo n.º 24
0
def find_N5dc_radical_delocalization_paths(atom1):
    """
    Find all the resonance structures of an N5dc nitrogen atom with a single bond to a radical N/O/S site, another
    single bond to a negatively charged N/O/S site, and one double bond (not participating in this transformation)

    Example:

    - N=[N+]([O])([O-]) <=> N=[N+]([O-])([O]), these structures are isomorphic but not identical, the transition is
      important for correct degeneracy calculations

    In this transition atom1 is the middle N+ (N5dc), atom2 is the radical site, and atom3 is negatively charged
    A "if atom1.atomType.label == 'N5dc'" check should be done before calling this function
    """
    cython.declare(paths=list, atom2=Atom, atom3=Atom, bond12=Bond, bond23=Bond)

    path = []

    for atom2, bond12 in atom1.edges.items():
        if atom2.radicalElectrons and bond12.isSingle() and not atom2.charge and is_atom_able_to_gain_lone_pair(atom2):
            for atom3, bond13 in atom1.edges.items():
                if (atom2 is not atom3 and bond13.isSingle() and atom3.charge < 0
                        and is_atom_able_to_lose_lone_pair(atom3)):
                    path.append([atom2, atom3])
                    return path  # there could only be one such path per atom1, return if found
    return path
Exemplo n.º 25
0
def getAtomType(atom, bonds):
    """
    Determine the appropriate atom type for an :class:`Atom` object `atom`
    with local bond structure `bonds`, a ``dict`` containing atom-bond pairs.
    """

    cython.declare(atomSymbol=str)
    cython.declare(molFeatureList=cython.list, atomTypeFeatureList=cython.list)

    # Use element and counts to determine proper atom type
    atomSymbol = atom.symbol
    #These elements do not do not have a more specific atomType
    if atomSymbol in nonSpecifics:
        return atomTypes[atomSymbol]

    molFeatureList = getFeatures(atom, bonds)
    for specificAtomType in atomTypes[atomSymbol].specific:
        atomtypeFeatureList = specificAtomType.getFeatures()
        for molFeature, atomtypeFeature in zip(molFeatureList, atomtypeFeatureList):
            if atomtypeFeature == []:
                continue
            elif molFeature not in atomtypeFeature:
                break
        else: return specificAtomType
    else:
        rDouble = molFeatureList[2]
        oDouble = molFeatureList[3]
        sDouble = molFeatureList[4]
        triple = molFeatureList[5]
        benzene = molFeatureList[6]

        raise AtomTypeError('Unable to determine atom type for atom {0}, which has {1:d} double bonds to C, {2:d} double bonds to O, {3:d} double bonds to S, {4:d} triple bonds, and {5:d} benzene bonds.'.format(atom, rDouble, oDouble, sDouble, triple, benzene))
Exemplo n.º 26
0
def findAllDelocalizationPathsLonePairRadical(atom1):
    """
    Find all the delocalization paths of lone electron pairs next to the radical center indicated
    by `atom1`. Used to generate resonance isomers.
    """
    cython.declare(paths=list)
    cython.declare(atom2=Atom, bond12=Bond)
    
    # No paths if atom1 is not a radical
    if atom1.radicalElectrons <= 0:
        return []
    
    # In a first step we only consider nitrogen and oxygen atoms as possible radical centers
    if not ((atom1.lonePairs == 0 and atom1.isNitrogen()) or(atom1.lonePairs == 2 and atom1.isOxygen())):
        return []
    
    # Find all delocalization paths
    paths = []
    for atom2, bond12 in atom1.edges.items():
        # Only single bonds are considered
        if bond12.isSingle():
            # Neighboring atom must posses a lone electron pair to loose it
            if ((atom2.lonePairs == 1 and atom2.isNitrogen()) or (atom2.lonePairs == 3 and atom2.isOxygen())) and (atom2.radicalElectrons == 0):
                paths.append([atom1, atom2])
                
    return paths
Exemplo n.º 27
0
def getElement(value):
    """
    Return the :class:`Element` object corresponding to the given parameter
    `value`. If an integer is provided, the value is treated as the atomic
    number. If a string is provided, the value is treated as the symbol. An
    :class:`ElementError` is raised if no matching element is found.
    """
    cython.declare(element=Element, number=cython.int, symbol=str)
    if isinstance(value, int) or isinstance(value, long):
        # The parameter is an integer; assume this is the atomic number
        number = value
        for element in elementList:
            if element.number == number:
                return element
        # If we reach this point that means we did not find an appropriate element,
        # so we raise an exception
        raise ElementError("No element found with atomic number %i." % (number))
    elif isinstance(value, str):
        # The parameter is a string; assume this is the element symbol
        symbol = value
        for element in elementList:
            if element.symbol == symbol:
                return element
        # If we reach this point that means we did not find an appropriate element,
        # so we raise an exception
        raise ElementError("No element found with symbol %s." % (symbol))
    else:
        raise ElementError('No element found based on parameter %s "%r".' % (type(value), value))
Exemplo n.º 28
0
 def isSpecificCaseOf(self, other):
     """
     Return ``True`` if `self` is a specific case of `other`, or ``False``
     otherwise. If `other` is an :class:`Atom` object, then this is the same
     as the :meth:`equivalent()` method. If `other` is an
     :class:`GroupAtom` object, then the atom must match or be more
     specific than any of the combinations in the atom pattern.
     """
     if isinstance(other, Atom):
         return self.equivalent(other)
     elif isinstance(other, GroupAtom):
         cython.declare(atom=GroupAtom, a=AtomType, radical=cython.short, spin=cython.short, charge=cython.short)
         atom = other
         for a in atom.atomType: 
             if self.atomType.isSpecificCaseOf(a): break
         else:
             return False
         for radical, spin in zip(atom.radicalElectrons, atom.spinMultiplicity):
             if self.radicalElectrons == radical and self.spinMultiplicity == spin: break
         else:
             return False
         for charge in atom.charge:
             if self.charge == charge: break
         else:
             return False
         return True
Exemplo n.º 29
0
 def equivalent(self, other):
     """
     Return ``True`` if `other` is indistinguishable from this atom, or
     ``False`` otherwise. If `other` is an :class:`Atom` object, then all
     attributes except `label` must match exactly. If `other` is an
     :class:`GroupAtom` object, then the atom must match any of the
     combinations in the atom pattern.
     """
     cython.declare(atom=Atom, ap=GroupAtom)
     if isinstance(other, Atom):
         atom = other
         return (self.element is atom.element and
             self.radicalElectrons == atom.radicalElectrons and
             self.spinMultiplicity == atom.spinMultiplicity and
             self.charge == atom.charge)
     elif isinstance(other, GroupAtom):
         cython.declare(a=AtomType, radical=cython.short, spin=cython.short, charge=cython.short)
         ap = other
         for a in ap.atomType:
             if self.atomType.equivalent(a): break
         else:
             return False
         for radical, spin in zip(ap.radicalElectrons, ap.spinMultiplicity):
             if self.radicalElectrons == radical and self.spinMultiplicity == spin: break
         else:
             return False
         for charge in ap.charge:
             if self.charge == charge: break
         else:
             return False
         return True
Exemplo n.º 30
0
    def toOBMol(self):
        """
        Convert a molecular structure to an OpenBabel OBMol object. Uses
        `OpenBabel <http://openbabel.org/>`_ to perform the conversion.
        """
        cython.declare(atom=Atom, atom1=Atom, bonds=dict, atom2=Atom, bond=Bond)
        cython.declare(index1=cython.int, index2=cython.int, order=cython.int)

        # Sort the atoms before converting to ensure output is consistent
        # between different runs
        self.sortAtoms()

        atoms = self.vertices

        obmol = openbabel.OBMol()
        for atom in atoms:
            a = obmol.NewAtom()
            a.SetAtomicNum(atom.number)
            a.SetFormalCharge(atom.charge)
        orders = {'S': 1, 'D': 2, 'T': 3, 'B': 5}
        for atom1 in self.vertices:
            for atom2, bond in atom1.edges.iteritems():
                index1 = atoms.index(atom1)
                index2 = atoms.index(atom2)
                if index1 < index2:
                    order = orders[bond.order]
                    obmol.AddBond(index1+1, index2+1, order)

        obmol.AssignSpinMultiplicity(True)

        return obmol
Exemplo n.º 31
0
# cython: infer_types=True, language_level=3, py2_import=True
#
#   Cython Scanner
#

import sys
import os
import platform

import cython
cython.declare(EncodedString=object,
               string_prefixes=object,
               raw_prefixes=object,
               IDENT=unicode,
               print_function=object)

from Cython import Plex, Utils
from Cython.Plex.Scanners import Scanner
from Cython.Plex.Errors import UnrecognizedInput
from Errors import CompileError, error
from Lexicon import string_prefixes, raw_prefixes, make_lexicon, IDENT
from Future import print_function

from StringEncoding import EncodedString

debug_scanner = 0
trace_scanner = 0
scanner_debug_flags = 0
scanner_dump_file = None

lexicon = None
Exemplo n.º 32
0
 def has_reactive_molecule(self):
     """
     `True` if the species has at least one reactive molecule, `False` otherwise
     """
     cython.declare(molecule=Molecule)
     return any([molecule.reactive for molecule in self.molecule])
Exemplo n.º 33
0
    def get_resonance_hybrid(self):
        """
        Returns a molecule object with bond orders that are the average 
        of all the resonance structures.
        """
        # get labeled resonance isomers
        self.generate_resonance_structures(keep_isomorphic=True)

        # only consider reactive molecules as representative structures
        molecules = [mol for mol in self.molecule if mol.reactive]

        # return if no resonance
        if len(molecules) == 1:
            return molecules[0]

        # create a sorted list of atom objects for each resonance structure
        cython.declare(
            atomsFromStructures=list,
            oldAtoms=list,
            newAtoms=list,
            numResonanceStructures=cython.short,
            structureNum=cython.short,
            oldBondOrder=cython.float,
            index1=cython.short,
            index2=cython.short,
            newMol=Molecule,
            oldMol=Molecule,
            atom1=Atom,
            atom2=Atom,
            bond=Bond,
            atoms=list,
        )

        atoms_from_structures = []
        for new_mol in molecules:
            new_mol.atoms.sort(key=lambda atom: atom.id)
            atoms_from_structures.append(new_mol.atoms)

        num_resonance_structures = len(molecules)

        # make original structure with no bonds
        new_mol = Molecule()
        original_atoms = atoms_from_structures[0]
        for atom1 in original_atoms:
            atom = new_mol.add_atom(Atom(atom1.element))
            atom.id = atom1.id

        new_atoms = new_mol.atoms

        # initialize bonds to zero order
        for index1, atom1 in enumerate(original_atoms):
            for atom2 in atom1.bonds:
                index2 = original_atoms.index(atom2)
                bond = Bond(new_atoms[index1], new_atoms[index2], 0)
                new_mol.add_bond(bond)

        # set bonds to the proper value
        for structureNum, oldMol in enumerate(molecules):
            old_atoms = atoms_from_structures[structureNum]

            for index1, atom1 in enumerate(old_atoms):
                # make bond orders average of resonance structures
                for atom2 in atom1.bonds:
                    index2 = old_atoms.index(atom2)

                    new_bond = new_mol.get_bond(new_atoms[index1],
                                                new_atoms[index2])
                    old_bond_order = oldMol.get_bond(
                        old_atoms[index1], old_atoms[index2]).get_order_num()
                    new_bond.apply_action(
                        ('CHANGE_BOND', None,
                         old_bond_order / num_resonance_structures / 2))
                # set radicals in resonance hybrid to maximum of all structures
                if atom1.radical_electrons > 0:
                    new_atoms[index1].radical_electrons = max(
                        atom1.radical_electrons,
                        new_atoms[index1].radical_electrons)
        new_mol.update_atomtypes(log_species=False, raise_exception=False)
        return new_mol
Exemplo n.º 34
0
class StrategyBase(Node):
    """
    Strategy Node. Used to define strategy logic within a tree.
    A Strategy's role is to allocate capital to it's children
    based on a function.

    Args:
        * name (str): Strategy name
        * children (dict, list): A collection of children. If dict,
            the format is {name: child}, if list then list of children.
            Children can be any type of Node.
        * parent (Node): The parent Node

    Attributes:
        * name (str): Strategy name
        * parent (Strategy): Strategy parent
        * root (Strategy): Root node of the tree (topmost node)
        * children (dict): Strategy's children
        * now (datetime): Used when backtesting to store current date
        * stale (bool): Flag used to determine if Strategy is stale and need
            updating
        * prices (TimeSeries): Prices of the Strategy - basically an index that
            reflects the value of the strategy over time.
        * outlays (DataFrame): Outlays for each SecurityBase child
        * price (float): last price
        * value (float): last value
        * weight (float): weight in parent
        * full_name (str): Name including parents' names
        * members (list): Current Strategy + strategy's children
        * securities (list): List of strategy children that are of type
            SecurityBase
        * commission_fn (fn(quantity, price)): A function used to determine the
            commission (transaction fee) amount. Could be used to model
            slippage (implementation shortfall). Note that often fees are
            symmetric for buy and sell and absolute value of quantity should
            be used for calculation.
        * capital (float): Capital amount in Strategy - cash
        * universe (DataFrame): Data universe available at the current time.
            Universe contains the data passed in when creating a Backtest. Use
            this data to determine strategy logic.

    """

    _capital = cy.declare(cy.double)
    _net_flows = cy.declare(cy.double)
    _last_value = cy.declare(cy.double)
    _last_price = cy.declare(cy.double)
    _last_fee = cy.declare(cy.double)
    _paper_trade = cy.declare(cy.bint)
    bankrupt = cy.declare(cy.bint)

    def __init__(self, name, children=None, parent=None):
        Node.__init__(self, name, children=children, parent=parent)
        self._capital = 0
        self._weight = 1
        self._value = 0
        self._price = 100

        # helper vars
        self._net_flows = 0
        self._last_value = 0
        self._last_price = 100
        self._last_fee = 0

        # default commission function
        self.commission_fn = self._dflt_comm_fn

        self._paper_trade = False
        self._positions = None
        self.bankrupt = False

    @property
    def price(self):
        """
        Current price.
        """
        if self.root.stale:
            self.root.update(self.now, None)
        return self._price

    @property
    def prices(self):
        """
        TimeSeries of prices.
        """
        if self.root.stale:
            self.root.update(self.now, None)
        return self._prices.loc[:self.now]

    @property
    def values(self):
        """
        TimeSeries of values.
        """
        if self.root.stale:
            self.root.update(self.now, None)
        return self._values.loc[:self.now]

    @property
    def capital(self):
        """
        Current capital - amount of unallocated capital left in strategy.
        """
        # no stale check needed
        return self._capital

    @property
    def cash(self):
        """
        TimeSeries of unallocated capital.
        """
        # no stale check needed
        return self._cash

    @property
    def fees(self):
        """
        TimeSeries of fees.
        """
        # no stale check needed
        return self._fees

    @property
    def universe(self):
        """
        Data universe available at the current time.
        Universe contains the data passed in when creating a Backtest.
        Use this data to determine strategy logic.
        """
        # avoid windowing every time
        # if calling and on same date return
        # cached value
        if self.now == self._last_chk:
            return self._funiverse
        else:
            self._last_chk = self.now
            self._funiverse = self._universe.loc[:self.now]
            return self._funiverse

    @property
    def securities(self):
        """
        Returns a list of children that are of type SecurityBase
        """
        return [x for x in self.members if isinstance(x, SecurityBase)]

    @property
    def outlays(self):
        """
        Returns a DataFrame of outlays for each child SecurityBase
        """
        return pd.DataFrame({x.name: x.outlays for x in self.securities})

    @property
    def positions(self):
        """
        TimeSeries of positions.
        """
        # if accessing and stale - update first
        if self.root.stale:
            self.root.update(self.root.now, None)

        vals = pd.DataFrame({
            x.name: x.positions
            for x in self.members if isinstance(x, SecurityBase)
        })
        self._positions = vals
        return vals

    def setup(self, universe):
        """
        Setup strategy with universe. This will speed up future calculations
        and updates.
        """
        # save full universe in case we need it
        self._original_data = universe

        # determine if needs paper trading
        # and setup if so
        if self is not self.parent:
            self._paper_trade = True
            self._paper_amount = 1000000

            paper = deepcopy(self)
            paper.parent = paper
            paper.root = paper
            paper._paper_trade = False
            paper.setup(self._original_data)
            paper.adjust(self._paper_amount)
            self._paper = paper

        # setup universe
        funiverse = universe

        if self._universe_tickers is not None:
            # if we have universe_tickers defined, limit universe to
            # those tickers
            valid_filter = list(
                set(universe.columns).intersection(self._universe_tickers))

            funiverse = universe[valid_filter].copy()

            # if we have strat children, we will need to create their columns
            # in the new universe
            if self._has_strat_children:
                for c in self._strat_children:
                    funiverse[c] = np.nan

            # must create to avoid pandas warning
            funiverse = pd.DataFrame(funiverse)

        self._universe = funiverse
        # holds filtered universe
        self._funiverse = funiverse
        self._last_chk = None

        # We're not bankrupt yet
        self.bankrupt = False

        # setup internal data
        self.data = pd.DataFrame(index=funiverse.index,
                                 columns=['price', 'value', 'cash', 'fees'],
                                 data=0.0)

        self._prices = self.data['price']
        self._values = self.data['value']
        self._cash = self.data['cash']
        self._fees = self.data['fees']

        # setup children as well - use original universe here - don't want to
        # pollute with potential strategy children in funiverse
        if self.children is not None:
            [c.setup(universe) for c in self._childrenv]

    @cy.locals(newpt=cy.bint, val=cy.double, ret=cy.double)
    def update(self, date, data=None, inow=None):
        """
        Update strategy. Updates prices, values, weight, etc.
        """
        # resolve stale state
        self.root.stale = False

        # update helpers on date change
        # also set newpt flag
        newpt = False
        if self.now == 0:
            newpt = True
        elif date != self.now:
            self._net_flows = 0
            self._last_price = self._price
            self._last_value = self._value
            self._last_fee = 0.0
            newpt = True

        # update now
        self.now = date
        if inow is None:
            if self.now == 0:
                inow = 0
            else:
                inow = self.data.index.get_loc(date)

        # update children if any and calculate value
        val = self._capital  # default if no children

        if self.children is not None:
            for c in self._childrenv:
                # avoid useless update call
                if c._issec and not c._needupdate:
                    continue
                c.update(date, data, inow)
                val += c.value

        if self.root == self:
            if (val < 0) and not self.bankrupt:
                # Declare a bankruptcy
                self.bankrupt = True
                self.flatten()

        # update data if this value is different or
        # if now has changed - avoid all this if not since it
        # won't change
        if newpt or self._value != val:
            self._value = val
            self._values.values[inow] = val

            bottom = self._last_value + self._net_flows
            if bottom != 0:
                ret = self._value / (self._last_value + self._net_flows) - 1
            else:
                if self._value == 0:
                    ret = 0
                else:
                    raise ZeroDivisionError(
                        'Could not update %s. Last value '
                        'was %s and net flows were %s. Current'
                        'value is %s. Therefore, '
                        'we are dividing by zero to obtain the return '
                        'for the period.' % (self.name, self._last_value,
                                             self._net_flows, self._value))

            self._price = self._last_price * (1 + ret)
            self._prices.values[inow] = self._price

        # update children weights
        if self.children is not None:
            for c in self._childrenv:
                # avoid useless update call
                if c._issec and not c._needupdate:
                    continue

                if val != 0:
                    c._weight = c.value / val
                else:
                    c._weight = 0.0

        # if we have strategy children, we will need to update them in universe
        if self._has_strat_children:
            for c in self._strat_children:
                # TODO: optimize ".loc" here as well
                self._universe.loc[date, c] = self.children[c].price

        # Cash should track the unallocated capital at the end of the day, so
        # we should update it every time we call "update".
        # Same for fees
        self._cash.values[inow] = self._capital
        self._fees.values[inow] = self._last_fee

        # update paper trade if necessary
        if newpt and self._paper_trade:
            self._paper.update(date)
            self._paper.run()
            self._paper.update(date)
            # update price
            self._price = self._paper.price
            self._prices.values[inow] = self._price

    @cy.locals(amount=cy.double, update=cy.bint, flow=cy.bint, fees=cy.double)
    def adjust(self, amount, update=True, flow=True, fee=0.0):
        """
        Adjust capital - used to inject capital to a Strategy. This injection
        of capital will have no effect on the children.

        Args:
            * amount (float): Amount to adjust by.
            * update (bool): Force update?
            * flow (bool): Is this adjustment a flow? A flow will not have an
                impact on the performance (price index). Example of flows are
                simply capital injections (say a monthly contribution to a
                portfolio). This should not be reflected in the returns. A
                non-flow (flow=False) does impact performance. A good example
                of this is a commission, or a dividend.

        """
        # adjust capital
        self._capital += amount
        self._last_fee += fee

        # if flow - increment net_flows - this will not affect
        # performance. Commissions and other fees are not flows since
        # they have a performance impact
        if flow:
            self._net_flows += amount

        if update:
            # indicates that data is now stale and must
            # be updated before access
            self.root.stale = True

    @cy.locals(amount=cy.double, update=cy.bint)
    def allocate(self, amount, child=None, update=True):
        """
        Allocate capital to Strategy. By default, capital is allocated
        recursively down the children, proportionally to the children's
        weights.  If a child is specified, capital will be allocated
        to that specific child.

        Allocation also have a side-effect. They will deduct the same amount
        from the parent's "account" to offset the allocation. If there is
        remaining capital after allocation, it will remain in Strategy.

        Args:
            * amount (float): Amount to allocate.
            * child (str): If specified, allocation will be directed to child
                only. Specified by name.
            * update (bool): Force update.

        """
        # allocate to child
        if child is not None:
            if child not in self.children:
                c = SecurityBase(child)
                c.setup(self._universe)
                # update to bring up to speed
                c.update(self.now)
                # add child to tree
                self._add_child(c)

            # allocate to child
            self.children[child].allocate(amount)
        # allocate to self
        else:
            # adjust parent's capital
            # no need to update now - avoids repetition
            if self.parent == self:
                self.parent.adjust(-amount, update=False, flow=True)
            else:
                # do NOT set as flow - parent will be another strategy
                # and therefore should not incur flow
                self.parent.adjust(-amount, update=False, flow=False)

            # adjust self's capital
            self.adjust(amount, update=False, flow=True)

            # push allocation down to children if any
            # use _weight to avoid triggering an update
            if self.children is not None:
                [
                    c.allocate(amount * c._weight, update=False)
                    for c in self._childrenv
                ]

            # mark as stale if update requested
            if update:
                self.root.stale = True

    @cy.locals(delta=cy.double, weight=cy.double, base=cy.double)
    def rebalance(self, weight, child, base=np.nan, update=True):
        """
        Rebalance a child to a given weight.

        This is a helper method to simplify code logic. This method is used
        when we want to se the weight of a particular child to a set amount.
        It is similar to allocate, but it calculates the appropriate allocation
        based on the current weight.

        Args:
            * weight (float): The target weight. Usually between -1.0 and 1.0.
            * child (str): child to allocate to - specified by name.
            * base (float): If specified, this is the base amount all weight
                delta calculations will be based off of. This is useful when we
                determine a set of weights and want to rebalance each child
                given these new weights. However, as we iterate through each
                child and call this method, the base (which is by default the
                current value) will change. Therefore, we can set this base to
                the original value before the iteration to ensure the proper
                allocations are made.
            * update (bool): Force update?

        """
        # if weight is 0 - we want to close child
        if weight == 0:
            if child in self.children:
                return self.close(child)
            else:
                return

        # if no base specified use self's value
        if np.isnan(base):
            base = self.value

        # else make sure we have child
        if child not in self.children:
            c = SecurityBase(child)
            c.setup(self._universe)
            # update child to bring up to speed
            c.update(self.now)
            self._add_child(c)

        # allocate to child
        # figure out weight delta
        c = self.children[child]
        delta = weight - c.weight
        c.allocate(delta * base)

    def close(self, child):
        """
        Close a child position - alias for rebalance(0, child). This will also
        flatten (close out all) the child's children.

        Args:
            * child (str): Child, specified by name.
        """
        c = self.children[child]
        # flatten if children not None
        if c.children is not None and len(c.children) != 0:
            c.flatten()

        if c.value != 0. and not np.isnan(c.value):
            c.allocate(-c.value)

    def flatten(self):
        """
        Close all child positions.
        """
        # go right to base alloc
        [c.allocate(-c.value) for c in self._childrenv if c.value != 0]

    def run(self):
        """
        This is the main logic method. Override this method to provide some
        algorithm to execute on each date change. This method is called by
        backtester.
        """
        pass

    def set_commissions(self, fn):
        """
        Set commission (transaction fee) function.

        Args:
            fn (fn(quantity, price)): Function used to determine commission
            amount.

        """
        self.commission_fn = fn

        for c in self._childrenv:
            if isinstance(c, StrategyBase):
                c.set_commissions(fn)

    @cy.locals(q=cy.double, p=cy.double)
    def _dflt_comm_fn(self, q, p):
        return 0.
Exemplo n.º 35
0
#=======================================================================
#
#   Python Lexical Analyser
#
#
#   Scanning an input stream
#
#=======================================================================

import cython
cython.declare(BOL=object, EOL=object, EOF=object, NOT_FOUND=object)

from . import Errors
from .Regexps import BOL, EOL, EOF

NOT_FOUND = object()

class Scanner(object):
  """
  A Scanner is used to read tokens from a stream of characters
  using the token set specified by a Plex.Lexicon.

  Constructor:

    Scanner(lexicon, stream, name = '')

      See the docstring of the __init__ method for details.

  Methods:

    See the docstrings of the individual methods for more
Exemplo n.º 36
0
class SecurityBase(Node):
    """
    Security Node. Used to define a security within a tree.
    A Security's has no children. It simply models an asset that can be bought
    or sold.

    Args:
        * name (str): Security name
        * multiplier (float): security multiplier - typically used for
            derivatives.

    Attributes:
        * name (str): Security name
        * parent (Security): Security parent
        * root (Security): Root node of the tree (topmost node)
        * now (datetime): Used when backtesting to store current date
        * stale (bool): Flag used to determine if Security is stale and need
            updating
        * prices (TimeSeries): Security prices.
        * price (float): last price
        * outlays (TimeSeries): Series of outlays. Positive outlays mean
            capital was allocated to security and security consumed that
            amount.  Negative outlays are the opposite. This can be useful for
            calculating turnover at the strategy level.
        * value (float): last value - basically position * price * multiplier
        * weight (float): weight in parent
        * full_name (str): Name including parents' names
        * members (list): Current Security + strategy's children
        * position (float): Current position (quantity).

    """

    _last_pos = cy.declare(cy.double)
    _position = cy.declare(cy.double)
    multiplier = cy.declare(cy.double)
    _prices_set = cy.declare(cy.bint)
    _needupdate = cy.declare(cy.bint)
    _outlay = cy.declare(cy.double)

    @cy.locals(multiplier=cy.double)
    def __init__(self, name, multiplier=1):
        Node.__init__(self, name, parent=None, children=None)
        self._value = 0
        self._price = 0
        self._weight = 0
        self._position = 0
        self.multiplier = multiplier

        # opt
        self._last_pos = 0
        self._issec = True
        self._needupdate = True
        self._outlay = 0

    @property
    def price(self):
        """
        Current price.
        """
        # if accessing and stale - update first
        if self._needupdate or self.now != self.parent.now:
            self.update(self.root.now)
        return self._price

    @property
    def prices(self):
        """
        TimeSeries of prices.
        """
        # if accessing and stale - update first
        if self._needupdate or self.now != self.parent.now:
            self.update(self.root.now)
        return self._prices.loc[:self.now]

    @property
    def values(self):
        """
        TimeSeries of values.
        """
        # if accessing and stale - update first
        if self._needupdate or self.now != self.parent.now:
            self.update(self.root.now)
        if self.root.stale:
            self.root.update(self.root.now, None)
        return self._values.loc[:self.now]

    @property
    def position(self):
        """
        Current position
        """
        # no stale check needed
        return self._position

    @property
    def positions(self):
        """
        TimeSeries of positions.
        """
        # if accessing and stale - update first
        if self._needupdate:
            self.update(self.root.now)
        if self.root.stale:
            self.root.update(self.root.now, None)
        return self._positions.loc[:self.now]

    @property
    def outlays(self):
        """
        TimeSeries of outlays. Positive outlays (buys) mean this security
        received and consumed capital (capital was allocated to it). Negative
        outlays are the opposite (the security close/sold, and returned capital
        to parent).
        """
        # if accessing and stale - update first
        return self._outlays.loc[:self.now]

    def setup(self, universe):
        """
        Setup Security with universe. Speeds up future runs.

        Args:
            * universe (DataFrame): DataFrame of prices with security's name as
                one of the columns.

        """
        # if we already have all the prices, we will store them to speed up
        # future updates
        try:
            prices = universe[self.name]
        except KeyError:
            prices = None

        # setup internal data
        if prices is not None:
            self._prices = prices
            self.data = pd.DataFrame(index=universe.index,
                                     columns=['value', 'position'],
                                     data=0.0)
            self._prices_set = True
        else:
            self.data = pd.DataFrame(index=universe.index,
                                     columns=['price', 'value', 'position'])
            self._prices = self.data['price']
            self._prices_set = False

        self._values = self.data['value']
        self._positions = self.data['position']

        # add _outlay
        self.data['outlay'] = 0.
        self._outlays = self.data['outlay']

    @cy.locals(prc=cy.double)
    def update(self, date, data=None, inow=None):
        """
        Update security with a given date and optionally, some data.
        This will update price, value, weight, etc.
        """
        # filter for internal calls when position has not changed - nothing to
        # do. Internal calls (stale root calls) have None data. Also want to
        # make sure date has not changed, because then we do indeed want to
        # update.
        if date == self.now and self._last_pos == self._position:
            return

        if inow is None:
            if date == 0:
                inow = 0
            else:
                inow = self.data.index.get_loc(date)

        # date change - update price
        if date != self.now:
            # update now
            self.now = date

            if self._prices_set:
                self._price = self._prices.values[inow]
            # traditional data update
            elif data is not None:
                prc = data[self.name]
                self._price = prc
                self._prices.values[inow] = prc

        self._positions.values[inow] = self._position
        self._last_pos = self._position

        if np.isnan(self._price):
            if self._position == 0:
                self._value = 0
            else:
                raise Exception(
                    'Position is open (non-zero) and latest price is NaN '
                    'for security %s. Cannot update node value.' % self.name)
        else:
            self._value = self._position * self._price * self.multiplier

        self._values.values[inow] = self._value

        if self._weight == 0 and self._position == 0:
            self._needupdate = False

        # save outlay to outlays
        if self._outlay != 0:
            self._outlays.values[inow] = self._outlay
            # reset outlay back to 0
            self._outlay = 0

    @cy.locals(amount=cy.double,
               update=cy.bint,
               q=cy.double,
               outlay=cy.double,
               i=cy.int)
    def allocate(self, amount, update=True):
        """
        This allocates capital to the Security. This is the method used to
        buy/sell the security.

        A given amount of shares will be determined on the current price, a
        commission will be calculated based on the parent's commission fn, and
        any remaining capital will be passed back up  to parent as an
        adjustment.

        Args:
            * amount (float): Amount of adjustment.
            * update (bool): Force update?

        """

        # will need to update if this has been idle for a while...
        # update if needupdate or if now is stale
        # fetch parent's now since our now is stale
        if self._needupdate or self.now != self.parent.now:
            self.update(self.parent.now)

        # ignore 0 alloc
        # Note that if the price of security has dropped to zero, then it
        # should never be selected by SelectAll, SelectN etc. I.e. we should
        # not open the position at zero price. At the same time, we are able
        # to close it at zero price, because at that point amount=0.
        # Note also that we don't erase the position in an asset which price
        # has dropped to zero (though the weight will indeed be = 0)
        if amount == 0:
            return

        if self.parent is self or self.parent is None:
            raise Exception('Cannot allocate capital to a parentless security')

        if self._price == 0 or np.isnan(self._price):
            raise Exception('Cannot allocate capital to '
                            '%s because price is %s as of %s' %
                            (self.name, self._price, self.parent.now))

        # buy/sell
        # determine quantity - must also factor in commission
        # closing out?
        if amount == -self._value:
            q = -self._position
        else:
            q = amount / (self._price * self.multiplier)
            if self.integer_positions:
                if (self._position > 0) or ((self._position == 0) and
                                            (amount > 0)):
                    # if we're going long or changing long position
                    q = math.floor(q)
                else:
                    # if we're going short or changing short position
                    q = math.ceil(q)

        # if q is 0 nothing to do
        if q == 0 or np.isnan(q):
            return

        # unless we are closing out a position (q == -position)
        # we want to ensure that
        #
        # - In the event of a positive amount, this indicates the maximum
        # amount a given security can use up for a purchase. Therefore, if
        # commissions push us above this amount, we cannot buy `q`, and must
        # decrease its value
        #
        # - In the event of a negative amount, we want to 'raise' at least the
        # amount indicated, no less. Therefore, if we have commission, we must
        # sell additional units to fund this requirement. As such, q must once
        # again decrease.
        #
        if not q == -self._position:
            full_outlay, _, _ = self.outlay(q)

            # if full outlay > amount, we must decrease the magnitude of `q`
            # this can potentially lead to an infinite loop if the commission
            # per share > price per share. However, we cannot really detect
            # that in advance since the function can be non-linear (say a fn
            # like max(1, abs(q) * 0.01). Nevertheless, we want to avoid these
            # situations.
            # cap the maximum number of iterations to 1e4 and raise exception
            # if we get there
            # if integer positions then we know we are stuck if q doesn't change

            # if integer positions is false then we want full_outlay == amount
            # if integer positions is true then we want to be at the q where
            #   if we bought 1 more then we wouldn't have enough cash
            i = 0
            last_q = q
            last_amount_short = full_outlay - amount
            while not np.isclose(full_outlay, amount, rtol=0.) and q != 0:

                dq_wout_considering_tx_costs = (full_outlay - amount) / (
                    self._price * self.multiplier)
                q = q - dq_wout_considering_tx_costs

                if self.integer_positions:
                    q = math.floor(q)

                full_outlay, _, _ = self.outlay(q)

                # if our q is too low and we have integer positions
                # then we know that the correct quantity is the one  where
                # the outlay of q + 1 < amount. i.e. if we bought one more
                # position then we wouldn't have enough cash
                if self.integer_positions:

                    full_outlay_of_1_more, _, _ = self.outlay(q + 1)

                    if full_outlay < amount and full_outlay_of_1_more > amount:
                        break

                # if not integer positions then we should keep going until
                # full_outlay == amount or is close enough

                i = i + 1
                if i > 1e4:
                    raise Exception(
                        'Potentially infinite loop detected. This occurred '
                        'while trying to reduce the amount of shares purchased'
                        ' to respect the outlay <= amount rule. This is most '
                        'likely due to a commission function that outputs a '
                        'commission that is greater than the amount of cash '
                        'a short sale can raise.')

                if self.integer_positions and last_q == q:
                    raise Exception(
                        'Newton Method like root search for quantity is stuck!'
                        ' q did not change in iterations so it is probably a bug'
                        ' but we are not entirely sure it is wrong! Consider '
                        ' changing to warning.')
                last_q = q

                if np.abs(full_outlay - amount) > np.abs(last_amount_short):
                    raise Exception(
                        'The difference between what we have raised with q and'
                        ' the amount we are trying to raise has gotten bigger since'
                        ' last iteration! full_outlay should always be approaching'
                        ' amount! There may be a case where the commission fn is'
                        ' not smooth')
                last_amount_short = full_outlay - amount

        # if last step led to q == 0, then we can return just like above
        if q == 0:
            return

        # this security will need an update, even if pos is 0 (for example if
        # we close the positions, value and pos is 0, but still need to do that
        # last update)
        self._needupdate = True

        # adjust position & value
        self._position += q

        # calculate proper adjustment for parent
        # parent passed down amount so we want to pass
        # -outlay back up to parent to adjust for capital
        # used
        full_outlay, outlay, fee = self.outlay(q)

        # store outlay for future reference
        self._outlay += outlay

        # call parent
        self.parent.adjust(-full_outlay, update=update, flow=False, fee=fee)

    @cy.locals(q=cy.double, p=cy.double)
    def commission(self, q, p):
        """
        Calculates the commission (transaction fee) based on quantity and
        price.  Uses the parent's commission_fn.

        Args:
            * q (float): quantity
            * p (float): price

        """
        return self.parent.commission_fn(q, p)

    @cy.locals(q=cy.double)
    def outlay(self, q):
        """
        Determines the complete cash outlay (including commission) necessary
        given a quantity q.
        Second returning parameter is a commission itself.

        Args:
            * q (float): quantity

        """
        fee = self.commission(q, self._price * self.multiplier)
        outlay = q * self._price * self.multiplier
        return outlay + fee, outlay, fee

    def run(self):
        """
        Does nothing - securities have nothing to do on run.
        """
        pass
Exemplo n.º 37
0
def fromRDKitMol(mol, rdkitmol):
    """
    Convert a RDKit Mol object `rdkitmol` to a molecular structure. Uses
    `RDKit <http://rdkit.org/>`_ to perform the conversion.
    This Kekulizes everything, removing all aromatic atom types.
    """
    cython.declare(i=cython.int,
                   radicalElectrons=cython.int,
                   charge=cython.int,
                   lonePairs=cython.int,
                   number=cython.int,
                   order=cython.float,
                   atom=mm.Atom,
                   atom1=mm.Atom,
                   atom2=mm.Atom,
                   bond=mm.Bond)

    mol.vertices = []

    # Add hydrogen atoms to complete molecule if needed
    rdkitmol.UpdatePropertyCache(strict=False)
    rdkitmol = Chem.AddHs(rdkitmol)
    Chem.rdmolops.Kekulize(rdkitmol, clearAromaticFlags=True)

    # iterate through atoms in rdkitmol
    for i in xrange(rdkitmol.GetNumAtoms()):
        rdkitatom = rdkitmol.GetAtomWithIdx(i)

        # Use atomic number as key for element
        number = rdkitatom.GetAtomicNum()
        isotope = rdkitatom.GetIsotope()
        element = elements.getElement(number, isotope or -1)

        # Process charge
        charge = rdkitatom.GetFormalCharge()
        radicalElectrons = rdkitatom.GetNumRadicalElectrons()

        atom = mm.Atom(element, radicalElectrons, charge, '', 0)
        mol.vertices.append(atom)

        # Add bonds by iterating again through atoms
        for j in xrange(0, i):
            rdkitbond = rdkitmol.GetBondBetweenAtoms(i, j)
            if rdkitbond is not None:
                order = 0

                # Process bond type
                rdbondtype = rdkitbond.GetBondType()
                if rdbondtype.name == 'SINGLE': order = 1
                elif rdbondtype.name == 'DOUBLE': order = 2
                elif rdbondtype.name == 'TRIPLE': order = 3
                elif rdbondtype.name == 'AROMATIC': order = 1.5

                bond = mm.Bond(mol.vertices[i], mol.vertices[j], order)
                mol.addBond(bond)

    # We need to update lone pairs first because the charge was set by RDKit
    mol.updateLonePairs()
    # Set atom types and connectivity values
    mol.update()

    # Assume this is always true
    # There are cases where 2 radicalElectrons is a singlet, but
    # the triplet is often more stable,
    mol.multiplicity = mol.getRadicalCount() + 1
    # mol.updateAtomTypes()

    return mol
Exemplo n.º 38
0
    def equivalent(self, other):
        """
        Returns ``True`` if `other` is equivalent to `self` or ``False`` if not,
        where `other` can be either an :class:`Atom` or an :class:`GroupAtom`
        object. When comparing two :class:`GroupAtom` objects, this function
        respects wildcards, e.g. ``R!H`` is equivalent to ``C``.
        
        """
        cython.declare(group=GroupAtom)
        if not isinstance(other, GroupAtom):
            # Let the equivalent method of other handle it
            # We expect self to be an Atom object, but can't test for it here
            # because that would create an import cycle
            return other.equivalent(self)
        group = other

        cython.declare(atomType1=AtomType,
                       atomtype2=AtomType,
                       radical1=cython.short,
                       radical2=cython.short,
                       lp1=cython.short,
                       lp2=cython.short,
                       charge1=cython.short,
                       charge2=cython.short)
        # Compare two atom groups for equivalence
        # Each atom type in self must have an equivalent in other (and vice versa)
        for atomType1 in self.atomType:
            for atomType2 in group.atomType:
                if atomType1.equivalent(atomType2): break
            else:
                return False
        for atomType1 in group.atomType:
            for atomType2 in self.atomType:
                if atomType1.equivalent(atomType2): break
            else:
                return False
        # Each free radical electron state in self must have an equivalent in other (and vice versa)
        for radical1 in self.radicalElectrons:
            if group.radicalElectrons:  # Only check if the list is non-empty.  An empty list indicates a wildcard.
                for radical2 in group.radicalElectrons:
                    if radical1 == radical2: break
                else:
                    return False
        for radical1 in group.radicalElectrons:
            if self.radicalElectrons:
                for radical2 in self.radicalElectrons:
                    if radical1 == radical2: break
                else:
                    return False
        for lp1 in self.lonePairs:
            if group.lonePairs:
                for lp2 in group.lonePairs:
                    if lp1 == lp2: break
                else:
                    return False
        #Each charge in self must have an equivalent in other (and vice versa)
        for charge1 in self.charge:
            if group.charge:
                for charge2 in group.charge:
                    if charge1 == charge2: break
                else:
                    return False
        for charge1 in group.charge:
            if self.charge:
                for charge2 in self.charge:
                    if charge1 == charge2: break
                else:
                    return False
        # Otherwise the two atom groups are equivalent
        return True
Exemplo n.º 39
0
# cython: infer_types=True, language_level=3, auto_pickle=False
#
#   Cython Scanner
#

from __future__ import absolute_import

import cython
cython.declare(make_lexicon=object, lexicon=object,
               print_function=object, error=object, warning=object,
               os=object, platform=object)

import os
import platform
from unicodedata import normalize

from .. import Utils
from ..Plex.Scanners import Scanner
from ..Plex.Errors import UnrecognizedInput
from .Errors import error, warning
from .Lexicon import any_string_prefix, make_lexicon, IDENT
from .Future import print_function

debug_scanner = 0
trace_scanner = 0
scanner_debug_flags = 0
scanner_dump_file = None

lexicon = None

Exemplo n.º 40
0
class Node(object):
    """
    The Node is the main building block in bt's tree structure design.
    Both StrategyBase and SecurityBase inherit Node. It contains the
    core functionality of a tree node.

    Args:
        * name (str): The Node name
        * parent (Node): The parent Node
        * children (dict, list): A collection of children. If dict,
            the format is {name: child}, if list then list of children.

    Attributes:
        * name (str): Node name
        * parent (Node): Node parent
        * root (Node): Root node of the tree (topmost node)
        * children (dict): Node's children
        * now (datetime): Used when backtesting to store current date
        * stale (bool): Flag used to determine if Node is stale and need
            updating
        * prices (TimeSeries): Prices of the Node. Prices for a security will
            be the security's price, for a strategy it will be an index that
            reflects the value of the strategy over time.
        * price (float): last price
        * value (float): last value
        * weight (float): weight in parent
        * full_name (str): Name including parents' names
        * members (list): Current Node + node's children

    """

    _price = cy.declare(cy.double)
    _value = cy.declare(cy.double)
    _weight = cy.declare(cy.double)
    _issec = cy.declare(cy.bint)
    _has_strat_children = cy.declare(cy.bint)

    def __init__(self, name, parent=None, children=None):

        self.name = name

        # strategy children helpers
        self._has_strat_children = False
        self._strat_children = []

        # if children is not None, we assume that we want to limit the
        # available children space to the provided list.
        if children is not None:
            if isinstance(children, list):
                # if all strings - just save as universe_filter
                if all(isinstance(x, str) for x in children):
                    self._universe_tickers = children
                    # empty dict - don't want to uselessly create
                    # tons of children when they might not be needed
                    children = {}
                else:
                    # this will be case if we pass in children
                    # (say a bunch of sub-strategies)
                    tmp = {}
                    ut = []
                    for c in children:
                        if type(c) == str:
                            tmp[c] = SecurityBase(c)
                            ut.append(c)
                        else:
                            # deepcopy object for possible later reuse
                            tmp[c.name] = deepcopy(c)

                            # if strategy, turn on flag and add name to list
                            # strategy children have special treatment
                            if isinstance(c, StrategyBase):
                                self._has_strat_children = True
                                self._strat_children.append(c.name)
                            # if not strategy, then we will want to add this to
                            # universe_tickers to filter on setup
                            else:
                                ut.append(c.name)

                    children = tmp
                    # we want to keep whole universe in this case
                    # so set to None
                    self._universe_tickers = ut

        if parent is None:
            self.parent = self
            self.root = self
            # by default all positions are integer
            self.integer_positions = True
        else:
            self.parent = parent
            self.root = parent.root
            parent._add_child(self)

        # default children
        if children is None:
            children = {}
            self._universe_tickers = None
        self.children = children

        self._childrenv = list(children.values())
        for c in self._childrenv:
            c.parent = self
            c.root = self.root

        # set default value for now
        self.now = 0
        # make sure root has stale flag
        # used to avoid unnecessary update
        # sometimes we change values in the tree and we know that we will need
        # to update if another node tries to access a given value (say weight).
        # This avoid calling the update until it is actually needed.
        self.root.stale = False

        # helper vars
        self._price = 0
        self._value = 0
        self._weight = 0

        # is security flag - used to avoid updating 0 pos securities
        self._issec = False

    def __getitem__(self, key):
        return self.children[key]

    def use_integer_positions(self, integer_positions):
        """
        Set indicator to use (or not) integer positions for a given strategy or
        security.

        By default all positions in number of stocks should be integer.
        However this may lead to unexpected results when working with adjusted
        prices of stocks. Because of series of reverse splits of stocks, the
        adjusted prices back in time might be high. Thus rounding of desired
        amount of stocks to buy may lead to having 0, and thus ignoring this
        stock from backtesting.
        """
        self.integer_positions = integer_positions
        for c in self._childrenv:
            c.use_integer_positions(integer_positions)

    @property
    def prices(self):
        """
        A TimeSeries of the Node's price.
        """
        # can optimize depending on type -
        # securities don't need to check stale to
        # return latest prices, whereas strategies do...
        raise NotImplementedError()

    @property
    def price(self):
        """
        Current price of the Node
        """
        # can optimize depending on type -
        # securities don't need to check stale to
        # return latest prices, whereas strategies do...
        raise NotImplementedError()

    @property
    def value(self):
        """
        Current value of the Node
        """
        if self.root.stale:
            self.root.update(self.root.now, None)
        return self._value

    @property
    def weight(self):
        """
        Current weight of the Node (with respect to the parent).
        """
        if self.root.stale:
            self.root.update(self.root.now, None)
        return self._weight

    def setup(self, dates):
        """
        Setup method used to initialize a Node with a set of dates.
        """
        raise NotImplementedError()

    def _add_child(self, child):
        child.parent = self
        child.root = self.root
        child.integer_positions = self.integer_positions

        if self.children is None:
            self.children = {child.name: child}
        else:
            self.children[child.name] = child

        self._childrenv = list(self.children.values())

    def update(self, date, data=None, inow=None):
        """
        Update Node with latest date, and optionally some data.
        """
        raise NotImplementedError()

    def adjust(self, amount, update=True, isflow=True):
        """
        Adjust Node value by amount.
        """
        raise NotImplementedError()

    def allocate(self, amount, update=True):
        """
        Allocate capital to Node.
        """
        raise NotImplementedError()

    @property
    def members(self):
        """
        Node members. Members include current node as well as Node's
        children.
        """
        res = [self]
        for c in list(self.children.values()):
            res.extend(c.members)
        return res

    @property
    def full_name(self):
        if self.parent == self:
            return self.name
        else:
            return '%s>%s' % (self.parent.full_name, self.name)

    def __repr__(self):
        return '<%s %s>' % (self.__class__.__name__, self.full_name)
Exemplo n.º 41
0
# cython: language_level=3str
# cython: auto_pickle=True

from __future__ import absolute_import

import cython
cython.declare(PyrexTypes=object,
               ExprNodes=object,
               Nodes=object,
               Builtin=object,
               InternalError=object,
               error=object,
               warning=object,
               fake_rhs_expr=object,
               TypedExprNode=object)

from . import Builtin
from . import ExprNodes
from . import Nodes
from . import Options
from . import PyrexTypes

from .Visitor import TreeVisitor, CythonTransform
from .Errors import error, warning, InternalError
from .Optimize import ConstantFolding


class TypedExprNode(ExprNodes.ExprNode):
    # Used for declaring assignments of a specified type without a known entry.
    def __init__(self, type, may_be_none=None, pos=None):
        super(TypedExprNode, self).__init__(pos)
Exemplo n.º 42
0
#	modulation
#	layer mapping
#	precoding
#	mapping to resource elements
#	OFDM signal generation
#
#	Input to the physical layer: codewords
#  
#  # Copyright (c) 2015 - 2016 Austin Aigbe ([email protected])

import cython

from libc.math cimport *

# Frame structure (4)
Ts = cython.declare(cython.double, 1/(15000*2048)) # seconds (basic time unit)
Tf = cython.declare(cython.double, 307200 * Ts)    # 10ms duration (radio frame duration)

LTE_PHY_RADIO_FRAME_TYPE1 = "FDD"
LTE_PHY_RADIO_FRAME_TYPE2 = "TDD"

LTE_PHY_FDD_HALF_DUPLEX = 0
LTE_PHY_FDD_FULL_DUPLEX = 1

# Frame structure type 1 (4.1)
# 
#   1 radio frame (Tf) = 20 slots = 10 subframes = 10ms
#   1 slot (T_slot_fdd) = 0.5ms
#   1 subframe = 2 slots = 1ms [2i and 2i+1, i = subframe]
#   
#   For FDD, DL transmission = 10 subframes, UL transmission = 10 subframes
Exemplo n.º 43
0
def generate_aryne_resonance_structures(mol):
    """
    Generate aryne resonance structures, including the cumulene and alkyne forms.

    For all 6-membered rings, check for the following bond patterns:

      - DDDSDS
      - STSDSD

    This does NOT cover all possible aryne resonance forms, only the simplest ones.
    Especially for polycyclic arynes, enumeration of all resonance forms is
    related to enumeration of all Kekule structures, which is very difficult.
    """
    cython.declare(rings=list, ring=list, new_mol_list=list, bond_list=list,
                   i=cython.int, j=cython.int, bond_orders=str, new_orders=str,
                   ind=cython.int, bond=Bond, new_mol=Molecule)

    rings = mol.get_relevant_cycles()
    rings = [ring for ring in rings if len(ring) == 6]

    new_mol_list = []
    for ring in rings:
        # Get bond orders
        bond_list = mol.get_edges_in_cycle(ring)
        bond_orders = ''.join([bond.get_order_str() for bond in bond_list])
        new_orders = None
        # Check for expected bond patterns
        if bond_orders.count('T') == 1:
            # Reorder the list so the triple bond is first
            ind = bond_orders.index('T')
            bond_orders = bond_orders[ind:] + bond_orders[:ind]
            bond_list = bond_list[ind:] + bond_list[:ind]
            # Check for patterns
            if bond_orders == 'TSDSDS':
                new_orders = 'DDSDSD'
        elif bond_orders.count('D') == 4:
            # Search for DDD and reorder the list so that it comes first
            if 'DDD' in bond_orders:
                ind = bond_orders.index('DDD')
                bond_orders = bond_orders[ind:] + bond_orders[:ind]
                bond_list = bond_list[ind:] + bond_list[:ind]
            elif bond_orders.startswith('DD') and bond_orders.endswith('D'):
                bond_orders = bond_orders[-1:] + bond_orders[:-1]
                bond_list = bond_list[-1:] + bond_list[:-1]
            elif bond_orders.startswith('D') and bond_orders.endswith('DD'):
                bond_orders = bond_orders[-2:] + bond_orders[:-2]
                bond_list = bond_list[-2:] + bond_list[:-2]
            # Check for patterns
            if bond_orders == 'DDDSDS':
                new_orders = 'STSDSD'

        if new_orders is not None:
            # We matched one of our patterns, so we can now change the bonds
            for i, bond in enumerate(bond_list):
                bond.set_order_str(new_orders[i])
            # Make a copy of the molecule
            new_mol = mol.copy(deep=True)
            # Undo the changes to the current molecule
            for i, bond in enumerate(bond_list):
                bond.set_order_str(bond_orders[i])
            # Try to update atom types
            try:
                new_mol.update_atomtypes(log_species=False)
            except AtomTypeError:
                pass  # Don't append resonance structure if it creates an undefined atomtype
            else:
                new_mol_list.append(new_mol)

    return new_mol_list
Exemplo n.º 44
0
def calcContactC(numNodes, nAtoms, cutoffDist, 
                 tmpDists, 
                 tmpDistsAtms, 
                 contactMat,
                 atomToNode,
                 nodeGroupIndicesNP,
                 nodeGroupIndicesNPAux):
    '''Translates MDAnalysis distance calculation to node contact matrix.
    
    This function is Cython compiled to optimize the search for nodes in contact. It relies on the results of MDAnalysis' `self_distance_array` calculation, stored in a 1D NumPy array of shape (n*(n-1)/2,), which acts as an unwrapped triangular matrix.
    
    In this function, the distances between all atoms in an atom groups of all pairs of nodes are verified to check if any pair of atoms were closer than a cutoff distance. This is done for all pairs of nodes in the system, and all frames in the trajectory. The pre-allocated contact matrix passed as an argument to this function is used to store the number of frames where each pair of nodes had at least one contact.
    
    Args:
        numNodes (int): Number of nodes in the system.
        nAtoms (int) : Number of atoms in atom groups represented by system nodes. Usually hydrogen atoms are not included in contact detection, and are not present in atom groups.
        cutoffDist (float) : Distance at which atoms are no longer considered 'in contact'.
        tmpDists (obj) : Temporary pre-allocated NumPy array with atom distances. This is the result of MDAnalysis `self_distance_array` calculation.
        tmpDistsAtms (obj) : Temporary pre-allocated NumPy array to store the shortest distance between atoms in different nodes.
        contactMat (obj) : Pre-allocated NumPy matrix where node contacts will be stored.
        atomToNode (obj) : NumPy array that maps atoms in atom groups to their respective nodes.
        nodeGroupIndicesNP (obj) : NumPy array with atom indices for all atoms in each node group.
        nodeGroupIndicesNPAux (obj) : Auxiliary NumPy array with the indices of the first atom in each atom group, as listed in `nodeGroupIndicesNP`.
    
    '''
    
    #cdef int nextIfirst, j1, jend, i, nodeI_k, nodeAtmIndx, nodeAtmIndxNext
    nextIfirst = cython.declare(cython.int)
    
    # Cython types are evaluated as for cdef declarations
    j1: cython.int
    jend: cython.int
    i: cython.int
    nodeI_k: cython.int
    nodeAtmIndx: cython.int
    nodeAtmIndxNext: cython.int
    
    
    # We iterate untill we have only one node left
    for i in range(numNodes - 1):
        
        # Initializes the array for this node.
        tmpDistsAtms.fill(cutoffDist*2)
        
        # index of first atom in node "i"
        nodeAtmIndx = nodeGroupIndicesNPAux[i]
        # index of first atom in node "i+1"
        nodeAtmIndxNext = nodeGroupIndicesNPAux[i+1]
        
        # Gets first atom in next node
        nextIfirst = nodeGroupIndicesNP[nodeAtmIndxNext]
        
        # Per node:  Iterate over atoms from node i
        for nodeI_k in nodeGroupIndicesNP[nodeAtmIndx:nodeAtmIndxNext]:
            
            # Go from 2D indices to 1D (n*(n-1)/2) indices:
            j1 = getLinIndexC(nodeI_k, nextIfirst, nAtoms)
            jend = j1 + (nAtoms - nextIfirst)
            
            # Gets the shortest distance between atoms in different nodes
            tmpDistsAtms[nextIfirst:] = np.where(tmpDists[j1: jend] < tmpDistsAtms[nextIfirst:], 
                     tmpDists[j1: jend], 
                     tmpDistsAtms[nextIfirst:])
        
        # Adds one to the contact to indicate that this frame had a contact.
        contactMat[i, np.unique( atomToNode[ np.where(tmpDistsAtms < cutoffDist)[0] ] )] += 1
Exemplo n.º 45
0
def _generate_resonance_structures(mol_list, method_list, keep_isomorphic=False, copy=False, filter_structures=True):
    """
    Iteratively generate all resonance structures for a list of starting molecules using the specified methods.

    Args:
        mol_list             starting list of molecules
        method_list          list of resonance structure algorithms
        keep_isomorphic      if False, removes any structures that give is_isomorphic=True (default)
                            if True, only remove structures that give is_identical=True
        copy                if False, append new resonance structures to input list (default)
                            if True, make a new list with all of the resonance structures
    """
    cython.declare(index=cython.int, molecule=Molecule, new_mol_list=list, new_mol=Molecule, mol=Molecule)

    if copy:
        # Make a copy of the list so we don't modify the input list
        mol_list = mol_list[:]

    min_octet_deviation = min(filtration.get_octet_deviation_list(mol_list))
    min_charge_span = min(filtration.get_charge_span_list(mol_list))

    # Iterate over resonance structures
    index = 0
    while index < len(mol_list):
        molecule = mol_list[index]
        new_mol_list = []

        # On-the-fly filtration: Extend methods only for molecule that don't deviate too much from the octet rule
        # (a +2 distance from the minimal deviation is used, octet deviations per species are in increments of 2)
        # Sometimes rearranging the structure requires an additional higher charge span structure, so allow
        # structures with a +1 higher charge span compared to the minimum, e.g., [O-]S#S[N+]#N
        # This is run by default even if filter_structures=False.
        octet_deviation = filtration.get_octet_deviation(molecule)
        charge_span = molecule.get_charge_span()
        if octet_deviation <= min_octet_deviation + 2 and charge_span <= min_charge_span + 1:
            for method in method_list:
                new_mol_list.extend(method(molecule))
            if octet_deviation < min_octet_deviation:
                # update min_octet_deviation to make this criterion tighter
                min_octet_deviation = octet_deviation
            if charge_span < min_charge_span:
                # update min_charge_span to make this criterion tighter
                min_charge_span = charge_span

        for new_mol in new_mol_list:
            # Append to structure list if unique
            for mol in mol_list:
                if not keep_isomorphic and mol.is_isomorphic(new_mol):
                    break
                elif keep_isomorphic and mol.is_identical(new_mol):
                    break
            else:
                mol_list.append(new_mol)

        # Move to the next resonance structure
        index += 1

    # check net charge
    for mol in mol_list:
        if mol.get_net_charge() != 0:
            raise ResonanceError('Resonance generation gave a net charged molecule:\n{0}'
                                 'Ions are not yet supported in RMG.'.format(
                mol.to_adjacency_list()))

    return mol_list
Exemplo n.º 46
0
def _clar_optimization(mol, constraints=None, max_num=None):
    """
    Implements linear programming algorithm for finding Clar structures. This algorithm maximizes the number
    of Clar sextets within the constraints of molecular geometry and atom valency.

    Returns a list of valid Clar solutions in the form of a tuple, with the following entries:
        [0] Molecule object
        [1] List of aromatic rings
        [2] List of bonds
        [3] Optimization solution

    The optimization solution is a list of boolean values with sextet assignments followed by double bond assignments,
    with indices corresponding to the list of aromatic rings and list of bonds, respectively.

    Method adapted from:
        Hansen, P.; Zheng, M. The Clar Number of a Benzenoid Hydrocarbon and Linear Programming.
            J. Math. Chem. 1994, 15 (1), 93–107.
    """
    cython.declare(molecule=Molecule, aromatic_rings=list, exo=list, l=cython.int, m=cython.int, n=cython.int,
                   a=list, objective=list, status=cython.int, solution=list, innerSolutions=list)

    from lpsolve55 import lpsolve

    # Make a copy of the molecule so we don't destroy the original
    molecule = mol.copy(deep=True)

    aromatic_rings = molecule.get_aromatic_rings()[0]
    aromatic_rings.sort(key=lambda x: sum([atom.id for atom in x]))

    if not aromatic_rings:
        return []

    # Get list of atoms that are in rings
    atoms = set()
    for ring in aromatic_rings:
        atoms.update(ring)
    atoms = sorted(atoms, key=lambda x: x.id)

    # Get list of bonds involving the ring atoms, ignoring bonds to hydrogen
    bonds = set()
    for atom in atoms:
        bonds.update([atom.bonds[key] for key in atom.bonds.keys() if key.is_non_hydrogen()])
    bonds = sorted(bonds, key=lambda x: (x.atom1.id, x.atom2.id))

    # Identify exocyclic bonds, and save their bond orders
    exo = []
    for bond in bonds:
        if bond.atom1 not in atoms or bond.atom2 not in atoms:
            if bond.is_double():
                exo.append(1)
            else:
                exo.append(0)
        else:
            exo.append(None)

    # Dimensions
    l = len(aromatic_rings)
    m = len(atoms)
    n = l + len(bonds)

    # Connectivity matrix which indicates which rings and bonds each atom is in
    # Part of equality constraint Ax=b
    a = []
    for atom in atoms:
        in_ring = [1 if atom in ring else 0 for ring in aromatic_rings]
        in_bond = [1 if atom in [bond.atom1, bond.atom2] else 0 for bond in bonds]
        a.append(in_ring + in_bond)

    # Objective vector for optimization: sextets have a weight of 1, double bonds have a weight of 0
    objective = [1] * l + [0] * len(bonds)

    # Solve LP problem using lpsolve
    lp = lpsolve('make_lp', m, n)               # initialize lp with constraint matrix with m rows and n columns
    lpsolve('set_verbose', lp, 2)               # reduce messages from lpsolve
    lpsolve('set_obj_fn', lp, objective)        # set objective function
    lpsolve('set_maxim', lp)                    # set solver to maximize objective
    lpsolve('set_mat', lp, a)                   # set left hand side to constraint matrix
    lpsolve('set_rh_vec', lp, [1] * m)          # set right hand side to 1 for all constraints
    for i in range(m):                          # set all constraints as equality constraints
        lpsolve('set_constr_type', lp, i + 1, '=')
    lpsolve('set_binary', lp, [True] * n)       # set all variables to be binary

    # Constrain values of exocyclic bonds, since we don't want to modify them
    for i in range(l, n):
        if exo[i - l] is not None:
            # NOTE: lpsolve indexes from 1, so the variable we're changing should be i + 1
            lpsolve('set_bounds', lp, i + 1, exo[i - l], exo[i - l])

    # Add constraints to problem if provided
    if constraints is not None:
        for constraint in constraints:
            try:
                lpsolve('add_constraint', lp, constraint[0], '<=', constraint[1])
            except Exception as e:
                logging.debug('Unable to add constraint: {0} <= {1}'.format(constraint[0], constraint[1]))
                logging.debug(mol.to_adjacency_list())
                if str(e) == 'invalid vector.':
                    raise ILPSolutionError('Unable to add constraint, likely due to '
                                           'inconsistent aromatic ring perception.')
                else:
                    raise

    status = lpsolve('solve', lp)
    obj_val, solution = lpsolve('get_solution', lp)[0:2]
    lpsolve('delete_lp', lp)  # Delete the LP problem to clear up memory

    # Check that optimization was successful
    if status != 0:
        raise ILPSolutionError('Optimization could not find a valid solution.')

    # Check that we the result contains at least one aromatic sextet
    if obj_val == 0:
        return []

    # Check that the solution contains the maximum number of sextets possible
    if max_num is None:
        max_num = obj_val  # This is the first solution, so the result should be an upper limit
    elif obj_val < max_num:
        raise ILPSolutionError('Optimization obtained a sub-optimal solution.')

    if any([x != 1 and x != 0 for x in solution]):
        raise ILPSolutionError('Optimization obtained a non-integer solution.')

    # Generate constraints based on the solution obtained
    y = solution[0:l]
    new_a = y + [0] * len(bonds)
    new_b = sum(y) - 1
    if constraints is not None:
        constraints.append((new_a, new_b))
    else:
        constraints = [(new_a, new_b)]

    # Run optimization with additional constraints
    try:
        inner_solutions = _clar_optimization(mol, constraints=constraints, max_num=max_num)
    except ILPSolutionError:
        inner_solutions = []

    return inner_solutions + [(molecule, aromatic_rings, bonds, solution)]
Exemplo n.º 47
0
Arquivo: core.py Projeto: wlblount/bt
class SecurityBase(Node):

    """
    Security Node. Used to define a security within a tree.
    A Security's has no children. It simply models an asset that can be bought
    or sold.

    Args:
        * name (str): Security name
        * multiplier (float): security multiplier - typically used for
            derivatives.

    Attributes:
        * name (str): Security name
        * parent (Security): Security parent
        * root (Security): Root node of the tree (topmost node)
        * now (datetime): Used when backtesting to store current date
        * stale (bool): Flag used to determine if Security is stale and need
            updating
        * prices (TimeSeries): Security prices.
        * price (float): last price
        * value (float): last value - basically position * price * multiplier
        * weight (float): weight in parent
        * full_name (str): Name including parents' names
        * members (list): Current Security + strategy's children
        * position (float): Current position (quantity).

    """

    _last_pos = cy.declare(cy.double)
    _position = cy.declare(cy.double)
    multiplier = cy.declare(cy.double)
    _prices_set = cy.declare(cy.bint)
    _needupdate = cy.declare(cy.bint)

    @cy.locals(multiplier=cy.double)
    def __init__(self, name, multiplier=1):
        Node.__init__(self, name, parent=None, children=None)
        self._value = 0
        self._price = 0
        self._weight = 0
        self._position = 0
        self.multiplier = multiplier

        # opt
        self._last_pos = 0
        self._issec = True
        self._needupdate = True

    @property
    def price(self):
        """
        Current price.
        """
        # if accessing and stale - update first
        if self._needupdate or self.now != self.parent.now:
            self.update(self.root.now)
        return self._price

    @property
    def prices(self):
        """
        TimeSeries of prices.
        """
        # if accessing and stale - update first
        if self._needupdate or self.now != self.parent.now:
            self.update(self.root.now)
        return self._prices.ix[:self.now]

    @property
    def values(self):
        """
        TimeSeries of values.
        """
        # if accessing and stale - update first
        if self._needupdate or self.now != self.parent.now:
            self.update(self.root.now)
        if self.root.stale:
            self.root.update(self.root.now, None)
        return self._values.ix[:self.now]

    @property
    def position(self):
        """
        Current position
        """
        # no stale check needed
        return self._position

    @property
    def positions(self):
        """
        TimeSeries of positions.
        """
        # if accessing and stale - update first
        if self._needupdate:
            self.update(self.root.now)
        if self.root.stale:
            self.root.update(self.root.now, None)
        return self._positions.ix[:self.now]

    def setup(self, universe):
        """
        Setup Security with universe. Speeds up future runs.

        Args:
            * universe (DataFrame): DataFrame of prices with security's name as
                one of the columns.

        """
        # if we already have all the prices, we will store them to speed up
        # future udpates
        try:
            prices = universe[self.name]
        except KeyError:
            prices = None

        # setup internal data
        if prices is not None:
            self._prices = prices
            self.data = pd.DataFrame(index=universe.index,
                                     columns=['value', 'position'],
                                     data=0.0)
            self._prices_set = True
        else:
            self.data = pd.DataFrame(index=universe.index,
                                     columns=['price', 'value', 'position'])
            self._prices = self.data['price']
            self._prices_set = False

        self._values = self.data['value']
        self._positions = self.data['position']

    @cy.locals(prc=cy.double)
    def update(self, date, data=None, inow=None):
        """
        Update security with a given date and optionally, some data.
        This will update price, value, weight, etc.
        """
        # filter for internal calls when position has not changed - nothing to
        # do. Internal calls (stale root calls) have None data. Also want to
        # make sure date has not changed, because then we do indeed want to
        # update.
        if date == self.now and self._last_pos == self._position:
            return

        if inow is None:
            if date == 0:
                inow = 0
            else:
                inow = self.data.index.get_loc(date)

        # date change - update price
        if date != self.now:
            # update now
            self.now = date

            if self._prices_set:
                self._price = self._prices.values[inow]
            # traditional data update
            elif data is not None:
                prc = data[self.name]
                self._price = prc
                self._prices.values[inow] = prc

        self._positions.values[inow] = self._position
        self._last_pos = self._position

        self._value = self._position * self._price * self.multiplier
        self._values.values[inow] = self._value

        if self._weight == 0 and self._position == 0:
            self._needupdate = False

    @cy.locals(amount=cy.double, update=cy.bint, q=cy.double, outlay=cy.double)
    def allocate(self, amount, update=True):
        """
        This allocates capital to the Security. This is the method used to
        buy/sell the security.

        A given amount of shares will be determined on the current price, a
        commisison will be calculated based on the parent's commission fn, and
        any remaining capital will be passed back up  to parent as an
        adjustment.

        Args:
            * amount (float): Amount of adjustment.
            * update (bool): Force update?

        """

        # will need to update if this has been idle for a while...
        # update if needupdate or if now is stale
        # fetch parent's now since our now is stale
        if self._needupdate or self.now != self.parent.now:
            self.update(self.parent.now)

        # ignore 0 alloc
        # Note that if the price of security has dropped to zero, then it should
        # never be selected by SelectAll, SelectN etc. I.e. we should not open
        # the position at zero price. At the same time, we are able to close
        # it at zero price, because at that point amount=0.
        # Note also that we don't erase the position in an asset which price has
        # dropped to zero (though the weight will indeed be = 0)
        if amount == 0:
            return

        if self.parent is self or self.parent is None:
            raise Exception(
                'Cannot allocate capital to a parentless security')

        if self._price == 0 or np.isnan(self._price):
            raise Exception(
                'Cannot allocate capital to '
                '%s because price is %s as of %s'
                % (self.name, self._price, self.parent.now))

        # buy/sell
        # determine quantity - must also factor in commission
        # closing out?
        if amount == -self._value:
            q = -self._position
        else:
            q = amount / (self._price * self.multiplier)
            if self.integer_positions:
                if (self._position > 0) or ((self._position == 0) and (amount > 0)):
                    # if we're going long or changing long position
                    q = math.floor(q)
                else:
                    # if we're going short or changing short position
                    q = math.ceil(q)

        # if q is 0 nothing to do
        if q == 0 or np.isnan(q):
            return

        # this security will need an update, even if pos is 0 (for example if
        # we close the positions, value and pos is 0, but still need to do that
        # last update)
        self._needupdate = True

        # adjust position & value
        self._position += q

        # calculate proper adjustment for parent
        # parent passed down amount so we want to pass
        # -outlay back up to parent to adjust for capital
        # used
        outlay, fee = self.outlay(q)

        # call parent
        self.parent.adjust(-outlay, update=update, flow=False, fee=fee)

    @cy.locals(q=cy.double, p=cy.double)
    def commission(self, q, p):
        """
        Calculates the commission (transaction fee) based on quantity and price.
        Uses the parent's commission_fn.

        Args:
            * q (float): quantity
            * p (float): price

        """
        return self.parent.commission_fn(q, p)

    @cy.locals(q=cy.double)
    def outlay(self, q):
        """
        Determines the complete cash outlay (including commission) necessary
        given a quantity q.
        Second returning parameter is a commission itself.

        Args:
            * q (float): quantity

        """
        fee = self.commission(q, self._price * self.multiplier)
        full_outlay = q * self._price * self.multiplier + fee
        return full_outlay, fee

    def run(self):
        """
        Does nothing - securities have nothing to do on run.
        """
        pass
Exemplo n.º 48
0
def generate_optimal_aromatic_resonance_structures(mol, features=None):
    """
    Generate the aromatic form of the molecule. For radicals, generates the form with the most aromatic rings.

    Returns result as a list.
    In most cases, only one structure will be returned.
    In certain cases where multiple forms have the same number of aromatic rings, multiple structures will be returned.
    If there's an error (eg. in RDKit) it just returns an empty list.
    """
    cython.declare(molecule=Molecule, rings=list, aromaticBonds=list, kekuleList=list, maxNum=cython.int, mol_list=list,
                   new_mol_list=list, ring=list, bond=Bond, order=float, originalBonds=list, originalOrder=list,
                   i=cython.int, counter=cython.int)

    if features is None:
        features = analyze_molecule(mol)

    if not features['is_cyclic']:
        return []

    # Copy the molecule so we don't affect the original
    molecule = mol.copy(deep=True)

    # Attempt to rearrange electrons to obtain a structure with the most aromatic rings
    # Possible rearrangements include aryne resonance and allyl resonance
    res_list = [generate_aryne_resonance_structures]
    if features['is_radical'] and not features['is_aryl_radical']:
        res_list.append(generate_allyl_delocalization_resonance_structures)

    if molecule.is_aromatic():
        kekule_list = generate_kekule_structure(molecule)
    else:
        kekule_list = [molecule]

    _generate_resonance_structures(kekule_list, res_list)

    # Sort all of the generated structures by number of perceived aromatic rings
    mol_dict = {}
    for mol0 in kekule_list:
        aromatic_bonds = mol0.get_aromatic_rings()[1]
        num_aromatic = len(aromatic_bonds)
        mol_dict.setdefault(num_aromatic, []).append((mol0, aromatic_bonds))

    # List of potential number of aromatic rings, sorted from largest to smallest
    arom_options = sorted(mol_dict.keys(), reverse=True)

    new_mol_list = []
    for num in arom_options:
        mol_list = mol_dict[num]
        # Generate the aromatic resonance structure(s)
        for mol0, aromatic_bonds in mol_list:
            # Aromatize the molecule in place
            result = generate_aromatic_resonance_structure(mol0, aromatic_bonds, copy=False)
            if not result:
                # We failed to aromatize this molecule
                # This could be due to incorrect aromaticity perception by RDKit
                continue

            for mol1 in new_mol_list:
                if mol1.is_isomorphic(mol0):
                    break
            else:
                new_mol_list.append(mol0)

        if new_mol_list:
            # We found the most aromatic resonance structures so there's no need to try smaller numbers
            break

    return new_mol_list
Exemplo n.º 49
0
import cython
cython.declare(PyrexTypes=object,
               Naming=object,
               ExprNodes=object,
               Nodes=object,
               Options=object,
               UtilNodes=object,
               ModuleNode=object,
               LetNode=object,
               LetRefNode=object,
               TreeFragment=object,
               TemplateTransform=object,
               EncodedString=object,
               error=object,
               warning=object,
               copy=object)

from . import Builtin
from . import ExprNodes
from . import Nodes
from .PyrexTypes import py_object_type, unspecified_type

from .Visitor import TreeVisitor, CythonTransform
from .Errors import error, warning, CompileError, InternalError

from cython import set


class TypedExprNode(ExprNodes.ExprNode):
    # Used for declaring assignments of a specified type whithout a known entry.
    def __init__(self, type):
Exemplo n.º 50
0
def generate_resonance_structures(mol, clar_structures=True, keep_isomorphic=False, filter_structures=True):
    """
    Generate and return all of the resonance structures for the input molecule.

    Most of the complexity of this method goes into handling aromatic species, particularly to generate an accurate
    set of resonance structures that is consistent regardless of the input structure. The following considerations
    are made:

    1. False positives from RDKit aromaticity detection can occur if a molecule has exocyclic double bonds
    2. False negatives from RDKit aromaticity detection can occur if a radical is delocalized into an aromatic ring
    3. sp2 hybridized radicals in the plane of an aromatic ring do not participate in hyperconjugation
    4. Non-aromatic resonance structures of PAHs are not important resonance contributors (assumption)

    Aromatic species are broken into the following categories for resonance treatment:

    - Radical polycyclic aromatic species: Kekule structures are generated in order to generate adjacent resonance
      structures. The resulting structures are then used for Clar structure generation. After all three steps, any
      non-aromatic structures are removed, under the assumption that they are not important resonance contributors.
    - Radical monocyclic aromatic species: Kekule structures are generated along with adjacent resonance structures.
      All are kept regardless of aromaticity because the radical is more likely to delocalize into the ring.
    - Stable polycyclic aromatic species: Clar structures are generated
    - Stable monocyclic aromatic species: Kekule structures are generated
    """
    cython.declare(mol_list=list, new_mol_list=list, features=dict, method_list=list)

    # Check that mol is a valid structure in terms of atomTypes and net charge. Since SMILES with hypervalance
    # heteroatoms are not always read correctly, print a suggestion to input the structure using an adjList.
    try:
        mol.update()
    except AtomTypeError:
        logging.error("The following molecule has at least one atom with an undefined atomtype:\n{0}"
                      "\nIf this structure was entered in SMILES, try using the adjacencyList format for an unambiguous"
                      " definition.".format(mol.to_adjacency_list()))
        raise
    if mol.get_net_charge() != 0:
        raise ValueError("Got the following structure:\nSMILES: {0}\nAdjacencyList:\n{1}\nNet charge: {2}\n\n"
                         "Currently RMG cannot process charged species correctly."
                         "\nIf this structure was entered in SMILES, try using the adjacencyList format for an"
                         " unambiguous definition.".format(mol.to_smiles(), mol.to_adjacency_list(), mol.get_net_charge()))

    if not mol.reactive:
        raise ResonanceError('Can only generate resonance structures for reactive molecules! Got the following '
                             'unreactive structure:\n{0}Reactive = {1}'.format(mol.to_adjacency_list(), mol.reactive))

    mol_list = [mol]

    # Analyze molecule
    features = analyze_molecule(mol)

    # Use generate_optimal_aromatic_resonance_structures to check for false positives and negatives
    if features['is_aromatic'] or (features['is_cyclic'] and features['is_radical'] and not features['is_aryl_radical']):
        new_mol_list = generate_optimal_aromatic_resonance_structures(mol, features)
        if len(new_mol_list) == 0:
            # Encountered false positive, ie. the molecule is not actually aromatic
            features['is_aromatic'] = False
            features['isPolycyclicAromatic'] = False
        else:
            features['is_aromatic'] = True
            if len(new_mol_list[0].get_aromatic_rings()[0]) > 1:
                features['isPolycyclicAromatic'] = True
            for new_mol in new_mol_list:
                # Append to structure list if unique
                if not keep_isomorphic and mol.is_isomorphic(new_mol):
                    continue
                elif keep_isomorphic and mol.is_identical(new_mol):
                    continue
                else:
                    mol_list.append(new_mol)

    # Special handling for aromatic species
    if features['is_aromatic']:
        if features['is_radical'] and not features['is_aryl_radical']:
            _generate_resonance_structures(mol_list, [generate_kekule_structure],
                                           keep_isomorphic=keep_isomorphic, filter_structures=filter_structures)
            _generate_resonance_structures(mol_list, [generate_allyl_delocalization_resonance_structures],
                                           keep_isomorphic=keep_isomorphic, filter_structures=filter_structures)
        if features['isPolycyclicAromatic'] and clar_structures:
            _generate_resonance_structures(mol_list, [generate_clar_structures],
                                           keep_isomorphic=keep_isomorphic, filter_structures=filter_structures)
        else:
            _generate_resonance_structures(mol_list, [generate_aromatic_resonance_structure],
                                           keep_isomorphic=keep_isomorphic, filter_structures=filter_structures)

    # Generate remaining resonance structures
    method_list = populate_resonance_algorithms(features)
    _generate_resonance_structures(mol_list, method_list, keep_isomorphic=keep_isomorphic,
                                   filter_structures=filter_structures)

    if filter_structures:
        return filtration.filter_structures(mol_list, features=features)

    return mol_list
Exemplo n.º 51
0
# cython: infer_types=True, language_level=3, py2_import=True
#
#   Cython Scanner
#

from __future__ import absolute_import

import os
import platform

import cython
cython.declare(EncodedString=object, any_string_prefix=unicode, IDENT=unicode,
               print_function=object, error=object, warning=object)

from .. import Utils
from ..Plex.Scanners import Scanner
from ..Plex.Errors import UnrecognizedInput
from .Errors import error, warning
from .Lexicon import any_string_prefix, make_lexicon, IDENT
from .Future import print_function

from .StringEncoding import EncodedString

debug_scanner = 0
trace_scanner = 0
scanner_debug_flags = 0
scanner_dump_file = None

lexicon = None

def get_lexicon():
Exemplo n.º 52
0
class GroundTruthRetriever(BuildDirector):
    prodBuilder = declare(ProductBuilder)

    def __init__(self):

        super(GroundTruthRetriever, self).__init__()
        self.prodBuilder: ProductBuilder = ProductBuilder()
        self.__loadBuildDirector()

    def __loadBuildDirector(self) -> None:
        self.builder = self.prodBuilder

    @locals(frameArr=ndarray)
    def __collisionRetrievalOnFrame(self, frameArr: ndarray) -> ndarray:

        return where(frameArr != 0.)  # collect index values bigger than 0.

    @locals(data=char, trialSize=cint)
    def groundTruthRetrievalOnTrial(self,
                                    dataName: str,
                                    direction: str,
                                    trialSize: int = 10) -> Dict[str, int]:

        data: Generator = self.__arrayDataGenerator(dataName,
                                                    direction=direction,
                                                    stringArr=False)
        dataArr: ndarray = None
        #next(data)

        counterF: int = 1
        counterT: int = 1
        currRow: int = None
        finalArr: Dict[str, int] = dict()
        #for row in range(self.__dataLength):
        while True:
            try:
                dataArr = next(data)
                currRow = 1 if dataArr[self.__collisionRetrievalOnFrame(
                    dataArr)].size > 0 else 0
                #########
                finalArr["frame_" + str(counterF) + ', trial_' +
                         str(counterT)] = currRow
                counterF += 1
            except StopIteration as s:
                print(s)
                break
            if (counterF >= trialSize + 1):
                counterF = 1
                counterT += 1
        return finalArr

    @locals(dataName=char)
    def __arrayDataGenerator(self,
                             dataName,
                             direction: str,
                             stringArr: bool = True) -> Generator:

        data: ndarray = self.buildCoordinateData(dataName, direction=direction)
        self.__dataLength: int = len(data)
        if (stringArr):
            return data
        else:
            currArr: ndarray = None
            row: str = None

            dataArr: List[ndarray] = list()
            for row in range(len(data)):
                yield self.__deleteFirstElement(
                    self.__numpyStringToFloat(data[row]))
                # currArr = self.__deleteFirstElement(self.__numpyStringToFloat(data[row]));
                #dataArr.append(currArr)
        # return asarray(dataArr);

    @staticmethod
    @locals(oneDimArray=ndarray)
    def __numpyStringToFloat(oneDimArray: ndarray) -> ndarray:
        elem: str = None
        num: str = None
        floatingArr: ndarray = None
        try:
            return oneDimArray.astype(float32)
        except ValueError as e:
            try:
                for elem in range(len(oneDimArray)):
                    floatingArr = asarray(
                        [float(num) for num in oneDimArray[elem].split(",")],
                        dtype=float32)
            except Exception as e:
                print(e)

        return floatingArr

    @staticmethod
    @locals(oneDimArray=ndarray)
    def __deleteFirstElement(oneDimArray: ndarray) -> ndarray:
        return delete(oneDimArray, 0, None)

    @staticmethod
    @locals(dictData=Dict)
    def sumValuesDict(dictData: Dict) -> str:
        noncollision: int = sum(value == 0 for value in dictData.values())
        collision: int = sum(value == 1 for value in dictData.values())
        return "no-collisions: {} | collisions: {}".format(
            noncollision, collision)

    @staticmethod
    @locals(dictData=Dict)
    def sumValuesDictPerTrial(dictData: Dict[str, int]) -> str:

        currKey: str = None
        previousKey: str = None
        collisions: int = 0
        noncollisions: int = 0
        tempList: List[int] = list()
        for key, value in dictData.items():
            keys: List[str] = key.split(',')
            currKey = keys[1]
            if (currKey == previousKey):
                tempList.append(value)

            else:
                previousKey = currKey
                if (len(tempList) != 0):
                    #collisions += 1 if 1 in tempList else 0;
                    collisions += tempList.count(1)
                    noncollisions += 1 if not 1 in tempList else 0
                tempList.clear()
                tempList.append(value)

        return 'number of frames {} | number of trials {} | non-collisions:{} | collisions: {}'.format(
            len(dictData),
            len(dictData) // 10, noncollisions, collisions)

    @staticmethod
    def cythonFunction(dictData: Dict[str, int]) -> str:
        return counter.getNonCollisionsOnly(dictData)

    @staticmethod
    @locals(dictData=array)
    def getKeysDictAccordingCollision(dictData: Dict,
                                      collision: bool = True) -> List[str]:
        if (collision):
            return [key for key, value in dictData.items() if value == 1]
        elif (collision is False):
            return [key for key, value in dictData.items() if value == 0]
Exemplo n.º 53
0
import cython

cython.declare(x=cython.int, y=cython.double)  # cdef int x; cdef double y
Exemplo n.º 54
0
def find_lowest_u_layer(mol, u_layer, equivalent_atoms):
    """
    Searches for the "minimum" combination of indices of atoms that bear unpaired electrons.

    It does so by using the information on equivalent atoms to permute equivalent atoms to 
    obtain a combination of atoms that is the (numerically) lowest possible combination.

    Each possible combination is valid if and only if the distances between the atoms of the
    combination is identical to the distances between the original combination.

    First, the algorithm partitions equivalent atoms that bear an unpaired electron.
    Next, the combinations are generated, and for each combination it is verified whether
    it pertains to a "valid" combination.

    Returns a list of indices corresponding to the lowest combination of atom indices bearing
    unpaired electrons.
    """

    cython.declare(
        new_u_layer=list,
        grouped_electrons=list,
        corresponding_E_layers=list,
        group=list,
        e_layer=list,
        combos=list,
        orig_agglomerates=list,
        orig_distances=list,
        selected_group=list,
        combo=list,
    )
    if not equivalent_atoms:
        return u_layer

    new_u_layer = []

    grouped_electrons, corresponding_E_layers = partition(
        u_layer, equivalent_atoms)

    # don't process atoms that do not belong to an equivalence layer
    for group, e_layer in zip(grouped_electrons[:], corresponding_E_layers[:]):
        if not e_layer:
            new_u_layer.extend(group)
            grouped_electrons.remove(group)
            corresponding_E_layers.remove(e_layer)

    combos = generate_combo(grouped_electrons, corresponding_E_layers)
    # compute original distance:
    orig_agglomerates = agglomerate(grouped_electrons)
    orig_distances = compute_agglomerate_distance(orig_agglomerates, mol)

    # deflate the list of lists to be able to numerically compare them
    selected_group = sorted(itertools.chain.from_iterable(grouped_electrons))

    # see if any of the combos is valid and results in a lower numerical combination than the original
    for combo in combos:
        if is_valid_combo(combo, mol, orig_distances):
            combo = sorted(itertools.chain.from_iterable(combo))
            if combo < selected_group:
                selected_group = combo

    # add the minimized unpaired electron positions to the u-layer:
    new_u_layer.extend(selected_group)

    return sorted(new_u_layer)
Exemplo n.º 55
0
from __future__ import absolute_import, print_function

import sys
import inspect

from . import TypeSlots
from . import Builtin
from . import Nodes
from . import ExprNodes
from . import Errors
from . import DebugFlags
from . import Future

import cython

cython.declare(_PRINTABLE=tuple)

if sys.version_info[0] >= 3:
    _PRINTABLE = (bytes, str, int, float)
else:
    _PRINTABLE = (str, unicode, long, int, float)


class TreeVisitor(object):
    """
    Base class for writing visitors for a Cython tree, contains utilities for
    recursing such trees using visitors. Each node is
    expected to have a child_attrs iterable containing the names of attributes
    containing child nodes or lists of child nodes. Lists are not considered
    part of the tree structure (i.e. contained nodes are considered direct
    children of the parent node).
Exemplo n.º 56
0
class SimpleIOPayload(object):
    """ Represents a payload, i.e. individual response elements, set via SimpleIO.
    """
    sio = cy.declare(cy.object, visibility='public')  # type: CySimpleIO
    all_output_elem_names = cy.declare(list, visibility='public')  # type: list
    output_repeated = cy.declare(cy.bint, visibility='public')  # type: bool

    cid = cy.declare(cy.object, visibility='public')  # type: past_unicode
    data_format = cy.declare(cy.object,
                             visibility='public')  # type: past_unicode

    # One of the two will be used to produce a response
    user_attrs_dict = cy.declare(dict, visibility='public')  # type: dict
    user_attrs_list = cy.declare(list, visibility='public')  # type: list

    # This is used by Zato internal services only
    zato_meta = cy.declare(cy.object, visibility='public')  # type: object

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

    def __cinit__(self, sio: CySimpleIO, all_output_elem_names: list, cid,
                  data_format):
        self.sio = sio
        self.all_output_elem_names = all_output_elem_names
        self.output_repeated = self.sio.definition.output_repeated
        self.cid = cid
        self.data_format = data_format
        self.user_attrs_dict = {}
        self.user_attrs_list = []
        self.zato_meta = None

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

    def __iter__(self):
        return iter(self.user_attrs_list if self.output_repeated else self.
                    user_attrs_dict)

    def __setitem__(self, key, value):

        if isinstance(key, slice):
            self.user_attrs_list[key.start:key.stop] = value
            self.output_repeated = True
        else:
            setattr(self, key, value)

    def __setattr__(self, key, value):

        # Special-case Zato's own internal attributes
        if key == 'zato_meta':
            self.zato_meta = value
        else:
            self.user_attrs_dict[key] = value

    def __getattr__(self, key):
        try:
            return self.user_attrs_dict[key]
        except KeyError:
            raise KeyError('{}; No such key `{}` among `{}` ({})'.format(
                self.sio.service_class, key, self.user_attrs_dict,
                hex(id(self))))

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

    @cy.returns(bool)
    def has_data(self):
        return bool(self.user_attrs_dict or self.user_attrs_list)

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

    @cy.returns(dict)
    def _extract_payload_attrs(self, item: object) -> dict:
        """ Extract response attributes from a single object. Used with items other than dicts.
        """
        extracted: dict = {}
        is_dict: cy.bint = isinstance(item, dict)

        # Use a different function depending on whether the object is dict-like or not.
        # Note that we need .get to be able to provide a default value.
        has_get = hasattr(item, 'get')  # type: bool
        name = None

        for name in self.all_output_elem_names:  # type: str
            if is_dict:
                value = cy.cast(dict, item).get(name, _not_given)
            elif has_get:
                value = item.get(name, _not_given)
            else:
                value = getattr(item, name, _not_given)
            if value is not _not_given:
                extracted[name] = value

        return extracted

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

    @cy.returns(dict)
    def _extract_payload_attrs_dict(self, item: object) -> dict:
        """ Extract response attributes from a dict.
        """
        extracted: dict = {}
        name = None

        for name in self.all_output_elem_names:
            value = item.get(name, _not_given)
            if value is not _not_given:
                extracted[name] = value

        return extracted

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

    @cy.cfunc
    def _is_sqlalchemy(self, item: object):
        return hasattr(item, '_sa_class_manager')

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

    @cy.cfunc
    def _preprocess_payload_attrs(self, value):

        # First, check if this is not a response from a Zato service wrapped in a response element.
        # If it is, extract the actual inner response first.
        if isinstance(value, dict) and len(value) == 1:
            response_name: str = list(value.keys())[0]  # type: str
            if response_name.startswith('zato') and response_name.endswith(
                    '_response'):
                return value[response_name]
        else:
            return value

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

    @cy.ccall
    def set_payload_attrs(self, value: object):
        """ Assigns user-defined attributes to what will eventually be a response.
        """
        value = self._preprocess_payload_attrs(value)
        is_dict: cy.bint = isinstance(value, dict)

        # Shortcut in case we know already this is a dict on input
        if is_dict:
            dict_attrs: dict = self._extract_payload_attrs_dict(value)
            self.user_attrs_dict.update(dict_attrs)
        else:
            # Check if this is something that can be explicitly serialised for our purposes
            if hasattr(value, 'to_zato'):
                value = value.to_zato()

            if isinstance(value, (list, tuple)):
                for item in value:
                    self.user_attrs_list.append(
                        self._extract_payload_attrs(item))
            else:
                self.user_attrs_dict.update(self._extract_payload_attrs(value))

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

    @cy.ccall
    def getvalue(self,
                 serialize: bool = True,
                 force_dict_serialisation: bool = True):
        """ Returns a service's payload, either serialised or not.
        """
        if self.data_format == DATA_FORMAT_DICT:
            if force_dict_serialisation:
                serialize = True

        # If data format is DICT, we force serialisation to that format
        # unless overridden on input.
        value = self.user_attrs_list if self.output_repeated else self.user_attrs_dict

        # Special-case internal services that return metadata (e.g GetList-like ones)
        if self.zato_meta:

            # If search is provided, we need to first get output,
            # append the search the metadata and then serialise ..
            search = self.zato_meta.get('search')
            if search:
                output = self.sio.get_output(value, self.data_format, False)
                output['_meta'] = search
                return self.sio.serialise(output, self.data_format)

            # .. otherwise, with no search metadata provided,
            # we can data, serialised or not, immediately.
            return self.sio.get_output(
                value, self.data_format) if serialize else value

        else:
            out = self.sio.get_output(value,
                                      self.data_format) if serialize else value
            return out

    def append(self, value):
        self.user_attrs_list.append(value)
        self.output_repeated = True
Exemplo n.º 57
0
def test_declare_c_types(n):
    """
    >>> test_declare_c_types(0)
    >>> test_declare_c_types(1)
    >>> test_declare_c_types(2)
    """
    #
    b00 = cython.declare(cython.bint, 0)
    b01 = cython.declare(cython.bint, 1)
    b02 = cython.declare(cython.bint, 2)
    #
    i00 = cython.declare(cython.uchar, n)
    i01 = cython.declare(cython.char, n)
    i02 = cython.declare(cython.schar, n)
    i03 = cython.declare(cython.ushort, n)
    i04 = cython.declare(cython.short, n)
    i05 = cython.declare(cython.sshort, n)
    i06 = cython.declare(cython.uint, n)
    i07 = cython.declare(cython.int, n)
    i08 = cython.declare(cython.sint, n)
    i09 = cython.declare(cython.slong, n)
    i10 = cython.declare(cython.long, n)
    i11 = cython.declare(cython.ulong, n)
    i12 = cython.declare(cython.slonglong, n)
    i13 = cython.declare(cython.longlong, n)
    i14 = cython.declare(cython.ulonglong, n)

    i20 = cython.declare(cython.Py_ssize_t, n)
    i21 = cython.declare(cython.size_t, n)
    #
    f00 = cython.declare(cython.float, n)
    f01 = cython.declare(cython.double, n)
    f02 = cython.declare(cython.longdouble, n)
class TrialRetriever:

    builDirector = cython.declare(BuildDirector)
    csvBuilder = cython.declare(ProductBuilder)

    def __init__(self):
        self._buildDirector = BuildDirector()
        self._dataBuilder = ProductBuilder()
        self.__loadBuilDirector()

    def __loadBuilDirector(self) -> None:
        self._buildDirector.builder = self._dataBuilder

    @cython.locals(dataName=cython.char, trialSize=cython.int)
    def callImgDataArr(self,
                       dataName: str,
                       trialSize: int = 15) -> Dict[str, ndarray]:
        data: List[ndarray] = None
        if (dataName == 'binocular_img'):
            data = self._buildDirector.buildImgArray(dataName)
        elif (dataName == 'scene_img'):
            data = self._buildDirector.buildImgArray(dataName)
        else:
            print('binocular_img or scene_img')
            raise AssertionError("this data names are not available")
        counterF: int = 1
        counterT: int = 1
        imgArrayDict: Dict[str, ndarray] = dict()
        currArr: ndarray = None
        for arr in data:
            for imgArr in arr:
                imgArrayDict["frame_" + str(counterF) + ', trial_' +
                             str(counterT)] = imgArr
                counterF += 1
                if (counterF >= trialSize + 1):
                    counterF = 1
                    counterT += 1
        return imgArrayDict

    @staticmethod
    @cython.locals(data=List[ndarray])
    def __genImgArray(data: List[ndarray]) -> Generator:
        for arr in data:
            for imgArr in arr:
                yield imgArr

    @cython.locals(dataName=cython.char)
    def callTrialDataArr(self,
                         dataName: str,
                         stringArr: bool = True) -> ndarray:
        data: ndarray = self._buildDirector.buildCoordinateData(dataName)
        if (stringArr):
            return data
        else:
            currArr: ndarray = None
            row: str = None

            dataArr: List[ndarray] = list()
            for row in range(len(data)):
                currArr = self.__deleteFirstElement(
                    self.__numpyStringToFloat(data[row]))
                dataArr.append(currArr)
            return asarray(dataArr)

    @cython.locals(data=cython.array, trialSize=cython.int)
    def callTrialDataArrAsDict(self,
                               dataName: str,
                               trialSize: int = 10) -> Dict[str, ndarray]:
        data: ndarray = self._buildDirector.buildCoordinateData(dataName)
        counterF: int = 1
        counterT: int = 1
        currRow: ndarray = None
        finalArr: Dict[str, ndarray] = dict()
        for row in range(len(data)):
            #currRow: ndarray = insert(data[row], 0, counter, axis=0);
            #finalArr.append(currRow);
            currRow = self.__deleteFirstElement(
                self.__numpyStringToFloat(data[row]))
            finalArr["frame_" + str(counterF) + ', trial_' +
                     str(counterT)] = currRow
            counterF += 1
            if (counterF >= trialSize + 1):
                counterF = 1
                counterT += 1

        return finalArr

    @staticmethod
    @cython.locals(oneDimArray=ndarray)
    def __numpyStringToFloat(oneDimArray: ndarray) -> ndarray:
        elem: str = None
        num: str = None
        floatingArr: ndarray = None
        try:
            return oneDimArray.astype(Float)
        except ValueError as e:
            try:
                for elem in range(len(oneDimArray)):
                    floatingArr = asarray(
                        [float(num) for num in oneDimArray[elem].split(",")],
                        dtype=float32)
            except Exception as e:
                print(e)

        return floatingArr

    @staticmethod
    @cython.locals(oneDimArray=ndarray)
    def __deleteFirstElement(oneDimArray: ndarray) -> ndarray:
        return delete(oneDimArray, 0, None)
Exemplo n.º 59
0
from __future__ import absolute_import

import cython

cython.declare(PyrexTypes=object,
               ExprNodes=object,
               Nodes=object,
               Builtin=object,
               InternalError=object,
               error=object,
               warning=object,
               py_object_type=object,
               unspecified_type=object,
               object_expr=object,
               fake_rhs_expr=object,
               TypedExprNode=object)

from . import Builtin
from . import ExprNodes
from . import Nodes
from . import Options
from .PyrexTypes import py_object_type, unspecified_type
from . import PyrexTypes

from .Visitor import TreeVisitor, CythonTransform
from .Errors import error, warning, InternalError
from .Optimize import ConstantFolding


class TypedExprNode(ExprNodes.ExprNode):
    # Used for declaring assignments of a specified type without a known entry.
Exemplo n.º 60
0
def generateClarStructures(mol):
    """
    Generate Clar structures for a given molecule.

    Returns a list of :class:`Molecule` objects corresponding to the Clar structures.
    """
    cython.declare(output=list,
                   molList=list,
                   newmol=Molecule,
                   aromaticRings=list,
                   bonds=list,
                   solution=list,
                   y=list,
                   x=list,
                   index=cython.int,
                   bond=Bond,
                   ring=list)

    if not mol.isCyclic():
        return []

    try:
        output = _clarOptimization(mol)
    except ILPSolutionError:
        # The optimization algorithm did not work on the first iteration
        return []

    molList = []

    for newmol, aromaticRings, bonds, solution in output:

        # The solution includes a part corresponding to rings, y, and a part corresponding to bonds, x, using
        # nomenclature from the paper. In y, 1 means the ring as a sextet, 0 means it does not.
        # In x, 1 corresponds to a double bond, 0 either means a single bond or the bond is part of a sextet.
        y = solution[0:len(aromaticRings)]
        x = solution[len(aromaticRings):]

        # Apply results to molecule - double bond locations first
        for index, bond in enumerate(bonds):
            if x[index] == 0:
                bond.order = 1  # single
            elif x[index] == 1:
                bond.order = 2  # double
            else:
                raise ValueError(
                    'Unaccepted bond value {0} obtained from optimization.'.
                    format(x[index]))

        # Then apply locations of aromatic sextets by converting to benzene bonds
        for index, ring in enumerate(aromaticRings):
            if y[index] == 1:
                _clarTransformation(newmol, ring)

        try:
            newmol.updateAtomTypes()
        except AtomTypeError:
            pass
        else:
            molList.append(newmol)

    return molList