$darkmode
DENOPTIM
FitnessProviderTest.java
Go to the documentation of this file.
1package denoptim.fitness;
2
3import static org.junit.Assert.assertNotNull;
4
5/*
6 * DENOPTIM
7 * Copyright (C) 2019 Vishwesh Venkatraman <vishwesh.venkatraman@ntnu.no>
8 * and Marco Foscato <marco.foscato@uib.no>
9 *
10 * This program is free software: you can redistribute it and/or modify
11 * it under the terms of the GNU Affero General Public License as published
12 * by the Free Software Foundation, either version 3 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Affero General Public License for more details.
19 *
20 * You should have received a copy of the GNU Affero General Public License
21 * along with this program. If not, see <http://www.gnu.org/licenses/>.
22 */
23
24import static org.junit.jupiter.api.Assertions.assertEquals;
25import static org.junit.jupiter.api.Assertions.assertTrue;
26
27import java.io.File;
28import java.util.ArrayList;
29import java.util.List;
30import java.util.Map;
31import java.util.logging.Level;
32import java.util.logging.Logger;
33
34import org.junit.jupiter.api.BeforeEach;
35import org.junit.jupiter.api.Test;
36import org.junit.jupiter.api.io.TempDir;
37import org.openscience.cdk.DefaultChemObjectBuilder;
38import org.openscience.cdk.exception.InvalidSmilesException;
39import org.openscience.cdk.fingerprint.IBitFingerprint;
40import org.openscience.cdk.fingerprint.PubchemFingerprinter;
41import org.openscience.cdk.interfaces.IAtomContainer;
42import org.openscience.cdk.qsar.DescriptorEngine;
43import org.openscience.cdk.qsar.IDescriptor;
44import org.openscience.cdk.silent.SilentChemObjectBuilder;
45import org.openscience.cdk.smiles.SmilesParser;
46
47import denoptim.constants.DENOPTIMConstants;
48import denoptim.fitness.descriptors.SocketProvidedDescriptorTest;
49import denoptim.fitness.descriptors.TanimotoMolSimilarity;
50import denoptim.io.DenoptimIO;
51
59{
60 private SmilesParser sp;
61 private static final String SEP = System.getProperty("file.separator");
62
63 @TempDir
64 static File tempDir;
65
66 private Logger logger;
67
68 @BeforeEach
69 void setUp()
70 {
71 assertTrue(tempDir.isDirectory(),"Should be a directory ");
72 sp = new SmilesParser(SilentChemObjectBuilder.getInstance());
73 logger = Logger.getLogger("DummyLogger");
74 logger.setLevel(Level.SEVERE);
75 }
76
77//------------------------------------------------------------------------------
78
79 @Test
80 public void testConfigureDescriptorsList() throws Exception
81 {
82 List<String> classNames = new ArrayList<String>();
83 classNames.add("org.openscience.cdk.qsar.descriptors.molecular."
84 + "ZagrebIndexDescriptor");
85 classNames.add("org.openscience.cdk.qsar.descriptors.molecular."
86 + "AtomCountDescriptor");
87 DescriptorEngine engine = new DescriptorEngine(classNames,null);
88 List<IDescriptor> iDescs = engine.instantiateDescriptors(classNames);
89
90 List<DescriptorForFitness> descriptors =
91 new ArrayList<DescriptorForFitness>();
92 for (int i=0; i<iDescs.size(); i++)
93 {
94 IDescriptor iDesc = iDescs.get(i);
96 iDesc.getDescriptorNames()[0],
97 classNames.get(i), iDesc, 0);
98 descriptors.add(dv);
99 }
100
101 FitnessProvider fp = new FitnessProvider(descriptors, "no eq needed",
102 logger);
103
104 assertEquals(2, fp.engine.getDescriptorInstances().size(),
105 "Number of descriptors from custom list");
106 }
107
108//------------------------------------------------------------------------------
109
110 @Test
111 public void testGetFitness() throws Exception
112 {
113 IAtomContainer mol = null;
114 try {
115 mol = sp.parseSmiles("C(C)CO");
116 } catch (InvalidSmilesException e) {
117 // This cannot happen
118 }
119
120 List<String> classNames = new ArrayList<String>();
121 classNames.add("org.openscience.cdk.qsar.descriptors.molecular."
122 + "ZagrebIndexDescriptor");
123 classNames.add("org.openscience.cdk.qsar.descriptors.molecular."
124 + "AtomCountDescriptor");
125 DescriptorEngine engine = new DescriptorEngine(classNames,null);
126 List<IDescriptor> iDescs = engine.instantiateDescriptors(classNames);
127
128 List<DescriptorForFitness> descriptors =
129 new ArrayList<DescriptorForFitness>();
130 String[] varNames = new String[] {"desc0", "desc1"};
131 for (int i=0; i<iDescs.size(); i++)
132 {
133 IDescriptor iDesc = iDescs.get(i);
135 iDesc.getDescriptorNames()[0],
136 classNames.get(i), iDesc, 0);
137 dff.addDependentVariable(new Variable(varNames[i]));
138 descriptors.add(dff);
139 }
140
141 String expression = "${" + varNames[0] + " + " + varNames[1] + "}";
142
143 FitnessProvider fp = new FitnessProvider(descriptors, expression,
144 logger);
145 double fitness = fp.getFitness(mol);
146
147 // The descriptors values must be already in the mol properties map
148 Map<Object, Object> props = mol.getProperties();
149 // 6 properties: title, descpSpec, descSpec, Zagreb val, nAtom val, fitness
150 assertEquals(6,props.size(),"Number of properties in processed mol");
151 List<Object> keys = new ArrayList<Object>();
152 for (Object k : props.keySet())
153 {
154 keys.add(k);
155 }
156 if (props.get(varNames[0]).toString().equals("10.0"))
157 {
158 assertEquals("12.0",props.get(varNames[1]).toString(),
159 "Unexpected descriptor value (A)");
160 } else if (props.get(varNames[1]).toString().equals("10.0"))
161 {
162 assertEquals("10.0",props.get(varNames[0]).toString(),
163 "Unexpected descriptor value (B)");
164 } else {
165 assertTrue(false, "Unexpected descriptor value (C)");
166 }
167
168 double trsh = 0.001;
169 assertTrue(Math.abs(22.0 - fitness) < trsh,
170 "Fitness value should be 22.0 but is " + fitness);
171 }
172
173//------------------------------------------------------------------------------
174
175 @Test
176 public void testGetFitnessWithCustomDescriptors() throws Exception
177 {
178 // Construct a descriptor implementation
179 List<String> classNames = new ArrayList<String>();
180 classNames.add("denoptim.fitness.descriptors.TanimotoMolSimilarity");
181 DescriptorEngine engine = new DescriptorEngine(classNames,null);
182 engine.instantiateDescriptors(classNames);
183 IDescriptor iDesc = new TanimotoMolSimilarity();
184
185 //Customise parameters used to calculate descriptors
186 IAtomContainer ref = sp.parseSmiles("CNC(=O)c1cc(OC)ccc1");
187 PubchemFingerprinter fpMaker = new PubchemFingerprinter(
188 DefaultChemObjectBuilder.getInstance());
189 IBitFingerprint fpRef = fpMaker.getBitFingerprint(ref);
190 Object[] params = {"PubchemFingerprinter", fpRef};
191 iDesc.setParameters(params);
192
193 String myVarName = "myVar";
194
195 //Configure fitness provider
197 iDesc.getDescriptorNames()[0],
198 iDesc.getClass().getName(), iDesc, 0);
199 dff.addDependentVariable(new Variable (myVarName));
200 List<DescriptorForFitness> descriptors =
201 new ArrayList<DescriptorForFitness>();
202 descriptors.add(dff);
203 String expression = "${" + myVarName + "}";
204 FitnessProvider fp = new FitnessProvider(descriptors, expression,
205 logger);
206
207 //Construct a molecule to be evaluated by the fitness provider
208 IAtomContainer mol = sp.parseSmiles("COc1ccccc1");
209
210 //Calculate fitness
211 double fitness = fp.getFitness(mol);
212
213 //Get the result and check it
214 Object propObj = mol.getProperty(DENOPTIMConstants.FITNESSTAG);
215 assertTrue(propObj!=null,"Fitness is not null.");
216 double trsh = 0.01;
217 assertTrue(Math.abs(((double) propObj) - fitness) < trsh,
218 "Fitness value should be 0.6 but is " + fitness);
219 assertTrue(Math.abs(0.6 - fitness) < trsh,
220 "Fitness value should be 0.6 but is " + fitness);
221 }
222
223//------------------------------------------------------------------------------
224
230 @Test
231 public void testGetFitnessWithParametrizedDescriptors() throws Exception
232 {
233 String fileName = tempDir.getAbsolutePath() + SEP + "ref.sdf";
234 IAtomContainer ref = sp.parseSmiles("CNC(=O)c1cc(OC)ccc1");
235 DenoptimIO.writeSDFFile(fileName, ref, false);
236
238
239 String[] lines = new String[] {
240 "FP-Equation=${taniSym + taniBis + 0.02 * Zagreb - aHyb_1 +"
241 + " aHyb_2}",
242 "FP-DescriptorSpecs=${Variable.atomSpecific('aHyb_1','aHyb','[$([C])]')}",
243 "FP-DescriptorSpecs=${Variable.atomSpecific('aHyb_2','aHyb','[$([O])]')}",
244 "FP-DescriptorSpecs=${Variable.parametrized('taniSym',"
245 + "'TanimotoSimilarity','PubchemFingerprinter, "
246 + "FILE:" + fileName + "')}",
247 "FP-DescriptorSpecs=${Variable.parametrized('taniBis',"
248 + "'TanimotoSimilarity','GraphOnlyFingerprinter, "
249 + "FILE:" + fileName + "')}"};
250 for (int i=0; i<lines.length; i++)
251 {
252 String line = lines[i];
253 fitPar.interpretKeyword(line);
254 }
255 fitPar.processParameters();
256
258 fitPar.getDescriptors(),
259 fitPar.getFitnessExpression(),
260 logger);
261
262 IAtomContainer mol = sp.parseSmiles("COc1ccccc1");
263
264 fp.getFitness(mol);
265
266 String[] expectedProps = new String[] {DENOPTIMConstants.FITNESSTAG,
267 "Zagreb","taniBis","taniSym","aHyb_1","aHyb_2"};
268 double[] expectedValue = new double[] {
269 2.5755, // fitness
270 34.000, // Zagreb
271 0.4318, // taniBis
272 0.6065, // taniSym
273 2.1428, // aHyb_1
274 3.0000 // aHyb_2
275 };
276 for (int i=0; i<expectedProps.length; i++)
277 {
278 Object p = mol.getProperty(expectedProps[i]);
279 double value = Double.parseDouble(p.toString());
280 assertTrue(closeEnough(expectedValue[i], value),
281 "Value of property '" + expectedProps[i] + "' should be "
282 + expectedValue[i] + " but is " + value);
283 }
284 }
285
286//------------------------------------------------------------------------------
287
288 @Test
289 public void testGetConstantFitness() throws Exception
290 {
292 fitPar.interpretKeyword("FP-Equation=${1.23456}");
293 fitPar.processParameters();
294
296 fitPar.getDescriptors(),
297 fitPar.getFitnessExpression(),
298 logger);
299
300 IAtomContainer mol = sp.parseSmiles("COc1ccccc1");
301
302 fp.getFitness(mol);
303
304 Object prop = mol.getProperty(DENOPTIMConstants.FITNESSTAG);
305 assertTrue(prop != null, "Fitness property found in molecule");
306 assertTrue(closeEnough(1.23456, Double.parseDouble(prop.toString())),
307 "Numerical result (" + Double.parseDouble(prop.toString())
308 + ") is correct");
309 }
310
311//------------------------------------------------------------------------------
312
313 @Test
314 public void testNaNFitness() throws Exception
315 {
316 SocketProvidedDescriptorTest socketServerUtil =
318 try {
319 socketServerUtil.setUpServer();
320
322 fitPar.interpretKeyword("FP-Equation=${fromSocket}");
323 fitPar.interpretKeyword("FP-DescriptorSpecs="
324 + "${Variable.parametrized('fromSocket',"
325 + "'SocketProvidedDescriptor','localhost, "
326 + socketServerUtil.descriptor.getParameters()[1] + "')}");
327 fitPar.processParameters();
328
330 fitPar.getDescriptors(),
331 fitPar.getFitnessExpression(),
332 logger);
333
334 String smiles = "O";
335 IAtomContainer mol = sp.parseSmiles(smiles);
336 mol.setProperty("SMILES", smiles);
337
338 double fitness = fp.getFitness(mol);
339
340 assertTrue(Double.isNaN(fitness));
341 Object prop = mol.getProperty(DENOPTIMConstants.MOLERRORTAG);
342 assertNotNull(prop);
343 assertTrue(prop.toString().contains(socketServerUtil.fakeErrorMsg));
344
345 } finally {
346 socketServerUtil.closeServer();
347 }
348 }
349
350//------------------------------------------------------------------------------
351
352 private boolean closeEnough(double expected, double actual)
353 {
354 double threshold = 0.0001;
355 double delta = Math.abs(expected-actual);
356 return delta < threshold;
357 }
358
359//------------------------------------------------------------------------------
360
361}
General set of constants used in DENOPTIM.
static final String MOLERRORTAG
SDF tag containing errors during execution of molecule specific tasks.
static final String FITNESSTAG
SDF tag containing the fitness of a candidate.
This is a reference to a specific descriptor value.
void addDependentVariable(Variable v)
Append the reference to a variable that used data produced by the calculation of this descriptor.
Settings defining the calculation of fitness.
void processParameters()
Processes all parameters and initialize related objects.
List< DescriptorForFitness > getDescriptors()
void interpretKeyword(String key, String value)
Processes a keyword/value pair and assign the related parameters.
DENOPTIM's (internal) fitness provider calculates the value of Variables that are used in an expressi...
DescriptorEngine engine
The engine that collects and calculates descriptors.
double getFitness(IAtomContainer iac)
Calculated the fitness according to the current configuration.
Unit test for internal fitness provider.
boolean closeEnough(double expected, double actual)
void testGetFitnessWithParametrizedDescriptors()
This test is reproducing most of what done in FitnessParametersTest#testProcessExpressions() so if bo...
A variable in the expression defining the fitness.
Definition: Variable.java:42
Unit test for descriptor SocketProvidedDescriptor.
Calculates the molecular similarity against a target compound the fingerprint of which is given as pa...
Utility methods for input/output.
static void writeSDFFile(String fileName, IAtomContainer mol)
Writes IAtomContainer to SDF file.