$darkmode
DENOPTIM
FitnessTask.java
Go to the documentation of this file.
1/*
2 * DENOPTIM
3 * Copyright (C) 2019 Vishwesh Venkatraman <vishwesh.venkatraman@ntnu.no> and
4 * Marco Foscato <marco.foscato@uib.no>
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Affero General Public License as published
8 * by the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Affero General Public License for more details.
15 *
16 * You should have received a copy of the GNU Affero General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20package denoptim.task;
21
22import java.io.File;
23import java.io.IOException;
24import java.util.logging.Level;
25
26import org.apache.commons.io.FileUtils;
27import org.openscience.cdk.Atom;
28import org.openscience.cdk.AtomContainer;
29import org.openscience.cdk.interfaces.IAtomContainer;
30import org.openscience.cdk.interfaces.IChemObjectBuilder;
31import org.openscience.cdk.silent.SilentChemObjectBuilder;
32
33import denoptim.combinatorial.GraphBuildingTask;
34import denoptim.constants.DENOPTIMConstants;
35import denoptim.exception.DENOPTIMException;
36import denoptim.fitness.FitnessParameters;
37import denoptim.fitness.FitnessProvider;
38import denoptim.graph.Candidate;
39import denoptim.graph.DGraph;
40import denoptim.io.DenoptimIO;
41import denoptim.molecularmodeling.ThreeDimTreeBuilder;
42import denoptim.utils.MoleculeUtils;
43import denoptim.utils.TaskUtils;
44
49public abstract class FitnessTask extends Task
50{
54 protected DGraph dGraph;
55
64 protected IAtomContainer fitProvMol = null;
65
69 protected Candidate result;
70
74 protected String fitProvInputFile = "noName"
76
80 protected String fitProvOutFile = "noName"
82
87 protected String fitProvPNGFile = "noName"
89
94 protected String fitProvUIDFile = null;
95
100 protected boolean fitnessIsRequired = false;
101
106
107//------------------------------------------------------------------------------
108
110 {
112 this.fitnessSettings = settings;
113 this.result = c;
114 this.dGraph = c.getGraph();
115 }
116
117//------------------------------------------------------------------------------
118
126 protected void runFitnessProvider() throws DENOPTIMException
127 {
128 // Ensure these two variables have been set
130 if (fitProvMol == null)
131 {
136 }
137
138 if (fitProvMol.getProperty(DENOPTIMConstants.PROVENANCE) == null ||
139 fitProvMol.getProperty(
140 DENOPTIMConstants.PROVENANCE).toString().equals(""))
141 {
143 }
144
145 // Run fitness provider
146 boolean status = false;
148 // Write file with input data to fitness provider
150
151 // NB: inside this call we change fitProvMol for a reordered copy:
152 // reference will not work!
153 status = runExternalFitness();
154 } else {
155 // NB: the internal fitness provider removes dummy atoms before
156 // calculating CDK descriptors, so the 'fitProvMol' changes
157 status = runInternalFitness();
158 }
159
160 // Write the FIT file
162 if (this instanceof GraphBuildingTask
164 {
166 false);
167 }
168
169 // Optional image creation
170 if (status && fitnessSettings.makePictures())
171 {
172 try
173 {
177 }
178 catch (Exception ex)
179 {
180 result.setImageFile(null);
181 fitnessSettings.getLogger().log(Level.WARNING,
182 "Unable to create image. {0}", ex.getMessage());
183 }
184 }
185 }
186
187//------------------------------------------------------------------------------
188
197 private boolean runExternalFitness() throws DENOPTIMException
198 {
199 StringBuilder sb = new StringBuilder();
201 sb.append(" ").append(fitnessSettings.getExternalFitnessProvider())
202 .append(" ").append(fitProvInputFile)
203 .append(" ").append(fitProvOutFile)
204 .append(" ").append(workDir)
205 .append(" ").append(id);
206 if (fitProvUIDFile != null)
207 {
208 sb.append(" ").append(fitProvUIDFile);
209 }
210
211 String msg = "Calling external fitness provider: => " + sb + NL;
212 fitnessSettings.getLogger().log(Level.INFO, msg);
213
214 // run the process
215 processHandler = new ProcessHandler(sb.toString(),Integer.toString(id));
216
218 if (processHandler.getExitCode() != 0)
219 {
220 msg = "Failed to execute fitness provider "
222 .toString()
224 + "' on " + fitProvInputFile;
225 fitnessSettings.getLogger().severe(msg);
226 fitnessSettings.getLogger().severe(
228 throw new DENOPTIMException(msg);
229 }
230 processHandler = null;
231
232 // Read results from fitness provider
233 IChemObjectBuilder builder = SilentChemObjectBuilder.getInstance();
234 IAtomContainer processedMol = builder.newAtomContainer();
235 boolean unreadable = false;
236 try
237 {
238 processedMol = DenoptimIO.readAllAtomContainers(new File(
239 fitProvOutFile)).get(0);
240 if (processedMol.isEmpty())
241 {
242 unreadable=true;
243 }
244 Object o = processedMol.getProperty(DENOPTIMConstants.ATMPROPVERTEXID);
245 if (o != null)
246 {
247 String[] parts = o.toString().trim().split("\\s+");
248 if (processedMol.getAtomCount() != parts.length)
249 {
250 throw new DENOPTIMException("Inconsistent number of vertex "
251 + "IDs (" + parts.length + ") and atoms ("
252 + processedMol.getAtomCount() + ") in candidate "
253 + "processed by external fitness provider.");
254 }
255 for (int i=0; i<processedMol.getAtomCount(); i++)
256 {
257 processedMol.getAtom(i).setProperty(
259 Integer.parseInt(parts[i]));
260 }
261 }
262 }
263 catch (Throwable t)
264 {
265 unreadable=true;
266 }
267
268 if (unreadable)
269 {
270 // If file is not properly readable, we keep track of the
271 // unreadable file, and we label the candidate to signal the
272 // error, and we replace the unreadable one with a file that
273 // is readable.
274
275 msg = "Unreadable file from fitness provider run (Task " + id
276 + "). Check " + result.getName() + ".";
277 fitnessSettings.getLogger().log(Level.WARNING, msg);
278
279 String fileBkp = fitProvOutFile
281 try {
282 FileUtils.copyFile(new File(fitProvOutFile), new File(fileBkp));
283 } catch (IOException e) {
284 // At this point the file must be there!
285 throw new DENOPTIMException("File '"+ fitProvOutFile + "' has "
286 + "disappeared (it was there, but not anymore!)");
287 }
288 FileUtils.deleteQuietly(new File(fitProvOutFile));
289
290 String err = "#FTask: Unable to retrive data. See " + fileBkp;
291 processedMol = new AtomContainer();
292 processedMol.addAtom(new Atom("H"));
293
294 result.setChemicalRepresentation(processedMol);
295 result.setError(err);
296 return false;
297 }
298
299 // Unique identifier might be updated by the fitness provider, so
300 // we need to update the returned value
301 if (processedMol.getProperty(DENOPTIMConstants.UNIQUEIDTAG) != null)
302 {
303 result.setUID(processedMol.getProperty(
304 DENOPTIMConstants.UNIQUEIDTAG).toString());
305 }
306
307 if (processedMol.getProperty(DENOPTIMConstants.MOLERRORTAG) != null)
308 {
309 String err = processedMol.getProperty(
310 DENOPTIMConstants.MOLERRORTAG).toString();
311 msg = result.getName() + " has an error ("+err+")";
312 fitnessSettings.getLogger().info(msg);
313
314 result.setChemicalRepresentation(processedMol);
315 result.setError(err);
316 return false;
317 }
318
319 if (processedMol.getProperty(DENOPTIMConstants.FITNESSTAG) != null)
320 {
321 String fitprp = processedMol.getProperty(
322 DENOPTIMConstants.FITNESSTAG).toString();
323 double fitVal = 0.0;
324 try
325 {
326 fitVal = Double.parseDouble(fitprp);
327 }
328 catch (Throwable t)
329 {
330 // TODO: why sync? Is it really needed?
331 synchronized (lock)
332 {
333 hasException = true;
334 msg = "Fitness value '" + fitprp + "' of "
335 + result.getName() + " could not be converted "
336 + "to double.";
337 errMsg = msg;
338 thrownExc = t;
339 }
340 fitnessSettings.getLogger().severe(msg);
341 dGraph.cleanup();
342 throw new DENOPTIMException(msg);
343 }
344
345 if (Double.isNaN(fitVal))
346 {
347 synchronized (lock)
348 {
349 hasException = true;
350 msg = "Fitness value is NaN for " + result.getName();
351 errMsg = msg;
352 }
353 fitnessSettings.getLogger().severe(msg);
354 dGraph.cleanup();
355 throw new DENOPTIMException(msg);
356 }
357
358 //TODO: consider this...
359 // We want to retain as much as possible of the info we had on the
360 // initial, pre-processing molecular representation. However, the
361 // external task may have altered the molecular representation
362 // to the point we cannot recover. Still, since the graph may be
363 // conceptual, i.e., it intentionally does not translate into a valid
364 // molecular representation within DENOPTIM, but it does so only
365 // within the external fitness provider, we might still prefer
366 // to collect the final molecular representation that generated by
367 // the external tasks. This could be something to be made optional.
368
369 // Replace initial molecular representation of this object with
370 // that coming from the external fitness provider.
371 fitProvMol = processedMol;
372 result.setChemicalRepresentation(processedMol);
373 result.setFitness(fitVal);
374 } else {
376 {
377 synchronized (lock)
378 {
379 hasException = true;
380 msg = "Could not find '" + DENOPTIMConstants.FITNESSTAG
381 + "' tag in: " + fitProvOutFile;
382 errMsg = msg;
383 }
384 fitnessSettings.getLogger().severe(msg);
385 throw new DENOPTIMException(msg);
386 }
387 }
388 return true;
389 }
390
391//------------------------------------------------------------------------------
392
400 private boolean runInternalFitness() throws DENOPTIMException
401 {
402 String msg = "Calling internal fitness provider. "+ NL;
403 fitnessSettings.getLogger().log(Level.FINE, msg);
404
405 double fitVal = Double.NaN;
406 try {
411 // NB: here we remove dummy atoms!
412 fitVal = fp.getFitness(fitProvMol);
413 } catch (Exception e) {
414 throw new DENOPTIMException("Failed to calculate fitness.", e);
415 }
416
417 if (Double.isNaN(fitVal))
418 {
419 msg = "Fitness value is NaN for " + result.getName();
420 errMsg = msg;
421 fitnessSettings.getLogger().severe(msg);
422
425 "#InternalFitness: NaN value");
426 result.setError(msg);
427
428 //TODO-V3 make ignoring of NaN optional
429 /*
430 dGraph.cleanup();
431 throw new DENOPTIMException(msg);
432 */
433 } else {
434 result.setFitness(fitVal);
435 }
436
437 return true;
438 }
439
440//------------------------------------------------------------------------------
441
442}
Task that builds a graph by appending a given combination of fragments onto a given list of attachmen...
General set of constants used in DENOPTIM.
static final String UNREADABLEFILEPOSTFIX
Postfix used to mark a file that cannot be read.
static final String PROVENANCE
SDF tag containing provenance data for a graph.
static final String CANDIDATE2DEXTENSION
Extension of output file with 2D picture of candidate.
static final String ATMPROPVERTEXID
String tag of Atom property used to store the unique ID of the Vertex corresponding to the molecular ...
static final String UNIQUEIDTAG
SDF tag containing the unique identifier of a candidate.
static final String MOLERRORTAG
SDF tag containing errors during execution of molecule specific tasks.
static final String FITFILENAMEEXTOUT
Ending and extension of output file of external fitness provider.
static final String FITNESSTAG
SDF tag containing the fitness of a candidate.
static final String FITFILENAMEEXTIN
Ending and extension of input file of external fitness provider.
Settings defining the calculation of fitness.
String getExternalFitnessProvider()
Gets the pathname of the external executable file.
String getExternalFitnessProviderInterpreter()
Gets the interpreter used to run the external fitness provider.
boolean useExternalFitness
Flag indication we want to use external fitness provider.
List< DescriptorForFitness > getDescriptors()
boolean makePictures
Flag controlling production of png graphics for each candidate.
boolean writeCandidatesOnDisk
Flag requesting to write a file that collects all info on an evaluated candidate, i....
DENOPTIM's (internal) fitness provider calculates the value of Variables that are used in an expressi...
double getFitness(IAtomContainer iac)
Calculated the fitness according to the current configuration.
A candidate is the combination of a denoptim graph with molecular representation and may include also...
Definition: Candidate.java:40
void setSDFFile(String molFile)
Definition: Candidate.java:445
void setImageFile(String imgFile)
Definition: Candidate.java:452
void setFitness(double fitness)
Definition: Candidate.java:480
void setUID(String uid)
Definition: Candidate.java:466
void setError(String error)
Definition: Candidate.java:488
void setChemicalRepresentation(IAtomContainer iac)
Just place the argument in the IAtomContainer field of this object.
Definition: Candidate.java:378
Container for the list of vertices and the edges that connect them.
Definition: DGraph.java:102
void cleanup()
Wipes the data in this graph.
Definition: DGraph.java:3368
Utility methods for input/output.
static void writeSDFFile(String fileName, IAtomContainer mol)
Writes IAtomContainer to SDF file.
static void writeCandidateToFile(File file, Candidate candidate, boolean append)
Writes one candidate item to file.
static List< IAtomContainer > readAllAtomContainers(File file)
Returns a single collection with all atom containers found in a file of any format.
Tool to build build three-dimensional (3D) tree-like molecular structures from DGraph.
IAtomContainer convertGraphTo3DAtomContainer(DGraph graph)
Created a three-dimensional molecular representation from a given DGraph.
Logger getLogger()
Get the name of the program specific logger.
Randomizer getRandomizer()
Returns the current program-specific randomizer.
Task that assesses the fitness of a given graph.
DGraph dGraph
The graph representation of the entity to evaluate.
String fitProvInputFile
The file where we store the input to the fitness provider.
void runFitnessProvider()
This method runs the actual evaluation of the fitness, whether that is run internally (i....
FitnessTask(FitnessParameters settings, Candidate c)
String fitProvUIDFile
The file where we store the list of unique identifiers or previously evaluated candidates.
FitnessParameters fitnessSettings
Settings for the calculation of the fitness.
boolean fitnessIsRequired
Flag specifying if a valid fitness value is required to consider the task successfully completed.
Candidate result
The data structure holding the results of this task.
String fitProvOutFile
The file where we store the final output from the fitness provider.
IAtomContainer fitProvMol
The chemical representation of the entity to evaluate.
String fitProvPNGFile
The file where we store the graphical representation of the candidate (i.e., a picture).
void runProcess()
Run the process associated with the command.
int getExitCode()
Get the exit code returned by the sub-process.
String getErrorOutput()
Get the content of the error output for the process.
A task that can throw exceptions.
Definition: Task.java:30
File workDir
The file system location where we want to be placed when doing the work.
Definition: Task.java:78
Object lock
Lock for addressing synchronization issues.
Definition: Task.java:51
ProcessHandler processHandler
Executor for external bash script.
Definition: Task.java:73
String errMsg
Error message produced by any subtask.
Definition: Task.java:56
boolean hasException
Flag about exception.
Definition: Task.java:46
final String NL
System-dependent line separator (newline)
Definition: Task.java:93
Throwable thrownExc
Exception thrown.
Definition: Task.java:61
Utilities for molecule conversion.
static void moleculeToPNG(IAtomContainer mol, String filename, Logger logger)
Generate a PNG image from molecule mol
Utilities for tasks.
Definition: TaskUtils.java:31
static synchronized int getUniqueTaskIndex()
Unique counter for tasks.
Definition: TaskUtils.java:41