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

Add DummyBox #1120

Merged
merged 28 commits into from
Nov 14, 2023
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
316eb6b
Define ResourceData.
cqc-alec Jul 10, 2023
356598c
Add skeleton source files.
cqc-alec Nov 1, 2023
541d9d6
Add OpType::DummyBox.
cqc-alec Nov 1, 2023
013fb8e
Construct DummyBox from ResourceData.
cqc-alec Nov 1, 2023
58a9918
Add copy constructor.
cqc-alec Nov 1, 2023
076a978
Add symbol_substitution() and free_symbols() methods.
cqc-alec Nov 1, 2023
1562e2d
Add get_resource_data() method.
cqc-alec Nov 1, 2023
97b17c3
Add qubits, bits, get_signature, serialization and equality checking.
cqc-alec Nov 1, 2023
e41cc51
Add test for serialization and equality checking.
cqc-alec Nov 2, 2023
733ca42
Make binders.
cqc-alec Nov 1, 2023
371e143
Fix: include WASM inputs and outputs.
cqc-alec Nov 9, 2023
1789480
Implement Circuit::get_resources().
cqc-alec Nov 7, 2023
d6f881a
Bind get_resources().
cqc-alec Nov 9, 2023
432b8d9
Implement __repr__() for ResourceData.
cqc-alec Nov 10, 2023
4560a94
Add python tests.
cqc-alec Nov 6, 2023
63ba059
Improve docs.
cqc-alec Nov 10, 2023
3c35aa6
Add to docs.
cqc-alec Nov 10, 2023
a9c198b
Update changelog.
cqc-alec Nov 10, 2023
cd085d5
Bump tket version.
cqc-alec Nov 10, 2023
268a3e2
Fix includes.
cqc-alec Nov 10, 2023
0473c68
Update stubs.
cqc-alec Nov 10, 2023
c527262
Add C++ test.
cqc-alec Nov 13, 2023
da00f47
Fix for mypy.
cqc-alec Nov 13, 2023
a8e4b16
Merge branch 'develop' into feature/resourceboxes
cqc-alec Nov 14, 2023
0ec146a
Validate json.
cqc-alec Nov 14, 2023
c9b23b4
Check actual number of qubits on operation.
cqc-alec Nov 14, 2023
879dee9
Add schema for resource_data
yao-cqc Nov 14, 2023
931d6ab
Merge branch 'develop' into feature/resourceboxes
cqc-alec Nov 14, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions pytket/binders/circuit/Circuit/add_op.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,14 @@
#include "tket/Circuit/ClassicalExpBox.hpp"
#include "tket/Circuit/ConjugationBox.hpp"
#include "tket/Circuit/DiagonalBox.hpp"
#include "tket/Circuit/DummyBox.hpp"
#include "tket/Circuit/Multiplexor.hpp"
#include "tket/Circuit/PauliExpBoxes.hpp"
#include "tket/Circuit/StatePreparation.hpp"
#include "tket/Circuit/ToffoliBox.hpp"
#include "tket/Converters/PhasePoly.hpp"
#include "tket/Gate/OpPtrFunctions.hpp"
#include "tket/Utils/UnitID.hpp"
#include "typecast.hpp"
namespace py = pybind11;

Expand Down Expand Up @@ -332,6 +334,30 @@ void init_circuit_add_op(py::class_<Circuit, std::shared_ptr<Circuit>> &c) {
"qubits: Indices of the qubits to append the box to"
"\n:return: the new :py:class:`Circuit`",
py::arg("toffolibox"), py::arg("qubits"))
.def(
"add_dummybox",
[](Circuit *circ, const DummyBox &box,
const py::tket_custom::SequenceVec<unsigned> &qubits,
const py::tket_custom::SequenceVec<unsigned> &bits,
const py::kwargs &kwargs) {
std::vector<UnitID> args;
for (unsigned i : qubits) {
args.push_back(Qubit(i));
}
for (unsigned i : bits) {
args.push_back(Bit(i));
}
return add_box_method<UnitID>(
circ, std::make_shared<DummyBox>(box), args, kwargs);
},
"Append a :py:class:`DummyBox` to the circuit."
"\n\n:param dummybox: The box to append"
"\n:param qubits: Indices (in the default register) of the qubits to "
"append the box to"
"\n:param bits: Indices of the bits (in the default register) to "
"append the box to"
"\n:return: the new :py:class:`Circuit`",
py::arg("dummybox"), py::arg("qubits"), py::arg("bits"))
.def(
"add_qcontrolbox",
[](Circuit *circ, const QControlBox &box,
Expand Down Expand Up @@ -603,6 +629,22 @@ void init_circuit_add_op(py::class_<Circuit, std::shared_ptr<Circuit>> &c) {
"qubits: Indices of the qubits to append the box to"
"\n:return: the new :py:class:`Circuit`",
py::arg("toffolibox"), py::arg("qubits"))
.def(
"add_dummybox",
[](Circuit *circ, const DummyBox &box,
const py_qubit_vector_t &qubits, const py_bit_vector_t &bits,
const py::kwargs &kwargs) {
std::vector<UnitID> args = {qubits.begin(), qubits.end()};
args.insert(args.end(), bits.begin(), bits.end());
return add_box_method<UnitID>(
circ, std::make_shared<DummyBox>(box), args, kwargs);
},
"Append a :py:class:`DummyBox` to the circuit."
"\n\n:param dummybox: The box to append"
"\n:param qubits: Qubits to append the box to"
"\n:param bits: Bits to append the box to"
"\n:return: the new :py:class:`Circuit`",
py::arg("dummybox"), py::arg("qubits"), py::arg("bits"))
.def(
"add_qcontrolbox",
[](Circuit *circ, const QControlBox &box,
Expand Down
77 changes: 77 additions & 0 deletions pytket/binders/circuit/Circuit/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#include "tket/Circuit/Boxes.hpp"
#include "tket/Circuit/Circuit.hpp"
#include "tket/Circuit/Command.hpp"
#include "tket/Circuit/DummyBox.hpp"
#include "tket/Circuit/PauliExpBoxes.hpp"
#include "tket/Circuit/Simulation/CircuitSimulator.hpp"
#include "tket/Circuit/ToffoliBox.hpp"
Expand Down Expand Up @@ -763,6 +764,17 @@ void def_circuit(py::class_<Circuit, std::shared_ptr<Circuit>> &pyCircuit) {
":param opgroup: the name of the operations group to replace\n"
":return: whether any replacements were made",
py::arg("box"), py::arg("opgroup"))
.def(
"substitute_named",
[](Circuit &circ, const DummyBox &box, const std::string &opgroup) {
return circ.substitute_named(box, opgroup);
},
"Substitute all ops with the given name for the given box."
"The replacement boxes retain the same name.\n\n"
":param box: the replacement DummyBox\n"
":param opgroup: the name of the operations group to replace\n"
":return: whether any replacements were made",
py::arg("box"), py::arg("opgroup"))
.def(
"substitute_named",
[](Circuit &circ, const QControlBox &box,
Expand Down Expand Up @@ -854,6 +866,71 @@ void def_circuit(py::class_<Circuit, std::shared_ptr<Circuit>> &pyCircuit) {
"\n\n:param optype: operation type"
"\n\n:return: list of :py:class:`Command`",
py::arg("optype"))
.def(
"get_resources", &Circuit::get_resources,
"Calculate the overall resources of the circuit."
"\n\nThis takes account of the data stored in each "
"py:class:`DummyBox` within the circuit, as well as other gates, "
"to compute upper and lower bounds."
"\n\n:return: bounds on resources of the circuit"
"\n\n"
">>> resource_data0 = ResourceData(\n"
"... op_type_count={\n"
"... OpType.T: ResourceBounds(1, 2),\n"
"... OpType.H: ResourceBounds(0, 1),\n"
"... OpType.CX: ResourceBounds(1, 2),\n"
"... OpType.CZ: ResourceBounds(3, 3),\n"
"... },\n"
"... gate_depth=ResourceBounds(5, 8),\n"
"... op_type_depth={\n"
"... OpType.T: ResourceBounds(0, 10),\n"
"... OpType.H: ResourceBounds(0, 10),\n"
"... OpType.CX: ResourceBounds(1, 2),\n"
"... OpType.CZ: ResourceBounds(3, 3),\n"
"... },\n"
"... two_qubit_gate_depth=ResourceBounds(4, 5),\n"
"... )\n"
">>> dbox0 = DummyBox(n_qubits=2, n_bits=0, "
"resource_data=resource_data0)\n"
">>> resource_data1 = ResourceData(\n"
"... op_type_count={\n"
"... OpType.T: ResourceBounds(2, 2),\n"
"... OpType.H: ResourceBounds(1, 1),\n"
"... OpType.CX: ResourceBounds(2, 3),\n"
"... OpType.CZ: ResourceBounds(3, 5),\n"
"... },\n"
"... gate_depth=ResourceBounds(5, 10),\n"
"... op_type_depth={\n"
"... OpType.T: ResourceBounds(1, 2),\n"
"... OpType.H: ResourceBounds(2, 4),\n"
"... OpType.CX: ResourceBounds(1, 1),\n"
"... OpType.CZ: ResourceBounds(3, 4),\n"
"... },\n"
"... two_qubit_gate_depth=ResourceBounds(3, 5),\n"
"... )\n"
">>> dbox1 = DummyBox(n_qubits=3, n_bits=0, "
"resource_data=resource_data1)\n"
">>> c = (\n"
"... Circuit(3)\n"
"... .H(0)\n"
"... .CX(1, 2)\n"
"... .CX(0, 1)\n"
"... .T(2)\n"
"... .H(1)\n"
"... .add_dummybox(dbox0, [0, 1], [])\n"
"... .CZ(1, 2)\n"
"... .add_dummybox(dbox1, [0, 1, 2], [])\n"
"... .H(2)\n"
"... )\n"
">>> resource_data = c.get_resources()\n"
">>> print(resource_data)\n"
"ResourceData(op_type_count={OpType.T: ResourceBounds(4, 5), "
"OpType.H: ResourceBounds(4, 5), OpType.CX: ResourceBounds(5, 7), "
"OpType.CZ: ResourceBounds(7, 9), }, gate_depth=ResourceBounds(15, "
"23), op_type_depth={OpType.T: ResourceBounds(2, 12), OpType.H: "
"ResourceBounds(5, 17), OpType.CX: ResourceBounds(4, 5), OpType.CZ: "
"ResourceBounds(7, 8), }, two_qubit_gate_depth=ResourceBounds(10, "
"13))")
.def_property_readonly(
"_dag_data",
[](Circuit &circ) {
Expand Down
130 changes: 130 additions & 0 deletions pytket/binders/circuit/boxes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,22 @@
#include <pybind11/eigen.h>
#include <pybind11/pybind11.h>

#include <memory>
#include <sstream>

#include "binder_json.hpp"
#include "binder_utils.hpp"
#include "tket/Circuit/Circuit.hpp"
#include "tket/Circuit/ConjugationBox.hpp"
#include "tket/Circuit/DiagonalBox.hpp"
#include "tket/Circuit/DummyBox.hpp"
#include "tket/Circuit/Multiplexor.hpp"
#include "tket/Circuit/PauliExpBoxes.hpp"
#include "tket/Circuit/ResourceData.hpp"
#include "tket/Circuit/StatePreparation.hpp"
#include "tket/Circuit/ToffoliBox.hpp"
#include "tket/Converters/PhasePoly.hpp"
#include "tket/OpType/OpType.hpp"
#include "tket/Utils/HelperFunctions.hpp"
#include "tket/Utils/Json.hpp"
#include "typecast.hpp"
Expand Down Expand Up @@ -453,6 +459,130 @@ void init_boxes(py::module &m) {
.def(
"get_rotation_axis", &ToffoliBox::get_rotation_axis,
":return: the rotation axis");
py::class_<ResourceBounds<unsigned>>(
m, "ResourceBounds",
"Structure holding a minimum and maximum value of some resource, where "
"both values are unsigned integers.")
.def(
py::init([](unsigned min, unsigned max) {
if (min > max) {
throw std::invalid_argument(
"minimum must be less than or equal to maximum");
}
return ResourceBounds<unsigned>{min, max};
}),
"Constructs a ResourceBounds object.\n\n"
":param min: minimum value\n"
":param max: maximum value\n",
py::arg("min"), py::arg("max"))
.def(
"get_min",
[](const ResourceBounds<unsigned> &resource_bounds) {
return resource_bounds.min;
},
":return: the minimum value")
.def(
"get_max",
[](const ResourceBounds<unsigned> &resource_bounds) {
return resource_bounds.max;
},
":return: the maximum value");
py::class_<ResourceData>(
m, "ResourceData",
"An object holding resource data for use in a :py:class:`DummyBox`."
"\n\nThe object holds several fields representing minimum and maximum "
"values for certain resources. The absence of an :py:class:`OpType` in "
"one of these fields is interpreted as the absence of gates of that type "
"in the (imagined) circuit."
"\n\nSee :py:meth:`Circuit.get_resources` for how to use this data.")
.def(
py::init([](std::map<OpType, ResourceBounds<unsigned>> op_type_count,
ResourceBounds<unsigned> gate_depth,
std::map<OpType, ResourceBounds<unsigned>> op_type_depth,
ResourceBounds<unsigned> two_qubit_gate_depth) {
return ResourceData{
op_type_count, gate_depth, op_type_depth, two_qubit_gate_depth};
}),
"Constructs a ResourceData object.\n\n"
":param op_type_count: dictionary of counts of selected "
":py:class:`OpType`\n"
":param gate_depth: overall gate depth\n"
":param op_type_depth: dictionary of depths of selected "
":py:class:`OpType`\n"
":param two_qubit_gate_depth: overall two-qubit-gate depth",
py::arg("op_type_count"), py::arg("gate_depth"),
py::arg("op_type_depth"), py::arg("two_qubit_gate_depth"))
.def(
"get_op_type_count",
[](const ResourceData &resource_data) {
return resource_data.OpTypeCount;
},
":return: bounds on the op type count")
.def(
"get_gate_depth",
[](const ResourceData &resource_data) {
return resource_data.GateDepth;
},
":return: bounds on the gate depth")
.def(
"get_op_type_depth",
[](const ResourceData &resource_data) {
return resource_data.OpTypeDepth;
},
":return: bounds on the op type depth")
.def(
"get_two_qubit_gate_depth",
[](const ResourceData &resource_data) {
return resource_data.TwoQubitGateDepth;
},
":return: bounds on the two-qubit-gate depth")
.def("__repr__", [](const ResourceData &resource_data) {
std::stringstream ss;
ss << "ResourceData(";
ss << "op_type_count={";
for (const auto &pair : resource_data.OpTypeCount) {
ss << "OpType." << optypeinfo().at(pair.first).name << ": "
<< "ResourceBounds(" << pair.second.min << ", " << pair.second.max
<< "), ";
}
ss << "}, ";
ss << "gate_depth=ResourceBounds(" << resource_data.GateDepth.min
<< ", " << resource_data.GateDepth.max << "), ";
ss << "op_type_depth={";
for (const auto &pair : resource_data.OpTypeDepth) {
ss << "OpType." << optypeinfo().at(pair.first).name << ": "
<< "ResourceBounds(" << pair.second.min << ", " << pair.second.max
<< "), ";
}
ss << "}, ";
ss << "two_qubit_gate_depth=ResourceBounds("
<< resource_data.TwoQubitGateDepth.min << ", "
<< resource_data.TwoQubitGateDepth.max << ")";
ss << ")";
return ss.str();
});
py::class_<DummyBox, std::shared_ptr<DummyBox>, Op>(
m, "DummyBox",
"A placeholder operation that holds resource data. This box type cannot "
"be decomposed into a circuit. It only serves to record resource data "
"for a region of a circuit: for example, upper and lower bounds on gate "
"counts and depth. A circuit containing such a box cannot be executed.")
.def(
py::init([](unsigned n_qubits, unsigned n_bits,
const ResourceData &resource_data) {
return DummyBox(n_qubits, n_bits, resource_data);
}),
"Construct a new instance from some resource data.",
py::arg("n_qubits"), py::arg("n_bits"), py::arg("resource_data"))
.def(
"get_n_qubits", &DummyBox::get_n_qubits,
":return: the number of qubits covered by the box")
.def(
"get_n_bits", &DummyBox::get_n_bits,
":return: the number of bits covered by the box")
.def(
"get_resource_data", &DummyBox::get_resource_data,
":return: the associated resource data");
py::class_<QControlBox, std::shared_ptr<QControlBox>, Op>(
m, "QControlBox",
"A user-defined controlled operation specified by an "
Expand Down
3 changes: 3 additions & 0 deletions pytket/binders/circuit/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,9 @@ PYBIND11_MODULE(circuit, m) {
.value(
"ToffoliBox", OpType::ToffoliBox,
"A permutation of classical basis states")
.value(
"DummyBox", OpType::DummyBox,
"A placeholder operation that holds resource data")
.value(
"CustomGate", OpType::CustomGate,
":math:`(\\alpha, \\beta, \\ldots) \\mapsto` A user-defined "
Expand Down
2 changes: 1 addition & 1 deletion pytket/conanfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def package(self):
cmake.install()

def requirements(self):
self.requires("tket/1.2.67@tket/stable")
self.requires("tket/1.2.68@tket/stable")
self.requires("tklog/0.3.3@tket/stable")
self.requires("tkrng/0.3.3@tket/stable")
self.requires("tkassert/0.3.4@tket/stable")
Expand Down
3 changes: 3 additions & 0 deletions pytket/docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ Minor new features:
* Add optional parameter to QASM conversion methods to set the maximum allowed
width of classical registers (default 32).
* New ``OpType.CS`` and ``OpType.CSdg``.
* New classes ``ResourceBounds``, ``ResourceData`` and ``DummyBox``, and method
``Circuit.get_resources()``, allowing reasoning about resource requirements
on circuit templates.

Fixes:

Expand Down
11 changes: 10 additions & 1 deletion pytket/docs/circuit.rst
Original file line number Diff line number Diff line change
Expand Up @@ -102,4 +102,13 @@ pytket.circuit
:members:
.. autoclass:: pytket._tket.circuit.ConjugationBox
:special-members:
:members:
:members:
.. autoclass:: pytket._tket.circuit.ResourceBounds
:special-members:
:members:
.. autoclass:: pytket._tket.circuit.ResourceData
:special-members:
:members:
.. autoclass:: pytket._tket.circuit.DummyBox
:special-members:
:members:
Loading
Loading