Exemplo n.º 1
0
def stereogenic_atom_keys(gra, assigned=False):
    """ Find stereogenic atoms in this graph.

    If the `assigned` flag is set to `False`, only  unassigned stereogenic
    atoms will be detected.

    :param gra: the graph
    :param assigned: Include atoms that already have stereo assignments?
    :param assigned: bool
    :returns: the stereogenic atom keys
    :rtype: frozenset
    """
    gra = without_bond_orders(gra)
    gra = explicit(gra)  # for simplicity, add the explicit hydrogens back in
    atm_keys = dict_.keys_by_value(atom_bond_valences(gra), lambda x: x == 4)
    if not assigned:
        # Remove assigned stereo keys
        atm_keys -= atom_stereo_keys(gra)

    atm_ngb_keys_dct = atoms_neighbor_atom_keys(gra)

    def _is_stereogenic(atm_key):
        atm_ngb_keys = list(atm_ngb_keys_dct[atm_key])
        pri_vecs = [
            stereo_priority_vector(gra, atm_key, atm_ngb_key)
            for atm_ngb_key in atm_ngb_keys
        ]
        ret = not any(pv1 == pv2
                      for pv1, pv2 in itertools.combinations(pri_vecs, r=2))
        return ret

    ste_gen_atm_keys = frozenset(filter(_is_stereogenic, atm_keys))
    return ste_gen_atm_keys
Exemplo n.º 2
0
def nonconserved_atom_stereo_keys(ste_tsg, check=True):
    """ Determine atom stereo centers which are not conserved by the reaction.

    This includes atoms which are stereogenic for the products but not for the
    reactants ("created" stereo centers) and atoms which are stereogenic for
    the reactants but not for the products ("destroyed" stereo centers).

    :param ste_tsg: The TS graph, with stereo assignments.
    :returns: Created and destroyed atom stereo centers, respectively.
    :rtype: (frozenset, frozenset)
    """
    rcts_gra = reactants_graph(ste_tsg)
    prds_gra = products_graph(ste_tsg)

    if check:
        ste_atm_keys = stereogenic_atom_keys(rcts_gra)
        ste_bnd_keys = stereogenic_bond_keys(rcts_gra)
        assert not ste_atm_keys, ("Unassigned atom stereo centers: {}".format(
            str(ste_atm_keys)))
        assert not ste_bnd_keys, ("Unassigned bond stereo centers: {}".format(
            str(ste_bnd_keys)))

    keys1 = atom_stereo_keys(rcts_gra)
    keys2 = stereogenic_atom_keys(prds_gra, assigned=True)

    cre_ste_atm_keys = frozenset(keys2 - keys1)
    des_ste_atm_keys = frozenset(keys1 - keys2)

    return cre_ste_atm_keys, des_ste_atm_keys
Exemplo n.º 3
0
def atoms_stereo_sorted_neighbor_atom_keys(sgr):
    """ Obtain neighbor atom keys for all stereo atoms, sorted by stereo
    priority.

    Includes all stereo atoms and atoms constituting stereo bonds. For stereo
    bonds, the neighbors for each atom in the bond exclude the other atom in
    the bond.

    :param sgr: the graph
    :returns: Neighbor atom keys, sorted by stereo priority, keyed by atom.
    :rtype: dict
    """
    atm_ste_keys = atom_stereo_keys(sgr)
    bnd_ste_keys = bond_stereo_keys(sgr)
    atm_ngb_keys_dct = atoms_neighbor_atom_keys(sgr)

    ste_atm_ngb_keys_dct = {}
    for atm_key in atm_ste_keys:
        atm_ngb_keys = atm_ngb_keys_dct[atm_key]

        ste_atm_ngb_keys_dct[atm_key] = atom_stereo_sorted_neighbor_atom_keys(
            sgr, atm_key, atm_ngb_keys)

    for bnd_key in bnd_ste_keys:
        atm1_key, atm2_key = sorted(bnd_key)

        atm1_ngb_keys = atm_ngb_keys_dct[atm1_key] - bnd_key
        atm2_ngb_keys = atm_ngb_keys_dct[atm2_key] - bnd_key

        ste_atm_ngb_keys_dct[atm1_key] = atom_stereo_sorted_neighbor_atom_keys(
            sgr, atm1_key, atm1_ngb_keys)
        ste_atm_ngb_keys_dct[atm2_key] = atom_stereo_sorted_neighbor_atom_keys(
            sgr, atm2_key, atm2_ngb_keys)

    return ste_atm_ngb_keys_dct
Exemplo n.º 4
0
def to_index_based_stereo(sgr):
    """ Convert a graph to index-based stereo assignments, where parities are
    defined relative to the ordering of indices rather than the absolute stereo
    priority.

    :param sgr: a graph with absolute stereo assignments
    :returns: a graph with index-based stereo assignments
    """
    assert sgr == explicit(sgr), (
        f"Not an explicit graph:\n{string(sgr, one_indexed=False)}")

    abs_srt_keys_dct = atoms_stereo_sorted_neighbor_atom_keys(sgr)
    atm_ste_keys = atom_stereo_keys(sgr)
    bnd_ste_keys = bond_stereo_keys(sgr)

    abs_atm_ste_par_dct = atom_stereo_parities(sgr)
    abs_bnd_ste_par_dct = bond_stereo_parities(sgr)

    idx_atm_ste_par_dct = {}
    idx_bnd_ste_par_dct = {}

    # Determine index-based stereo assignments for atoms
    for atm_key in atm_ste_keys:
        abs_srt_keys = abs_srt_keys_dct[atm_key]
        idx_srt_keys = sorted(abs_srt_keys)

        if util.is_even_permutation(idx_srt_keys, abs_srt_keys):
            idx_atm_ste_par_dct[atm_key] = abs_atm_ste_par_dct[atm_key]
        else:
            idx_atm_ste_par_dct[atm_key] = not abs_atm_ste_par_dct[atm_key]

    # Determine index-based stereo assignments for bonds
    for bnd_key in bnd_ste_keys:
        atm1_key, atm2_key = sorted(bnd_key)

        atm1_abs_srt_keys = abs_srt_keys_dct[atm1_key]
        atm2_abs_srt_keys = abs_srt_keys_dct[atm2_key]
        atm1_idx_srt_keys = sorted(atm1_abs_srt_keys)
        atm2_idx_srt_keys = sorted(atm2_abs_srt_keys)

        if not ((atm1_idx_srt_keys[0] != atm1_abs_srt_keys[0]) ^
                (atm2_idx_srt_keys[0] != atm2_abs_srt_keys[0])):
            idx_bnd_ste_par_dct[bnd_key] = abs_bnd_ste_par_dct[bnd_key]
        else:
            idx_bnd_ste_par_dct[bnd_key] = not abs_bnd_ste_par_dct[bnd_key]

    sgr = set_atom_stereo_parities(sgr, idx_atm_ste_par_dct)
    sgr = set_bond_stereo_parities(sgr, idx_bnd_ste_par_dct)
    return sgr
Exemplo n.º 5
0
def compatible_reverse_stereomers(ste_tsg):
    """ Given a TS graph with stereo assignments, expand all possible reverse
    graphs compatble with the forward graph.

    :param ste_tsg: The TS graph, with stereo assignments.
    :returns: All possible reverse TS graphs.
    """
    frm_bnd_keys = forming_bond_keys(ste_tsg)
    brk_bnd_keys = breaking_bond_keys(ste_tsg)
    _, des_ste_atm_keys = nonconserved_atom_stereo_keys(ste_tsg)
    _, des_ste_bnd_keys = nonconserved_bond_stereo_keys(ste_tsg)
    cons_atm_keys = sorted(atom_stereo_keys(ste_tsg) - des_ste_atm_keys)
    cons_bnd_keys = sorted(bond_stereo_keys(ste_tsg) - des_ste_bnd_keys)

    # 1. Determine index-based stereo assignments for conserved stereo centers
    idx_tsg = to_index_based_stereo(ste_tsg)
    cons_idx_atm_pars = dict_.values_by_key(atom_stereo_parities(idx_tsg),
                                            cons_atm_keys)
    cons_idx_bnd_pars = dict_.values_by_key(bond_stereo_parities(idx_tsg),
                                            cons_bnd_keys)

    # 2. Determine all possible index-based stereo assignments for the reverse
    #    reaction.
    prds_gra = without_stereo_parities(products_graph(ste_tsg))
    prds_sgrs = _stereomers(prds_gra)
    prds_idx_sgrs = list(map(_to_index_based_stereo, prds_sgrs))
    rev_idx_tsgs_pool = [
        graph(p, brk_bnd_keys, frm_bnd_keys) for p in prds_idx_sgrs
    ]

    # 3. Find possibilities which match the assignments for the conserved
    #    stereo centers.
    rev_idx_tsgs = []
    for rev_idx_tsg in rev_idx_tsgs_pool:
        rev_cons_idx_atm_pars = dict_.values_by_key(
            atom_stereo_parities(rev_idx_tsg), cons_atm_keys)
        rev_cons_idx_bnd_pars = dict_.values_by_key(
            bond_stereo_parities(rev_idx_tsg), cons_bnd_keys)
        if (rev_cons_idx_atm_pars == cons_idx_atm_pars
                and rev_cons_idx_bnd_pars == cons_idx_bnd_pars):
            rev_idx_tsgs.append(rev_idx_tsg)

    # 4. Convert the matching reverse graphs back from index-based stereo
    #    assignments to absolute stereo assignments.
    rev_ste_tsgs = list(map(from_index_based_stereo, rev_idx_tsgs))
    return rev_ste_tsgs
Exemplo n.º 6
0
def _atom_stereo_layer(gra):
    """ AMChI atom stereo (t, m) layer from graph

        S = counterclockwise = '@'  = '-' = False
        R = clockwise        = '@@' = '+' = True

        :param gra: molecular graph
        :type gra: automol graph data structure
        :rtype: (str, str)
    """
    # Generate the atom stereo layer strings
    atm_keys = sorted(atom_stereo_keys(gra))
    atm_par_dct = atom_stereo_parities(gra)

    atm_pars = list(map(atm_par_dct.__getitem__, atm_keys))
    atm_sgns = ['+' if p else '-' for p in atm_pars]
    atm_strs = [f'{i+1}{s}' for i, s in zip(atm_keys, atm_sgns)]

    atm_ste_lyr = ','.join(atm_strs)
    return atm_ste_lyr
Exemplo n.º 7
0
def from_index_based_stereo(sgr):
    """ Convert a graph from index-based stereo assignments back to absolute
    stereo assignments, where parities are independent of atom ordering.

    :param sgr: a graph with index-based stereo assignments
    :returns: a graph with absolute stereo assignments
    """
    assert sgr == explicit(sgr), (
        f"Not an explicit graph:\n{string(sgr, one_indexed=False)}")

    gra = without_stereo_parities(sgr)

    if has_stereo(sgr):
        atm_keys_pool = atom_stereo_keys(sgr)
        bnd_keys_pool = bond_stereo_keys(sgr)

        idx_atm_ste_par_dct = atom_stereo_parities(sgr)
        idx_bnd_ste_par_dct = bond_stereo_parities(sgr)

        atm_ngb_keys_dct = atoms_neighbor_atom_keys(sgr)

        atm_keys = set()
        bnd_keys = set()

        last_gra = None

        # Do the assignments iteratively to handle higher-order stereo
        while last_gra != gra:
            last_gra = gra

            abs_atm_ste_par_dct = {}
            abs_bnd_ste_par_dct = {}

            atm_keys.update(stereogenic_atom_keys(gra) & atm_keys_pool)
            bnd_keys.update(stereogenic_bond_keys(gra) & bnd_keys_pool)

            # Determine absolute stereo assignments for atoms
            for atm_key in atm_keys:
                abs_srt_keys = atom_stereo_sorted_neighbor_atom_keys(
                    gra, atm_key, atm_ngb_keys_dct[atm_key])
                idx_srt_keys = sorted(abs_srt_keys)

                if util.is_even_permutation(idx_srt_keys, abs_srt_keys):
                    abs_atm_ste_par_dct[atm_key] = (
                        idx_atm_ste_par_dct[atm_key])
                else:
                    abs_atm_ste_par_dct[atm_key] = (
                        not idx_atm_ste_par_dct[atm_key])

            # Determine absolute stereo assignments for bonds
            for bnd_key in bnd_keys:
                atm1_key, atm2_key = sorted(bnd_key)

                atm1_abs_srt_keys = atom_stereo_sorted_neighbor_atom_keys(
                    gra, atm1_key, atm_ngb_keys_dct[atm1_key] - bnd_key)
                atm2_abs_srt_keys = atom_stereo_sorted_neighbor_atom_keys(
                    gra, atm2_key, atm_ngb_keys_dct[atm2_key] - bnd_key)
                atm1_idx_srt_keys = sorted(atm1_abs_srt_keys)
                atm2_idx_srt_keys = sorted(atm2_abs_srt_keys)

                if not ((atm1_idx_srt_keys[0] != atm1_abs_srt_keys[0]) ^
                        (atm2_idx_srt_keys[0] != atm2_abs_srt_keys[0])):
                    abs_bnd_ste_par_dct[bnd_key] = (
                        idx_bnd_ste_par_dct[bnd_key])
                else:
                    abs_bnd_ste_par_dct[bnd_key] = (
                        not idx_bnd_ste_par_dct[bnd_key])

            gra = set_atom_stereo_parities(gra, abs_atm_ste_par_dct)
            gra = set_bond_stereo_parities(gra, abs_bnd_ste_par_dct)

        atm_ste_keys = atom_stereo_keys(gra)
        bnd_ste_keys = bond_stereo_keys(gra)
        assert atm_ste_keys == atm_keys_pool, (
            "Index-based to absolute stereo conversion failed:\n"
            f"{str(atm_ste_keys)} != {str(atm_keys_pool)}")
        assert bnd_ste_keys == bnd_keys_pool, (
            "Index-based to absolute stereo conversion failed:\n"
            f"{str(bnd_ste_keys)} != {str(bnd_keys_pool)}")

    return gra