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
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)
def test_normal(self): groups = [[1, 2, 3], [4], [5, 6], [7]] agglomerates = agglomerate(groups) expected = [[1, 2, 3], [5, 6], [4, 7]] self.assertEquals(agglomerates, expected)
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)