OpenFPGA/vpr/scripts/compare_timing_reports.py

311 lines
9.4 KiB
Python
Raw Normal View History

#!/usr/bin/env python3
import argparse
import re
from collections import OrderedDict
import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec
import numpy as np
from sklearn.metrics import mean_absolute_error, mean_squared_error
class TimingPath:
def __init__(self, startpoint, endpoint):
self.startpoint = startpoint
self.endpoint = endpoint
def parse_args():
parser = argparse.ArgumentParser()
parser.add_argument("first_report");
parser.add_argument("second_report");
parser.add_argument("--type",
choices=["path", "endpoint"],
default="endpoint")
return parser.parse_args()
def main():
args = parse_args()
print("Parsing {}".format(args.first_report))
first_paths = parse_timing_report(args, args.first_report)
print("Parsing {}".format(args.second_report))
second_paths = parse_timing_report(args, args.second_report)
plot_correlation(first_paths, second_paths, args.first_report, args.second_report)
def parse_timing_report(args, filename):
regex = re.compile(r".*?Endpoint : (?P<end>\S+).*?", re.DOTALL)
start_regex = re.compile(r"Startpoint: (?P<start>\S+)")
end_regex = re.compile(r"Endpoint\s*: (?P<end>\S+)")
slack_regex = re.compile(r"slack \((VIOLATED|MET)\)\s+?(?P<slack>\S+)")
position_regex = re.compile(r"at \((?P<x>\d+),(?P<y>\d+)\)\)")
lines = None
with open(filename) as f:
lines = f.readlines()
lines = ''.join(lines)
paths_lines = lines.split("#Path")
paths = OrderedDict()
for path_lines in paths_lines:
distance = None
startpoint = None
endpoint = None
slack = None
prev_x = None
prev_y = None
match = start_regex.search(path_lines)
if match:
startpoint = match.groupdict()['start']
match = end_regex.search(path_lines)
if match:
endpoint = match.groupdict()['end']
match = slack_regex.search(path_lines)
if match:
slack = float(match.groupdict()['slack'])
for match in position_regex.finditer(path_lines):
x = int(match.groupdict()['x'])
y = int(match.groupdict()['y'])
if prev_x == None and prev_y == None:
distance = 0
else:
dx = abs(x - prev_x)
dy = abs(y - prev_y)
distance += dx + dy
prev_x = x
prev_y = y
if endpoint == None:
continue
if args.type == "endpoint":
start_end = (None, endpoint)
else:
start_end = (startpoint, endpoint)
if start_end not in paths:
paths[start_end] = slack, distance
else:
paths[start_end] = min(paths[start_end][0], slack), distance #Keep least slack
return paths
def correlate_paths(first_paths, second_paths):
first_keys = set(first_paths.keys())
second_keys = set(second_paths.keys())
common_keys = first_keys & second_keys
first_unique_keys = first_keys.difference(common_keys)
second_unique_keys = second_keys.difference(common_keys)
correlated_paths = []
first_only = []
second_only = []
for path in common_keys:
correlated_paths.append( (path, first_paths[path], second_paths[path]) )
for path in first_unique_keys:
first_only.append( (path, first_paths[path]) )
for path in second_unique_keys:
second_only.append( (path, second_paths[path]) )
return correlated_paths, first_only, second_only
def plot_correlation(first_paths, second_paths, first_name, second_name):
correlated_paths, first_only, second_only = correlate_paths(first_paths, second_paths)
print("Correlated {} paths".format(len(correlated_paths)))
print("First only {} paths".format(len(first_only)))
print("Second only {} paths".format(len(second_only)))
first_slacks = [x[0] for x in first_paths.values()]
second_slacks = [x[0] for x in second_paths.values()]
first_only_slacks = [x[1][0] for x in first_only]
second_only_slacks = [x[1][0] for x in second_only]
min_value = min(min(first_slacks), min(second_slacks))
max_value = max(max(first_slacks), max(second_slacks))
x = []
y = []
dist = []
for (start, end), (first_slack, first_distance), (second_slack, second_distance) in correlated_paths:
x.append(first_slack)
y.append(second_slack)
dist.append(first_distance) #Use first distance for now...
if 0:
#Correlation plot
plt.subplot (5, 1, 1)
plt.scatter(x, y)
equal = np.linspace(*plt.xlim())
plt.plot(equal, equal)
plt.xlabel("{} slack".format(first_name))
plt.ylabel("{} slack".format(second_name))
plt.xlim(min_value, max_value)
plt.ylim(min_value, max_value)
#First histogram
nbins=30
plt.subplot (5, 1, 2)
plt.hist(first_slacks, range=(min_value,max_value), log=True, bins=nbins)
plt.xlim(min_value, max_value)
plt.ylim(bottom=0.5)
plt.xlabel("{} Slack".format(first_name))
plt.ylabel("Path Count")
#Second histogram
plt.subplot (5, 1, 3)
plt.hist(second_slacks, range=(min_value,max_value), log=True, bins=nbins)
plt.xlim(min_value, max_value)
plt.ylim(bottom=0.5)
plt.xlabel("{} Slack".format(second_name))
plt.ylabel("Path Count")
#First residuals histogram
plt.subplot (5, 1, 4)
plt.hist(first_only_slacks, range=(min_value,max_value), log=True, bins=nbins)
plt.xlim(min_value, max_value)
plt.ylim(bottom=0.5)
plt.xlabel("{} Residuals Slack".format(first_name))
plt.ylabel("Path Count")
#Second residuals histogram
plt.subplot (5, 1, 5)
plt.hist(second_only_slacks, range=(min_value,max_value), log=True, bins=nbins)
plt.xlim(min_value, max_value)
plt.ylim(bottom=0.5)
plt.xlabel("{} Residuals Slack".format(second_name))
plt.ylabel("Path Count")
else:
fig = plt.figure()
gs = GridSpec(7,4)
nbins=30
#Axies
ax_scatter = fig.add_subplot(gs[1:4,0:3])
ax_cb = fig.add_axes([0.124, 0.375, 0.573, 0.03])
ax_hist_x = fig.add_subplot(gs[0,0:3])
ax_hist_y = fig.add_subplot(gs[1:4,3])
ax_error = fig.add_subplot(gs[0,3])
ax_second_hist = fig.add_subplot(gs[6,0:2])
ax_first_hist = fig.add_subplot(gs[5,0:2], sharex=ax_second_hist)
ax_second_only_hist = fig.add_subplot(gs[6,2:4], sharey=ax_second_hist)
ax_first_only_hist = fig.add_subplot(gs[5,2:4], sharex=ax_second_only_hist, sharey=ax_first_hist)
#errors
mae = mean_absolute_error(x, y)
mse = mean_squared_error(x, y)
ax_error.text(0.1, 0.1, s="MAE: {:.3f}\nMSE: {:.3f}\nWNS: {:.3f}".format(mae, mse, min(first_slacks)))
ax_error.set_axis_off()
#Scatter
sc = ax_scatter.scatter(x,y, c=dist, vmin=0, facecolors='none', label="Paths/Endpoints")
ax_scatter.set_xlim(min_value, max_value)
ax_scatter.set_ylim(min_value, max_value)
#Linear
equal = np.linspace(*ax_scatter.get_xlim())
ax_scatter.plot(equal, equal, label="Ideal")
ax_scatter.legend()
fig.colorbar(sc, cax=ax_cb, orientation='horizontal', label="Path Distance")
#Marginals
ax_hist_x.hist(x, log=True, bins=nbins)
ax_hist_x.set_ylim(bottom=0.5)
ax_hist_x.set_xlim(min_value, max_value)
ax_hist_y.hist(y, log=True, bins=nbins, orientation="horizontal")
ax_hist_y.set_xlim(left=0.5)
ax_hist_y.set_ylim(min_value, max_value)
# Turn off tick labels
plt.setp(ax_hist_x.get_xticklabels(), visible=False)
plt.setp(ax_hist_y.get_yticklabels(), visible=False)
#Full histograms
ax_first_hist.set_title("Full Histograms")
ax_first_hist.hist(first_slacks, log=True, bins=nbins)
ax_first_hist.set_xlim(min_value, max_value)
ax_first_hist.set_ylim(bottom=0.5)
ax_first_hist.set_ylabel("{}\nCount".format(first_name))
plt.setp(ax_first_hist.get_xticklabels(), visible=False)
ax_second_hist.hist(second_slacks, log=True, bins=nbins)
ax_second_hist.set_xlim(min_value, max_value)
ax_second_hist.set_ylim(bottom=0.5)
ax_second_hist.set_ylabel("{}\nCount".format(second_name))
ax_second_hist.set_xlabel("Slack")
#Residual histograms
ax_first_only_hist.set_title("Residual Histograms")
ax_first_only_hist.hist(first_only_slacks, log=True, bins=nbins)
ax_first_only_hist.set_xlim(min_value, max_value)
ax_first_only_hist.set_ylim(bottom=0.5)
plt.setp(ax_first_only_hist.get_xticklabels(), visible=False)
ax_second_only_hist.hist(second_only_slacks, log=True, bins=nbins)
ax_second_only_hist.set_xlim(min_value, max_value)
ax_second_only_hist.set_ylim(bottom=0.5)
ax_second_only_hist.set_xlabel("Slack")
# Set labels on joint
ax_scatter.set_xlabel('{} slack'.format(first_name))
ax_scatter.set_ylabel('{} slack'.format(second_name))
# Set labels on marginals
ax_hist_y.set_xlabel('Path/Endpoint Count')
ax_hist_x.set_ylabel('Path/Endpoint Count')
plt.tight_layout()
plt.show()
if __name__ == "__main__":
main()