# ---
# jupyter:
#   jupytext:
#     text_representation:
#       extension: .py
#       format_name: light
#       format_version: '1.5'
#       jupytext_version: 1.14.4
#   kernelspec:
#     display_name: Python 3
#     language: python
#     name: python3
# ---

# # LUT-based Hierarchical Reversible Logic Synthesis (Experimental Results)
#
# This notebook generates Tables 3 and 4 of the experimental evaluation in the paper *LUT-based Hierarchical Reversible Logic Synthesis*.  It extracts the data from the database of computed synthesis results for the EPFL benchmarks.

# +
import itertools
import pandas as pd
import numpy as np
from scipy.stats import gmean
import matplotlib
import matplotlib.patches as patches
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker

from lib.database import Database
from lib.utils import thousand_sep
# -

# configure pandas display format
pd.set_option('display.max_columns', 50)
pd.set_option('display.float_format', lambda x: '%.2f' % x)

# configure matplotlib
matplotlib.rcParams.update({
    'axes.linewidth': 0.4,
    'font.family': 'serif',
    'font.serif': 'times',
    'font.size': 8,
    'hatch.color': 'red',
    'hatch.linewidth': 0.2,
    'legend.edgecolor': 'red',
    'legend.fancybox': False,
    'text.usetex': True,
    'text.color': 'red',
    'xtick.color': 'red',
    'ytick.color': 'red',
    'xtick.labelsize': 8,
    'ytick.labelsize': 8,
    'axes.labelcolor': 'red',
    'axes.edgecolor': 'red',
    'axes.labelsize': 8})

# ## Benchmarks
#
# As benchmarks we consider all arithmetic instances of the EPFL combinational benchmark suite in two versions.  The first version is the original AIG as provided in the benchmark suite and the second version is the AIG derived from the best-size 6-LUT mapping (version 2017.1).  The latter ones are suffixed using `_bl`.

benchmarks = ["adder", "bar", "div", "hyp", "log2", "max", "multiplier", "sin", "sqrt", "square"]
benchmarks_bl = ["{}_bl".format(benchmark) for benchmark in benchmarks]

# The following list contains all synthesis approaches and their configurations that were used in the experimental evaluation.

configurations = [
    ("DXS", "dxs", {}),
    ("CBS", "cbs", {"threshold": 10}),
    ("BDD", "hdbs", {}),
    ("LHRS-6-direct-def", "lhrs", {"esopcovermethod": "bdd", "mapping_strategy": "direct", "esopscript": "def", "cut_size": 6, "class_method": 1}),
    ("LHRS-6-mindb-def", "lhrs", {"esopcovermethod": "bdd", "mapping_strategy": "min_db", "esopscript": "def", "cut_size": 6, "class_method": 1}),
    ("LHRS-6-direct-def_wo4", "lhrs", {"esopcovermethod": "bdd", "mapping_strategy": "direct", "esopscript": "def_wo4", "cut_size": 6, "class_method": 1}),
    ("LHRS-6-mindb-def_wo4", "lhrs", {"esopcovermethod": "bdd", "mapping_strategy": "min_db", "esopscript": "def_wo4", "cut_size": 6, "class_method": 1}),
    ("LHRS-10-direct-def", "lhrs", {"esopcovermethod": "bdd", "mapping_strategy": "direct", "esopscript": "def", "cut_size": 10, "class_method": 1}),
    ("LHRS-10-mindb-def", "lhrs", {"esopcovermethod": "bdd", "mapping_strategy": "min_db", "esopscript": "def", "cut_size": 10, "class_method": 1}),
    ("LHRS-10-direct-def_wo4", "lhrs", {"esopcovermethod": "bdd", "mapping_strategy": "direct", "esopscript": "def_wo4", "cut_size": 10, "class_method": 1}),
    ("LHRS-10-mindb-def_wo4", "lhrs", {"esopcovermethod": "bdd", "mapping_strategy": "min_db", "esopscript": "def_wo4", "cut_size": 10, "class_method": 1}),
    ("LHRS-16-direct-def", "lhrs", {"esopcovermethod": "bdd", "mapping_strategy": "direct", "esopscript": "def", "cut_size": 16, "class_method": 1}),
    ("LHRS-16-mindb-def", "lhrs", {"esopcovermethod": "bdd", "mapping_strategy": "min_db", "esopscript": "def", "cut_size": 16, "class_method": 1}),
    ("LHRS-16-direct-def_wo4", "lhrs", {"esopcovermethod": "bdd", "mapping_strategy": "direct", "esopscript": "def_wo4", "cut_size": 16, "class_method": 1}),
    ("LHRS-16-mindb-def_wo4", "lhrs", {"esopcovermethod": "bdd", "mapping_strategy": "min_db", "esopscript": "def_wo4", "cut_size": 16, "class_method": 1})    
]
configurations_dict = dict((l[0], l) for l in configurations)

db = Database()

# ## Evaluationg the Effect of Parameters
#
# The following code generates Table 3 of the paper.

# +
index = pd.MultiIndex.from_product([benchmarks, [6, 10, 16], ["Best-LUT", "Original"]], names=['Benchmark', 'LUT size', 'Design'])
columns = pd.MultiIndex(levels=[["", "direct", "hybrid"], ["", "def", "def_wo4"], ["Qubits", "T-count", "Time"]], labels=[[0, 1, 1, 1, 1, 2, 2, 2, 2], [0, 1, 1, 2, 2, 1, 1, 2, 2], [0, 1, 2, 1, 2, 1, 2, 1, 2]], names=["Mapping", "Script", "Metric"])

table = pd.DataFrame(index = index, columns = columns)

col_combinations = list(itertools.product(["direct", "mindb"], ["def", "def_wo4"]))
row_combinations = list(itertools.product([6, 10, 16], [benchmarks_bl, benchmarks]))

for i, (mapping_strategy, esopscript) in enumerate(col_combinations):
    conf_pattern = "LHRS-{{}}-{}-{}".format(mapping_strategy, esopscript)

    get_stats = lambda benchmarks, configuration: list(zip(*db.get_main_statistics(benchmarks, "lhrs", configurations_dict[configuration][2])))

    stats = [get_stats(benchmarks, conf_pattern.format(cut_size)) for cut_size, benchmarks in row_combinations]

    qubits = [s[0] for s in stats]
    tcount = [s[1] for s in stats]
    runtime = [s[2] for s in stats]
    
    if i == 0:
        table.iloc[:,0] = list(sum(zip(*qubits), ()))
    table.iloc[:,i * 2 + 1] = list(sum(zip(*tcount), ()))
    table.iloc[:,i * 2 + 2] = list(sum(zip(*runtime), ()))

table.style.format(thousand_sep)
# -

# ## Comparison to State-of-the-Art Algorithms
#
# The following code generates Tables 4 and 5 of the paper.

# +
index = list(sum(zip(benchmarks_bl, benchmarks), ()))
columns = pd.MultiIndex.from_product([["CBS", "DXS", "BDD", "LHRS-6", "LHRS-10", "LHRS-16"], ["Qubits", "T-count", "Time"]], names=["Approach", "Metric"])

table = pd.DataFrame(index = index, columns = columns)

for i, key in enumerate(["CBS", "DXS", "BDD", "LHRS-6-mindb-def_wo4", "LHRS-10-mindb-def_wo4", "LHRS-16-mindb-def_wo4"]):
    configuration = configurations_dict[key]
    qubits, tcount, runtime = zip(*db.get_main_statistics(index, configuration[1], configuration[2]))
    
    table.iloc[:,3 * i] = qubits
    table.iloc[:,3 * i + 1] = tcount
    table.iloc[:,3 * i + 2] = runtime

table.style.format(thousand_sep)
# -

# We separately compute the geometric mean for qubits and *T*-count, normalized to LHRS-6.

# +
q_mean = gmean(table.iloc[:,[0,3,9,12,15]])
t_mean = gmean(table.iloc[:,[1,4,10,13,16]])

data = np.array((sum(zip(q_mean / q_mean[2], t_mean / t_mean[2]), ()),))

columns = pd.MultiIndex.from_product([["CBS", "DXS", "LHRS-6", "LHRS-10", "LHRS-16"], ["Qubits", "T-count"]], names=["Approach", "Metric"])
table_gmean = pd.DataFrame(columns = columns, data = data)

table_gmean
# -

# ## Distribution of AN-classes
#
# The following code generates Fig. 9 of the paper.

# +
# %matplotlib inline

counters = [db.get_class_counters(configuration) for _, method, configuration in configurations if method == 'lhrs']
sums = [sum(l) for l in zip(*counters)][0:3]
sums = [x[1:] for x in sums]
sumsp = [(x / np.sum(x)) for x in sums]

tcounts = [[7, 0], [15, 7, 16, 0, 7], [40, 15, 47, 7, 23, 43, 16, 40, 0, 79, 27, 48, 140, 7, 12, 61, 23]]
classes = [[0x1, 0x3], [0x01, 0x03, 0x07, 0x0f, 0x17], [0x0001, 0x0003, 0x0007, 0x000f, 0x0017, 0x001f, 0x003f, 0x007f, 0x00ff, 0x0117, 0x011f, 0x013f, 0x017f, 0x033f, 0x0356, 0x0357, 0x035f]]
ylabels = []

hatches = ['xxxxx', '|||||', '///////']
fig, ax = plt.subplots()
fig.subplots_adjust(left=.16, bottom=0.055, right=.999, top=0.999)
b = 0
for i, w in enumerate(sumsp):
    p = ax.barh(y = range(b, b + len(w)), width = [x * 100 for x in w], hatch = hatches[i], height = 0.6, fill = False, linewidth = .4, edgecolor = 'red')
    for j, r in enumerate(p.patches):
        ax.text(r.get_x() + r.get_width() + 1, r.get_y() + r.get_height() / 2, '%s' % thousand_sep( int( sums[i][j] ) ), ha = 'left', va = 'center', fontsize = 6)
    b += len(w)
    fmt = '$^{\\mathrm{\\#}}$\\texttt{%0' + str(2**i) + 'x} {\\scriptsize %s%d}'
    ylabels += [fmt % (c, '\\phantom{0}' * (3 - len(str(t))), t) for (c, t) in zip(classes[i], tcounts[i])]
ax.xaxis.set_major_formatter(ticker.FormatStrFormatter('%d\%%'))
ax.set_xlim([0, 110])
ax.set_yticks(range(b))
ax.set_yticklabels(ylabels)
l = ax.legend(handles = [patches.Patch(hatch = style, edgecolor = 'red', facecolor = 'white', linewidth = 0.4, linestyle = '-', label = '%d-input functions' % (i + 2)) for i, style in enumerate(hatches)])
l.legendPatch.set_linewidth(0.4)

fig.set_size_inches(3.487, 3.5)
fig.savefig('classes.pdf')

StarcoderdataJupyterScriptsDedupFiltered