311 lines
9.4 KiB
Python
311 lines
9.4 KiB
Python
|
#!/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()
|