Xplace_for_ICCAD/utils/visualization.py

306 lines
11 KiB
Python
Raw Normal View History

2022-10-20 22:46:17 +08:00
from typing import List, Tuple
import torch
import os
import matplotlib.pyplot as plt
import numpy as np
import logging
matplotlib_logger = logging.getLogger("matplotlib")
matplotlib_logger.setLevel(logging.INFO)
def scatter_drawer(pos: torch.Tensor, fix_mask: torch.Tensor, filename, title, args):
res_root = os.path.join(args.result_dir, args.exp_id)
png_path = os.path.join(res_root, args.eval_dir, filename)
if not os.path.exists(os.path.dirname(png_path)):
os.makedirs(os.path.dirname(png_path))
# pos = pos.cpu().numpy()
# pos = pos.T
# plt.scatter(pos[0], pos[1])
mov_pos = pos[fix_mask.squeeze(1) < 0.5].T.cpu().numpy()
fix_pos = pos[fix_mask.squeeze(1) > 0.5].T.cpu().numpy()
plt.scatter(mov_pos[0], mov_pos[1], label="mov")
plt.scatter(fix_pos[0], fix_pos[1], label="fix")
plt.legend()
plt.title(title)
plt.savefig(png_path)
plt.close()
def draw_fig(batch, pos, fix_mask, info, args):
epoch, idx, iteration, hpwl = info
filename = "epoch%d_id%d_iter%d.png" % (epoch, idx, iteration)
title = "hpwl %.4f" % hpwl
num_items = batch.num_of_graph_nodes[0]
scatter_drawer(pos[:num_items], fix_mask[:num_items], filename, title, args)
def scatter_drawer_new(pos: torch.Tensor, filename, title, args):
res_root = os.path.join(args.result_dir, args.exp_id)
png_path = os.path.join(res_root, args.eval_dir, filename)
if not os.path.exists(os.path.dirname(png_path)):
os.makedirs(os.path.dirname(png_path))
pos = pos.cpu().numpy()
pos = pos.T
plt.scatter(pos[0], pos[1])
plt.title(title)
plt.savefig(png_path)
plt.close()
def draw_fig_new(pos, info, args):
iteration, hpwl, design_name = info
filename = "%s_iter%d.png" % (design_name, iteration)
title = "hpwl %.4f" % hpwl
scatter_drawer_new(pos, filename, title, args)
def draw_fig_with_cairo(
mov_node_pos,
mov_node_size,
fix_node_pos,
fix_node_size,
filler_node_pos,
filler_node_size,
data,
info,
args,
base_size=2048,
):
import cairocffi as cairo
iteration, hpwl, design_name = info
filename = "%s_iter%d.png" % (design_name, iteration)
res_root = os.path.join(args.result_dir, args.exp_id)
png_path = os.path.join(res_root, args.eval_dir, filename)
if not os.path.exists(os.path.dirname(png_path)):
os.makedirs(os.path.dirname(png_path))
lx, ly, hx, hy = data.ori_die_lx, data.ori_die_ly, data.ori_die_hx, data.ori_die_hy
WIDTH = base_size
HEIGHT = int(WIDTH * (hx - lx) / (hy - ly))
num_bin_x = data.num_bin_x
num_bin_y = data.num_bin_y
surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, WIDTH, HEIGHT)
ctx = cairo.Context(surface)
# Scale Image
ratio0, ratio1 = WIDTH / (hx - lx), HEIGHT / (hy - ly)
ctx.translate(-lx * ratio0, HEIGHT + ly * ratio1)
ctx.scale(ratio0, -ratio1)
# White Background
ctx.rectangle(lx, ly, hx - lx, hy - ly)
ctx.set_source_rgb(1.0, 1.0, 1.0)
ctx.fill()
# Bins / Grids
ctx.set_line_width(0.0005)
ctx.set_source_rgb(0.3, 0.3, 0.3)
for i in range(1, num_bin_x):
ctx.move_to(i * (hx - lx) / num_bin_x + lx, ly)
ctx.line_to(i * (hx - lx) / num_bin_x + lx, hy)
ctx.stroke()
for i in range(1, num_bin_y):
ctx.move_to(lx, i * (hy - ly) / num_bin_y + ly)
ctx.line_to(hx, i * (hy - ly) / num_bin_y + ly)
ctx.stroke()
# Movable Nodes
if mov_node_pos is not None and mov_node_size is not None:
mov_node_pos = mov_node_pos.cpu()
mov_node_size = mov_node_size.cpu()
for i in range(mov_node_pos.shape[0]):
pos_x = round(mov_node_pos[i][0].item() * (hx - lx) + lx)
pos_y = round(mov_node_pos[i][1].item() * (hy - ly) + ly)
size_x = round(mov_node_size[i][0].item() * (hx - lx))
size_y = round(mov_node_size[i][1].item() * (hy - ly))
ctx.rectangle(pos_x - size_x / 2, pos_y - size_y / 2, size_x, size_y)
ctx.set_source_rgba(0.475, 0.706, 0.718, 0.8)
ctx.fill()
# Fixed Nodes
if fix_node_pos is not None and fix_node_size is not None:
fix_node_pos = fix_node_pos.cpu()
fix_node_size = fix_node_size.cpu()
for i in range(fix_node_pos.shape[0]):
pos_x = round(fix_node_pos[i][0].item() * (hx - lx) + lx)
pos_y = round(fix_node_pos[i][1].item() * (hy - ly) + ly)
size_x = round(fix_node_size[i][0].item() * (hx - lx))
size_y = round(fix_node_size[i][1].item() * (hy - ly))
ctx.rectangle(pos_x - size_x / 2, pos_y - size_y / 2, size_x, size_y)
ctx.set_source_rgba(0.878, 0.365, 0.365, 0.8)
ctx.fill()
# Filler Nodes
if filler_node_pos is not None and filler_node_size is not None:
filler_node_pos = filler_node_pos.cpu()
filler_node_size = filler_node_size.cpu()
for i in range(filler_node_pos.shape[0]):
pos_x = round(filler_node_pos[i][0].item() * (hx - lx) + lx)
pos_y = round(filler_node_pos[i][1].item() * (hy - ly) + ly)
size_x = round(filler_node_size[i][0].item() * (hx - lx))
size_y = round(filler_node_size[i][1].item() * (hy - ly))
ctx.rectangle(pos_x - size_x / 2, pos_y - size_y / 2, size_x, size_y)
ctx.set_source_rgba(0.082, 0.176, 0.208, 0.33)
ctx.fill()
surface.write_to_png(png_path)
def draw_fig_with_cairo_cpp(node_pos, node_size, data, info, args, base_size=2048):
from cpp_to_py import draw_placement
die_info = tuple(data.__ori_die_info__.tolist())
scaleX, scaleY = data.die_scale[0].cpu(), data.die_scale[1].cpu()
shiftX, shiftY = data.die_shift[0].cpu(), data.die_shift[1].cpu()
lx, hx, ly, hy = die_info
node_pos_x: List[float] = (node_pos.cpu()[:, 0] * scaleX + shiftX).tolist()
node_pos_y: List[float] = (node_pos.cpu()[:, 1] * scaleY + shiftY).tolist()
node_size_x: List[float] = (node_size.cpu()[:, 0] * scaleX).tolist()
node_size_y: List[float] = (node_size.cpu()[:, 1] * scaleY).tolist()
node_name: List[str] = ["%d" % i for i in range(node_pos.shape[0])]
iteration, hpwl, design_name = info
filename = "%s_iter%d.png" % (design_name, iteration)
res_root = os.path.join(args.result_dir, args.exp_id)
png_path: str = os.path.join(res_root, args.eval_dir, filename)
if not os.path.exists(os.path.dirname(png_path)):
os.makedirs(os.path.dirname(png_path))
site_info = (data.site_width, data.site_height)
bin_size_info = (
round(1 / data.num_bin_x * (hx - lx)),
round(1 / data.num_bin_y * (hy - ly)),
)
node_type_indices = data.node_type_indices
ele_type_to_rgba_vec: List[Tuple[str, float, float, float, float]] = [
("Bin", 0.1, 0.1, 0.1, 1.0),
("Mov", 0.475, 0.706, 0.718, 0.8),
("Filler", 0.8, 0.8, 0.8, 0.8),
("Buffer", 0.65, 0.08, 0.9, 0.8),
("FF", 0.65, 0.9, 0.08, 0.7),
2022-10-20 22:46:17 +08:00
]
node_special_type: List[int] = (data.node_special_type.cpu()).tolist()
2022-10-20 22:46:17 +08:00
width = base_size
height = round(width * (hy - ly) / (hx - lx))
draw_contents: List[str] = ["Nodes", "NodesText"]
status = draw_placement.draw(
node_pos_x,
node_pos_y,
node_size_x,
node_size_y,
node_name,
die_info,
site_info,
bin_size_info,
node_type_indices,
ele_type_to_rgba_vec,
node_special_type,
2022-10-20 22:46:17 +08:00
png_path,
width,
height,
draw_contents,
)
def visualize_electronic_variables(density_map, potential_map, force_map, info, args):
import cv2
iteration, design_name = info
M, N = density_map.shape
def get_png_path(filename):
res_root = os.path.join(args.result_dir, args.exp_id)
png_path = os.path.join(res_root, args.eval_dir, filename)
if not os.path.exists(os.path.dirname(png_path)):
os.makedirs(os.path.dirname(png_path))
return png_path
# 1) Visualize density_map
filename = "%s_iter%d_density.png" % (design_name, iteration)
png_path = get_png_path(filename)
fig, ax = plt.subplots(figsize=(12, 10))
im = ax.imshow(density_map.cpu().numpy(), cmap="YlGnBu")
fig.colorbar(im, ax=ax)
ax.title.set_text("Density Map")
plt.savefig(png_path, bbox_inches="tight")
plt.close()
# 2) Visualize potential_map
filename = "%s_iter%d_potential.png" % (design_name, iteration)
png_path = get_png_path(filename)
fig, ax = plt.subplots(figsize=(12, 10))
im = ax.imshow(potential_map.cpu().numpy(), cmap="YlGnBu")
fig.colorbar(im, ax=ax)
ax.title.set_text("Potential Map")
plt.savefig(png_path, bbox_inches="tight")
plt.close()
# 3) Visualize force_map
filename = "%s_iter%d_force.png" % (design_name, iteration)
png_path = get_png_path(filename)
# 3.1) Init background image
GRID_SIZE = 100
img = np.ones((M * GRID_SIZE, N * GRID_SIZE, 3)) * 255
# 3.2) Draw grid line
for i in range(0, M * GRID_SIZE - 1, GRID_SIZE):
cv2.line(img, (i, 0), (i, N * GRID_SIZE), (0, 0, 0), 1, 1)
for j in range(0, N * GRID_SIZE - 1, GRID_SIZE):
cv2.line(img, (0, j), (M * GRID_SIZE, j), (0, 0, 0), 1, 1)
# 3.3) Normalize force
max_force = torch.sum(torch.pow(force_map, 2), axis=0).sqrt().max().item()
force_map = (force_map / max_force).cpu().numpy()
# 3.4) Draw force arrows
for i in range(0, M, 1):
centre_x = i * GRID_SIZE + GRID_SIZE / 2
for j in range(0, N, 1):
centre_y = j * GRID_SIZE + GRID_SIZE / 2
cv2.arrowedLine(
img,
(
int(centre_x - force_map[0][i][j] * GRID_SIZE / 2),
int(centre_y - force_map[1][i][j] * GRID_SIZE / 2),
),
(
int(centre_x + force_map[0][i][j] * GRID_SIZE / 2),
int(centre_y + force_map[1][i][j] * GRID_SIZE / 2),
),
color=(230, 216, 173),
thickness=10,
tipLength=0.3,
)
cv2.imwrite(png_path, img)
def draw_grad_abs_mean(
wl_grads, density_grads, iterations, info, args,
):
iteration, design_name = info
filename = "%s_iter%d_grad_magnitude_mean.png" % (design_name, iteration)
res_root = os.path.join(args.result_dir, args.exp_id)
png_path = os.path.join(res_root, args.eval_dir, filename)
if not os.path.exists(os.path.dirname(png_path)):
os.makedirs(os.path.dirname(png_path))
colors = ["tab:blue", "tab:red"]
fig, ax1 = plt.subplots()
ax1.set_xlabel("iterations")
ax1.set_ylabel("Wirelength Gradient Magnitude", color=colors[0])
ax1.plot(iterations, wl_grads, color=colors[0])
ax1.tick_params(axis="y", labelcolor=colors[0])
ax2 = ax1.twinx()
ax2.set_ylabel("Density Gradient Magnitude", color=colors[1])
ax2.plot(iterations, density_grads, color=colors[1])
ax2.tick_params(axis="y", labelcolor=colors[1])
plt.title("Gradient Magnitude Mean")
fig.tight_layout()
plt.savefig(png_path)
plt.close()