114 """Parse benchmark log files and store the parsed data in a sqlite3 database."""
116 def isInvalidValue(value):
117 return len(value) == 0
or value
in [
"nan",
"-nan",
"inf",
"-inf"]
119 conn = sqlite3.connect(dbname)
121 c.execute(
"PRAGMA FOREIGN_KEYS = ON")
125 """CREATE TABLE IF NOT EXISTS experiments
126 (id INTEGER PRIMARY KEY ON CONFLICT REPLACE AUTOINCREMENT, name VARCHAR(512),
127 totaltime REAL, timelimit REAL, memorylimit REAL, runcount INTEGER,
128 version VARCHAR(128), hostname VARCHAR(1024), cpuinfo TEXT,
129 date DATETIME, seed INTEGER, setup TEXT);
130 CREATE TABLE IF NOT EXISTS plannerConfigs
131 (id INTEGER PRIMARY KEY AUTOINCREMENT,
132 name VARCHAR(512) NOT NULL, settings TEXT);
133 CREATE TABLE IF NOT EXISTS enums
134 (name VARCHAR(512), value INTEGER, description TEXT,
135 PRIMARY KEY (name, value));
136 CREATE TABLE IF NOT EXISTS runs
137 (id INTEGER PRIMARY KEY AUTOINCREMENT, experimentid INTEGER, plannerid INTEGER,
138 FOREIGN KEY (experimentid) REFERENCES experiments(id) ON DELETE CASCADE,
139 FOREIGN KEY (plannerid) REFERENCES plannerConfigs(id) ON DELETE CASCADE);
140 CREATE TABLE IF NOT EXISTS progress
141 (runid INTEGER, time REAL, PRIMARY KEY (runid, time),
142 FOREIGN KEY (runid) REFERENCES runs(id) ON DELETE CASCADE)"""
146 allExperimentsName =
"all_experiments"
147 allExperimentsValues = {
159 addAllExperiments = len(filenames) > 0
160 if addAllExperiments:
162 "INSERT INTO experiments VALUES (?,?,?,?,?,?,?,?,?,?,?,?)",
163 (
None, allExperimentsName) + tuple(allExperimentsValues.values()),
165 allExperimentsId = c.lastrowid
167 for i, filename
in enumerate(filenames):
168 print(
"Processing " + filename)
169 logfile = open(filename,
"r")
170 start_pos = logfile.tell()
174 logfile.seek(start_pos)
179 version =
" ".join([libname, version])
181 "experiment name", logfile, -1, {0:
"Experiment"}
184 date =
" ".join(
ensurePrefix(logfile.readline(),
"Starting").split()[2:])
192 "time limit", logfile, 0, {-3:
"seconds", -2:
"per", -1:
"run"}
197 "memory limit", logfile, 0, {-3:
"MB", -2:
"per", -1:
"run"}
201 logfile, 0, {-3:
"runs", -2:
"per", -1:
"planner"}
204 if nrrunsOrNone !=
None:
205 nrruns = int(nrrunsOrNone)
206 allExperimentsValues[
"runcount"] += nrruns
209 "total time", logfile, 0, {-3:
"collect", -2:
"the", -1:
"data"}
213 allExperimentsValues[
"totaltime"] += totaltime
214 allExperimentsValues[
"memorylimit"] = max(
215 allExperimentsValues[
"memorylimit"], totaltime
217 allExperimentsValues[
"timelimit"] = max(
218 allExperimentsValues[
"timelimit"], totaltime
222 allExperimentsValues[
"version"] = version
223 allExperimentsValues[
"date"] = date
224 allExperimentsValues[
"setup"] = expsetup
225 allExperimentsValues[
"hostname"] = hostname
226 allExperimentsValues[
"cpuinfo"] = cpuinfo
229 if numEnumsOrNone !=
None:
230 numEnums = int(numEnumsOrNone)
231 for i
in range(numEnums):
232 enum = logfile.readline()[:-1].split(
"|")
233 c.execute(
'SELECT * FROM enums WHERE name IS "%s"' % enum[0])
234 if c.fetchone() ==
None:
235 for j
in range(len(enum) - 1):
237 "INSERT INTO enums VALUES (?,?,?)", (enum[0], j, enum[j + 1])
240 "INSERT INTO experiments VALUES (?,?,?,?,?,?,?,?,?,?,?,?)",
256 experimentId = c.lastrowid
260 for i
in range(numPlanners):
261 plannerName = logfile.readline()[:-1]
262 print(
"Parsing data for " + plannerName)
265 numCommon = int(logfile.readline().split()[0])
267 for j
in range(numCommon):
268 settings = settings + logfile.readline() +
";"
272 "SELECT id FROM plannerConfigs WHERE (name=? AND settings=?)",
281 "INSERT INTO plannerConfigs VALUES (?,?,?)",
288 plannerId = c.lastrowid
293 c.execute(
"PRAGMA table_info(runs)")
294 columnNames = [col[1]
for col
in c.fetchall()]
297 numProperties = int(logfile.readline().split()[0])
298 propertyNames = [
"experimentid",
"plannerid"]
299 for j
in range(numProperties):
300 field = logfile.readline().split()
301 propertyType = field[-1]
302 propertyName =
"_".join(field[:-1])
303 if propertyName
not in columnNames:
305 "ALTER TABLE runs ADD %s %s" % (propertyName, propertyType)
307 propertyNames.append(propertyName)
311 +
",".join(propertyNames)
313 +
",".join(
"?" * len(propertyNames))
316 numRuns = int(logfile.readline().split()[0])
318 for j
in range(numRuns):
320 None if isInvalidValue(x)
else x
321 for x
in logfile.readline().split(
"; ")[:-1]
323 values = tuple([experimentId, plannerId] + runValues)
324 c.execute(insertFmtStr, values)
327 runIds.append(c.lastrowid)
329 if addAllExperiments:
330 values = tuple([allExperimentsId, plannerId] + runValues)
331 c.execute(insertFmtStr, values)
333 nextLine = logfile.readline().strip()
338 c.execute(
"PRAGMA table_info(progress)")
339 columnNames = [col[1]
for col
in c.fetchall()]
342 numProgressProperties = int(nextLine.split()[0])
343 progressPropertyNames = [
"runid"]
344 for i
in range(numProgressProperties):
345 field = logfile.readline().split()
346 progressPropertyType = field[-1]
347 progressPropertyName =
"_".join(field[:-1])
348 if progressPropertyName
not in columnNames:
350 "ALTER TABLE progress ADD %s %s"
351 % (progressPropertyName, progressPropertyType)
353 progressPropertyNames.append(progressPropertyName)
356 "INSERT INTO progress ("
357 +
",".join(progressPropertyNames)
359 +
",".join(
"?" * len(progressPropertyNames))
362 numRuns = int(logfile.readline().split()[0])
363 for j
in range(numRuns):
364 dataSeries = logfile.readline().split(
";")[:-1]
365 for dataSample
in dataSeries:
369 None if isInvalidValue(x)
else x
370 for x
in dataSample.split(
",")[:-1]
374 c.execute(insertFmtStr, values)
375 except sqlite3.IntegrityError:
377 "Ignoring duplicate progress data. Consider increasing ompl::tools::Benchmark::Request::timeBetweenUpdates."
384 if addAllExperiments:
385 updateString =
"UPDATE experiments SET"
386 for i, (key, val)
in enumerate(allExperimentsValues.items()):
389 updateString +=
" " + str(key) +
"='" + str(val) +
"'"
390 updateString +=
"WHERE id='" + str(allExperimentsId) +
"'"
391 c.execute(updateString)
397 """Create a plot for a particular attribute. It will include data for
398 all planners that have data for this attribute."""
402 if typename ==
"ENUM":
403 cur.execute(
'SELECT description FROM enums where name IS "%s"' % attribute)
404 descriptions = [t[0]
for t
in cur.fetchall()]
405 numValues = len(descriptions)
406 for planner
in planners:
408 "SELECT %s FROM runs WHERE plannerid = %s AND %s IS NOT NULL"
409 % (attribute, planner[0], attribute)
411 measurement = [t[0]
for t
in cur.fetchall()
if t[0] !=
None]
412 if len(measurement) > 0:
414 "SELECT count(*) FROM runs WHERE plannerid = %s AND %s IS NULL"
415 % (planner[0], attribute)
417 nanCounts.append(cur.fetchone()[0])
418 labels.append(planner[1])
419 if typename ==
"ENUM":
420 scale = 100.0 / len(measurement)
422 [measurement.count(i) * scale
for i
in range(numValues)]
425 measurements.append(measurement)
427 if len(measurements) == 0:
428 print(
'Skipping "%s": no available measurements' % attribute)
433 if typename ==
"ENUM":
435 measurements = np.transpose(np.vstack(measurements))
436 colsum = np.sum(measurements, axis=1)
437 rows = np.where(colsum != 0)[0]
438 heights = np.zeros((1, measurements.shape[1]))
439 ind = range(measurements.shape[1])
447 color=matplotlib.cm.hot(int(floor(i * 256 / numValues))),
448 label=descriptions[i],
450 heights = heights + measurements[i]
451 xtickNames = plt.xticks(
452 [x + width / 2.0
for x
in ind], labels, rotation=30, fontsize=8, ha=
"right"
454 ax.set_ylabel(attribute.replace(
"_",
" ") +
" (%)")
455 box = ax.get_position()
456 ax.set_position([box.x0, box.y0, box.width * 0.8, box.height])
457 props = matplotlib.font_manager.FontProperties()
458 props.set_size(
"small")
459 ax.legend(loc=
"center left", bbox_to_anchor=(1, 0.5), prop=props)
460 elif typename ==
"BOOLEAN":
462 measurementsPercentage = [sum(m) * 100.0 / len(m)
for m
in measurements]
463 ind = range(len(measurements))
464 plt.bar(ind, measurementsPercentage, width)
470 xtickNames = plt.xticks(
471 [x + width / 2.0
for x
in ind], labels, rotation=30, fontsize=8, ha=
"right"
473 ax.set_ylabel(attribute.replace(
"_",
" ") +
" (%)")
478 if int(matplotlibversion.split(
".")[0]) < 1:
479 plt.boxplot(measurements, notch=0, sym=
"k+", vert=1, whis=1.5)
482 measurements, notch=0, sym=
"k+", vert=1, whis=1.5, bootstrap=1000
484 ax.set_ylabel(attribute.replace(
"_",
" "))
493 xtickNames = plt.setp(ax, xticklabels=labels)
494 plt.setp(xtickNames, rotation=30, fontsize=8, ha=
"right")
497 )
in ax.xaxis.get_major_ticks():
498 tick.label.set_fontsize(8)
502 ax.set_xlabel(
"Motion planning algorithm", fontsize=12)
503 ax.yaxis.grid(
True, linestyle=
"-", which=
"major", color=
"lightgrey", alpha=0.5)
504 if max(nanCounts) > 0:
505 maxy = max([max(y)
for y
in measurements])
506 for i
in range(len(labels)):
507 x = i + width / 2
if typename ==
"BOOLEAN" else i + 1
514 """Plot data for a single planner progress attribute. Will create an
515 average time-plot with error bars of the attribute over all runs for
518 import numpy.ma
as ma
522 ax.set_xlabel(
"time (s)")
523 ax.set_ylabel(attribute.replace(
"_",
" "))
525 for planner
in planners:
527 """SELECT count(progress.%s) FROM progress INNER JOIN runs
528 ON progress.runid = runs.id AND runs.plannerid=%s
529 AND progress.%s IS NOT NULL"""
530 % (attribute, planner[0], attribute)
532 if cur.fetchone()[0] > 0:
533 plannerNames.append(planner[1])
535 """SELECT DISTINCT progress.runid FROM progress INNER JOIN runs
536 WHERE progress.runid=runs.id AND runs.plannerid=?""",
539 runids = [t[0]
for t
in cur.fetchall()]
545 "SELECT time, %s FROM progress WHERE runid = %s ORDER BY time"
548 (time, data) = zip(*(cur.fetchall()))
549 timeTable.append(time)
550 dataTable.append(data)
555 fewestSamples = min(len(time[:])
for time
in timeTable)
556 times = np.array(timeTable[0][:fewestSamples])
557 dataArrays = np.array([data[:fewestSamples]
for data
in dataTable])
558 filteredData = ma.masked_array(
559 dataArrays, np.equal(dataArrays,
None), dtype=float
562 means = np.mean(filteredData, axis=0)
563 stddevs = np.std(filteredData, axis=0, ddof=1)
567 times, means, yerr=2 * stddevs, errorevery=max(1, len(times) // 20)
569 ax.legend(plannerNames)
570 if len(plannerNames) > 0:
577 """Create a PDF file with box plots for all attributes."""
578 print(
"Generating plots...")
579 conn = sqlite3.connect(dbname)
581 c.execute(
"PRAGMA FOREIGN_KEYS = ON")
582 c.execute(
"SELECT id, name FROM plannerConfigs")
584 (t[0], t[1].replace(
"geometric_",
"").replace(
"control_",
""))
585 for t
in c.fetchall()
587 c.execute(
"PRAGMA table_info(runs)")
588 colInfo = c.fetchall()[3:]
595 or col[2] ==
"INTEGER"
599 pp.savefig(plt.gcf())
601 c.execute(
"PRAGMA table_info(progress)")
602 colInfo = c.fetchall()[2:]
605 pp.savefig(plt.gcf())
610 c.execute(
"""SELECT id, name, timelimit, memorylimit FROM experiments""")
611 experiments = c.fetchall()
612 for experiment
in experiments:
614 """SELECT count(*) FROM runs WHERE runs.experimentid = %d
615 GROUP BY runs.plannerid"""
618 numRuns = [run[0]
for run
in c.fetchall()]
619 numRuns = numRuns[0]
if len(set(numRuns)) == 1
else ",".join(numRuns)
621 plt.figtext(pagex, pagey,
'Experiment "%s"' % experiment[1])
622 plt.figtext(pagex, pagey - 0.05,
"Number of averaged runs: %d" % numRuns)
624 pagex, pagey - 0.10,
"Time limit per run: %g seconds" % experiment[2]
626 plt.figtext(pagex, pagey - 0.15,
"Memory limit per run: %g MB" % experiment[3])
629 pp.savefig(plt.gcf())