3 # Copyright 2019, University of Maryland and the MANGO development team.
5 # This file is part of MANGO.
7 # MANGO is free software: you can redistribute it and/or modify it
8 # under the terms of the GNU Lesser General Public License as
9 # published by the Free Software Foundation, either version 3 of the
10 # License, or (at your option) any later version.
12 # MANGO is distributed in the hope that it will be useful, but
13 # WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 # Lesser General Public License for more details.
17 # You should have received a copy of the GNU Lesser General Public
18 # License along with MANGO. If not, see
19 # <https://www.gnu.org/licenses/>.
22 # This script uses the database of algorithms in "algorithms.dat" to populate
23 # several sections of mango.hpp and other files, ensuring they are consistent.
25 print(__file__ + ": Using the database of algorithms to generate code in mango.hpp and other source files.")
27 index_deterministic = 7
29 # Read in the database of algorithms:
30 database_filename = "algorithms.dat"
32 f = open(database_filename)
34 print("Error! Unable to open "+database_filename)
40 def TF_to_truefalse(str):
41 str = str.strip().upper()
47 print("Error! TF_to_truefalse called with argument "+str+", which is neither T nor F")
50 # Filter out any commented rows or rows that are blank
53 stripline = line.strip().lower()
54 if len(stripline) < 2:
56 if stripline[0] == '!' or stripline[0] == '#' or stripline[0] == '%':
58 splitline = stripline.split(',')
59 if len(splitline) != 8:
60 print('Error! The following line of '+database_filename+' has the wrong number of entries:')
63 for j in [2,3,4,5,6,7]:
64 splitline[j] = TF_to_truefalse(splitline[j])
65 # If the algorithm uses derivatives, ensure 'parallel' is true:
66 if splitline[3] == 'true':
68 # If an algorithm requires bound constraints, it must allow bound constraints
69 if splitline[5] == 'false' and splitline[6] == 'true':
70 print('Error! An algorithm requires bound constraints but does not allow bound constraints. Original line:')
73 database.append([s.strip() for s in splitline])
76 # Form the algorithm names:
78 for entry in database:
80 str = entry[0]+'_'+entry[1]
85 # Form the set of packages:
86 packages = list(set([entry[0] for entry in database]))
87 packages_for_least_squares = []
88 packages_for_non_least_squares = []
89 for package in packages:
90 has_least_squares_algorithm = False
91 has_non_least_squares_algorithm = False
92 for entry in database:
93 if entry[0]==package and entry[2]=='true':
94 has_least_squares_algorithm = True
95 if entry[0]==package and entry[2]=='false':
96 has_non_least_squares_algorithm = True
97 packages_for_least_squares.append(has_least_squares_algorithm)
98 packages_for_non_least_squares.append(has_non_least_squares_algorithm)
99 print('Packages: ',packages)
100 print('Has least-squares algorithm? ',packages_for_least_squares)
101 print('Has non-least-squares algorithm? ',packages_for_non_least_squares)
103 def find_markup_single(lines,str,comment):
105 for j in range(len(lines)):
106 stripline = lines[j].strip()
107 if stripline[:len(comment)] == comment and stripline[len(comment):].strip() == '<'+str+'>':
111 print("Error! Unable to find a line '"+comment+" <"+str+">'")
114 print("Error! Found more than 1 line '"+comment+" <"+str+">'")
118 def find_markup(lines,str,comment):
119 start = find_markup_single(lines,str,comment)
120 end = find_markup_single(lines,'/'+str,comment)
123 ##################################################################
125 ##################################################################
127 # Read in the file to preprocess
128 filename = 'api/mango.hpp'
132 print("Error! Unable to open "+filename+" for reading")
135 lines = f.readlines()
138 # Replace <enum> .. </enum>
139 comment_line = [' // This section was automatically generated by '+__file__+'\n']
140 start, end = find_markup(lines,'enum','//')
141 newlines = lines[:start+1] + comment_line
143 str1 = ' '+name+',\n'
144 newlines.append(str1.upper())
145 lines = newlines + lines[end:]
147 # Replace <database> .. </database>
148 start, end = find_markup(lines,'database','//')
149 newlines = lines[:start+1] + comment_line
150 newlines += [' // name, package, least_squares, uses_derivatives, parallel, allows_bound_constraints, requires_bound_constraints\n']
151 for j in range(len(database)):
153 name = ('"'+names[j]+'",').lower()
154 package = 'PACKAGE_'+entry[0].upper()+','
155 str = ' {' + name.ljust(35) + package.ljust(17) + (entry[2]+',').ljust(15) + (entry[3]+',').ljust(18) + (entry[4]+',').ljust(10) + (entry[5]+',').ljust(26) + entry[6].ljust(5) + '}'
156 if j != len(database)-1:
158 newlines.append(str+'\n')
159 lines = newlines + lines[end:]
161 # Replace <packages> .. </packages>
162 start, end = find_markup(lines,'packages','//')
163 newlines = lines[:start+1] + comment_line
164 for package in packages:
165 newlines.append(' PACKAGE_'+package.upper()+',\n')
166 lines = newlines + lines[end:]
169 #filename = 'mango_new.hpp'
171 f = open(filename,'w')
173 print("Error! Unable to open "+filename+" for writing")
178 ##################################################################
179 # Editing set_package.cpp
180 ##################################################################
182 # Read in the file to preprocess
183 filename = 'api/set_package.cpp'
187 print("Error! Unable to open "+filename+" for reading")
190 lines = f.readlines()
193 # Replace <includes> .. </includes>
194 start, end = find_markup(lines,'includes','//')
195 newlines = lines[:start+1] + comment_line
196 for j in range(len(packages)):
197 newlines.append('#include "Package_'+packages[j]+'.hpp"\n')
198 lines = newlines + lines[end:]
200 # Replace <packages> .. </packages>
201 start, end = find_markup(lines,'packages','//')
202 newlines = lines[:start+1] + comment_line
203 for j in range(len(packages)):
204 newlines.append(' case PACKAGE_'+packages[j].upper()+':\n')
205 newlines.append(' package = new Package_'+packages[j]+'();\n')
206 newlines.append(' break;\n')
207 lines = newlines + lines[end:]
211 f = open(filename,'w')
213 print("Error! Unable to open "+filename+" for writing")
218 ##################################################################
219 # Editing optimize_nlopt.cpp
220 ##################################################################
222 # Read in the file to preprocess
223 filename = 'api/optimize_nlopt.cpp'
227 print("Error! Unable to open "+filename+" for reading")
230 lines = f.readlines()
233 # Replace <nlopt_algorithms> .. </nlopt_algorithms>
234 start, end = find_markup(lines,'nlopt_algorithms','//')
235 newlines = lines[:start+1] + comment_line
236 for j in range(len(database)):
237 if database[j][0] == 'nlopt':
238 newlines.append(' case mango::'+names[j].upper()+':\n')
239 newlines.append(' mango_nlopt_algorithm = nlopt::'+database[j][1].upper()+';\n')
240 newlines.append(' break;\n')
241 lines = newlines + lines[end:]
245 f = open(filename,'w')
247 print("Error! Unable to open "+filename+" for writing")
252 ##################################################################
253 # Editing optimize_least_squares_mango.cpp
254 ##################################################################
256 # Read in the file to preprocess
257 filename = 'api/optimize_least_squares_mango.cpp'
261 print("Error! Unable to open "+filename+" for reading")
264 lines = f.readlines()
267 # Replace <includes> .. </includes>
268 start, end = find_markup(lines,'includes','//')
269 newlines = lines[:start+1] + comment_line
270 for j in range(len(database)):
271 if database[j][0] == 'mango' and database[j][2] == 'true': # If least-squares
272 newlines.append('#include "'+database[j][1].capitalize()+'.hpp"\n')
273 lines = newlines + lines[end:]
275 # Replace <algorithms> .. </algorithms>
276 start, end = find_markup(lines,'algorithms','//')
277 newlines = lines[:start+1] + comment_line
278 for j in range(len(database)):
279 if database[j][0] == 'mango' and database[j][2] == 'true': # If least-squares
280 newlines.append(' case '+names[j].upper()+':\n')
281 newlines.append(' algorithm = new '+database[j][1].capitalize()+'(solver);\n')
282 newlines.append(' break;\n')
283 lines = newlines + lines[end:]
287 f = open(filename,'w')
289 print("Error! Unable to open "+filename+" for writing")
294 ##################################################################
295 # Editing optimize_mango.cpp
296 ##################################################################
298 # Read in the file to preprocess
299 filename = 'api/optimize_mango.cpp'
303 print("Error! Unable to open "+filename+" for reading")
306 lines = f.readlines()
309 # Replace <includes> .. </includes>
310 start, end = find_markup(lines,'includes','//')
311 newlines = lines[:start+1] + comment_line
312 for j in range(len(database)):
313 if database[j][0] == 'mango' and database[j][2] == 'false': # If not least-squares
314 algorithmName = database[j][1].capitalize()
315 newlines.append('#include "'+algorithmName+'.hpp"\n')
316 lines = newlines + lines[end:]
318 # Replace <algorithms> .. </algorithms>
319 start, end = find_markup(lines,'algorithms','//')
320 newlines = lines[:start+1] + comment_line
321 for j in range(len(database)):
322 if database[j][0] == 'mango' and database[j][2] == 'false': # If not least-squares
323 newlines.append(' case '+names[j].upper()+':\n')
324 algorithmName = database[j][1].capitalize()
325 newlines.append(' algorithm = new '+algorithmName+'(solver);\n')
326 newlines.append(' break;\n')
327 lines = newlines + lines[end:]
331 f = open(filename,'w')
333 print("Error! Unable to open "+filename+" for writing")
339 ##################################################################
340 # Editing ../examples/tests/nondeterministic_algorithms.py
341 ##################################################################
343 # Read in the file to preprocess
344 filename = '../examples/tests/nondeterministic_algorithms.py'
348 print("Error! Unable to open "+filename+" for reading")
351 lines = f.readlines()
354 # Replace <nondeterministic_algorithms> .. </nondeterministic_algorithms>
355 comment_line = ['## This section was automatically generated by '+__file__+'\n']
356 start, end = find_markup(lines,'nondeterministic_algorithms','#')
357 newlines = lines[:start+1] + comment_line
358 newline = 'nondeterministic_algorithms = ['
359 for j in range(len(database)):
360 if database[j][index_deterministic] == 'false':
361 newline += '"'+names[j]+'",'
362 newline = newline[:-1] + ']\n'
363 lines = newlines + [newline] + lines[end:]
367 f = open(filename,'w')
369 print("Error! Unable to open "+filename+" for writing")
374 ##################################################################
375 # Editing algorithms.md in the documentation
376 ##################################################################
378 # Read in the file to preprocess
379 filename = '../docs/algorithms.md'
383 print("Error! Unable to open "+filename+" for reading")
386 lines = f.readlines()
389 # Replace <algorithms> .. </algorithms>
390 comment_line = ['--><!-- This section was automatically generated by '+__file__+' -->\n']
391 start, end = find_markup(lines,'algorithms','<!--')
392 newlines = lines[:start+1] + comment_line
393 for j_name in range(len(names)):
396 if j_name < len(names)-1:
398 newlines.append(str1+'\n')
399 lines = newlines + lines[end:]
403 f = open(filename,'w')
405 print("Error! Unable to open "+filename+" for writing")