Platypus
Platypus copied to clipboard
Example/document for interfacing external evaluatior through file I/O
If the evaluatior is an external excutable program, platypus may take the objectives from the external program by reading the program output and then generate inputs to a text file for the next batch of evaluation by running the program. It is not clear how to achieve this by defining a new class inheriting the Problem class. An example or detailed documentation will be very helpful. Also, it would be better that the run method allows callback, so one can monitor the progress after each generation. Thanks.
Hi, thanks for the question and feedback. To call an external program, your evaluation function would look something like:
def call_external(vars):
# Write vars to input.txt
# Call the program
subprocess.call(["extProgram.exe", "-i", "input.txt", "-o", "output.txt"])
# Read the objectives from output.txt
objs = ...
return objs
It gets a little bit more complicated if you want to parallelize the evaluations since you need to ensure the filenames used by each thread/process are unique.
The run method does take a callback argument that is called once per iteration. It takes a single argument, the algorithm, which lets you access the current NFE, population, etc.:
algorithm.run(10000, callback = lambda a : print("Finished iteration. NFE:", a.nfe))
Alternatively, you can use the built-in logger to track progress:
import sys
import logging
from platypus import NSGAII, DTLZ2
logging.basicConfig(stream=sys.stdout, level=logging.INFO)
problem = DTLZ2()
algorithm = NSGAII(problem, log_frequency=100)
algorithm.run(10000)
for solution in algorithm.result:
print(solution.objectives)
Note that you must both enable the logger by calling logger.basicConfig(...)
AND set log_frequency
for the log messages to display.
INFO:Platypus:NSGAII starting
INFO:Platypus:NSGAII running; NFE Complete: 100, Elapsed Time: 0:00:00.004999
INFO:Platypus:NSGAII running; NFE Complete: 200, Elapsed Time: 0:00:00.065000
INFO:Platypus:NSGAII running; NFE Complete: 300, Elapsed Time: 0:00:00.114025
INFO:Platypus:NSGAII running; NFE Complete: 400, Elapsed Time: 0:00:00.163500
INFO:Platypus:NSGAII running; NFE Complete: 500, Elapsed Time: 0:00:00.213500
INFO:Platypus:NSGAII running; NFE Complete: 600, Elapsed Time: 0:00:00.260503
INFO:Platypus:NSGAII running; NFE Complete: 700, Elapsed Time: 0:00:00.306002
...
Thank you. I made a quick demo and it works.
class Schaffer_external(Problem):
def __init__(self):
super().__init__(1, 2)
self.types[:] = Real(-10, 10)
def evaluate(self, solution):
x = solution.variables[:]
with open('input.txt', 'w') as f:
f.write(' '.join([str(s) for s in x]))
os.system('./Schaffer.exe < input.txt > output.txt')
with open('output.txt', 'r') as f:
solution.objectives[:] = [float(obj) for obj in f.readline().strip().split()]
The Fortran code to make the executable Schaffer.exe is here
program schaffer read(5, *)x y1 = x ** 2 y2 = (x - 2) ** 2 write(6, *)y1, y2 end program schaffer gfortran Schaffer.f -o Schaffer.exe to compile
Now I need to figure out parallel tasks. Does Platypus run population in each generation in parallel? If so, what parameter I can use from the solution object to distinguish each sibling of a generation? Thanks.
In parallel case, is there anyway I can send the process number (population number) to the problem evaluator? If so, I can create files with different names corresponding to different population. Thanks!
This issue is stale and will be closed soon. If you feel this issue is still relevant, please comment to keep it active. Please also consider working on a fix and submitting a PR.