Exemple #1
0
def vmatrix(gra, keys=None, rng_keys=None):
    """ v-matrix for a connected graph

    :param gra: the graph
    :param keys: restrict the v-matrix to a subset of keys, which must span a
        connected graph
    :param rng_keys: keys for a ring to start from
    """
    if keys is not None:
        gra = subgraph(gra, keys)

    assert is_connected(gra), "Graph must be connected!"

    # Start with the ring systems and their connections. If there aren't any,
    # start with the first terminal atom
    if ring_systems(gra):
        vma, zma_keys = connected_ring_systems(gra, rng_keys=rng_keys)
    else:
        term_keys = sorted(terminal_heavy_atom_keys(gra))
        if term_keys:
            start_key = term_keys[0]
        else:
            start_key = sorted(atom_keys(gra))[0]

        vma, zma_keys = start_at(gra, start_key)

    rem_keys = atom_keys(gra) - set(zma_keys)
    vma, zma_keys = continue_vmatrix(gra, rem_keys, vma, zma_keys)
    return vma, zma_keys
Exemple #2
0
def ring_arc_complement_atom_keys(gra, rng):
    """ non-intersecting arcs from a ring that shares segments with a graph
    """
    gra_atm_bnd_dct = atoms_bond_keys(gra)
    rng_atm_bnd_dct = atoms_bond_keys(rng)

    # 1. find divergence points, given by the atom at which the divergence
    # occurs and the bond followed by the ring as it diverges
    div_dct = {}

    for atm_key in atom_keys(gra) & atom_keys(rng):
        div = rng_atm_bnd_dct[atm_key] - gra_atm_bnd_dct[atm_key]
        if div:
            bnd_key, = div
            div_dct[atm_key] = bnd_key

    # 2. cycle through the ring atoms; if you meet a starting divergence, start
    # an arc; extend the arc until you meet an ending divergence; repeat until
    # all divergences are accounted for
    atm_keys = sorted_ring_atom_keys_from_bond_keys(bond_keys(rng))

    arcs = []
    arc = []
    for atm_key, next_atm_key in mit.windowed(itertools.cycle(atm_keys), 2):
        bnd_key = frozenset({atm_key, next_atm_key})

        # if we haven't started an arc, see if we are at a starting divergence;
        # if so, start the arc now and cross the divergence from our list
        if not arc:
            if atm_key in div_dct and div_dct[atm_key] == bnd_key:
                div_dct.pop(atm_key)

                arc.append(atm_key)
        # if we've started an arc, extend it; then, check if we are at an
        # ending divergence; if so, end the arc and cross the divergence from
        # our list; add it to our list of arcs
        else:
            arc.append(atm_key)

            if next_atm_key in div_dct and div_dct[next_atm_key] == bnd_key:
                div_dct.pop(next_atm_key)

                arc.append(next_atm_key)
                arcs.append(arc)
                arc = []

        # if no divergences are left, break out of the loop
        if not div_dct:
            break

    arcs = tuple(map(tuple, arcs))
    return arcs
Exemple #3
0
def union(gra1, gra2, check=True):
    """ a union of two graphs
    """
    if check:
        assert not atom_keys(gra1) & atom_keys(gra2)
    atm_dct = {}
    atm_dct.update(atoms(gra1))
    atm_dct.update(atoms(gra2))

    bnd_dct = {}
    bnd_dct.update(bonds(gra1))
    bnd_dct.update(bonds(gra2))
    return _create.graph.from_atoms_and_bonds(atm_dct, bnd_dct)
Exemple #4
0
def standard_keys_for_sequence(gras):
    """ assigns non-overlapping keys to a sequence of graphs

    (returns a series of key maps for each)
    """
    atm_key_dcts = []

    shift = 0
    for gra in gras:
        natms = atom_count(gra, dummy=True, with_implicit=False)

        atm_key_dct = {
            atm_key: idx + shift
            for idx, atm_key in enumerate(sorted(atom_keys(gra)))
        }
        atm_key_dcts.append(atm_key_dct)

        shift += natms

    gras = [
        relabel(gra, atm_key_dct)
        for gra, atm_key_dct in zip(gras, atm_key_dcts)
    ]

    return gras, atm_key_dcts
Exemple #5
0
def linear_segments_atom_keys(gra, lin_keys=None):
    """ atom keys for linear segments in the graph
    """
    ngb_keys_dct = atoms_neighbor_atom_keys(without_dummy_atoms(gra))

    lin_keys = (dummy_atoms_neighbor_atom_key(gra).values()
                if lin_keys is None else lin_keys)

    lin_keys = [k for k in lin_keys if len(ngb_keys_dct[k]) <= 2]

    lin_segs = connected_components(subgraph(gra, lin_keys))

    lin_keys_lst = []
    for lin_seg in lin_segs:
        lin_seg_keys = atom_keys(lin_seg)
        if len(lin_seg_keys) == 1:
            key, = lin_seg_keys
            lin_keys_lst.append([key])
        else:
            end_key1, end_key2 = sorted([
                key
                for key, ngb_keys in atoms_neighbor_atom_keys(lin_seg).items()
                if len(ngb_keys) == 1
            ])
            ngb_keys_dct = atoms_neighbor_atom_keys(lin_seg)

            key = None
            keys = [end_key1]
            while key != end_key2:
                key, = ngb_keys_dct[keys[-1]] - set(keys)
                keys.append(key)
            lin_keys_lst.append(keys)

    lin_keys_lst = tuple(map(tuple, lin_keys_lst))
    return lin_keys_lst
Exemple #6
0
def insert_bonded_atom(gra,
                       sym,
                       atm_key,
                       bnd_atm_key=None,
                       imp_hyd_vlc=None,
                       atm_ste_par=None,
                       bnd_ord=None,
                       bnd_ste_par=None):
    """ insert a single atom with a bond to an atom already in the graph

    Keys will be standardized upon insertion
    """
    keys = sorted(atom_keys(gra))
    bnd_atm_key_ = max(keys) + 1

    gra = add_bonded_atom(gra,
                          sym,
                          atm_key,
                          bnd_atm_key=bnd_atm_key_,
                          imp_hyd_vlc=imp_hyd_vlc,
                          atm_ste_par=atm_ste_par,
                          bnd_ord=bnd_ord,
                          bnd_ste_par=bnd_ste_par)
    if bnd_atm_key != bnd_atm_key_:
        assert bnd_atm_key in keys
        idx = keys.index(bnd_atm_key)
        key_dct = {}
        key_dct.update({k: k for k in keys[:idx]})
        key_dct[bnd_atm_key_] = bnd_atm_key
        key_dct.update({k: k + 1 for k in keys[idx:]})
        gra = relabel(gra, key_dct)

    return gra
Exemple #7
0
def closest_unbonded_atoms(geo, gra=None):
    """ Determine which pair of unbonded atoms in a molecular geometry
        are closest together.

        :param geo: molecular geometry
        :type geo: automol geometry data structure
        :param gra: the graph describing connectivity; if None, a connectivity
            graph will be generated using default distance thresholds
        :type gra: automol graph data structure
        :rtype: (frozenset(int), float)
    """

    gra = _connectivity_graph(geo) if gra is None else gra
    atm_keys = atom_keys(gra)
    bnd_keys = bond_keys(gra)
    poss_bnd_keys = set(map(frozenset, itertools.combinations(atm_keys, r=2)))

    # The set of candidates includes all unbonded pairs of atoms
    cand_bnd_keys = poss_bnd_keys - bnd_keys

    min_bnd_key = None
    min_dist_val = 1000.
    for bnd_key in cand_bnd_keys:
        dist_val = distance(geo, *bnd_key)
        if dist_val < min_dist_val:
            min_dist_val = dist_val
            min_bnd_key = bnd_key

    return min_bnd_key, min_dist_val
Exemple #8
0
def angle_key_filler_(gra, keys=None, check=True):
    """ returns a function that fills in the first or last element of an angle
    key in a dictionary with a neighboring atom
    (works for central or dihedral angles)
    """
    keys = atom_keys(gra) if keys is None else keys

    def _fill_in_angle_key(ang_key):
        end1_key = ang_key[0]
        end2_key = ang_key[-1]
        mid_keys = list(ang_key[1:-1])
        assert not any(k is None for k in mid_keys)

        if end1_key is None:
            end1_key = atom_neighbor_atom_key(
                gra, mid_keys[0], excl_atm_keys=[end2_key]+mid_keys,
                incl_atm_keys=keys)
        if end2_key is None:
            end2_key = atom_neighbor_atom_key(
                gra, mid_keys[-1], excl_atm_keys=[end1_key]+mid_keys,
                incl_atm_keys=keys)

        ang_key = [end1_key] + mid_keys + [end2_key]

        if any(k is None for k in ang_key):
            if check:
                raise ValueError("Angle key {} couldn't be filled in"
                                 .format(str(ang_key)))
            ang_key = None
        else:
            ang_key = tuple(ang_key)

        return ang_key

    return _fill_in_angle_key
Exemple #9
0
def set_stereo_from_geometry(gra, geo, geo_idx_dct=None):
    """ set graph stereo from a geometry

    (coordinate distances need not match connectivity -- what matters is the
    relative positions at stereo sites)
    """
    gra = without_stereo_parities(gra)
    last_gra = None

    atm_keys = sorted(atom_keys(gra))
    geo_idx_dct = (geo_idx_dct if geo_idx_dct is not None else
                   {atm_key: idx
                    for idx, atm_key in enumerate(atm_keys)})

    # set atom and bond stereo, iterating to self-consistency
    atm_keys = set()
    bnd_keys = set()
    while last_gra != gra:
        last_gra = gra
        atm_keys.update(stereogenic_atom_keys(gra))
        bnd_keys.update(stereogenic_bond_keys(gra))
        gra = _set_atom_stereo_from_geometry(gra, atm_keys, geo, geo_idx_dct)
        gra = _set_bond_stereo_from_geometry(gra, bnd_keys, geo, geo_idx_dct)

    return gra
Exemple #10
0
def _bond_stereo_corrected_geometry(gra, bnd_ste_par_dct, geo, geo_idx_dct):
    """ correct the bond stereo parities of a geometry, for a subset of bonds
    """
    bnd_keys = list(bnd_ste_par_dct.keys())
    for bnd_key in bnd_keys:
        par = bnd_ste_par_dct[bnd_key]
        curr_par = _bond_stereo_parity_from_geometry(gra, bnd_key, geo,
                                                     geo_idx_dct)

        if curr_par != par:
            xyzs = automol.geom.coordinates(geo)

            atm1_key, atm2_key = bnd_key
            atm1_xyz = xyzs[geo_idx_dct[atm1_key]]
            atm2_xyz = xyzs[geo_idx_dct[atm2_key]]

            rot_axis = numpy.subtract(atm2_xyz, atm1_xyz)

            rot_atm_keys = atom_keys(
                branch(gra, atm1_key, {atm1_key, atm2_key}))

            rot_idxs = list(map(geo_idx_dct.__getitem__, rot_atm_keys))

            geo = automol.geom.rotate(geo,
                                      rot_axis,
                                      numpy.pi,
                                      orig_xyz=atm1_xyz,
                                      idxs=rot_idxs)

        assert _bond_stereo_parity_from_geometry(gra, bnd_key, geo,
                                                 geo_idx_dct) == par
        gra = set_bond_stereo_parities(gra, {bnd_key: par})

    return geo, gra
Exemple #11
0
def _atom_stereo_corrected_geometry(gra, atm_ste_par_dct, geo, geo_idx_dct):
    """ correct the atom stereo parities of a geometry, for a subset of atoms
    """
    ring_atm_keys = set(itertools.chain(*rings_atom_keys(gra)))
    atm_ngb_keys_dct = atoms_neighbor_atom_keys(gra)

    atm_keys = list(atm_ste_par_dct.keys())
    for atm_key in atm_keys:
        par = atm_ste_par_dct[atm_key]
        curr_par = _atom_stereo_parity_from_geometry(gra, atm_key, geo,
                                                     geo_idx_dct)

        if curr_par != par:
            atm_ngb_keys = atm_ngb_keys_dct[atm_key]
            # for now, we simply exclude rings from the pivot keys
            # (will not work for stereo atom at the intersection of two rings)
            atm_piv_keys = list(atm_ngb_keys - ring_atm_keys)[:2]
            assert len(atm_piv_keys) == 2
            atm3_key, atm4_key = atm_piv_keys

            # get coordinates
            xyzs = coordinates(geo)
            atm_xyz = xyzs[geo_idx_dct[atm_key]]
            atm3_xyz = xyzs[geo_idx_dct[atm3_key]]
            atm4_xyz = xyzs[geo_idx_dct[atm4_key]]

            # do the rotation
            rot_axis = util.vec.unit_bisector(atm3_xyz,
                                              atm4_xyz,
                                              orig_xyz=atm_xyz)

            rot_atm_keys = (
                atom_keys(branch(gra, atm_key, {atm_key, atm3_key}))
                | atom_keys(branch(gra, atm_key, {atm_key, atm4_key})))
            rot_idxs = list(map(geo_idx_dct.__getitem__, rot_atm_keys))

            geo = automol.geom.rotate(geo,
                                      rot_axis,
                                      numpy.pi,
                                      orig_xyz=atm_xyz,
                                      idxs=rot_idxs)

        assert _atom_stereo_parity_from_geometry(gra, atm_key, geo,
                                                 geo_idx_dct) == par
        gra = set_atom_stereo_parities(gra, {atm_key: par})

    return geo, gra
Exemple #12
0
def longest_chain(gra):
    """ longest chain in the graph
    """
    atm_keys = atom_keys(gra)

    max_chain = max((atom_longest_chain(gra, atm_key) for atm_key in atm_keys),
                    key=len)
    return max_chain
Exemple #13
0
def ring_system_decomposed_atom_keys(rsy, rng_keys=None, check=True):
    """ decomposed atom keys for a polycyclic ring system in a graph

    The ring system is decomposed into a ring and a series of arcs that can
    be used to successively construct the system

    :param rsy: the ring system
    :param rng_keys: keys for the first ring in the decomposition; if None, the
        smallest ring in the system will be chosen
    """
    if rng_keys is None:
        rng = sorted(rings(rsy), key=atom_count)[0]
        rng_keys = sorted_ring_atom_keys(rng)

    # check the arguments, if requested
    if check:
        # check that the graph is connected
        assert is_connected(rsy), "Ring system can't be disconnected."

        # check that the graph is actually a ring system
        assert is_ring_system(rsy), (
            "This is not a ring system graph:\n{:s}".format(string(rsy)))

        # check that rng is a subgraph of rsy
        assert set(rng_keys) <= atom_keys(rsy), (
            "{}\n^ Rings system doesn't contain ring as subgraph:\n{}".format(
                string(rsy, one_indexed=False), str(rng_keys)))

    bnd_keys = list(mit.windowed(rng_keys + rng_keys[:1], 2))

    # Remove bonds for the ring
    rsy = remove_bonds(rsy, bnd_keys)
    keys_lst = [rng_keys]
    done_keys = set(rng_keys)

    while bond_keys(rsy):

        # Determine shortest paths for the graph with one more ring/arc deleted
        sp_dct = atom_shortest_paths(rsy)

        # The shortest path will be the next shortest arc in the system
        arc_keys = min((sp_dct[i][j]
                        for i, j in itertools.combinations(done_keys, 2)
                        if j in sp_dct[i]),
                       key=len)

        # Add this arc to the list
        keys_lst.append(arc_keys)

        # Add these keys to the list of done keys
        done_keys |= set(arc_keys)

        # Delete tbond keys for the new arc and continue to the next iteration
        bnd_keys = list(map(frozenset, mit.windowed(arc_keys, 2)))
        rsy = remove_bonds(rsy, bnd_keys)

    keys_lst = tuple(map(tuple, keys_lst))
    return keys_lst
Exemple #14
0
def rotational_bond_keys(gra, lin_keys=None, with_h_rotors=True):
    """ get all rotational bonds for a graph

    :param gra: the graph
    :param lin_keys: keys to linear atoms in the graph
    """
    gra = explicit(gra)
    sym_dct = atom_symbols(gra)
    ngb_keys_dct = atoms_neighbor_atom_keys(gra)
    bnd_ord_dct = resonance_dominant_bond_orders(gra)
    rng_bnd_keys = list(itertools.chain(*rings_bond_keys(gra)))

    def _is_rotational_bond(bnd_key):
        ngb_keys_lst = [ngb_keys_dct[k] - bnd_key for k in bnd_key]

        is_single = max(bnd_ord_dct[bnd_key]) <= 1
        has_neighbors = all(ngb_keys_lst)
        not_in_ring = bnd_key not in rng_bnd_keys

        is_h_rotor = any(
            set(map(sym_dct.__getitem__, ks)) == {'H'} for ks in ngb_keys_lst)

        return is_single and has_neighbors and not_in_ring and (
            not is_h_rotor or with_h_rotors)

    rot_bnd_keys = frozenset(filter(_is_rotational_bond, bond_keys(gra)))

    lin_keys_lst = linear_segments_atom_keys(gra, lin_keys=lin_keys)
    dum_keys = tuple(atom_keys(gra, sym='X'))
    for keys in lin_keys_lst:
        bnd_keys = sorted((k for k in rot_bnd_keys if k & set(keys)),
                          key=sorted)

        # Check whether there are neighboring atoms on either side of the
        # linear segment
        excl_keys = set(keys) | set(dum_keys)

        end_key1 = atom_neighbor_atom_key(gra,
                                          keys[0],
                                          excl_atm_keys=excl_keys)

        excl_keys |= {end_key1}
        end_key2 = atom_neighbor_atom_key(gra,
                                          keys[-1],
                                          excl_atm_keys=excl_keys)

        end_keys = {end_key1, end_key2}
        ngb_keys_lst = [ngb_keys_dct[k] - excl_keys for k in end_keys]
        has_neighbors = all(ngb_keys_lst)

        if not has_neighbors:
            rot_bnd_keys -= set(bnd_keys)
        else:
            rot_bnd_keys -= set(bnd_keys[:-1])

    return rot_bnd_keys
Exemple #15
0
def atom_longest_chains(gra):
    """ longest chains, by atom
    """
    atm_keys = atom_keys(gra)

    long_chain_dct = {
        atm_key: atom_longest_chain(gra, atm_key)
        for atm_key in atm_keys
    }
    return long_chain_dct
Exemple #16
0
def equivalent_atoms(gra, atm_key, stereo=True, dummy=True):
    """ Identify sets of isomorphically equivalent atoms

    Two atoms are equivalent if they transform into each other under an
    automorphism

    :param gra: A graph
    :param atm_key: An atom key for the graph
    :param stereo: Consider stereo?
    :type stereo: bool
    :param dummy: Consider dummy atoms?
    :type dummy: bool
    :returns: Keys to equivalent atoms
    :rtype: frozenset
    """
    assert atm_key in atom_keys(gra), ("{} not in {}".format(
        atm_key, atom_keys(gra)))

    atm_symb_dct = atom_symbols(gra)
    atm_ngbs_dct = atoms_neighbor_atom_keys(gra)

    def _neighbor_symbols(key):
        return sorted(map(atm_symb_dct.__getitem__, atm_ngbs_dct[key]))

    # 1. Find atoms with the same symbols
    atm_symb = atm_symb_dct[atm_key]
    cand_keys = atom_keys(gra, sym=atm_symb)

    # 2. Of those, find atoms with the same neighboring atom types
    atm_ngb_symbs = _neighbor_symbols(atm_key)
    cand_keys = [k for k in cand_keys if _neighbor_symbols(k) == atm_ngb_symbs]

    # 3. Find the equivalent atoms from the list of candidates.
    # Strategy: Change the atom symbol to 'Ts' and check for isomorphism.
    # Assumes none of the compounds have element 117.
    ref_gra = set_atom_symbols(gra, {atm_key: 'Ts'})
    atm_keys = []
    for key in cand_keys:
        comp_gra = set_atom_symbols(gra, {key: 'Ts'})
        if isomorphism(ref_gra, comp_gra, stereo=stereo, dummy=dummy):
            atm_keys.append(key)

    return frozenset(atm_keys)
Exemple #17
0
def atom_count_by_type(gra, sym, keys=None):
    """ count the number of atoms with a given type (symbol)

    :param gra: the graph
    :param sym: the symbol
    :param keys: optionally, restrict the count to a subset of keys
    """
    keys = atom_keys(gra) if keys is None else keys
    symb_dct = atom_symbols(gra)
    symbs = list(map(symb_dct.__getitem__, keys))
    return symbs.count(sym)
Exemple #18
0
def nonresonant_radical_atom_keys(rgr):
    """ keys for radical atoms that are not in resonance
    """
    rgr = without_fractional_bonds(rgr)
    atm_keys = list(atom_keys(rgr))
    atm_rad_vlcs_by_res = [
        dict_.values_by_key(atom_unsaturated_valences(dom_rgr), atm_keys)
        for dom_rgr in dominant_resonances(rgr)
    ]
    atm_rad_vlcs = [min(rad_vlcs) for rad_vlcs in zip(*atm_rad_vlcs_by_res)]
    atm_rad_keys = frozenset(
        atm_key for atm_key, atm_rad_vlc in zip(atm_keys, atm_rad_vlcs)
        if atm_rad_vlc)
    return atm_rad_keys
Exemple #19
0
def geometry(gra, keys=None, ntries=5, max_dist_err=0.2):
    """ sample a qualitatively-correct stereo geometry

    :param gra: the graph, which may or may not have stereo
    :param keys: graph keys, in the order in which they should appear in the
        geometry
    :param ntries: number of tries for finding a valid geometry
    :param max_dist_err: maximum distance error convergence threshold

    Qualitatively-correct means it has the right connectivity and the right
    stero parities, but its bond lengths and bond angles may not be
    quantitatively realistic
    """
    assert gra == explicit(gra), (
        "Graph => geometry conversion requires explicit hydrogens!\n"
        "Use automol.graph.explicit() to convert to an explicit graph.")

    # 0. Get keys and symbols
    symb_dct = atom_symbols(gra)

    keys = sorted(atom_keys(gra)) if keys is None else keys
    symbs = tuple(map(symb_dct.__getitem__, keys))

    # 1. Generate bounds matrices
    lmat, umat = distance_bounds_matrices(gra, keys)
    chi_dct = chirality_constraint_bounds(gra, keys)
    pla_dct = planarity_constraint_bounds(gra, keys)
    conv1_ = qualitative_convergence_checker_(gra, keys)
    conv2_ = embed.distance_convergence_checker_(lmat, umat, max_dist_err)

    def conv_(xmat, err, grad):
        return conv1_(xmat, err, grad) & conv2_(xmat, err, grad)

    # 2. Generate coordinates with correct stereo, trying a few times
    for _ in range(ntries):
        xmat = embed.sample_raw_distance_coordinates(lmat, umat, dim4=True)
        xmat, conv = embed.cleaned_up_coordinates(
            xmat, lmat, umat, pla_dct=pla_dct, chi_dct=chi_dct, conv_=conv_)
        if conv:
            break

    if not conv:
        raise error.FailedGeometryGenerationError

    # 3. Generate a geometry data structure from the coordinates
    xyzs = xmat[:, :3]
    geo = _create.geom.from_data(symbs, xyzs, angstrom=True)

    return geo
Exemple #20
0
def sing_res_dom_radical_atom_keys(rgr):
    """ resonance-dominant radical atom keys,for one resonance
    """
    rgr = without_fractional_bonds(rgr)
    atm_keys = list(atom_keys(rgr))
    atm_rad_vlcs_by_res = [
        dict_.values_by_key(atom_unsaturated_valences(dom_rgr), atm_keys)
        for dom_rgr in dominant_resonances(rgr)
    ]
    first_atm_rad_val = [atm_rad_vlcs_by_res[0]]
    atm_rad_vlcs = [max(rad_vlcs) for rad_vlcs in zip(*first_atm_rad_val)]
    atm_rad_keys = frozenset(
        atm_key for atm_key, atm_rad_vlc in zip(atm_keys, atm_rad_vlcs)
        if atm_rad_vlc)
    return atm_rad_keys
Exemple #21
0
def resonance_dominant_radical_atom_keys(rgr):
    """ resonance-dominant radical atom keys

    (keys of resonance-dominant radical sites)
    """
    rgr = without_fractional_bonds(rgr)
    atm_keys = list(atom_keys(rgr))
    atm_rad_vlcs_by_res = [
        dict_.values_by_key(atom_unsaturated_valences(dom_rgr), atm_keys)
        for dom_rgr in dominant_resonances(rgr)
    ]
    atm_rad_vlcs = [max(rad_vlcs) for rad_vlcs in zip(*atm_rad_vlcs_by_res)]
    atm_rad_keys = frozenset(
        atm_key for atm_key, atm_rad_vlc in zip(atm_keys, atm_rad_vlcs)
        if atm_rad_vlc)
    return atm_rad_keys
Exemple #22
0
def from_graph(gra):
    """ networkx graph object from a molecular graph
    """
    nxg = networkx.Graph()
    nxg.add_nodes_from(atom_keys(gra))
    nxg.add_edges_from(bond_keys(gra))
    networkx.set_node_attributes(nxg, atom_symbols(gra), 'symbol')
    networkx.set_node_attributes(nxg, atom_implicit_hydrogen_valences(gra),
                                 'implicit_hydrogen_valence')
    networkx.set_node_attributes(nxg, atom_stereo_parities(gra),
                                 'stereo_parity')
    networkx.set_edge_attributes(nxg, bond_orders(gra), 'order')
    networkx.set_edge_attributes(nxg, bond_stereo_parities(gra),
                                 'stereo_parity')

    return nxg
Exemple #23
0
def add_dummy_atoms(gra, dummy_key_dct):
    """ add dummy atoms to the graph, with dummy bonds to particular atoms

    :param dummy_key_dct: keys are atoms in the graph on which to place a dummy
        atom; values are the desired keys of the dummy atoms themselves, which
        must not overlap with already existing atoms
    """
    atm_keys = atom_keys(gra)
    assert set(dummy_key_dct.keys()) <= atm_keys, (
        "Keys must be existing atoms in the graph.")
    assert not set(dummy_key_dct.values()) & atm_keys, (
        "Dummy atom keys cannot overlap with existing atoms.")

    for key, dummy_key in sorted(dummy_key_dct.items()):
        gra = add_bonded_atom(gra, 'X', key, bnd_atm_key=dummy_key, bnd_ord=0)

    return gra
Exemple #24
0
def relabel_for_geometry(gra):
    """ relabel a z-matrix graph to line up with a geometry

    The result will line up with a geometry converted from the z-matrix, with
    dummy atoms removed.

    Removes dummy atoms and relabels in geometry order.

    Graph keys should correspond to the z-matrix used for conversion.

    :param gra: the graph
    """
    dummy_keys = sorted(atom_keys(gra, sym='X'))

    for dummy_key in reversed(dummy_keys):
        gra = _shift_remove_dummy_atom(gra, dummy_key)
    return gra
Exemple #25
0
def frozen(gra):
    """ hashable, sortable, immutable container of graph data
    """
    atm_keys = sorted(atom_keys(gra))
    bnd_keys = sorted(bond_keys(gra), key=sorted)

    # make it sortable by replacing Nones with -infinity
    atm_vals = numpy.array(dict_.values_by_key(atoms(gra), atm_keys),
                           dtype=numpy.object)
    bnd_vals = numpy.array(dict_.values_by_key(bonds(gra), bnd_keys),
                           dtype=numpy.object)
    atm_vals[numpy.equal(atm_vals, None)] = -numpy.inf
    bnd_vals[numpy.equal(bnd_vals, None)] = -numpy.inf

    frz_atms = tuple(zip(atm_keys, map(tuple, atm_vals)))
    frz_bnds = tuple(zip(bnd_keys, map(tuple, bnd_vals)))
    return (frz_atms, frz_bnds)
Exemple #26
0
def inchi_with_sort_from_geometry(gra, geo=None, geo_idx_dct=None):
    """ Generate an InChI string from a molecular graph.
        If coordinates are passed in, they are used to determine stereo.

        :param gra: molecular graph
        :type gra: automol graph data structure
        :param geo: molecular geometry
        :type geo: automol geometry data structure
        :param geo_idx_dct:
        :type geo_idx_dct: dict[:]
        :rtype: (str, tuple(int))
    """
    gra = without_dummy_atoms(gra)
    gra = dominant_resonance(gra)
    atm_keys = sorted(atom_keys(gra))
    bnd_keys = list(bond_keys(gra))
    atm_syms = dict_.values_by_key(atom_symbols(gra), atm_keys)
    atm_bnd_vlcs = dict_.values_by_key(atom_bond_valences(gra), atm_keys)
    atm_rad_vlcs = dict_.values_by_key(atom_unsaturated_valences(gra),
                                       atm_keys)
    bnd_ords = dict_.values_by_key(bond_orders(gra), bnd_keys)

    if geo is not None:
        assert geo_idx_dct is not None
        atm_xyzs = coordinates(geo)
        atm_xyzs = [
            atm_xyzs[geo_idx_dct[atm_key]] if atm_key in geo_idx_dct else
            (0., 0., 0.) for atm_key in atm_keys
        ]
    else:
        atm_xyzs = None

    mlf, key_map_inv = _molfile.from_data(atm_keys,
                                          bnd_keys,
                                          atm_syms,
                                          atm_bnd_vlcs,
                                          atm_rad_vlcs,
                                          bnd_ords,
                                          atm_xyzs=atm_xyzs)
    rdm = _rdkit.from_molfile(mlf)
    ich, aux_info = _rdkit.to_inchi(rdm, with_aux_info=True)
    nums = _parse_sort_order_from_aux_info(aux_info)
    nums = tuple(map(key_map_inv.__getitem__, nums))

    return ich, nums
Exemple #27
0
def radical_dissociation_prods(gra, pgra1):
    """ given a dissociation product, determine the other product
    """
    gra = without_fractional_bonds(gra)

    pgra2 = None
    rads = sing_res_dom_radical_atom_keys(gra)
    adj_atms = atoms_neighbor_atom_keys(gra)
    # adj_idxs = tuple(adj_atms[rad] for rad in rads)
    for rad in rads:
        for adj in adj_atms[rad]:
            for group in atom_groups(gra, adj):
                if full_isomorphism(explicit(group), explicit(pgra1)):
                    pgra2 = remove_atoms(gra, atom_keys(group))
                    # pgra2 = remove_bonds(pgra2, bond_keys(group))
                    if bond_keys(group) in pgra2:
                        pgra2 = remove_bonds(pgra2, bond_keys(group))
    return (pgra1, pgra2)
Exemple #28
0
def add_bonded_atom(gra,
                    sym,
                    atm_key,
                    bnd_atm_key=None,
                    imp_hyd_vlc=None,
                    atm_ste_par=None,
                    bnd_ord=None,
                    bnd_ste_par=None):
    """ add a single atom with a bond to an atom already in the graph
    """
    atm_keys = atom_keys(gra)

    bnd_atm_key = max(atm_keys) + 1 if bnd_atm_key is None else bnd_atm_key

    symb_dct = {bnd_atm_key: sym}
    imp_hyd_vlc_dct = ({
        bnd_atm_key: imp_hyd_vlc
    } if imp_hyd_vlc is not None else None)
    atm_ste_par_dct = ({
        bnd_atm_key: atm_ste_par
    } if atm_ste_par is not None else None)

    gra = add_atoms(gra,
                    symb_dct,
                    imp_hyd_vlc_dct=imp_hyd_vlc_dct,
                    ste_par_dct=atm_ste_par_dct)

    bnd_key = frozenset({bnd_atm_key, atm_key})
    bnd_ord_dct = {bnd_key: bnd_ord} if bnd_ord is not None else None
    bnd_ste_par_dct = ({
        bnd_key: bnd_ste_par
    } if bnd_ste_par is not None else None)

    gra = add_bonds(gra, [bnd_key],
                    ord_dct=bnd_ord_dct,
                    ste_par_dct=bnd_ste_par_dct)

    return gra
Exemple #29
0
def from_yaml_dictionary(yaml_gra_dct, one_indexed=True):
    """ read the graph from a yaml dictionary
    """
    atm_dct = yaml_gra_dct['atoms']
    bnd_dct = yaml_gra_dct['bonds']

    atm_dct = dict_.transform_values(
        atm_dct, lambda x: tuple(map(x.__getitem__, ATM_PROP_NAMES)))

    bnd_dct = dict_.transform_keys(bnd_dct,
                                   lambda x: frozenset(map(int, x.split('-'))))

    bnd_dct = dict_.transform_values(
        bnd_dct, lambda x: tuple(map(x.__getitem__, BND_PROP_NAMES)))

    gra = _create.from_atoms_and_bonds(atm_dct, bnd_dct)

    if one_indexed:
        # revert one-indexing if the input is one-indexed
        atm_key_dct = {atm_key: atm_key - 1 for atm_key in atom_keys(gra)}
        gra = relabel(gra, atm_key_dct)

    return gra
Exemple #30
0
def connected_ring_systems(gra, rng_keys=None, check=True):
    """ generate a v-matrix covering a graph's ring systems and the connections
    between them
    """
    if check:
        assert is_connected(gra), "Graph must be connected!"

    rsys = sorted(ring_systems(gra), key=atom_count)

    # Construct the v-matrix for the first ring system, choosing which ring
    # to start from
    if rng_keys is None:
        rsy = rsys.pop(0)
        rngs = sorted(rings(rsy), key=atom_count)
        rng_keys = sorted_ring_atom_keys(rngs.pop(0))
    else:
        idx = next((i for i, ks in enumerate(map(atom_keys, rsys))
                    if set(rng_keys) <= ks), None)
        assert idx is not None, (
            "The ring {} is not in this graph:\n{}".format(
                str(rng_keys), string(gra, one_indexed=False)))
        rsy = rsys.pop(idx)

    keys_lst = list(ring_system_decomposed_atom_keys(rsy, rng_keys=rng_keys))

    vma, zma_keys = ring_system(gra, keys_lst)

    keys = atom_keys(gra) - set(zma_keys)

    vma, zma_keys = continue_connected_ring_systems(gra,
                                                    keys,
                                                    vma,
                                                    zma_keys,
                                                    rsys=rsys,
                                                    check=False)

    return vma, zma_keys