99from fragment_capping .helpers .parameters import MAX_ABSOLUTE_CHARGE , MIN_ABSOLUTE_CHARGE , MAX_NONBONDED_ELECTRONS , \
1010 MAX_BOND_ORDER , MIN_BOND_ORDER , VALENCE_ELECTRONS , ELECTRONS_PER_BOND , MUST_BE_INT , Capping_Strategy , NO_CAP , H_CAP , \
1111 H2_CAP , H3_CAP , H4_CAP , ELECTRONEGATIVITIES , new_atom_for_capping_strategy , min_valence_for
12- from fragment_capping .helpers .misc import write_to_debug
12+ from fragment_capping .helpers .misc import write_to_debug , atom_short_desc
1313from fragment_capping .helpers .graphs import unique_molecules
14- from fragment_capping .helpers .rings import bonds_in_small_rings_for , SMALL_RING
14+ from fragment_capping .helpers .rings import bonds_in_small_rings_for , SMALL_RING , atoms_in_small_rings_for
1515
1616def get_all_tautomers_naive (
1717 molecule : 'Molecule' ,
@@ -54,6 +54,8 @@ def maximum_number_hydrogens_for(atom: Atom) -> int:
5454 if len (bond & capping_atom_ids ) != 0
5555 }
5656
57+ non_capping_bonds = molecule .bonds - capping_bonds
58+
5759 charges = {
5860 atom .index : LpVariable ("C_{i}" .format (i = atom .index ), - MAX_ABSOLUTE_CHARGE , MAX_ABSOLUTE_CHARGE , LpInteger )
5961 for atom in molecule .atoms .values ()
@@ -107,7 +109,7 @@ def maximum_number_hydrogens_for(atom: Atom) -> int:
107109 OBJECTIVES .append (MAX (sum (non_bonded_electrons .values ())))
108110
109111 for atom in map (lambda atom_index : molecule .atoms [atom_index ], charges .keys ()):
110- problem += charges [atom .index ] == VALENCE_ELECTRONS [atom .element ] - sum ([bond_orders [bond ] for bond in molecule .bonds if atom .index in bond ]) - ELECTRON_MULTIPLIER * non_bonded_electrons [atom .index ], '{element}_{index}' . format ( element = atom . element , index = atom . index )
112+ problem += charges [atom .index ] == VALENCE_ELECTRONS [atom .element ] - sum ([bond_orders [bond ] for bond in molecule .bonds if atom .index in bond ]) - ELECTRON_MULTIPLIER * non_bonded_electrons [atom .index ], atom_short_desc ( atom )
111113
112114 # Deal with absolute values
113115 problem += charges [atom .index ] <= absolute_charges [atom .index ], 'Absolute charge contraint left {i}' .format (i = atom .index )
@@ -180,7 +182,7 @@ def debug_failed_ILP(n: Optional[int] = None) -> None:
180182
181183 # Solve once to find optimal solution
182184 try :
183- print ( 'Solving' )
185+ write_to_debug ( debug , 'Solving' )
184186 problem .sequentialSolve (OBJECTIVES )
185187 except Exception as e :
186188 problem .writeLP ('debug.lp' )
@@ -205,7 +207,7 @@ def debug_failed_ILP(n: Optional[int] = None) -> None:
205207 'Solution {n}' .format (n = n )
206208
207209 s = encode_solution ()
208- print ( 'Excluding solution {0}' .format (s ))
210+ write_to_debug ( debug , 'Excluding solution {0}' .format (s ))
209211 solutions .append (s )
210212
211213 try :
@@ -217,7 +219,7 @@ def debug_failed_ILP(n: Optional[int] = None) -> None:
217219 if problem .status == 1 :
218220 pass
219221 else :
220- print ( problem .status , LpStatus [problem .status ])
222+ write_to_debug ( debug , problem .status , LpStatus [problem .status ])
221223 break
222224
223225 all_tautomers .append (new_molecule_for_current_solution (n = n ))
@@ -232,9 +234,10 @@ def get_all_tautomers(
232234 net_charge : Optional [int ] = None ,
233235 enforce_octet_rule : bool = True ,
234236 allow_radicals : bool = False ,
235- max_tautomers : Optional [int ] = 100 ,
237+ max_tautomers : Optional [int ] = 2000 ,
236238 disallow_triple_bond_in_small_rings : bool = True ,
237- use_gurobi : bool = False ,
239+ disallow_allenes_in_small_rings : bool = True ,
240+ use_gurobi : bool = True ,
238241 debug : Optional [TextIO ] = None ,
239242) -> 'Molecule' :
240243 if len ([1 for atom in molecule .atoms .values () if atom .element == 'H' ]) > 0 :
@@ -291,7 +294,6 @@ def possible_capping_strategies_for_atom(atom: Atom) -> List[Capping_Strategy]:
291294 possible_capping_strategies = possible_capping_strategies_for_atom (uncapped_atom )
292295 if len (possible_capping_strategies ) == 0 or len (possible_capping_strategies ) == 1 and possible_capping_strategies [0 ] == NO_CAP :
293296 pass
294- print ('PASS' )
295297 else :
296298 for (i , capping_strategy ) in enumerate (sorted (possible_capping_strategies ), start = 1 ):
297299 write_to_debug (debug , uncapped_atom , capping_strategy , i )
@@ -311,7 +313,7 @@ def possible_capping_strategies_for_atom(atom: Atom) -> List[Capping_Strategy]:
311313 fragment_H_scores [uncapped_atom .index , i ] = len ([atom for atom in capping_atoms_for [uncapped_atom .index , i ] if atom .element == 'H' ])
312314
313315 # Only choose one capping strategy at a time
314- problem += (lpSum (F_i for ((atom_id , _ ), F_i ) in fragment_switches .items () if atom_id == uncapped_atom .index ) == 1 , 'Single capping strategy for atom {element}_{index} ' .format (element = uncapped_atom . element , index = uncapped_atom . index ))
316+ problem += (lpSum (F_i for ((atom_id , _ ), F_i ) in fragment_switches .items () if atom_id == uncapped_atom .index ) == 1 , 'Single capping strategy for atom {atom_desc} ' .format (atom_desc = atom_short_desc ( uncapped_atom ) ))
315317
316318 all_capping_atoms = {atom for atoms in capping_atoms_for .values () for atom in atoms }
317319 all_capping_atom_ids = {atom .index for atom in all_capping_atoms }
@@ -342,6 +344,11 @@ def possible_capping_strategies_for_atom(atom: Atom) -> List[Capping_Strategy]:
342344 )
343345 }
344346
347+ non_capping_bonds = {
348+ bond for bond in molecule .bonds
349+ if len (bond & all_capping_atom_ids ) == 0
350+ }
351+
345352 if True :
346353 molecule .write_graph ('debug' )
347354
@@ -436,6 +443,13 @@ def possible_capping_strategies_for_atom(atom: Atom) -> List[Capping_Strategy]:
436443 'Octet for atom {element}_{index}' .format (element = atom .element , index = atom .index ),
437444 )
438445
446+ for atom in atoms_in_small_rings_for (molecule ):
447+ if disallow_allenes_in_small_rings :
448+ if atom .element in ['C' , 'N' ]:
449+ adjacent_non_hydrogen_bonds = [bond for bond in non_capping_bonds if atom .index in bond ]
450+ if len (adjacent_non_hydrogen_bonds ) == 2 :
451+ problem += sum (bond_orders [bond ] for bond in adjacent_non_hydrogen_bonds ) <= 3 , 'No allenes for atom {atom_desc} in short ring' .format (atom_desc = atom_short_desc (atom ))
452+
439453 def debug_failed_ILP (n : Optional [int ] = None ) -> None :
440454 debug_filename = 'debug{0}.lp' .format ('' if n is None else '_{n}' .format (n = n ))
441455 problem .writeLP (debug_filename )
@@ -509,7 +523,9 @@ def new_molecule_for_current_solution(n: Optional[int] = None) -> 'Molecule':
509523 debug_failed_ILP (0 )
510524 raise
511525
512- debug_failed_ILP (0 )
526+ if debug is not None :
527+ debug_failed_ILP (0 )
528+
513529 all_tautomers = [new_molecule_for_current_solution (n = 0 )]
514530
515531 # Remove redundant constraints from previous multi-objective optimistion
@@ -526,7 +542,7 @@ def new_molecule_for_current_solution(n: Optional[int] = None) -> 'Molecule':
526542 'Solution {n}' .format (n = n )
527543
528544 s = encode_solution ()
529- print ( 'Excluding solution {0}' .format (s ))
545+ write_to_debug ( debug , 'Excluding solution {0}' .format (s ))
530546 solutions .append (s )
531547
532548 try :
@@ -540,8 +556,9 @@ def new_molecule_for_current_solution(n: Optional[int] = None) -> 'Molecule':
540556 if problem .status == 1 :
541557 pass
542558 else :
543- print (problem .status , LpStatus [problem .status ])
544- debug_failed_ILP (n )
559+ write_to_debug (debug , problem .status , LpStatus [problem .status ])
560+ if debug is not None :
561+ debug_failed_ILP (n )
545562 break
546563
547564 all_tautomers .append (new_molecule_for_current_solution (n = n ))
0 commit comments