B
    ž3Rcˆ5  ã               @   sª   d dl Z d dlmZ d dlmZ G dd„ deƒZG dd„ deƒZG dd	„ d	eƒZG d
d„ deƒZ	dd„ Z
G dd„ deƒZG dd„ deƒZeƒ fdd„Zeƒ dfdd„ZdS )é    N)ÚChem)ÚEmbedMoleculec               @   s   e Zd ZdZdZd	dd„ZdS )
ÚStereoEnumerationOptionsa7  
          - tryEmbedding: if set the process attempts to generate a standard RDKit distance geometry
            conformation for the stereisomer. If this fails, we assume that the stereoisomer is
            non-physical and don't return it. NOTE that this is computationally expensive and is
            just a heuristic that could result in stereoisomers being lost.

          - onlyUnassigned: if set (the default), stereocenters which have specified stereochemistry
            will not be perturbed unless they are part of a relative stereo
            group.

          - maxIsomers: the maximum number of isomers to yield, if the
            number of possible isomers is greater than maxIsomers, a
            random subset will be yielded. If 0, all isomers are
            yielded. Since every additional stereo center doubles the
            number of results (and execution time) it's important to
            keep an eye on this.

          - onlyStereoGroups: Only find stereoisomers that differ at the
            StereoGroups associated with the molecule.
    )ÚtryEmbeddingÚonlyUnassignedÚonlyStereoGroupsÚ
maxIsomersÚrandÚuniqueFTé   Nc             C   s(   || _ || _|| _|| _|| _|| _d S )N)r   r   r   r   r	   r
   )Úselfr   r   r   r	   r
   r   © r   ú@lib/python3.7/site-packages/rdkit/Chem/EnumerateStereoisomers.pyÚ__init__   s    z!StereoEnumerationOptions.__init__)FTr   NTF)Ú__name__Ú
__module__Ú__qualname__Ú__doc__Ú	__slots__r   r   r   r   r   r      s    r   c               @   s   e Zd Zdd„ Zdd„ ZdS )Ú_BondFlipperc             C   s
   || _ d S )N)Úbond)r   r   r   r   r   r   )   s    z_BondFlipper.__init__c             C   s*   |r| j  tjj¡ n| j  tjj¡ d S )N)r   Z	SetStereor   Ú
BondStereoZ	STEREOCISZSTEREOTRANS)r   Úflagr   r   r   Úflip,   s    z_BondFlipper.flipN)r   r   r   r   r   r   r   r   r   r   '   s   r   c               @   s   e Zd Zdd„ Zdd„ ZdS )Ú_AtomFlipperc             C   s
   || _ d S )N)Úatom)r   r   r   r   r   r   5   s    z_AtomFlipper.__init__c             C   s*   |r| j  tjj¡ n| j  tjj¡ d S )N)r   ÚSetChiralTagr   Ú
ChiralTypeÚCHI_TETRAHEDRAL_CWÚCHI_TETRAHEDRAL_CCW)r   r   r   r   r   r   8   s    z_AtomFlipper.flipN)r   r   r   r   r   r   r   r   r   r   3   s   r   c               @   s   e Zd Zdd„ Zdd„ ZdS )Ú_StereoGroupFlipperc             C   s   dd„ |  ¡ D ƒ| _d S )Nc             S   s   g | ]}||  ¡ f‘qS r   )ÚGetChiralTag)Ú.0Úar   r   r   ú
<listcomp>B   s    z0_StereoGroupFlipper.__init__.<locals>.<listcomp>)ÚGetAtomsÚ_original_parities)r   Úgroupr   r   r   r   A   s    z_StereoGroupFlipper.__init__c             C   sr   |r$xh| j D ]\}}| |¡ qW nJxH| j D ]>\}}|tjjkrP| tjj¡ q,|tjjkr,| tjj¡ q,W d S )N)r&   r   r   r   r   r   )r   r   r#   Zoriginal_parityr   r   r   r   D   s    z_StereoGroupFlipper.flipN)r   r   r   r   r   r   r   r   r   r    ?   s   r    c             C   sÔ   t  | ¡ g }|jsšx>|  ¡ D ]2}| d¡r|jrB| ¡ t jjkr| 	t
|ƒ¡ qW xD|  ¡ D ]8}| ¡ }|t jjkr^|jrˆ|t jjkr^| 	t|ƒ¡ q^W |jrÐx.|  ¡ D ]"}| ¡ t jjkrª| 	t|ƒ¡ qªW |S )NZ_ChiralityPossible)r   ZFindPotentialStereoBondsr   r%   ZHasPropr   r!   r   ZCHI_UNSPECIFIEDÚappendr   ÚGetBondsZ	GetStereor   Z
STEREONONEZ	STEREOANYr   ZGetStereoGroupsZGetGroupTypeZStereoGroupTypeZSTEREO_ABSOLUTEr    )ZmolÚoptionsÚflippersr   r   Zbstereor'   r   r   r   Ú_getFlippersP   s"    

r,   c               @   s   e Zd Zdd„ Zdd„ ZdS )Ú_RangeBitsGeneratorc             C   s
   || _ d S )N)ÚnCenters)r   r.   r   r   r   r   j   s    z_RangeBitsGenerator.__init__c             c   s"   xt d| j ƒD ]
}|V  qW d S )Né   )Úranger.   )r   Úvalr   r   r   Ú__iter__m   s    z_RangeBitsGenerator.__iter__N)r   r   r   r   r2   r   r   r   r   r-   h   s   r-   c               @   s   e Zd Zdd„ Zdd„ ZdS )Ú_UniqueRandomBitsGeneratorc             C   s   || _ || _|| _tƒ | _d S )N)r.   r   r	   ÚsetÚalready_seen)r   r.   r   r	   r   r   r   r   t   s    z#_UniqueRandomBitsGenerator.__init__c             c   sJ   xDt | jƒd| j k rD| j | j¡}|| jkr0q| j |¡ |V  qW d S )Nr/   )Úlenr5   r.   r	   ZgetrandbitsÚadd)r   Úbitsr   r   r   r2   z   s    
z#_UniqueRandomBitsGenerator.__iter__N)r   r   r   r   r2   r   r   r   r   r3   r   s   r3   c             C   s    t  | ¡}t||ƒ}dt|ƒ S )a   returns an estimate (upper bound) of the number of possible stereoisomers for a molecule

   Arguments:
      - m: the molecule to work with
      - options: parameters controlling the enumeration


    >>> from rdkit import Chem
    >>> from rdkit.Chem.EnumerateStereoisomers import EnumerateStereoisomers, StereoEnumerationOptions
    >>> m = Chem.MolFromSmiles('BrC(Cl)(F)CCC(O)C')
    >>> GetStereoisomerCount(m)
    4
    >>> m = Chem.MolFromSmiles('CC(Cl)(O)C')
    >>> GetStereoisomerCount(m)
    1

    double bond stereochemistry is also included:

    >>> m = Chem.MolFromSmiles('BrC(Cl)(F)C=CC(O)C')
    >>> GetStereoisomerCount(m)
    8

    r/   )r   ÚMolr,   r6   )Úmr*   Útmr+   r   r   r   ÚGetStereoisomerCount‡   s    

r<   Fc             c   sx  t  | ¡}x| ¡ D ]}| d¡ qW x.| ¡ D ]"}| ¡ t jjkr0| t jj	¡ q0W t
||ƒ}t|ƒ}|sv|V  dS |jdksŽd| |jkr˜t|ƒ}nd|jdkrÌtttdd„ | ¡ D ƒƒƒƒ}	t |	¡}
n"t|jtjƒrâ|j}
nt |j¡}
t||j|
ƒ}tƒ }d}xj|D ]`}x0t|ƒD ]$}t|d|> @ ƒ}||  |¡ qW t  |¡}t  |¡ |jdd	 t j|d
d
d
d |jr¦t j|d
d}||krœq| |¡ |j rt  !|¡}t"||d@ d}|dkrt  #| $¡ ¡}x,t| $¡ ƒD ]}| %|| &¡  '|¡¡ qîW | (|¡ nd}|dkrR|V  |d7 }|jdkrn||jkrnP n|rt)dt j|d
d ƒ qW dS )a)   returns a generator that yields possible stereoisomers for a molecule

    Arguments:
      - m: the molecule to work with
      - options: parameters controlling the enumeration
      - verbose: toggles how verbose the output is

    If m has stereogroups, they will be expanded

    A small example with 3 chiral atoms and 1 chiral bond (16 theoretical stereoisomers):

    >>> from rdkit import Chem
    >>> from rdkit.Chem.EnumerateStereoisomers import EnumerateStereoisomers, StereoEnumerationOptions
    >>> m = Chem.MolFromSmiles('BrC=CC1OC(C2)(F)C2(Cl)C1')
    >>> isomers = tuple(EnumerateStereoisomers(m))
    >>> len(isomers)
    16
    >>> for smi in sorted(Chem.MolToSmiles(x, isomericSmiles=True) for x in isomers):
    ...     print(smi)
    ...
    F[C@@]12C[C@@]1(Cl)C[C@@H](/C=C/Br)O2
    F[C@@]12C[C@@]1(Cl)C[C@@H](/C=C\Br)O2
    F[C@@]12C[C@@]1(Cl)C[C@H](/C=C/Br)O2
    F[C@@]12C[C@@]1(Cl)C[C@H](/C=C\Br)O2
    F[C@@]12C[C@]1(Cl)C[C@@H](/C=C/Br)O2
    F[C@@]12C[C@]1(Cl)C[C@@H](/C=C\Br)O2
    F[C@@]12C[C@]1(Cl)C[C@H](/C=C/Br)O2
    F[C@@]12C[C@]1(Cl)C[C@H](/C=C\Br)O2
    F[C@]12C[C@@]1(Cl)C[C@@H](/C=C/Br)O2
    F[C@]12C[C@@]1(Cl)C[C@@H](/C=C\Br)O2
    F[C@]12C[C@@]1(Cl)C[C@H](/C=C/Br)O2
    F[C@]12C[C@@]1(Cl)C[C@H](/C=C\Br)O2
    F[C@]12C[C@]1(Cl)C[C@@H](/C=C/Br)O2
    F[C@]12C[C@]1(Cl)C[C@@H](/C=C\Br)O2
    F[C@]12C[C@]1(Cl)C[C@H](/C=C/Br)O2
    F[C@]12C[C@]1(Cl)C[C@H](/C=C\Br)O2

    Because the molecule is constrained, not all of those isomers can
    actually exist. We can check that:

    >>> opts = StereoEnumerationOptions(tryEmbedding=True)
    >>> isomers = tuple(EnumerateStereoisomers(m, options=opts))
    >>> len(isomers)
    8
    >>> for smi in sorted(Chem.MolToSmiles(x,isomericSmiles=True) for x in isomers):
    ...     print(smi)
    ...
    F[C@@]12C[C@]1(Cl)C[C@@H](/C=C/Br)O2
    F[C@@]12C[C@]1(Cl)C[C@@H](/C=C\Br)O2
    F[C@@]12C[C@]1(Cl)C[C@H](/C=C/Br)O2
    F[C@@]12C[C@]1(Cl)C[C@H](/C=C\Br)O2
    F[C@]12C[C@@]1(Cl)C[C@@H](/C=C/Br)O2
    F[C@]12C[C@@]1(Cl)C[C@@H](/C=C\Br)O2
    F[C@]12C[C@@]1(Cl)C[C@H](/C=C/Br)O2
    F[C@]12C[C@@]1(Cl)C[C@H](/C=C\Br)O2

    Or we can force the output to only give us unique isomers:

    >>> m = Chem.MolFromSmiles('FC(Cl)C=CC=CC(F)Cl')
    >>> opts = StereoEnumerationOptions(unique=True)
    >>> isomers = tuple(EnumerateStereoisomers(m, options=opts))
    >>> len(isomers)
    10
    >>> for smi in sorted(Chem.MolToSmiles(x,isomericSmiles=True) for x in isomers):
    ...     print(smi)
    ...
    F[C@@H](Cl)/C=C/C=C/[C@@H](F)Cl
    F[C@@H](Cl)/C=C\C=C/[C@@H](F)Cl
    F[C@@H](Cl)/C=C\C=C\[C@@H](F)Cl
    F[C@H](Cl)/C=C/C=C/[C@@H](F)Cl
    F[C@H](Cl)/C=C/C=C/[C@H](F)Cl
    F[C@H](Cl)/C=C/C=C\[C@@H](F)Cl
    F[C@H](Cl)/C=C\C=C/[C@@H](F)Cl
    F[C@H](Cl)/C=C\C=C/[C@H](F)Cl
    F[C@H](Cl)/C=C\C=C\[C@@H](F)Cl
    F[C@H](Cl)/C=C\C=C\[C@H](F)Cl

    By default the code only expands unspecified stereocenters:

    >>> m = Chem.MolFromSmiles('BrC=C[C@H]1OC(C2)(F)C2(Cl)C1')
    >>> isomers = tuple(EnumerateStereoisomers(m))
    >>> len(isomers)
    8
    >>> for smi in sorted(Chem.MolToSmiles(x,isomericSmiles=True) for x in isomers):
    ...     print(smi)
    ...
    F[C@@]12C[C@@]1(Cl)C[C@@H](/C=C/Br)O2
    F[C@@]12C[C@@]1(Cl)C[C@@H](/C=C\Br)O2
    F[C@@]12C[C@]1(Cl)C[C@@H](/C=C/Br)O2
    F[C@@]12C[C@]1(Cl)C[C@@H](/C=C\Br)O2
    F[C@]12C[C@@]1(Cl)C[C@@H](/C=C/Br)O2
    F[C@]12C[C@@]1(Cl)C[C@@H](/C=C\Br)O2
    F[C@]12C[C@]1(Cl)C[C@@H](/C=C/Br)O2
    F[C@]12C[C@]1(Cl)C[C@@H](/C=C\Br)O2

    But we can change that behavior:

    >>> opts = StereoEnumerationOptions(onlyUnassigned=False)
    >>> isomers = tuple(EnumerateStereoisomers(m, options=opts))
    >>> len(isomers)
    16

    Since the result is a generator, we can allow exploring at least parts of very
    large result sets:

    >>> m = Chem.MolFromSmiles('Br' + '[CH](Cl)' * 20 + 'F')
    >>> opts = StereoEnumerationOptions(maxIsomers=0)
    >>> isomers = EnumerateStereoisomers(m, options=opts)
    >>> for x in range(5):
    ...   print(Chem.MolToSmiles(next(isomers),isomericSmiles=True))
    F[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)Br
    F[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@H](Cl)Br
    F[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@H](Cl)[C@@H](Cl)Br
    F[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@H](Cl)[C@H](Cl)Br
    F[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@H](Cl)[C@@H](Cl)[C@@H](Cl)Br

    Or randomly sample a small subset. Note that if we want that sampling to be consistent
    across python versions we need to provide a random number seed:

    >>> m = Chem.MolFromSmiles('Br' + '[CH](Cl)' * 20 + 'F')
    >>> opts = StereoEnumerationOptions(maxIsomers=3,rand=0xf00d)
    >>> isomers = EnumerateStereoisomers(m, options=opts)
    >>> for smi in isomers: #sorted(Chem.MolToSmiles(x, isomericSmiles=True) for x in isomers):
    ...     print(Chem.MolToSmiles(smi))
    F[C@@H](Cl)[C@H](Cl)[C@H](Cl)[C@H](Cl)[C@@H](Cl)[C@H](Cl)[C@H](Cl)[C@@H](Cl)[C@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@H](Cl)[C@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@H](Cl)[C@H](Cl)[C@H](Cl)[C@@H](Cl)Br
    F[C@H](Cl)[C@H](Cl)[C@H](Cl)[C@@H](Cl)[C@H](Cl)[C@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@H](Cl)[C@@H](Cl)[C@H](Cl)[C@H](Cl)[C@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@H](Cl)[C@H](Cl)[C@@H](Cl)[C@@H](Cl)Br
    F[C@@H](Cl)[C@@H](Cl)[C@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@H](Cl)[C@@H](Cl)[C@@H](Cl)[C@H](Cl)[C@@H](Cl)Br

    Z_CIPCodeNr   r/   c             S   s   g | ]}|  ¡ | ¡ f‘qS r   )Z	GetDegreeZGetAtomicNum)r"   r#   r   r   r   r$   7  s    z*EnumerateStereoisomers.<locals>.<listcomp>é   F)ZincludeRingsT)ZcleanItZforceZflagPossibleStereoCenters)ZisomericSmilesiÿÿÿ)Z
randomSeedz%s    failed to embed)*r   r9   r%   Z	ClearPropr)   Z
GetBondDirZBondDirZEITHERDOUBLEZ
SetBondDirZNONEr,   r6   r   r-   r	   ÚhashÚtupleÚsortedÚrandomZRandomÚ
isinstancer3   r4   r0   Úboolr   ZSetDoubleBondNeighborDirectionsZClearComputedPropsZAssignStereochemistryr
   ZMolToSmilesr7   r   ZAddHsr   Z	ConformerZGetNumAtomsZSetAtomPositionZGetConformerZGetAtomPositionZAddConformerÚprint)r:   r*   Úverboser;   r   r   r+   r.   Z	bitsourceZseedr	   ZisomersSeenZ
numIsomersZbitflagÚir   ZisomerZcansmiZntmZcidZconfZaidr   r   r   ÚEnumerateStereoisomers¤   sh     










rG   )rA   Zrdkitr   Zrdkit.Chem.rdDistGeomr   Úobjectr   r   r   r    r,   r-   r3   r<   rG   r   r   r   r   Ú<module>   s   !
