update_algorithms
Go to the documentation of this file.
1 #!/usr/bin/env python3
2 
3 # Copyright 2019, University of Maryland and the MANGO development team.
4 #
5 # This file is part of MANGO.
6 #
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.
11 #
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.
16 #
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/>.
20 
21 
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.
24 
25 print(__file__ + ": Using the database of algorithms to generate code in mango.hpp and other source files.")
26 
27 index_deterministic = 7
28 
29 # Read in the database of algorithms:
30 database_filename = "algorithms.dat"
31 try:
32  f = open(database_filename)
33 except:
34  print("Error! Unable to open "+database_filename)
35  raise
36 
37 lines = f.readlines()
38 f.close()
39 
40 def TF_to_truefalse(str):
41  str = str.strip().upper()
42  if str == "T":
43  return "true"
44  elif str == "F":
45  return "false"
46  else:
47  print("Error! TF_to_truefalse called with argument "+str+", which is neither T nor F")
48  exit(1)
49 
50 # Filter out any commented rows or rows that are blank
51 database = []
52 for line in lines:
53  stripline = line.strip().lower()
54  if len(stripline) < 2:
55  continue
56  if stripline[0] == '!' or stripline[0] == '#' or stripline[0] == '%':
57  continue
58  splitline = stripline.split(',')
59  if len(splitline) != 8:
60  print('Error! The following line of '+database_filename+' has the wrong number of entries:')
61  print(line)
62  exit(1)
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':
67  splitline[4] = '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:')
71  print(line)
72  exit(1)
73  database.append([s.strip() for s in splitline])
74 #print(database)
75 
76 # Form the algorithm names:
77 names = []
78 for entry in database:
79  if len(entry[1]) > 0:
80  str = entry[0]+'_'+entry[1]
81  else:
82  str = entry[0]
83  names.append(str)
84 
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)
102 
103 def find_markup_single(lines,str,comment):
104  found_it = 0
105  for j in range(len(lines)):
106  stripline = lines[j].strip()
107  if stripline[:len(comment)] == comment and stripline[len(comment):].strip() == '<'+str+'>':
108  found_it += 1
109  line = j
110  if found_it == 0:
111  print("Error! Unable to find a line '"+comment+" <"+str+">'")
112  exit(1)
113  if found_it > 1:
114  print("Error! Found more than 1 line '"+comment+" <"+str+">'")
115  exit(2)
116  return line
117 
118 def find_markup(lines,str,comment):
119  start = find_markup_single(lines,str,comment)
120  end = find_markup_single(lines,'/'+str,comment)
121  return start,end
122 
123 ##################################################################
124 # Editing mango.hpp
125 ##################################################################
126 
127 # Read in the file to preprocess
128 filename = 'api/mango.hpp'
129 try:
130  f = open(filename)
131 except:
132  print("Error! Unable to open "+filename+" for reading")
133  raise
134 
135 lines = f.readlines()
136 f.close()
137 
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
142 for name in names:
143  str1 = ' '+name+',\n'
144  newlines.append(str1.upper())
145 lines = newlines + lines[end:]
146 
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)):
152  entry = database[j]
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:
157  str += ','
158  newlines.append(str+'\n')
159 lines = newlines + lines[end:]
160 
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:]
167 
168 # Write new file
169 #filename = 'mango_new.hpp'
170 try:
171  f = open(filename,'w')
172 except:
173  print("Error! Unable to open "+filename+" for writing")
174  raise
175 f.writelines(lines)
176 f.close()
177 
178 ##################################################################
179 # Editing set_package.cpp
180 ##################################################################
181 
182 # Read in the file to preprocess
183 filename = 'api/set_package.cpp'
184 try:
185  f = open(filename)
186 except:
187  print("Error! Unable to open "+filename+" for reading")
188  raise
189 
190 lines = f.readlines()
191 f.close()
192 
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:]
199 
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:]
208 
209 # Write new file
210 try:
211  f = open(filename,'w')
212 except:
213  print("Error! Unable to open "+filename+" for writing")
214  raise
215 f.writelines(lines)
216 f.close()
217 
218 ##################################################################
219 # Editing optimize_nlopt.cpp
220 ##################################################################
221 
222 # Read in the file to preprocess
223 filename = 'api/optimize_nlopt.cpp'
224 try:
225  f = open(filename)
226 except:
227  print("Error! Unable to open "+filename+" for reading")
228  raise
229 
230 lines = f.readlines()
231 f.close()
232 
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:]
242 
243 # Write new file
244 try:
245  f = open(filename,'w')
246 except:
247  print("Error! Unable to open "+filename+" for writing")
248  raise
249 f.writelines(lines)
250 f.close()
251 
252 ##################################################################
253 # Editing optimize_least_squares_mango.cpp
254 ##################################################################
255 
256 # Read in the file to preprocess
257 filename = 'api/optimize_least_squares_mango.cpp'
258 try:
259  f = open(filename)
260 except:
261  print("Error! Unable to open "+filename+" for reading")
262  raise
263 
264 lines = f.readlines()
265 f.close()
266 
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:]
274 
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:]
284 
285 # Write new file
286 try:
287  f = open(filename,'w')
288 except:
289  print("Error! Unable to open "+filename+" for writing")
290  raise
291 f.writelines(lines)
292 f.close()
293 
294 ##################################################################
295 # Editing optimize_mango.cpp
296 ##################################################################
297 
298 # Read in the file to preprocess
299 filename = 'api/optimize_mango.cpp'
300 try:
301  f = open(filename)
302 except:
303  print("Error! Unable to open "+filename+" for reading")
304  raise
305 
306 lines = f.readlines()
307 f.close()
308 
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:]
317 
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:]
328 
329 # Write new file
330 try:
331  f = open(filename,'w')
332 except:
333  print("Error! Unable to open "+filename+" for writing")
334  raise
335 f.writelines(lines)
336 f.close()
337 
338 
339 ##################################################################
340 # Editing ../examples/tests/nondeterministic_algorithms.py
341 ##################################################################
342 
343 # Read in the file to preprocess
344 filename = '../examples/tests/nondeterministic_algorithms.py'
345 try:
346  f = open(filename)
347 except:
348  print("Error! Unable to open "+filename+" for reading")
349  raise
350 
351 lines = f.readlines()
352 f.close()
353 
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:]
364 
365 # Write new file
366 try:
367  f = open(filename,'w')
368 except:
369  print("Error! Unable to open "+filename+" for writing")
370  raise
371 f.writelines(lines)
372 f.close()
373 
374 ##################################################################
375 # Editing algorithms.md in the documentation
376 ##################################################################
377 
378 # Read in the file to preprocess
379 filename = '../docs/algorithms.md'
380 try:
381  f = open(filename)
382 except:
383  print("Error! Unable to open "+filename+" for reading")
384  raise
385 
386 lines = f.readlines()
387 f.close()
388 
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)):
394  name = names[j_name]
395  str1 = ' `'+name+'`'
396  if j_name < len(names)-1:
397  str1 += ',<br>'
398  newlines.append(str1+'\n')
399 lines = newlines + lines[end:]
400 
401 # Write new file
402 try:
403  f = open(filename,'w')
404 except:
405  print("Error! Unable to open "+filename+" for writing")
406  raise
407 f.writelines(lines)
408 f.close()
409