Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Benchmark compilation time for cancel_inverses and merge_rotations, catalyst-vs-PL #1207

Draft
wants to merge 13 commits into
base: main
Choose a base branch
from
Binary file added auto_peephole_comp.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added auto_peephole_comp_horizontal.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added circuit.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added circuit_optimized.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 10 additions & 0 deletions frontend/catalyst/compiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,14 @@ def run_writing_command(command: List[str], compile_options: Optional[CompileOpt
],
)

PEEPHOLE_BENCHMARK_PASS = (
"PeepholeBenchmarkPass",
[
"remove-chained-self-inverse{func-name=circuit}",
"merge-rotations{func-name=circuit}",
],
)

HLO_LOWERING_PASS = (
"HLOLoweringPass",
[
Expand Down Expand Up @@ -306,6 +314,7 @@ def run_writing_command(command: List[str], compile_options: Optional[CompileOpt


DEFAULT_PIPELINES = [
PEEPHOLE_BENCHMARK_PASS,
ENFORCE_RUNTIME_INVARIANTS_PASS,
HLO_LOWERING_PASS,
QUANTUM_COMPILATION_PASS,
Expand All @@ -323,6 +332,7 @@ def run_writing_command(command: List[str], compile_options: Optional[CompileOpt

DEFAULT_ASYNC_PIPELINES = [
ENFORCE_RUNTIME_INVARIANTS_PASS,
PEEPHOLE_BENCHMARK_PASS,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

irrelevant, as async is not used in the benchmark

HLO_LOWERING_PASS,
QUANTUM_COMPILATION_PASS,
BUFFERIZATION_PASS,
Expand Down
7 changes: 7 additions & 0 deletions mlir/lib/Quantum/Transforms/merge_rotation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@

#define DEBUG_TYPE "merge-rotation"

#include <chrono>
paul0403 marked this conversation as resolved.
Show resolved Hide resolved

#include "Catalyst/IR/CatalystDialect.h"
#include "Quantum/IR/QuantumOps.h"
#include "Quantum/Transforms/Patterns.h"
Expand All @@ -40,6 +42,7 @@ struct MergeRotationsPass : impl::MergeRotationsPassBase<MergeRotationsPass> {
{
LLVM_DEBUG(dbgs() << "merge rotation pass"
<< "\n");
auto start = std::chrono::high_resolution_clock::now();

Operation *module = getOperation();
Operation *targetfunc;
Expand Down Expand Up @@ -67,6 +70,10 @@ struct MergeRotationsPass : impl::MergeRotationsPassBase<MergeRotationsPass> {
if (failed(applyPatternsAndFoldGreedily(targetfunc, std::move(patterns)))) {
return signalPassFailure();
}

auto stop = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(stop - start);
llvm::errs() << "merge rotation pass runtime: " << duration.count() << " microseconds\n";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@paul0403 quick question, I saw I was tagged for review but I assume this branch is not going to be merged into main? 😅

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to review, this is just for visibility, or if others want to use the compile time timing/plotting script (which I will add)

}
};

Expand Down
6 changes: 6 additions & 0 deletions mlir/lib/Quantum/Transforms/remove_chained_self_inverse.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

#define DEBUG_TYPE "remove-chained-self-inverse"

#include <chrono>
#include <memory>
#include <vector>

Expand Down Expand Up @@ -51,6 +52,7 @@ struct RemoveChainedSelfInversePass
{
LLVM_DEBUG(dbgs() << "remove chained self inverse pass"
<< "\n");
auto start = std::chrono::high_resolution_clock::now();

// Run cse pass before running remove-chained-self-inverse,
// to aid identifying equivalent SSA values when verifying
Expand Down Expand Up @@ -88,6 +90,10 @@ struct RemoveChainedSelfInversePass
if (failed(applyPatternsAndFoldGreedily(targetfunc, std::move(patterns)))) {
return signalPassFailure();
}

auto stop = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(stop - start);
llvm::errs() << "cancel inverse pass runtime: " << duration.count() << " microseconds\n";
}
};

Expand Down
112 changes: 112 additions & 0 deletions my_toy_circuit.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import os

Check notice on line 1 in my_toy_circuit.py

View check run for this annotation

codefactor.io / CodeFactor

my_toy_circuit.py#L1

Unused import jax (unused-import)

Check notice on line 1 in my_toy_circuit.py

View check run for this annotation

codefactor.io / CodeFactor

my_toy_circuit.py#L1

Missing module docstring (missing-module-docstring)
import sys

Check notice on line 2 in my_toy_circuit.py

View check run for this annotation

codefactor.io / CodeFactor

my_toy_circuit.py#L2

Unused numpy imported as np (unused-import)

import jax
import numpy as np

Check notice on line 5 in my_toy_circuit.py

View check run for this annotation

codefactor.io / CodeFactor

my_toy_circuit.py#L5

Unused import catalyst (unused-import)
import pennylane as qml

import catalyst
from catalyst import qjit
from catalyst.debug import instrumentation

from timeit import default_timer as timer

dev = qml.device("lightning.qubit", wires=2)

peephole_pipeline = {"cancel_inverses": {}, "merge_rotations": {}}


# @qjit(seed=37, keep_intermediate=True)
# @qjit(keep_intermediate=True)
"""
@qjit(pipelines = [
("0_canonicalize", ["canonicalize"]),
("peephole",
[
"builtin.module(remove-chained-self-inverse{func-name=circuit})",
"builtin.module(merge-rotations{func-name=circuit})"
]
), # peephole
("inline_nested_module", ["inline-nested-module"]),
], # pipelines =
#circuit_transform_pipeline = peephole_pipeline,
autograph=True,
keep_intermediate=True)
"""


@qjit(

Check notice on line 38 in my_toy_circuit.py

View check run for this annotation

codefactor.io / CodeFactor

my_toy_circuit.py#L38

Missing function or method docstring (missing-function-docstring)
autograph=True,

Check notice on line 39 in my_toy_circuit.py

View check run for this annotation

codefactor.io / CodeFactor

my_toy_circuit.py#L39

Unused variable 'i' (unused-variable)
keep_intermediate=False,
# circuit_transform_pipeline = peephole_pipeline,
)
@qml.qnode(dev)
def circuit(theta, loop_size):
for i in range(loop_size):
# for j in range(loop_size):
qml.Hadamard(0)
qml.Hadamard(0)
qml.RX(theta, wires=1)
qml.RX(-theta, wires=1)
return qml.probs()


num_of_iters = int(sys.argv[1:][0])
os.remove("my_toy_circuit.yml")
with instrumentation("my_toy_circuit", filename="my_toy_circuit.yml", detailed=True):
res = circuit(12.3, num_of_iters)
# print(res)

# with open('my_toy_circuit.yml', 'r') as f:
# print(f.read())


####################### core PL #######################

Check notice on line 64 in my_toy_circuit.py

View check run for this annotation

codefactor.io / CodeFactor

my_toy_circuit.py#L64

Missing function or method docstring (missing-function-docstring)

Check notice on line 65 in my_toy_circuit.py

View check run for this annotation

codefactor.io / CodeFactor

my_toy_circuit.py#L65

Unused variable 'i' (unused-variable)

@qml.qnode(dev)
def circuit_corePL(theta, loop_size):
for i in range(loop_size):
# for j in range(loop_size):
qml.Hadamard(0)
qml.Hadamard(0)
qml.RX(theta, wires=1)
qml.RX(-theta, wires=1)
return qml.probs()


Check notice on line 77 in my_toy_circuit.py

View check run for this annotation

codefactor.io / CodeFactor

my_toy_circuit.py#L77

Import "from matplotlib.patches import Rectangle" should be placed at the top of the module (wrong-import-position)
from matplotlib.patches import Rectangle

draw = False
if draw:

Check notice on line 81 in my_toy_circuit.py

View check run for this annotation

codefactor.io / CodeFactor

my_toy_circuit.py#L81

Line too long (124/100) (line-too-long)
plt = qml.draw_mpl(circuit_corePL, show_all_wires=True, style="pennylane", label_options={"color": "white"})(1.23, 1)[0]
plt.text(0.5, 0.06, "repeated $N$ times", fontsize=12, ha="center")
square = Rectangle(
(-0.52, -0.5), 1, 2, linestyle="--", linewidth=1, edgecolor="r", facecolor="none"
)
plt.gca().add_patch(square)
plt.savefig("circuit_optimized")


(tape,), _ = qml.workflow.construct_batch(circuit_corePL, level=0)(12.3, num_of_iters)
start1 = timer()
_ = qml.transforms.cancel_inverses(tape)
end1 = timer()
elapsed_seconds = end1 - start1
elapsed_ms1 = elapsed_seconds * 1e3


tape = _[0][0] # <QuantumScript>
start2 = timer()
_ = qml.transforms.merge_rotations(tape)
end2 = timer()
elapsed_seconds = end2 - start2
elapsed_ms2 = elapsed_seconds * 1e3

total_elapsed_ms = elapsed_ms1 + elapsed_ms2
with open("core_peephole_time.txt", "w") as f:
print(total_elapsed_ms, file=f)


Check notice on line 110 in my_toy_circuit.py

View check run for this annotation

codefactor.io / CodeFactor

my_toy_circuit.py#L110

Using open without explicitly specifying an encoding (unspecified-encoding)
# res = circuit_corePL(12.3, num_of_iters)
# print(res)
Binary file added peephole_benchmark_data.npz
Binary file not shown.
Binary file added peephole_benchmark_data_geomspace25.npz
Binary file not shown.
Binary file added peephole_benchmark_data_small.npz
Binary file not shown.
132 changes: 132 additions & 0 deletions peephole_benchmark_driver.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
import subprocess

Check notice on line 1 in peephole_benchmark_driver.py

View check run for this annotation

codefactor.io / CodeFactor

peephole_benchmark_driver.py#L1

Missing module docstring (missing-module-docstring)

import matplotlib.pyplot as plt

Check notice on line 3 in peephole_benchmark_driver.py

View check run for this annotation

codefactor.io / CodeFactor

peephole_benchmark_driver.py#L3

Unused matplotlib.pyplot imported as plt (unused-import)
import numpy as np


Check notice on line 6 in peephole_benchmark_driver.py

View check run for this annotation

codefactor.io / CodeFactor

peephole_benchmark_driver.py#L6

Missing function or method docstring (missing-function-docstring)
def do(command):

Check failure on line 7 in peephole_benchmark_driver.py

View check run for this annotation

codefactor.io / CodeFactor

peephole_benchmark_driver.py#L7

subprocess call with shell=True identified, security issue. (B602)
subprocess.call(command, shell=True)

def stderr(list_of_data):

Check notice on line 10 in peephole_benchmark_driver.py

View check run for this annotation

codefactor.io / CodeFactor

peephole_benchmark_driver.py#L10

Missing function or method docstring (missing-function-docstring)

Check notice on line 10 in peephole_benchmark_driver.py

View check run for this annotation

codefactor.io / CodeFactor

peephole_benchmark_driver.py#L10

Missing function or method docstring (missing-function-docstring)
return np.std(list_of_data, ddof=1) / np.sqrt(len(list_of_data))

def run_one_circuit(timings, core_PL_timings, num_of_iters):
do(f"python3 my_toy_circuit.py {num_of_iters}")

Check notice on line 14 in peephole_benchmark_driver.py

View check run for this annotation

codefactor.io / CodeFactor

peephole_benchmark_driver.py#L14

Using open without explicitly specifying an encoding (unspecified-encoding)

with open("my_toy_circuit.yml", "r") as f:
lines = f.readlines()
for i, line in enumerate(lines):
if "PeepholeBenchmarkPass" in line:
walltime = lines[i + 1]
walltime = float(walltime[walltime.find(":") + 2 :].strip("\n"))
cputime = lines[i + 2]
cputime = float(cputime[cputime.find(":") + 2 :].strip("\n"))
programsize = lines[i + 3]
programsize = int(programsize[programsize.find(":") + 2 :].strip("\n"))

timings.append(
{"walltime": walltime, "cputime": cputime, "programsize": programsize}
)

Check notice on line 29 in peephole_benchmark_driver.py

View check run for this annotation

codefactor.io / CodeFactor

peephole_benchmark_driver.py#L29

Using open without explicitly specifying an encoding (unspecified-encoding)

with open("core_peephole_time.txt", "r") as f:
core_PL_time = float(f.readlines()[0].strip("\n"))
# print(core_PL_time)
core_PL_timings.append(core_PL_time)

return timings, core_PL_timings


def collect_mean(raw_data, core_PL_timings):
"""
raw_data is something like
[{'walltime': 0.307265, 'cputime': 0.306, 'programsize': 48},
{'walltime': 0.301666, 'cputime': 0.299, 'programsize': 48},
{'walltime': 0.342119, 'cputime': 0.341, 'programsize': 48},
{'walltime': 0.313134, 'cputime': 0.312, 'programsize': 48}]

Check notice on line 46 in peephole_benchmark_driver.py

View check run for this annotation

codefactor.io / CodeFactor

peephole_benchmark_driver.py#L46

Unused variable 'i' (unused-variable)
core_PL_timings is just a list of numbers
"""
mean_data = {"walltime": [], "cputime": [], "programsize": []}
for i, d in enumerate(raw_data):
mean_data["walltime"].append(d["walltime"])
mean_data["cputime"].append(d["cputime"])
mean_data["programsize"].append(d["programsize"])

_ = mean_data["walltime"]
mean_data["walltime"] = (np.mean(_), stderr(_))
_ = mean_data["cputime"]
mean_data["cputime"] = (np.mean(_), stderr(_))
_ = mean_data["programsize"]

Check notice on line 59 in peephole_benchmark_driver.py

View check run for this annotation

codefactor.io / CodeFactor

peephole_benchmark_driver.py#L59

Redefining name 'loopsize' from outer scope (line 79) (redefined-outer-name)

Check notice on line 59 in peephole_benchmark_driver.py

View check run for this annotation

codefactor.io / CodeFactor

peephole_benchmark_driver.py#L59

Missing function or method docstring (missing-function-docstring)
mean_data["programsize"] = (np.mean(_), stderr(_))

return mean_data, (np.mean(core_PL_timings), stderr(core_PL_timings))

Check notice on line 62 in peephole_benchmark_driver.py

View check run for this annotation

codefactor.io / CodeFactor

peephole_benchmark_driver.py#L62

Unused variable 'i' (unused-variable)


def run_one_loopsize(loopsize):
timings = []
core_PL_timings = []
for i in range(3):
_ = run_one_circuit(timings, core_PL_timings, loopsize)
timings = _[0]
core_PL_timings = _[1]

# print(timings)
# print(collect_mean(timings))
return collect_mean(timings, core_PL_timings)


############# main ##################
# loopsizes = [10, 50, 100, 500, 1000, 5000, 10000]
# loopsizes = [10, 20, 30, 40, 100, 150, 200]
loopsizes = np.geomspace(10, 50000, 25, dtype=int)
walltimes = []
cputimes = []
programsizes = []
core_PL_times = []

walltime_errs = []
cputime_errs = []
programsize_errs = []
core_PL_time_errs = []

for loopsize in loopsizes:
_ = run_one_loopsize(loopsize)
#breakpoint()
core_PL_times.append(_[1][0])
core_PL_time_errs.append(_[1][1])

catalyst_times = _[0]
walltimes.append(catalyst_times["walltime"][0])
cputimes.append(catalyst_times["cputime"][0])
programsizes.append(catalyst_times["programsize"][0])
walltime_errs.append(catalyst_times["walltime"][0])
cputime_errs.append(catalyst_times["cputime"][0])
programsize_errs.append(catalyst_times["programsize"][0])


print(loopsizes, walltimes, cputimes, programsizes, core_PL_times)

loopsizes = np.array(loopsizes)
walltimes = np.array(walltimes)
cputimes = np.array(cputimes)
programsizes = np.array(programsizes)
core_PL_times = np.array(core_PL_times)
walltime_errs = np.array(walltime_errs)
cputime_errs = np.array(cputime_errs)
programsize_errs = np.array(programsize_errs)
core_PL_time_errs = np.array(core_PL_time_errs)

np.savez(
"timeit_peephole_benchmark_data_geom25_err",
loopsizes=loopsizes,
walltimes=walltimes,
cputimes=cputimes,
programsizes=programsizes,
core_PL_times=core_PL_times,
walltime_errs=walltime_errs,
cputime_errs=cputime_errs,
programsize_errs=programsize_errs,
core_PL_time_errs=core_PL_time_errs
)

do("rm -rf core_peephole_time.txt my_toy_circuit.yml")
Loading
Loading