From d4684b36d63f2586cc979bd8d40e93ec980706c3 Mon Sep 17 00:00:00 2001 From: Freifrau von Bleifrei Date: Wed, 29 May 2024 07:11:48 +0000 Subject: [PATCH 01/22] __internal__::draw : separation of concerns starting to separate the layering from the drawing --- runtime/cudaq/algorithms/draw.cpp | 63 +++++++++++++++++++++---------- 1 file changed, 43 insertions(+), 20 deletions(-) diff --git a/runtime/cudaq/algorithms/draw.cpp b/runtime/cudaq/algorithms/draw.cpp index 445c9211e6..f40dca0b2a 100644 --- a/runtime/cudaq/algorithms/draw.cpp +++ b/runtime/cudaq/algorithms/draw.cpp @@ -395,9 +395,7 @@ std::string cudaq::__internal__::draw(const Trace &trace) { // can be drawn in the same diagram layer. For example, if I have a // CX(0, 2) and a X(1), then I cannot draw those gates on the same layer // in the circuit diagram - std::vector> boxes; std::vector layers; - std::vector layer_width; std::vector wire_layer(diagram.num_wires(), -1); auto convertToIDs = [](const std::vector &qudits) { @@ -413,19 +411,45 @@ std::string cudaq::__internal__::draw(const Trace &trace) { std::vector wires = convertToIDs(inst.targets); std::sort(wires.begin(), wires.end()); + auto min_dwire = wires.front(); + auto max_dwire = wires.back(); + + std::vector controls = convertToIDs(inst.controls); + for (auto control : controls) { + min_dwire = std::min(control, min_dwire); + max_dwire = std::max(control, max_dwire); + } + + int layer = -1; + for (auto i = min_dwire; i <= max_dwire; ++i) + layer = std::max(layer, wire_layer.at(i)); + layer += 1; + + if (static_cast(layer) == layers.size()) { + layers.emplace_back(); + } + layers.at(layer).emplace_back(ref); + for (auto i = min_dwire; i <= max_dwire; ++i) + wire_layer.at(i) = layer; + ref += 1; + } + + std::vector> boxes; + ref = 0; + for (const auto &inst : trace) { + std::vector wires = convertToIDs(inst.targets); + std::sort(wires.begin(), wires.end()); + auto min_target = wires.front(); auto max_target = wires.back(); - auto min_dwire = min_target; - auto max_dwire = max_target; bool overlap = false; std::vector controls = convertToIDs(inst.controls); + wires.reserve(wires.size() + controls.size()); for (auto control : controls) { wires.push_back(control); if (control > min_target && control < max_target) overlap = true; - min_dwire = std::min(control, min_dwire); - max_dwire = std::max(control, max_dwire); } int padding = 1; @@ -447,24 +471,23 @@ std::string cudaq::__internal__::draw(const Trace &trace) { shape = std::make_unique(label, wires, inst.targets.size(), inst.controls.size()); } - - int layer = -1; - for (auto i = min_dwire; i <= max_dwire; ++i) - layer = std::max(layer, wire_layer.at(i)); - layer += 1; - - if (static_cast(layer) == layers.size()) { - layers.emplace_back(); - layer_width.push_back(0); - } - layers.at(layer).emplace_back(ref); - for (auto i = min_dwire; i <= max_dwire; ++i) - wire_layer.at(i) = layer; - layer_width.at(layer) = std::max(layer_width.at(layer), shape->width()); boxes.push_back(std::move(shape)); ref += 1; } + std::vector layer_width(layers.size(), 0); + // set the width of the layers + for (size_t ref = 0; ref < boxes.size(); ++ref) { + // find the layer where the box is through ref + auto layer_it = + std::find_if(layers.begin(), layers.end(), [&ref](auto &layer) { + return std::find(layer.begin(), layer.end(), ref) != layer.end(); + }); + auto layer = std::distance(layers.begin(), layer_it); + layer_width.at(layer) = + std::max(layer_width.at(layer), boxes[ref]->width()); + } + // Draw labels std::size_t prefix_size = 0u; std::vector prefix(diagram.height(), ""); From 99857b9235de04b588c4c36436cab7ddca8b6244 Mon Sep 17 00:00:00 2001 From: Freifrau von Bleifrei Date: Thu, 30 May 2024 00:46:00 +0000 Subject: [PATCH 02/22] __internal__::draw : helper functions layers_from_trace and boxes_from_trace to separate the layering from the drawing --- runtime/cudaq/algorithms/draw.cpp | 86 ++++++++++++++++++++----------- 1 file changed, 55 insertions(+), 31 deletions(-) diff --git a/runtime/cudaq/algorithms/draw.cpp b/runtime/cudaq/algorithms/draw.cpp index f40dca0b2a..632e7d3da4 100644 --- a/runtime/cudaq/algorithms/draw.cpp +++ b/runtime/cudaq/algorithms/draw.cpp @@ -12,6 +12,7 @@ #include "cudaq/algorithms/draw.h" #include "common/FmtCore.h" #include +#include #include using namespace cudaq; @@ -383,41 +384,44 @@ class DiagramSwap : public Diagram::Operator { } }; -std::string cudaq::__internal__::draw(const Trace &trace) { - if (trace.begin() == trace.end()) - return ""; +// auto [min_dwire_it, max_dwire_it] = +// std::minmax_element(controls.begin(), controls.end()); +// auto min_dwire = std::min(*min_dwire_it, wires.front()); +// auto max_dwire = std::max(*max_dwire_it, wires.back()); - using Layer = std::vector; +namespace { +std::vector convertToIDs(const std::vector &qudits) { + std::vector ids; + ids.reserve(qudits.size()); + std::transform(qudits.cbegin(), qudits.cend(), std::back_inserter(ids), + [](auto &info) { return info.id; }); + return ids; +} - Diagram diagram(trace.getNumQudits()); +using Layer = std::vector; + +// Separate the instructions in layers. Each layer must contain gates that +// can be drawn in the same diagram layer. For example, if I have a +// CX(0, 2) and a X(1), then I cannot draw those gates on the same layer +// in the circuit diagram +std::vector layers_from_trace(const Trace &trace) { - // Separate the instructions in layers. Each layer must contain gates that - // can be drawn in the same diagram layer. For example, if I have a - // CX(0, 2) and a X(1), then I cannot draw those gates on the same layer - // in the circuit diagram std::vector layers; - std::vector wire_layer(diagram.num_wires(), -1); - - auto convertToIDs = [](const std::vector &qudits) { - std::vector ids; - ids.reserve(qudits.size()); - std::transform(qudits.cbegin(), qudits.cend(), std::back_inserter(ids), - [](auto &info) { return info.id; }); - return ids; - }; + std::vector wire_layer(trace.getNumQudits(), -1); std::size_t ref = 0; for (const auto &inst : trace) { std::vector wires = convertToIDs(inst.targets); - std::sort(wires.begin(), wires.end()); - - auto min_dwire = wires.front(); - auto max_dwire = wires.back(); - - std::vector controls = convertToIDs(inst.controls); - for (auto control : controls) { - min_dwire = std::min(control, min_dwire); - max_dwire = std::max(control, max_dwire); + const auto minmax_wires = std::minmax_element(begin(wires), end(wires)); + auto min_dwire = *minmax_wires.first; + auto max_dwire = *minmax_wires.second; + + if (!inst.controls.empty()) { + const std::vector controls = convertToIDs(inst.controls); + const auto minmax_dwire_controls = + std::minmax_element(begin(controls), end(controls)); + min_dwire = std::min(*minmax_dwire_controls.first, min_dwire); + max_dwire = std::max(*minmax_dwire_controls.second, max_dwire); } int layer = -1; @@ -429,28 +433,33 @@ std::string cudaq::__internal__::draw(const Trace &trace) { layers.emplace_back(); } layers.at(layer).emplace_back(ref); + // advance wire layer for (auto i = min_dwire; i <= max_dwire; ++i) wire_layer.at(i) = layer; ref += 1; } + return layers; +} +std::vector> +boxes_from_trace(const Trace &trace) { std::vector> boxes; - ref = 0; + boxes.reserve(std::distance(trace.begin(), trace.end())); + + std::size_t ref = 0; for (const auto &inst : trace) { std::vector wires = convertToIDs(inst.targets); std::sort(wires.begin(), wires.end()); auto min_target = wires.front(); auto max_target = wires.back(); - bool overlap = false; std::vector controls = convertToIDs(inst.controls); - wires.reserve(wires.size() + controls.size()); for (auto control : controls) { - wires.push_back(control); if (control > min_target && control < max_target) overlap = true; } + wires.insert(wires.end(), controls.begin(), controls.end()); int padding = 1; std::string name = inst.params.empty() @@ -474,6 +483,20 @@ std::string cudaq::__internal__::draw(const Trace &trace) { boxes.push_back(std::move(shape)); ref += 1; } + return boxes; +} + +} // namespace + +std::string cudaq::__internal__::draw(const Trace &trace) { + if (trace.begin() == trace.end()) { + return ""; + } + + const auto layers = layers_from_trace(trace); + + std::vector> boxes = + boxes_from_trace(trace); std::vector layer_width(layers.size(), 0); // set the width of the layers @@ -488,6 +511,7 @@ std::string cudaq::__internal__::draw(const Trace &trace) { std::max(layer_width.at(layer), boxes[ref]->width()); } + Diagram diagram(trace.getNumQudits()); // Draw labels std::size_t prefix_size = 0u; std::vector prefix(diagram.height(), ""); From cb338c2ad9b5728248945d77cae2492b656faf37 Mon Sep 17 00:00:00 2001 From: Freifrau von Bleifrei Date: Thu, 30 May 2024 01:24:21 +0000 Subject: [PATCH 03/22] draw.cpp: remove accidental include --- runtime/cudaq/algorithms/draw.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/runtime/cudaq/algorithms/draw.cpp b/runtime/cudaq/algorithms/draw.cpp index 632e7d3da4..f4d5db104b 100644 --- a/runtime/cudaq/algorithms/draw.cpp +++ b/runtime/cudaq/algorithms/draw.cpp @@ -12,7 +12,6 @@ #include "cudaq/algorithms/draw.h" #include "common/FmtCore.h" #include -#include #include using namespace cudaq; From e6e8308192e39fa9a0790fb1097782e86a790f36 Mon Sep 17 00:00:00 2001 From: Freifrau von Bleifrei Date: Thu, 30 May 2024 02:27:03 +0000 Subject: [PATCH 04/22] __internal__::draw : delegate whole string generation to helper function (new function string_diagram_from_trace) --- runtime/cudaq/algorithms/draw.cpp | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/runtime/cudaq/algorithms/draw.cpp b/runtime/cudaq/algorithms/draw.cpp index f4d5db104b..3040065719 100644 --- a/runtime/cudaq/algorithms/draw.cpp +++ b/runtime/cudaq/algorithms/draw.cpp @@ -383,11 +383,6 @@ class DiagramSwap : public Diagram::Operator { } }; -// auto [min_dwire_it, max_dwire_it] = -// std::minmax_element(controls.begin(), controls.end()); -// auto min_dwire = std::min(*min_dwire_it, wires.front()); -// auto max_dwire = std::max(*max_dwire_it, wires.back()); - namespace { std::vector convertToIDs(const std::vector &qudits) { std::vector ids; @@ -485,18 +480,10 @@ boxes_from_trace(const Trace &trace) { return boxes; } -} // namespace - -std::string cudaq::__internal__::draw(const Trace &trace) { - if (trace.begin() == trace.end()) { - return ""; - } - - const auto layers = layers_from_trace(trace); - +std::string string_diagram_from_trace(const Trace &trace, + const std::vector &layers) { std::vector> boxes = boxes_from_trace(trace); - std::vector layer_width(layers.size(), 0); // set the width of the layers for (size_t ref = 0; ref < boxes.size(); ++ref) { @@ -568,3 +555,15 @@ std::string cudaq::__internal__::draw(const Trace &trace) { } return str; } + +} // namespace + +std::string cudaq::__internal__::draw(const Trace &trace) { + if (trace.begin() == trace.end()) { + return ""; + } + + const auto layers = layers_from_trace(trace); + const auto str = string_diagram_from_trace(trace, layers); + return str; +} From 3e4608bb3a772b0ee795655f4bc10e9b8352b18c Mon Sep 17 00:00:00 2001 From: Freifrau von Bleifrei Date: Thu, 30 May 2024 13:29:27 +0000 Subject: [PATCH 05/22] python/tests: new python test, mirroring the C++ draw_tester (cherry picked from commit 96d573106fee3edcaf7edfb6f7187cdf86181b7d) --- python/tests/display/CMakeLists.txt | 14 +++++ python/tests/display/test_draw.py | 90 +++++++++++++++++++++++++++++ 2 files changed, 104 insertions(+) create mode 100644 python/tests/display/CMakeLists.txt create mode 100644 python/tests/display/test_draw.py diff --git a/python/tests/display/CMakeLists.txt b/python/tests/display/CMakeLists.txt new file mode 100644 index 0000000000..64e6dfb3fb --- /dev/null +++ b/python/tests/display/CMakeLists.txt @@ -0,0 +1,14 @@ +# ============================================================================ # +# Copyright (c) 2022 - 2024 NVIDIA Corporation & Affiliates. # +# All rights reserved. # +# # This source code and the accompanying materials are made available under # +# the terms of the Apache License 2.0 which accompanies this distribution. # +# ============================================================================ # + +add_test( + NAME cudaq-py-display-draw + COMMAND ${PYTHON_EXECUTABLE} test_draw.py + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) +set_tests_properties( + cudaq-py-display-draw PROPERTIES ENVIRONMENT + "PYTHONPATH=${CMAKE_BINARY_DIR}/python") diff --git a/python/tests/display/test_draw.py b/python/tests/display/test_draw.py new file mode 100644 index 0000000000..82e5e9b101 --- /dev/null +++ b/python/tests/display/test_draw.py @@ -0,0 +1,90 @@ +# ============================================================================ # +# Copyright (c) 2022 - 2024 NVIDIA Corporation & Affiliates. # +# All rights reserved. # +# # +# This source code and the accompanying materials are made available under # +# the terms of the Apache License 2.0 which accompanies this distribution. # +# ============================================================================ # + +import cudaq +import numpy as np +import os +import pytest + + +@cudaq.kernel +def bar(qvec: cudaq.qview): + rx(np.e, qvec[0]) + ry(np.pi, qvec[1]) + cudaq.adjoint(rz, np.pi, qvec[2]) + + +@cudaq.kernel +def zaz(qub: cudaq.qubit): + sdg(qub) + + +@cudaq.kernel +def kernel(): + q = cudaq.qvector(4) + h(q) + x.ctrl(q[0], q[1]) + y.ctrl(q[0], q[1], q[2]) + y.ctrl(q[2], q[0], q[1]) + y.ctrl(q[1], q[2], q[0]) + z(q[2]) + r1(3.14159, q[0]) + tdg(q[1]) + s(q[2]) + swap.ctrl(q[0], q[2]) + swap.ctrl(q[1], q[2]) + swap.ctrl(q[0], q[1]) + swap.ctrl(q[0], q[2]) + swap.ctrl(q[1], q[2]) + swap.ctrl(q[3], q[0], q[1]) + swap.ctrl(q[0], q[3], q[1], q[2]) + swap.ctrl(q[1], q[0], q[3]) + swap.ctrl(q[1], q[2], q[0], q[3]) + bar(q) + cudaq.control(zaz, q[1], q[0]) + cudaq.adjoint(bar, q) + + +def test_draw(): + """Test draw function, mainly copied from draw_tester.cpp""" + # fmt: off + expected_str = R""" + ╭───╮ ╭───╮╭───────────╮ ╭───────╮» +q0 : ┤ h ├──●────●────●──┤ y ├┤ r1(3.142) ├──────╳─────╳──╳─────╳──●─┤> ├» + ├───┤╭─┴─╮ │ ╭─┴─╮╰─┬─╯╰──┬─────┬──╯ │ │ │ │ │ │ │» +q1 : ┤ h ├┤ x ├──●──┤ y ├──●─────┤ tdg ├─────────┼──╳──╳──┼──╳──╳──╳─┤● ├» + ├───┤╰───╯╭─┴─╮╰─┬─╯ │ ╰┬───┬╯ ╭───╮ │ │ │ │ │ │ │ swap │» +q2 : ┤ h ├─────┤ y ├──●────●──────┤ z ├────┤ s ├─╳──╳─────╳──╳──┼──╳─│ │» + ├───┤ ╰───╯ ╰───╯ ╰───╯ │ │ │ │» +q3 : ┤ h ├──────────────────────────────────────────────────────●──●─┤> ├» + ╰───╯ ╰───────╯» + +################################################################################ + +╭───────╮╭───────────╮ ╭─────╮ ╭────────────╮ +┤> ├┤ rx(2.718) ├────┤ sdg ├───┤ rx(-2.718) ├ +│ │├───────────┤ ╰──┬──╯ ├────────────┤ +┤● ├┤ ry(3.142) ├───────●──────┤ ry(-3.142) ├ +│ swap │├───────────┴╮╭───────────╮╰────────────╯ +┤● ├┤ rz(-3.142) ├┤ rz(3.142) ├────────────── +│ │╰────────────╯╰───────────╯ +┤> ├───────────────────────────────────────── +╰───────╯ +""" + # fmt: on + expected_str = expected_str[1:] + produced_string = cudaq.draw(kernel) + print() + print(produced_string) + assert expected_str == produced_string + + +# leave for gdb debugging +if __name__ == "__main__": + loc = os.path.abspath(__file__) + pytest.main([loc, "-s"]) From 609c7c33d51bf021e34f34bd7061b6894326518c Mon Sep 17 00:00:00 2001 From: Freifrau von Bleifrei Date: Fri, 31 May 2024 01:37:52 +0000 Subject: [PATCH 06/22] test_draw: workaround for the adjoint and Euler number issue https://github.com/NVIDIA/cuda-quantum/issues/1734 --- python/tests/display/test_draw.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/python/tests/display/test_draw.py b/python/tests/display/test_draw.py index 82e5e9b101..e12df8ca2f 100644 --- a/python/tests/display/test_draw.py +++ b/python/tests/display/test_draw.py @@ -14,9 +14,13 @@ @cudaq.kernel def bar(qvec: cudaq.qview): - rx(np.e, qvec[0]) + #FIXME https://github.com/NVIDIA/cuda-quantum/issues/1734 + # rx(np.e, qvec[0])2.71828182845904523536028 + rx(2.71828182845904523536028, qvec[0]) ry(np.pi, qvec[1]) - cudaq.adjoint(rz, np.pi, qvec[2]) + #FIXME https://github.com/NVIDIA/cuda-quantum/issues/1734 + # cudaq.adjoint(rz, np.pi, qvec[2]) + rz(-np.pi, qvec[2]) @cudaq.kernel @@ -79,8 +83,6 @@ def test_draw(): # fmt: on expected_str = expected_str[1:] produced_string = cudaq.draw(kernel) - print() - print(produced_string) assert expected_str == produced_string From 244d5f42e83ab9dfde3d91751e18e7be24fd2e85 Mon Sep 17 00:00:00 2001 From: Freifrau von Bleifrei Date: Mon, 3 Jun 2024 02:39:01 +0000 Subject: [PATCH 07/22] draw: first implementation of getLaTeXString() and delegating draw overload specified in issue #1638 --- runtime/cudaq/algorithms/draw.cpp | 110 ++++++++++++++++++++++++++++-- runtime/cudaq/algorithms/draw.h | 48 +++++++++---- 2 files changed, 140 insertions(+), 18 deletions(-) diff --git a/runtime/cudaq/algorithms/draw.cpp b/runtime/cudaq/algorithms/draw.cpp index 3040065719..e17dbb0694 100644 --- a/runtime/cudaq/algorithms/draw.cpp +++ b/runtime/cudaq/algorithms/draw.cpp @@ -12,7 +12,9 @@ #include "cudaq/algorithms/draw.h" #include "common/FmtCore.h" #include +#include #include +#include using namespace cudaq; @@ -138,6 +140,10 @@ inline void merge_chars(char &c0, char c1) { std::swap(c0, c1); } +const std::map latex_gates_map = { + {"sdg", R"(S^\dag)"}, {"tdg", R"(T^\dag)"}, {"rx", "R_x"}, + {"ry", "R_y"}, {"rz", "R_z"}, {"r1", "R_1"}}; + class Diagram { public: using Wire = int; @@ -440,7 +446,7 @@ boxes_from_trace(const Trace &trace) { std::vector> boxes; boxes.reserve(std::distance(trace.begin(), trace.end())); - std::size_t ref = 0; + // same iteration order as in layers_from_trace for (const auto &inst : trace) { std::vector wires = convertToIDs(inst.targets); std::sort(wires.begin(), wires.end()); @@ -475,7 +481,6 @@ boxes_from_trace(const Trace &trace) { inst.controls.size()); } boxes.push_back(std::move(shape)); - ref += 1; } return boxes; } @@ -556,14 +561,109 @@ std::string string_diagram_from_trace(const Trace &trace, return str; } +std::string get_latex_name(const cudaq::Trace::Instruction &inst) { + if (latex_gates_map.find(inst.name) != latex_gates_map.end()) { + return latex_gates_map.at(inst.name); + } else { + auto latex_gate = inst.name; + // default: name is upper-cased + std::transform(latex_gate.begin(), latex_gate.end(), latex_gate.begin(), + [](unsigned char c) { return std::toupper(c); }); + latex_gate = inst.params.empty() + ? latex_gate + : fmt::format("{}({:.4})", latex_gate, + fmt::join(inst.params.begin(), + inst.params.end(), ",")); + return latex_gate; + } +} + +std::string latex_diagram_from_trace(const Trace &trace, + const std::vector &layers) { + // clang-format off + std::string latex_string = R"(\documentclass{minimal} +\usepackage{quantikz} +\begin{document} +\begin{quantikz})"; + // clang-format on + const std::string sep = " & "; + std::vector latex_lines(trace.getNumQudits()); + for (std::size_t row = 0; row < trace.getNumQudits(); ++row) { + latex_lines[row] += fmt::format(" \\lstick{{$q_{}$}}", row) + sep; + } + for (const auto &layer : layers) { + // unpack this layer + for (const auto &ref : layer) { + auto instruction = trace.begin() + ref; + auto name = get_latex_name(*instruction); + // (controlled) swap + if (name == "SWAP") { + std::vector wires = convertToIDs(instruction->targets); + std::sort(wires.begin(), wires.end()); + auto target_row0 = wires.at(0); + auto target_row1 = wires.at(1); + latex_lines[target_row0] += + R"(\swap{)" + std::to_string(target_row1 - target_row0) + "}"; + latex_lines[target_row1] += R"(\targX{})"; + std::vector controls = + convertToIDs(instruction->controls); + for (int control : controls) { + // draw control line to the swap symbol further away + if (std::abs(control - target_row0) > + std::abs(control - target_row1)) { + latex_lines[control] += + R"(\ctrl{)" + std::to_string(target_row0 - control) + "}"; + } else { + latex_lines[control] += + R"(\ctrl{)" + std::to_string(target_row1 - control) + "}"; + } + } + } else { + // (controlled) box + std::vector wires = convertToIDs(instruction->targets); + std::sort(wires.begin(), wires.end()); + for (auto wire : wires) { + latex_lines[wire] += R"(\gate{)" + name + "}"; + } + std::vector controls = + convertToIDs(instruction->controls); + for (int control : controls) { + latex_lines[control] += + R"(\ctrl{)" + std::to_string(wires.front() - control) + "}"; + } + } + } + for (auto &latex_line : latex_lines) { + // if nothing happened in this layer, add \qw symbol + if (latex_line.ends_with(sep)) { + latex_line += R"(\qw)"; + } + latex_line += sep; + } + } + for (auto &latex_line : latex_lines) { + latex_line += "\\qw \\\\ \n"; + latex_string += latex_line; + } + // clang-format off + latex_string.append(R"(\end{quantikz} +\end{document} +)"); + // clang-format on + return latex_string; +} + } // namespace +std::string cudaq::__internal__::getLaTeXString(const Trace &trace) { + const auto layers = layers_from_trace(trace); + return latex_diagram_from_trace(trace, layers); +} + std::string cudaq::__internal__::draw(const Trace &trace) { if (trace.begin() == trace.end()) { return ""; } - const auto layers = layers_from_trace(trace); - const auto str = string_diagram_from_trace(trace, layers); - return str; + return string_diagram_from_trace(trace, layers); } diff --git a/runtime/cudaq/algorithms/draw.h b/runtime/cudaq/algorithms/draw.h index f2ab4dfae1..3c57a211fd 100644 --- a/runtime/cudaq/algorithms/draw.h +++ b/runtime/cudaq/algorithms/draw.h @@ -17,14 +17,14 @@ namespace __internal__ { std::string draw(const Trace &trace); -} +std::string getLaTeXString(const Trace &trace); + +} // namespace __internal__ namespace details { -/// @brief Execute the given kernel functor and extract the -/// state representation. -template -std::string extractTrace(KernelFunctor &&kernel) { +template +cudaq::Trace traceFromKernel(KernelFunctor &&kernel, Args &&...args) { // Get the platform. auto &platform = cudaq::get_platform(); @@ -38,10 +38,17 @@ std::string extractTrace(KernelFunctor &&kernel) { // Perform the usual pattern set the context, execute and then reset platform.set_exec_ctx(&context); - kernel(); + kernel(args...); platform.reset_exec_ctx(); - return __internal__::draw(context.kernelTrace); + return context.kernelTrace; +} + +/// @brief Execute the given kernel functor and extract the +/// state representation. +template +std::string extractTrace(KernelFunctor &&kernel) { + return __internal__::draw(traceFromKernel(kernel)); } } // namespace details @@ -94,12 +101,27 @@ std::string extractTrace(KernelFunctor &&kernel) { // clang-format on template std::string draw(QuantumKernel &&kernel, Args &&...args) { - ExecutionContext context("tracer"); - auto &platform = get_platform(); - platform.set_exec_ctx(&context); - kernel(args...); - platform.reset_exec_ctx(); - return __internal__::draw(context.kernelTrace); + return __internal__::draw( + details::traceFromKernel(kernel, std::forward(args)...)); +} + +template +std::string getLaTeXString(QuantumKernel &&kernel, Args &&...args) { + return __internal__::getLaTeXString( + details::traceFromKernel(kernel, std::forward(args)...)); +} + +template +std::string cudaq::draw(std::string format, QuantumKernel &&kernel, + Args &&...args) { + if (format == "ascii") { + return draw(kernel, std::forward(args)...); + } else if (format == "latex") { + return getLaTeXString(kernel, std::forward(args)...); + } else { + throw std::runtime_error( + "Invalid format. Supported formats are 'ascii' and 'latex'."); + } } /// @brief Outputs the drawing of a circuit to an output stream. From 1dd35c2e855d53a80a9401fad1034ede6e032cae Mon Sep 17 00:00:00 2001 From: Freifrau von Bleifrei Date: Mon, 3 Jun 2024 03:28:17 +0000 Subject: [PATCH 08/22] test_draw.py: format --- python/tests/display/test_draw.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/python/tests/display/test_draw.py b/python/tests/display/test_draw.py index e12df8ca2f..4845c49358 100644 --- a/python/tests/display/test_draw.py +++ b/python/tests/display/test_draw.py @@ -14,11 +14,11 @@ @cudaq.kernel def bar(qvec: cudaq.qview): - #FIXME https://github.com/NVIDIA/cuda-quantum/issues/1734 - # rx(np.e, qvec[0])2.71828182845904523536028 + # FIXME https://github.com/NVIDIA/cuda-quantum/issues/1734 + # rx(np.e, qvec[0]) rx(2.71828182845904523536028, qvec[0]) ry(np.pi, qvec[1]) - #FIXME https://github.com/NVIDIA/cuda-quantum/issues/1734 + # FIXME https://github.com/NVIDIA/cuda-quantum/issues/1734 # cudaq.adjoint(rz, np.pi, qvec[2]) rz(-np.pi, qvec[2]) From d44e508265e17d0e86f4224c4bbaeccc41b5231e Mon Sep 17 00:00:00 2001 From: Freifrau von Bleifrei Date: Mon, 3 Jun 2024 12:59:39 +0000 Subject: [PATCH 09/22] draw.h: fix draw interface using invocable concept --- runtime/cudaq/algorithms/draw.h | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/runtime/cudaq/algorithms/draw.h b/runtime/cudaq/algorithms/draw.h index 3c57a211fd..13437f2bce 100644 --- a/runtime/cudaq/algorithms/draw.h +++ b/runtime/cudaq/algorithms/draw.h @@ -8,6 +8,8 @@ #pragma once +#include + #include "common/ExecutionContext.h" #include "cudaq/platform.h" @@ -24,6 +26,7 @@ std::string getLaTeXString(const Trace &trace); namespace details { template + requires std::invocable cudaq::Trace traceFromKernel(KernelFunctor &&kernel, Args &&...args) { // Get the platform. auto &platform = cudaq::get_platform(); @@ -99,25 +102,22 @@ std::string extractTrace(KernelFunctor &&kernel) { /// \endcode /// // clang-format on + template + requires std::invocable std::string draw(QuantumKernel &&kernel, Args &&...args) { return __internal__::draw( details::traceFromKernel(kernel, std::forward(args)...)); } template -std::string getLaTeXString(QuantumKernel &&kernel, Args &&...args) { - return __internal__::getLaTeXString( - details::traceFromKernel(kernel, std::forward(args)...)); -} - -template -std::string cudaq::draw(std::string format, QuantumKernel &&kernel, - Args &&...args) { + requires std::invocable +std::string draw(std::string format, QuantumKernel &&kernel, Args &&...args) { if (format == "ascii") { return draw(kernel, std::forward(args)...); } else if (format == "latex") { - return getLaTeXString(kernel, std::forward(args)...); + return __internal__::getLaTeXString( + details::traceFromKernel(kernel, std::forward(args)...)); } else { throw std::runtime_error( "Invalid format. Supported formats are 'ascii' and 'latex'."); From 8e92caa5c2c53c85ef196052ef831967774c51b9 Mon Sep 17 00:00:00 2001 From: Freifrau von Bleifrei Date: Mon, 3 Jun 2024 13:00:31 +0000 Subject: [PATCH 10/22] draw.cpp: fix latex draw output --- runtime/cudaq/algorithms/draw.cpp | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/runtime/cudaq/algorithms/draw.cpp b/runtime/cudaq/algorithms/draw.cpp index e17dbb0694..27686aa48a 100644 --- a/runtime/cudaq/algorithms/draw.cpp +++ b/runtime/cudaq/algorithms/draw.cpp @@ -562,20 +562,21 @@ std::string string_diagram_from_trace(const Trace &trace, } std::string get_latex_name(const cudaq::Trace::Instruction &inst) { + std::string latex_gate; if (latex_gates_map.find(inst.name) != latex_gates_map.end()) { - return latex_gates_map.at(inst.name); + latex_gate = latex_gates_map.at(inst.name); } else { - auto latex_gate = inst.name; + latex_gate = inst.name; // default: name is upper-cased std::transform(latex_gate.begin(), latex_gate.end(), latex_gate.begin(), [](unsigned char c) { return std::toupper(c); }); - latex_gate = inst.params.empty() - ? latex_gate - : fmt::format("{}({:.4})", latex_gate, - fmt::join(inst.params.begin(), - inst.params.end(), ",")); - return latex_gate; } + latex_gate = + inst.params.empty() + ? latex_gate + : fmt::format("{}({:.4})", latex_gate, + fmt::join(inst.params.begin(), inst.params.end(), ",")); + return latex_gate; } std::string latex_diagram_from_trace(const Trace &trace, @@ -584,7 +585,8 @@ std::string latex_diagram_from_trace(const Trace &trace, std::string latex_string = R"(\documentclass{minimal} \usepackage{quantikz} \begin{document} -\begin{quantikz})"; +\begin{quantikz} +)"; // clang-format on const std::string sep = " & "; std::vector latex_lines(trace.getNumQudits()); @@ -642,7 +644,7 @@ std::string latex_diagram_from_trace(const Trace &trace, } } for (auto &latex_line : latex_lines) { - latex_line += "\\qw \\\\ \n"; + latex_line += "\\qw \\\\\n"; latex_string += latex_line; } // clang-format off From d07199c7078e24397cbb08c9b778e4b47ea8084f Mon Sep 17 00:00:00 2001 From: Freifrau von Bleifrei Date: Mon, 3 Jun 2024 13:01:26 +0000 Subject: [PATCH 11/22] draw_tester: add LatexDrawTester --- unittests/integration/draw_tester.cpp | 74 +++++++++++++++++---------- 1 file changed, 47 insertions(+), 27 deletions(-) diff --git a/unittests/integration/draw_tester.cpp b/unittests/integration/draw_tester.cpp index 155deca3c4..72319ba4be 100644 --- a/unittests/integration/draw_tester.cpp +++ b/unittests/integration/draw_tester.cpp @@ -28,37 +28,36 @@ __qpu__ void bar(cudaq::qvector<> &q) { } __qpu__ void zaz(cudaq::qubit &q) { s(q); } -} // namespace - -CUDAQ_TEST(DrawTester, checkOps) { - auto kernel = []() __qpu__ { - cudaq::qvector q(4); - h(q); // Broadcast - x(q[0], q[1]); - y(q[0], q[1], q[2]); - y(q[2], q[0], q[1]); - y(q[1], q[2], q[0]); - z(q[2]); +auto kernel = []() __qpu__ { + cudaq::qvector q(4); + h(q); // Broadcast + x(q[0], q[1]); + y(q[0], q[1], q[2]); + y(q[2], q[0], q[1]); + y(q[1], q[2], q[0]); + z(q[2]); - r1(3.14159, q[0]); - t(q[1]); - s(q[2]); + r1(3.14159, q[0]); + t(q[1]); + s(q[2]); - swap(q[0], q[2]); - swap(q[1], q[2]); - swap(q[0], q[1]); - swap(q[0], q[2]); - swap(q[1], q[2]); - swap(q[3], q[0], q[1]); - swap(q[0], q[3], q[1], q[2]); - swap(q[1], q[0], q[3]); - swap(q[1], q[2], q[0], q[3]); - bar(q); - cudaq::control(zaz, q[1], q[0]); - cudaq::adjoint(bar, q); - }; + swap(q[0], q[2]); + swap(q[1], q[2]); + swap(q[0], q[1]); + swap(q[0], q[2]); + swap(q[1], q[2]); + swap(q[3], q[0], q[1]); + swap(q[0], q[3], q[1], q[2]); + swap(q[1], q[0], q[3]); + swap(q[1], q[2], q[0], q[3]); + bar(q); + cudaq::control(zaz, q[1], q[0]); + cudaq::adjoint(bar, q); +}; +} // namespace +CUDAQ_TEST(DrawTester, checkOps) { // clang-format off // CAUTION: Changing white spaces here will cause the test to fail. Thus be // careful that your editor does not remove them automatically! @@ -92,3 +91,24 @@ q3 : ┤ h ├────────────────────── EXPECT_EQ(expected_str.size(), produced_str.size()); EXPECT_EQ(expected_str, produced_str); } + +CUDAQ_TEST(LatexDrawTester, checkOps) { + // clang-format off + std::string expected_str = R"( +\documentclass{minimal} +\usepackage{quantikz} +\begin{document} +\begin{quantikz} + \lstick{$q_0$} & \gate{H} & \ctrl{1} & \ctrl{2} & \ctrl{1} & \gate{Y} & \gate{R_1(3.142)} & \qw & \swap{2} & \qw & \swap{1} & \swap{2} & \qw & \swap{1} & \ctrl{2} & \swap{3} & \swap{3} & \gate{R_x(2.718)} & \gate{S^\dag} & \gate{R_x(-2.718)} & \qw \\ + \lstick{$q_1$} & \gate{H} & \gate{X} & \ctrl{1} & \gate{Y} & \ctrl{-1} & \gate{T^\dag} & \qw & \qw & \swap{1} & \targX{} & \qw & \swap{1} & \targX{} & \swap{1} & \ctrl{2} & \ctrl{2} & \gate{R_y(3.142)} & \ctrl{-1} & \gate{R_y(-3.142)} & \qw \\ + \lstick{$q_2$} & \gate{H} & \qw & \gate{Y} & \ctrl{-1} & \ctrl{-2} & \gate{Z} & \gate{S} & \targX{} & \targX{} & \qw & \targX{} & \targX{} & \qw & \targX{} & \qw & \ctrl{-2} & \gate{R_z(-3.142)} & \gate{R_z(3.142)} & \qw & \qw \\ + \lstick{$q_3$} & \gate{H} & \qw & \qw & \qw & \qw & \qw & \qw & \qw & \qw & \qw & \qw & \qw & \ctrl{-3} & \ctrl{-2} & \targX{} & \targX{} & \qw & \qw & \qw & \qw \\ +\end{quantikz} +\end{document} +)"; + // clang-format on + expected_str = expected_str.substr(1); + std::string produced_str = cudaq::draw("latex", kernel); + EXPECT_EQ(expected_str.size(), produced_str.size()); + EXPECT_EQ(expected_str, produced_str); +} From c3cde81b5fd405b9da37059cb6d98a47fd3ea73a Mon Sep 17 00:00:00 2001 From: Freifrau von Bleifrei Date: Tue, 4 Jun 2024 02:15:32 +0000 Subject: [PATCH 12/22] draw.h: add details::extractTraceLatex which can take a kernel without passed arguments --- runtime/cudaq/algorithms/draw.h | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/runtime/cudaq/algorithms/draw.h b/runtime/cudaq/algorithms/draw.h index 13437f2bce..79f32eddff 100644 --- a/runtime/cudaq/algorithms/draw.h +++ b/runtime/cudaq/algorithms/draw.h @@ -25,6 +25,8 @@ std::string getLaTeXString(const Trace &trace); namespace details { +/// @brief execute the kernel functor (with optional arguments) and return the +/// trace of the execution path. template requires std::invocable cudaq::Trace traceFromKernel(KernelFunctor &&kernel, Args &&...args) { @@ -39,7 +41,7 @@ cudaq::Trace traceFromKernel(KernelFunctor &&kernel, Args &&...args) { // path ExecutionContext context("tracer"); - // Perform the usual pattern set the context, execute and then reset + // set the context, execute and then reset platform.set_exec_ctx(&context); kernel(args...); platform.reset_exec_ctx(); @@ -54,6 +56,13 @@ std::string extractTrace(KernelFunctor &&kernel) { return __internal__::draw(traceFromKernel(kernel)); } +/// @brief Execute the given kernel functor and extract the +/// state representation as LaTeX. +template +std::string extractTraceLatex(KernelFunctor &&kernel) { + return __internal__::getLaTeXString(traceFromKernel(kernel)); +} + } // namespace details // clang-format off From 254e8165a901e95eb8cefb25418991e4a3a56a17 Mon Sep 17 00:00:00 2001 From: Freifrau von Bleifrei Date: Tue, 4 Jun 2024 05:39:15 +0000 Subject: [PATCH 13/22] draw: add overload to python interface + test (format selection by string as in the C++ interface described in issue #1638) --- python/runtime/cudaq/algorithms/py_draw.cpp | 53 ++++++++++++++++++--- python/tests/display/test_draw.py | 21 ++++++++ 2 files changed, 67 insertions(+), 7 deletions(-) diff --git a/python/runtime/cudaq/algorithms/py_draw.cpp b/python/runtime/cudaq/algorithms/py_draw.cpp index 8721813b9e..8abf2c3ea1 100644 --- a/python/runtime/cudaq/algorithms/py_draw.cpp +++ b/python/runtime/cudaq/algorithms/py_draw.cpp @@ -9,18 +9,20 @@ #include "utils/OpaqueArguments.h" #include "mlir/Bindings/Python/PybindAdaptors.h" -#include #include #include +#include +#include +#include namespace cudaq { void pyAltLaunchKernel(const std::string &, MlirModule, OpaqueArguments &, const std::vector &); -/// @brief Run `cudaq::draw` on the provided kernel. -std::string pyDraw(py::object &kernel, py::args args) { - +namespace { +std::tuple +getKernelLaunchParameters(py::object &kernel, py::args args) { if (py::len(kernel.attr("arguments")) != args.size()) throw std::runtime_error("Invalid number of arguments passed to draw."); @@ -32,17 +34,54 @@ std::string pyDraw(py::object &kernel, py::args args) { args = simplifiedValidateInputArguments(args); auto *argData = toOpaqueArgs(args, kernelMod, kernelName); + return {kernelName, kernelMod, argData}; +} +} // namespace + +/// @brief Run `cudaq::draw` on the provided kernel. +std::string pyDraw(py::object &kernel, py::args args) { + auto [kernelName, kernelMod, argData] = + getKernelLaunchParameters(kernel, args); + return details::extractTrace([&]() mutable { pyAltLaunchKernel(kernelName, kernelMod, *argData, {}); delete argData; }); } +/// @brief Run `cudaq::draw`'s string overload on the provided kernel. +std::string pyDraw(std::string format, py::object &kernel, py::args args) { + if (format == "ascii") { + return pyDraw(kernel, args); + } else if (format == "latex") { + auto [kernelName, kernelMod, argData] = + getKernelLaunchParameters(kernel, args); + + return details::extractTraceLatex([&]() mutable { + pyAltLaunchKernel(kernelName, kernelMod, *argData, {}); + delete argData; + }); + } else { + throw std::runtime_error("Invalid format passed to draw."); + } +} + /// @brief Bind the draw cudaq function void bindPyDraw(py::module &mod) { - mod.def( - "draw", &pyDraw, - R"#(Return a UTF-8 encoded string representing drawing of the execution + mod.def("draw", + py::overload_cast(&pyDraw), + R"(Return a string representing the drawing of the execution path, + in the format specified as the first argument. If the format is + 'ascii', the output will be a UTF-8 encoded string. If the format + is 'latex', the output will be a LaTeX string. +Args: + format (str): The format of the output. Can be 'ascii' or 'latex'. + kernel (:class:`Kernel`): The :class:`Kernel` to draw. + *arguments (Optional[Any]): The concrete values to evaluate the kernel + function at. Leave empty if the kernel doesn't accept any arguments.)") + .def( + "draw", py::overload_cast(&pyDraw), + R"#(Return a UTF-8 encoded string representing drawing of the execution path, i.e., the trace, of the provided `kernel`. Args: diff --git a/python/tests/display/test_draw.py b/python/tests/display/test_draw.py index 4845c49358..a5295113c6 100644 --- a/python/tests/display/test_draw.py +++ b/python/tests/display/test_draw.py @@ -86,6 +86,27 @@ def test_draw(): assert expected_str == produced_string +def test_draw_latex(): + """Test draw function, mainly copied from draw_tester.cpp""" + # fmt: off + expected_str = R""" +\documentclass{minimal} +\usepackage{quantikz} +\begin{document} +\begin{quantikz} + \lstick{$q_0$} & \gate{H} & \ctrl{1} & \ctrl{2} & \ctrl{1} & \gate{Y} & \gate{R_1(3.142)} & \qw & \swap{2} & \qw & \swap{1} & \swap{2} & \qw & \swap{1} & \ctrl{2} & \swap{3} & \swap{3} & \gate{R_x(2.718)} & \gate{S^\dag} & \gate{R_x(-2.718)} & \qw \\ + \lstick{$q_1$} & \gate{H} & \gate{X} & \ctrl{1} & \gate{Y} & \ctrl{-1} & \gate{T^\dag} & \qw & \qw & \swap{1} & \targX{} & \qw & \swap{1} & \targX{} & \swap{1} & \ctrl{2} & \ctrl{2} & \gate{R_y(3.142)} & \ctrl{-1} & \gate{R_y(-3.142)} & \qw \\ + \lstick{$q_2$} & \gate{H} & \qw & \gate{Y} & \ctrl{-1} & \ctrl{-2} & \gate{Z} & \gate{S} & \targX{} & \targX{} & \qw & \targX{} & \targX{} & \qw & \targX{} & \qw & \ctrl{-2} & \gate{R_z(-3.142)} & \gate{R_z(3.142)} & \qw & \qw \\ + \lstick{$q_3$} & \gate{H} & \qw & \qw & \qw & \qw & \qw & \qw & \qw & \qw & \qw & \qw & \qw & \ctrl{-3} & \ctrl{-2} & \targX{} & \targX{} & \qw & \qw & \qw & \qw \\ +\end{quantikz} +\end{document} +""" + # fmt: on + expected_str = expected_str[1:] + produced_string = cudaq.draw("latex", kernel) + assert expected_str == produced_string + + # leave for gdb debugging if __name__ == "__main__": loc = os.path.abspath(__file__) From b54036d250915c733605d30332303d547a91962c Mon Sep 17 00:00:00 2001 From: Freifrau von Bleifrei Date: Fri, 7 Jun 2024 08:24:57 +0900 Subject: [PATCH 14/22] draw: add displaySVG() for IPython --- python/cudaq/__init__.py | 3 +++ python/cudaq/display/display_trace.py | 30 +++++++++++++++++++++ python/runtime/cudaq/algorithms/py_draw.cpp | 4 ++- 3 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 python/cudaq/display/display_trace.py diff --git a/python/cudaq/__init__.py b/python/cudaq/__init__.py index bbeb2d4de6..ffd5a98180 100644 --- a/python/cudaq/__init__.py +++ b/python/cudaq/__init__.py @@ -34,6 +34,7 @@ print("Could not find a suitable cuQuantum Python package.") pass +from .display import display_trace from .kernel.kernel_decorator import kernel, PyKernelDecorator from .kernel.kernel_builder import make_kernel, QuakeValue, PyKernel from .kernel.ast_bridge import globalAstRegistry, globalKernelRegistry @@ -102,6 +103,8 @@ AsyncStateResult = cudaq_runtime.AsyncStateResult vqe = cudaq_runtime.vqe draw = cudaq_runtime.draw +displaySVG = display_trace.displaySVG +getSVGstring = display_trace.getSVGstring ComplexMatrix = cudaq_runtime.ComplexMatrix to_qir = cudaq_runtime.get_qir diff --git a/python/cudaq/display/display_trace.py b/python/cudaq/display/display_trace.py new file mode 100644 index 0000000000..021e8a2d45 --- /dev/null +++ b/python/cudaq/display/display_trace.py @@ -0,0 +1,30 @@ +# ============================================================================ # +# Copyright (c) 2022 - 2024 NVIDIA Corporation & Affiliates. # +# All rights reserved. # +# # +# This source code and the accompanying materials are made available under # +# the terms of the Apache License 2.0 which accompanies this distribution. # +# ============================================================================ # + +import cudaq + + +def getSVGstring(kernel, *args): + from subprocess import check_output, STDOUT + from tempfile import TemporaryDirectory + + latex_string = cudaq.draw("latex", kernel, *args) + with TemporaryDirectory() as tmpdirname: + with open(tmpdirname + "/cudaq-trace.tex", "w") as f: + f.write(latex_string) + # this needs latex and quantikz to be installed, e.g. apt's texlive-latex-extra + check_output(["latex", "cudaq-trace"], cwd=tmpdirname, stderr=STDOUT) + check_output(["dvisvgm", "cudaq-trace"], cwd=tmpdirname, stderr=STDOUT) + with open(tmpdirname + "/cudaq-trace.svg", "rb") as f: + return f.read() + + +def displaySVG(kernel, *args): + from IPython.display import SVG, display + + display(SVG(getSVGstring(kernel, *args))) diff --git a/python/runtime/cudaq/algorithms/py_draw.cpp b/python/runtime/cudaq/algorithms/py_draw.cpp index 8abf2c3ea1..fd4490f8d2 100644 --- a/python/runtime/cudaq/algorithms/py_draw.cpp +++ b/python/runtime/cudaq/algorithms/py_draw.cpp @@ -24,7 +24,9 @@ namespace { std::tuple getKernelLaunchParameters(py::object &kernel, py::args args) { if (py::len(kernel.attr("arguments")) != args.size()) - throw std::runtime_error("Invalid number of arguments passed to draw."); + throw std::runtime_error("Invalid number of arguments passed to draw:" + + std::to_string(args.size()) + " expected " + + std::to_string(py::len(kernel.attr("arguments")))); if (py::hasattr(kernel, "compile")) kernel.attr("compile")(); From b86042b93b1ca381a1a208e63905f922cb6c99fb Mon Sep 17 00:00:00 2001 From: Freifrau von Bleifrei Date: Fri, 7 Jun 2024 08:55:42 +0900 Subject: [PATCH 15/22] display_trace: add encoding to returned string --- python/cudaq/display/display_trace.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/cudaq/display/display_trace.py b/python/cudaq/display/display_trace.py index 021e8a2d45..3fbe884f85 100644 --- a/python/cudaq/display/display_trace.py +++ b/python/cudaq/display/display_trace.py @@ -21,10 +21,10 @@ def getSVGstring(kernel, *args): check_output(["latex", "cudaq-trace"], cwd=tmpdirname, stderr=STDOUT) check_output(["dvisvgm", "cudaq-trace"], cwd=tmpdirname, stderr=STDOUT) with open(tmpdirname + "/cudaq-trace.svg", "rb") as f: - return f.read() + return str(f.read(), encoding="utf-8") def displaySVG(kernel, *args): from IPython.display import SVG, display - display(SVG(getSVGstring(kernel, *args))) + display(SVG(data=getSVGstring(kernel, *args))) From b73c40a0875f0f5fafedede3044d485fd18c0d8b Mon Sep 17 00:00:00 2001 From: Freifrau von Bleifrei Date: Fri, 7 Jun 2024 09:01:18 +0900 Subject: [PATCH 16/22] kernel_decorator: add _repr_svg_(self) for svg display in Jupyter notebooks --- python/cudaq/kernel/kernel_decorator.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/python/cudaq/kernel/kernel_decorator.py b/python/cudaq/kernel/kernel_decorator.py index 299b2c3fcf..71eccda3ce 100644 --- a/python/cudaq/kernel/kernel_decorator.py +++ b/python/cudaq/kernel/kernel_decorator.py @@ -179,6 +179,26 @@ def __str__(self): """ self.compile() return str(self.module) + + def _repr_svg_(self): + """ + Return the SVG representation of `self` (:class:`PyKernelDecorator`). + This assumes no arguments are required to execute the kernel, + and latex (with quantikz package) and dvisvgm are installed, + and the temporary directory is writable. + If any of these assumptions fail, returns None. + """ + if len(self.argTypes) != 0: + return None + from cudaq import getSVGstring + try: + from subprocess import CalledProcessError + try: + return getSVGstring(self) + except CalledProcessError: + return None + except ImportError: + return None def isCastable(self, fromTy, toTy): if F64Type.isinstance(toTy): From 81f86b2730859dd06a68134c43401d7be3112616 Mon Sep 17 00:00:00 2001 From: Freifrau von Bleifrei Date: Fri, 7 Jun 2024 09:45:43 +0900 Subject: [PATCH 17/22] kernel_decorator: account for empty args in _repr_svg_ --- python/cudaq/kernel/kernel_decorator.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/python/cudaq/kernel/kernel_decorator.py b/python/cudaq/kernel/kernel_decorator.py index 71eccda3ce..7cea8dd494 100644 --- a/python/cudaq/kernel/kernel_decorator.py +++ b/python/cudaq/kernel/kernel_decorator.py @@ -179,20 +179,22 @@ def __str__(self): """ self.compile() return str(self.module) - + def _repr_svg_(self): """ Return the SVG representation of `self` (:class:`PyKernelDecorator`). - This assumes no arguments are required to execute the kernel, - and latex (with quantikz package) and dvisvgm are installed, - and the temporary directory is writable. + This assumes no arguments are required to execute the kernel, + and latex (with quantikz package) and dvisvgm are installed, + and the temporary directory is writable. If any of these assumptions fail, returns None. """ - if len(self.argTypes) != 0: + if self.argTypes is None or len(self.argTypes) != 0: return None from cudaq import getSVGstring + try: from subprocess import CalledProcessError + try: return getSVGstring(self) except CalledProcessError: From c5891cc580055af0472d872200e0b4c880c0a108 Mon Sep 17 00:00:00 2001 From: Freifrau von Bleifrei Date: Fri, 7 Jun 2024 10:00:16 +0900 Subject: [PATCH 18/22] kernel_decorator: add compile() to _repr_svg_() --- python/cudaq/kernel/kernel_decorator.py | 1 + 1 file changed, 1 insertion(+) diff --git a/python/cudaq/kernel/kernel_decorator.py b/python/cudaq/kernel/kernel_decorator.py index 7cea8dd494..741bf1ecda 100644 --- a/python/cudaq/kernel/kernel_decorator.py +++ b/python/cudaq/kernel/kernel_decorator.py @@ -188,6 +188,7 @@ def _repr_svg_(self): and the temporary directory is writable. If any of these assumptions fail, returns None. """ + self.compile() # compile if not yet compiled if self.argTypes is None or len(self.argTypes) != 0: return None from cudaq import getSVGstring From fede8bf60bea07f7c084e61e0bddc09960b9a73f Mon Sep 17 00:00:00 2001 From: Freifrau von Bleifrei Date: Fri, 7 Jun 2024 10:18:27 +0900 Subject: [PATCH 19/22] display_trace: quote quantikz for spell checking --- python/cudaq/display/display_trace.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/cudaq/display/display_trace.py b/python/cudaq/display/display_trace.py index 3fbe884f85..5bfa93da7c 100644 --- a/python/cudaq/display/display_trace.py +++ b/python/cudaq/display/display_trace.py @@ -17,7 +17,7 @@ def getSVGstring(kernel, *args): with TemporaryDirectory() as tmpdirname: with open(tmpdirname + "/cudaq-trace.tex", "w") as f: f.write(latex_string) - # this needs latex and quantikz to be installed, e.g. apt's texlive-latex-extra + # this needs `latex` and `quantikz` to be installed, e.g. through `apt install texlive-latex-extra` check_output(["latex", "cudaq-trace"], cwd=tmpdirname, stderr=STDOUT) check_output(["dvisvgm", "cudaq-trace"], cwd=tmpdirname, stderr=STDOUT) with open(tmpdirname + "/cudaq-trace.svg", "rb") as f: From 60a6ed64ebb2a2ca5bdf123f79f10c270a82ded9 Mon Sep 17 00:00:00 2001 From: Freifrau von Bleifrei Date: Fri, 7 Jun 2024 10:43:12 +0900 Subject: [PATCH 20/22] kernel_decorator: quote quantikz for spell checking --- python/cudaq/kernel/kernel_decorator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/cudaq/kernel/kernel_decorator.py b/python/cudaq/kernel/kernel_decorator.py index 741bf1ecda..91d2f89ed5 100644 --- a/python/cudaq/kernel/kernel_decorator.py +++ b/python/cudaq/kernel/kernel_decorator.py @@ -184,7 +184,7 @@ def _repr_svg_(self): """ Return the SVG representation of `self` (:class:`PyKernelDecorator`). This assumes no arguments are required to execute the kernel, - and latex (with quantikz package) and dvisvgm are installed, + and `latex` (with `quantikz` package) and `dvisvgm` are installed, and the temporary directory is writable. If any of these assumptions fail, returns None. """ From 194575d1dc9b3dfffa0a341a6f1523e4fd3e09a8 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Wed, 19 Jun 2024 17:39:16 +0000 Subject: [PATCH 21/22] fixing docs generation issue --- python/runtime/cudaq/algorithms/py_draw.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/python/runtime/cudaq/algorithms/py_draw.cpp b/python/runtime/cudaq/algorithms/py_draw.cpp index fd4490f8d2..bb2c6bd5da 100644 --- a/python/runtime/cudaq/algorithms/py_draw.cpp +++ b/python/runtime/cudaq/algorithms/py_draw.cpp @@ -72,15 +72,16 @@ std::string pyDraw(std::string format, py::object &kernel, py::args args) { void bindPyDraw(py::module &mod) { mod.def("draw", py::overload_cast(&pyDraw), - R"(Return a string representing the drawing of the execution path, - in the format specified as the first argument. If the format is - 'ascii', the output will be a UTF-8 encoded string. If the format - is 'latex', the output will be a LaTeX string. + R"#(Return a string representing the drawing of the execution path, +in the format specified as the first argument. If the format is +'ascii', the output will be a UTF-8 encoded string. If the format +is 'latex', the output will be a LaTeX string. + Args: format (str): The format of the output. Can be 'ascii' or 'latex'. kernel (:class:`Kernel`): The :class:`Kernel` to draw. *arguments (Optional[Any]): The concrete values to evaluate the kernel - function at. Leave empty if the kernel doesn't accept any arguments.)") + function at. Leave empty if the kernel doesn't accept any arguments.)#") .def( "draw", py::overload_cast(&pyDraw), R"#(Return a UTF-8 encoded string representing drawing of the execution From 88b867bd5bafb21564954528a5796de6e1204c92 Mon Sep 17 00:00:00 2001 From: Bruno Schmitt <7152025+boschmitt@users.noreply.github.com> Date: Wed, 19 Jun 2024 20:17:14 +0200 Subject: [PATCH 22/22] Fix c++17 support --- runtime/cudaq/algorithms/draw.h | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/runtime/cudaq/algorithms/draw.h b/runtime/cudaq/algorithms/draw.h index 79f32eddff..e9f1ec21fc 100644 --- a/runtime/cudaq/algorithms/draw.h +++ b/runtime/cudaq/algorithms/draw.h @@ -28,7 +28,6 @@ namespace details { /// @brief execute the kernel functor (with optional arguments) and return the /// trace of the execution path. template - requires std::invocable cudaq::Trace traceFromKernel(KernelFunctor &&kernel, Args &&...args) { // Get the platform. auto &platform = cudaq::get_platform(); @@ -112,15 +111,27 @@ std::string extractTraceLatex(KernelFunctor &&kernel) { /// // clang-format on +#if CUDAQ_USE_STD20 template requires std::invocable +#else +template < + typename QuantumKernel, typename... Args, + typename = std::enable_if_t>> +#endif std::string draw(QuantumKernel &&kernel, Args &&...args) { return __internal__::draw( details::traceFromKernel(kernel, std::forward(args)...)); } +#if CUDAQ_USE_STD20 template requires std::invocable +#else +template < + typename QuantumKernel, typename... Args, + typename = std::enable_if_t>> +#endif std::string draw(std::string format, QuantumKernel &&kernel, Args &&...args) { if (format == "ascii") { return draw(kernel, std::forward(args)...);