$darkmode
DENOPTIM
PathClosabilityToolsTest.java
Go to the documentation of this file.
1/*
2 * DENOPTIM
3 * Copyright (C) 2022 Marco Foscato <marco.foscato@uib.no>
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Affero General Public License as published
7 * by the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Affero General Public License for more details.
14 *
15 * You should have received a copy of the GNU Affero General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19package denoptim.graph.rings;
20
21import static org.junit.jupiter.api.Assertions.assertEquals;
22import static org.junit.jupiter.api.Assertions.assertFalse;
23import static org.junit.jupiter.api.Assertions.assertNotEquals;
24import static org.junit.jupiter.api.Assertions.assertTrue;
25
26import java.util.ArrayList;
27import java.util.HashMap;
28import java.util.HashSet;
29import java.util.List;
30import java.util.Map;
31import java.util.Set;
32import java.util.logging.Logger;
33
34import javax.vecmath.Point3d;
35
36import org.junit.jupiter.api.Test;
37import org.openscience.cdk.Atom;
38import org.openscience.cdk.PseudoAtom;
39import org.openscience.cdk.interfaces.IAtom;
40import org.openscience.cdk.interfaces.IAtomContainer;
41import org.openscience.cdk.interfaces.IChemObjectBuilder;
42import org.openscience.cdk.silent.SilentChemObjectBuilder;
43
44import denoptim.exception.DENOPTIMException;
45import denoptim.fragspace.FragmentSpace;
46import denoptim.fragspace.FragmentSpaceParameters;
47import denoptim.graph.APClass;
48import denoptim.graph.DGraph;
49import denoptim.graph.Fragment;
50import denoptim.graph.Vertex;
51import denoptim.graph.Vertex.BBType;
52import denoptim.molecularmodeling.ThreeDimTreeBuilder;
53import denoptim.utils.Randomizer;
54
62{
63
64//------------------------------------------------------------------------------
65
66 @Test
67 public void testEvaluateConstitutionalClosability() throws Exception
68 {
69 /*
70 * Make the test graph (make it here so we have direct access to vertexes)
71 *
72 * RCV_P RCV_P RCV_P RCV_M
73 * | | | |
74 * RCV_M--[O]----[C]--[C]--[C]--[C]--[C]----[N]--RCV_M
75 * vO vC vC2 vC3 vC4 vC5 vN
76 *
77 */
78
79 APClass apc = APClass.make("A", 0);
80
81 IChemObjectBuilder builder = SilentChemObjectBuilder.getInstance();
82
83 IAtomContainer iacO = builder.newAtomContainer();
84 IAtom aO = new Atom("O",new Point3d(0,0,0));
85 iacO.addAtom(aO);
86 Fragment vO = new Fragment(0, iacO,BBType.FRAGMENT);
87 vO.addAP(0, new Point3d(0,-1,0), apc);
88 vO.addAP(0, new Point3d(2,0,0), apc);
89 vO.addAP(0, new Point3d(0,1,0), apc);
90
91 IAtomContainer iacC = builder.newAtomContainer();
92 IAtom aC = new Atom("C",new Point3d(0,0,0));
93 iacC.addAtom(aC);
94 Fragment vC = new Fragment(1, iacC,BBType.FRAGMENT);
95 vC.addAP(0, new Point3d(0,-1,0), apc);
96 vC.addAP(0, new Point3d(2,0,0), apc);
97 vC.addAP(0, new Point3d(0,1,0), apc);
98
99 IAtomContainer iacCd = builder.newAtomContainer();
100 IAtom aCd = new Atom("C",new Point3d(0,0,0));
101 iacCd.addAtom(aCd);
102 Fragment vC2 = new Fragment(2, iacCd,BBType.FRAGMENT);
103 vC2.addAP(0, new Point3d(0,-1,0), apc);
104 vC2.addAP(0, new Point3d(0,1,0), apc);
105
106 Fragment vC3 = vC2.clone();
107 vC3.setVertexId(33);
108
109 Fragment vC4 = vC2.clone();
110 vC4.setVertexId(34);
111
112 Fragment vC5 = vC.clone();
113 vC5.setVertexId(3);
114
115 IAtomContainer iacN = builder.newAtomContainer();
116 IAtom aN = new Atom("N",new Point3d(0,0,0));
117 iacN.addAtom(aN);
118 Fragment vN = new Fragment(4, iacN,BBType.FRAGMENT);
119 vN.addAP(0, new Point3d(0,-1,0), apc);
120 vN.addAP(0, new Point3d(2,0,0), apc);
121 vN.addAP(0, new Point3d(0,1,0), apc);
122
123 APClass atMinus = APClass.RCACLASSMINUS;
124
125 IAtomContainer iacD = builder.newAtomContainer();
126 iacD.addAtom(new PseudoAtom(RingClosingAttractor.RCALABELPERAPCLASS.get(atMinus),
127 new Point3d(0,0,0)));
128 Fragment rcvM = new Fragment(5, iacD,BBType.FRAGMENT);
129 rcvM.addAP(0, new Point3d(-1,0,0), atMinus);
130 rcvM.setAsRCV(true);
131
132 Fragment rcvM2 = rcvM.clone();
133 rcvM2.setVertexId(6);
134
135 Fragment rcvM3 = rcvM.clone();
136 rcvM3.setVertexId(7);
137
139
140 IAtomContainer iacE = builder.newAtomContainer();
141 iacE.addAtom(new PseudoAtom(RingClosingAttractor.RCALABELPERAPCLASS.get(atPlus),
142 new Point3d(0,0,0)));
143 Fragment rcvP = new Fragment(8, iacE,BBType.FRAGMENT);
144 rcvP.addAP(0, new Point3d(-1,0,0), atPlus);
145 rcvP.setAsRCV(true);
146
147 Fragment rcvP2 = rcvP.clone();
148 rcvP2.setVertexId(9);
149
150 Fragment rcvP3 = rcvP.clone();
151 rcvP3.setVertexId(10);
152
153 DGraph graph = new DGraph();
154 graph.addVertex(vC);
155 graph.appendVertexOnAP(vC.getAP(0), vO.getAP(2));
156 graph.appendVertexOnAP(vO.getAP(0), rcvM.getAP(0));
157 graph.appendVertexOnAP(vO.getAP(1), rcvP.getAP(0));
158 graph.appendVertexOnAP(vC.getAP(1), rcvP2.getAP(0));
159 graph.appendVertexOnAP(vC.getAP(2), vC2.getAP(1));
160 graph.appendVertexOnAP(vC2.getAP(0), vC3.getAP(0));
161 graph.appendVertexOnAP(vC3.getAP(1), vC4.getAP(0));
162 graph.appendVertexOnAP(vC4.getAP(1), vC5.getAP(0));
163 graph.appendVertexOnAP(vC5.getAP(1), rcvP3.getAP(0));
164 graph.appendVertexOnAP(vC5.getAP(2), vN.getAP(0));
165 graph.appendVertexOnAP(vN.getAP(1), rcvM2.getAP(0));
166 graph.appendVertexOnAP(vN.getAP(2), rcvM3.getAP(0));
167
168 // Prepare all that it is needed to run a ring-size management case
169
170 Logger logger = Logger.getLogger("DummyLogger");
171 Randomizer rng = new Randomizer();
172
173 //DenoptimIO.writeGraphToSDF(new File("/tmp/graph.sdf"), graph, false, logger, rng);
174
175 ThreeDimTreeBuilder t3d = new ThreeDimTreeBuilder(logger, rng);
176 t3d.setAlignBBsIn3D(false); //3D not needed
177 IAtomContainer mol = t3d.convertGraphTo3DAtomContainer(graph, true);
178
180 // By default no constitutional check is expected.
181
182 // uncomment to log on console
183 //rcParams.startConsoleLogger("logger");
184 //rcParams.setVerbosity(2);
185
186 //
187 // First case: no checking of constitution
188 //
189
190 List<Vertex> rcvs = graph.getRCVertices();
191 int numCompatibilities = 0;
192 for (int i=0; i<rcvs.size(); i++)
193 {
194 Vertex vI = rcvs.get(i);
195 for (int j=i+1; j<rcvs.size(); j++)
196 {
197 Vertex vJ = rcvs.get(j);
198 PathSubGraph path = new PathSubGraph(vI, vJ, graph);
199 if (PathClosabilityTools.isCloseable(path, mol, rcParams))
200 {
201 numCompatibilities++;
202 } else {
203 assertTrue(false);
204 }
205 }
206 }
207 assertEquals(15, numCompatibilities);
208
209 //
210 // Second case: constitutional constrains are empty, so any combination
211 // of RCVs is forbidden
212 //
213
214 rcParams.rceMode = 0; // ONLY constitution
215 for (int i=0; i<rcvs.size(); i++)
216 {
217 Vertex vI = rcvs.get(i);
218 for (int j=i+1; j<rcvs.size(); j++)
219 {
220 Vertex vJ = rcvs.get(j);
221 PathSubGraph path = new PathSubGraph(vI, vJ, graph);
222 assertFalse(PathClosabilityTools.isCloseable(path, mol, rcParams));
223 }
224 }
225
226 //
227 // Third case: constitutional constrains are present and allow
228 // to close rings only for a minority of possible combinations of RCVs.
229 //
230
231 Map<String,String> allowedConstitutions = new HashMap<String,String>();
232 allowedConstitutions.put("case-1", "[#6]1[#6][#8][#6][#6][#6]1");
233 allowedConstitutions.put("case-2", "[#6]1[#6][#7][#6][#6][#6]1");
234 rcParams.setConstitutionalClosabilityConds(allowedConstitutions);
235
236 Map<Vertex,Set<Vertex>> expectedRCCompatibilities =
237 new HashMap<Vertex,Set<Vertex>>();
238 expectedRCCompatibilities.put(rcvM, new HashSet<Vertex>());
239 expectedRCCompatibilities.get(rcvM).add(rcvP3);
240 expectedRCCompatibilities.put(rcvP2, new HashSet<Vertex>());
241 expectedRCCompatibilities.get(rcvP2).add(rcvM2);
242 expectedRCCompatibilities.get(rcvP2).add(rcvM3);
243 // NB: the following are combinations of RCVs that are NOT
244 // permitted by compatibility matrix, but the PathClosabilityTools
245 // do not check for APClass or RCV type compatibility, which is meant
246 // to be done elsewhere.
247 expectedRCCompatibilities.put(rcvP, new HashSet<Vertex>());
248 expectedRCCompatibilities.get(rcvP).add(rcvP3);
249
250 numCompatibilities = 0;
251 for (int i=0; i<rcvs.size(); i++)
252 {
253 Vertex vI = rcvs.get(i);
254 for (int j=i+1; j<rcvs.size(); j++)
255 {
256 Vertex vJ = rcvs.get(j);
257 PathSubGraph path = new PathSubGraph(vI, vJ, graph);
258 if (PathClosabilityTools.isCloseable(path, mol, rcParams))
259 {
260 numCompatibilities++;
261 assertTrue(expectedRCCompatibilities.containsKey(vI));
262 assertTrue(expectedRCCompatibilities.get(vI).contains(vJ));
263 }
264 }
265 }
266 assertEquals(4, numCompatibilities);
267
268 //
269 // Fourth case: constitutional constrains pertain only the presence of
270 // certain elements in the newly generated rings
271 //
272
273 rcParams = new RingClosureParameters();
274 rcParams.rceMode = 0; // ONLY constitution
275 Set<String> elementsRequired = new HashSet<String>();
276 elementsRequired.add("N");
277 elementsRequired.add("Ru");
278 rcParams.reqElInRings = elementsRequired;
279
280 expectedRCCompatibilities = new HashMap<Vertex,Set<Vertex>>();
281 expectedRCCompatibilities.put(rcvP, new HashSet<Vertex>());
282 expectedRCCompatibilities.get(rcvP).add(rcvM2);
283 expectedRCCompatibilities.get(rcvP).add(rcvM3);
284 expectedRCCompatibilities.put(rcvP2, new HashSet<Vertex>());
285 expectedRCCompatibilities.get(rcvP2).add(rcvM2);
286 expectedRCCompatibilities.get(rcvP2).add(rcvM3);
287 // NB: the following are combinations of RCVs that are NOT
288 // permitted by compatibility matrix, but the PathClosabilityTools
289 // do not check for APClass or RCV type compatibility, which is meant
290 // to be done elsewhere.
291 expectedRCCompatibilities.put(rcvM, new HashSet<Vertex>());
292 expectedRCCompatibilities.get(rcvM).add(rcvM2);
293 expectedRCCompatibilities.get(rcvM).add(rcvM3);
294 expectedRCCompatibilities.put(rcvP3, new HashSet<Vertex>());
295 expectedRCCompatibilities.get(rcvP3).add(rcvM2);
296 expectedRCCompatibilities.get(rcvP3).add(rcvM3);
297 expectedRCCompatibilities.put(rcvM2, new HashSet<Vertex>());
298 expectedRCCompatibilities.get(rcvM2).add(rcvM3);
299
300 numCompatibilities = 0;
301 for (int i=0; i<rcvs.size(); i++)
302 {
303 Vertex vI = rcvs.get(i);
304 for (int j=i+1; j<rcvs.size(); j++)
305 {
306 Vertex vJ = rcvs.get(j);
307 PathSubGraph path = new PathSubGraph(vI, vJ, graph);
308 if (PathClosabilityTools.isCloseable(path, mol, rcParams))
309 {
310 numCompatibilities++;
311 assertTrue(expectedRCCompatibilities.containsKey(vI));
312 assertTrue(expectedRCCompatibilities.get(vI).contains(vJ));
313 }
314 }
315 }
316 assertEquals(9, numCompatibilities);
317 }
318
319//------------------------------------------------------------------------------
320
321}
static final APClass RCACLASSPLUS
Conventional class of attachment points on ring-closing vertexes.
Definition: APClass.java:84
static final APClass RCACLASSMINUS
Conventional class of attachment points on ring-closing vertexes.
Definition: APClass.java:91
static APClass make(String ruleAndSubclass)
Creates an APClass if it does not exist already, or returns the reference to the existing instance.
Definition: APClass.java:136
Container for the list of vertices and the edges that connect them.
Definition: DGraph.java:102
void addVertex(Vertex vertex)
Appends a vertex to this graph without creating any edge.
Definition: DGraph.java:1097
ArrayList< Vertex > getRCVertices()
Search for ring closing vertices: vertices that contain only a RingClosingAttractor
Definition: DGraph.java:962
void appendVertexOnAP(AttachmentPoint srcAP, AttachmentPoint trgAP)
Append a vertex to this graph: adds the new vertex to the list of vertices belonging to the graph,...
Definition: DGraph.java:5776
Class representing a continuously connected portion of chemical object holding attachment points.
Definition: Fragment.java:61
void addAP(int atomPositionNumber)
Adds an attachment point with a dummy APClass.
Definition: Fragment.java:343
Fragment clone()
Returns a deep copy of this fragments.
Definition: Fragment.java:733
A vertex is a data structure that has an identity and holds a list of AttachmentPoints.
Definition: Vertex.java:61
void setVertexId(long vertexId2)
Definition: Vertex.java:261
void setAsRCV(boolean isRCV)
Definition: Vertex.java:254
AttachmentPoint getAP(int i)
Get attachment point i on this vertex.
Definition: Vertex.java:920
Tool box for determining whether a chain of atoms, i.e., a path, can be folded as to form a ring-clos...
static boolean isCloseable(PathSubGraph subGraph, IAtomContainer mol, RingClosureParameters settings)
Method to evaluate the closability of a single path in a graph.
This object represents a path in a DGraph.
The RingClosingAttractor represent the available valence/connection that allows to close a ring.
static final HashMap< APClass, String > RCALABELPERAPCLASS
Conventional labels for attractor pseudoatom.
Parameters and setting related to handling ring closures.
void setConstitutionalClosabilityConds(Map< String, String > ringClosabCondAsSMARTS)
Sets the list of constitutions that can be formed by ring closure.
Tool to build build three-dimensional (3D) tree-like molecular structures from DGraph.
void setAlignBBsIn3D(boolean align)
Sets the flag that controls whether building blocks have to be aligned according to the AP vectors or...
IAtomContainer convertGraphTo3DAtomContainer(DGraph graph)
Created a three-dimensional molecular representation from a given DGraph.
Tool to generate random numbers and random decisions.
Definition: Randomizer.java:35
The type of building block.
Definition: Vertex.java:86