Skip to content

Commit

Permalink
merge
Browse files Browse the repository at this point in the history
  • Loading branch information
cqc-melf committed Nov 29, 2024
2 parents 3d7685b + c39f1cc commit a1a06b5
Show file tree
Hide file tree
Showing 29 changed files with 688 additions and 149 deletions.
5 changes: 5 additions & 0 deletions pytket/binders/circuit/Circuit/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,11 @@ void def_circuit(py::class_<Circuit, std::shared_ptr<Circuit>> &pyCircuit) {
.def_property_readonly(
"n_gates", &Circuit::n_gates,
":return: the number of gates in the Circuit")
.def_property_readonly(
"wasm_uid",
[](const Circuit &circ) { return circ.get_wasm_file_uid(); },
":return: the unique WASM UID of the circuit, or `None` if the "
"circuit has none")
.def_property_readonly(
"n_qubits", &Circuit::n_qubits,
":return: the number of qubits in the circuit")
Expand Down
2 changes: 2 additions & 0 deletions pytket/binders/include/unit_downcast.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ struct polymorphic_type_hook<tket::UnitID> {
// Node has no additional info but is more specific
// If Qubit is needed, then subtyping is sufficient
type = &typeid(tket::Node);
} else if (src->type() == tket::UnitType::WasmState) {
type = &typeid(tket::WasmState);
} else {
type = &typeid(tket::Bit);
}
Expand Down
57 changes: 56 additions & 1 deletion pytket/binders/passes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,30 @@ const PassPtr &DecomposeClassicalExp() {
return pp;
}

std::optional<OpTypeSet> get_gate_set(const BasePass &base_pass) {
std::optional<OpTypeSet> allowed_ops;
for (const std::pair<const std::type_index, std::shared_ptr<tket::Predicate>>
&p : base_pass.get_conditions().first) {
std::shared_ptr<GateSetPredicate> gsp_ptr =
std::dynamic_pointer_cast<GateSetPredicate>(p.second);
if (!gsp_ptr) {
continue;
}
OpTypeSet candidate_allowed_ops = gsp_ptr->get_allowed_types();
if (!allowed_ops) {
allowed_ops = candidate_allowed_ops;
} else {
OpTypeSet intersection;
std::set_intersection(
candidate_allowed_ops.begin(), candidate_allowed_ops.end(),
allowed_ops->begin(), allowed_ops->end(),
std::inserter(intersection, intersection.begin()));
allowed_ops = intersection;
}
}
return allowed_ops;
}

PYBIND11_MODULE(passes, m) {
py::module_::import("pytket._tket.predicates");
m.def(
Expand Down Expand Up @@ -212,7 +236,6 @@ PYBIND11_MODULE(passes, m) {
);
}
};

py::class_<BasePass, PassPtr, PyBasePass>(
m, "BasePass", "Base class for passes.")
.def(
Expand Down Expand Up @@ -268,6 +291,38 @@ PYBIND11_MODULE(passes, m) {
return py::cast(serialise(base_pass));
},
":return: A JSON serializable dictionary representation of the Pass.")
.def(
"get_preconditions",
[](const BasePass &base_pass) {
std::vector<PredicatePtr> pre_conditions;
for (const std::pair<
const std::type_index, std::shared_ptr<tket::Predicate>>
&p : base_pass.get_conditions().first) {
pre_conditions.push_back(p.second);
}
return pre_conditions;
},
"Returns the precondition Predicates for the given pass."
"\n:return: A list of Predicate")
.def(
"get_postconditions",
[](const BasePass &base_pass) {
std::vector<PredicatePtr> post_conditions;
for (const std::pair<
const std::type_index, std::shared_ptr<tket::Predicate>> &
p : base_pass.get_conditions().second.specific_postcons_) {
post_conditions.push_back(p.second);
}
return post_conditions;
},
"Returns the postcondition Predicates for the given pass."
"\n\n:return: A list of :py:class:`Predicate`")
.def(
"get_gate_set", &get_gate_set,
"Returns the intersection of all set of OpType for all "
"GateSetPredicate in the `BasePass` preconditions, or `None` "
"if there are no gate-set predicates.",
"\n\n:return: A set of allowed OpType")
.def_static(
"from_dict",
[](const py::dict &base_pass_dict,
Expand Down
2 changes: 1 addition & 1 deletion pytket/binders/predicates.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ PYBIND11_MODULE(predicates, m) {
"implies", &Predicate::implies,
":return: True if predicate implies another one, else False",
py::arg("other"))
.def("__str__", [](const Predicate &) { return "<tket::Predicate>"; })
.def("__str__", &Predicate::to_string)
.def("__repr__", &Predicate::to_string)
.def(
"to_dict",
Expand Down
42 changes: 41 additions & 1 deletion pytket/binders/unitid.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ PYBIND11_MODULE(unit_id, m) {
m, "UnitType",
"Enum for data types of units in circuits (e.g. Qubits vs Bits).")
.value("qubit", UnitType::Qubit, "A single Qubit")
.value("wasmstate", UnitType::WasmState, "A single WasmState")
.value("bit", UnitType::Bit, "A single classical Bit");

py::class_<UnitID>(
Expand All @@ -114,7 +115,7 @@ PYBIND11_MODULE(unit_id, m) {
.def_property_readonly(
"type", &UnitID::type,
"Type of unit, either ``UnitType.qubit`` or "
"``UnitType.bit``");
"``UnitType.bit`` or ``UnitType.wasmstate``");

py::class_<Qubit, UnitID>(m, "Qubit", "A handle to a qubit")
.def("__copy__", [](const Qubit &id) { return Qubit(id); })
Expand Down Expand Up @@ -231,6 +232,45 @@ PYBIND11_MODULE(unit_id, m) {
"Construct Bit instance from JSON serializable "
"list representation of the Bit.");

py::class_<WasmState, UnitID>(m, "WasmState", "A handle to a wasmstate")
.def("__copy__", [](const WasmState &id) { return WasmState(id); })
.def(
"__deepcopy__",
[](const WasmState &id, const py::dict &) { return WasmState(id); })
.def(
py::init<unsigned>(),
"Constructs an id for some index in the default wasm "
"register\n\n:param index: The index in the register",
py::arg("index"))
.def("__eq__", &py_equals<WasmState>)
.def("__hash__", [](const WasmState &b) { return hash_value(b); })
.def(py::pickle(
[](const WasmState &b) {
return py::make_tuple(b.reg_name(), b.index());
},
[](const py::tuple &t) {
if (t.size() != 2)
throw std::runtime_error(
"Invalid state: tuple size: " + std::to_string(t.size()));
return WasmState(
t[0].cast<std::string>(), t[1].cast<std::vector<unsigned>>());
}))
.def(
"to_list",
[](const WasmState &b) {
return py::object(json(b)).cast<py::list>();
},
"Return a JSON serializable list representation of "
"the WasmState."
"\n\n:return: list containing register name and index")
.def_static(
"from_list",
[](const py::list &py_list) {
return json(py_list).get<WasmState>();
},
"Construct WasmState instance from JSON serializable "
"list representation of the WasmState.");

py::class_<Node, Qubit>(m, "Node", "A handle to a device node")
.def("__copy__", [](const Node &id) { return Node(id); })
.def(
Expand Down
2 changes: 1 addition & 1 deletion pytket/conanfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ def requirements(self):
self.requires("pybind11_json/0.2.14")
self.requires("symengine/0.13.0")
self.requires("tkassert/0.3.4@tket/stable")
self.requires("tket/1.3.48@tket/stable")
self.requires("tket/1.3.52@tket/stable")
self.requires("tklog/0.3.3@tket/stable")
self.requires("tkrng/0.3.3@tket/stable")
self.requires("tktokenswap/0.3.9@tket/stable")
Expand Down
25 changes: 25 additions & 0 deletions pytket/docs/changelog.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,31 @@
Changelog
=========

1.36.0 (November 2024)
----------------------

Features:

* Add `BasePass.get_preconditions()` and `BasePass.get_postconditions()`.

API changes:

* Remove the deprecated methods `auto_rebase_pass()` and `auto_squash_pass()`.

Features:

* Add `Circuit.wasm_uid` property to get the wasm UID from the circuit

Performance:

* Optimization in Clifford simplification, reducing compilation times for
`FullPeeopholeOptimise` by an order of magnitude on x86_64.

Fixes:

* Fix `Circuit.append` for circuits containing wasm
* Fix issue with wrong parameters at `add_wasm`

1.35.0 (November 2024)
----------------------

Expand Down
4 changes: 2 additions & 2 deletions pytket/docs/faqs.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ Q: Can I convert a pytket :py:class:`Circuit` to a gateset of my choice?
A: Yes, this can be done in many cases provided the target gateset is a set of one and two qubit pytket :py:class:`OpType` s.
There are two types of rebase

1) :py:meth:`auto_rebase_pass` - this uses a set of hardcoded decompositions to convert between gatesets. This can be used quickly when the gateset is one widely used on quantum hardware e.g. IBM's {X, SX, Rz, CX} gateset.
1) :py:meth:`AutoRebase` - this uses a set of hardcoded decompositions to convert between gatesets. This can be used quickly when the gateset is one widely used on quantum hardware e.g. IBM's {X, SX, Rz, CX} gateset.

2) :py:class:`RebaseCustom` - This can be used instead of `auto_rebase_pass` in cases where there is no hardcoded conversion available.
2) :py:class:`RebaseCustom` - This can be used instead of `AutoRebase` in cases where there is no hardcoded conversion available.
In this case the user will have to specify how to implement TKET's {TK1, CX} or {TK1, TK2} operations in terms of the target :py:class:`OpType` s.

See the manual section on `rebases <https://docs.quantinuum.com/tket/user-guide/manual/manual_compiler.html#rebases>`_ for examples.
Expand Down
7 changes: 1 addition & 6 deletions pytket/docs/passes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ There are passes such as `FullPeepholeOptimise <https://docs.quantinuum.com/tket

Also there are special purpose passes such as `OptimisePhaseGadgets <https://docs.quantinuum.com/tket/api-docs/passes.html#pytket.passes.OptimisePhaseGadgets>`_ and `PauliSimp <https://docs.quantinuum.com/tket/api-docs/passes.html#pytket.passes.PauliSimp>`_ which perform optimisation by targeting phase gadget and Pauli gadget structures within circuits. For more on these optimisation techniques see the `corresponding publication <https://arxiv.org/abs/1906.01734>`_.

Rebase passes can be used to convert a circuit to a desired gateset. See `RebaseCustom <https://docs.quantinuum.com/tket/api-docs/passes.html#pytket.passes.RebaseCustom>`_ and `auto_rebase_pass <https://docs.quantinuum.com/tket/api-docs/passes.html#pytket.passes.auto_rebase.auto_rebase_pass>`_.
Rebase passes can be used to convert a circuit to a desired gateset. See `RebaseCustom <https://docs.quantinuum.com/tket/api-docs/passes.html#pytket.passes.RebaseCustom>`_ and `AutoRebase <https://docs.quantinuum.com/tket/api-docs/passes.html#pytket._tket.passes.AutoRebase>`_.

For more on pytket passes see the `compilation <https://docs.quantinuum.com/tket/user-guide/manual/manual_compiler.html>`_ section of the user manual or the `notebook tutorials <https://docs.quantinuum.com/tket/examples>`_

Expand All @@ -25,8 +25,3 @@ pytket.passes.script

.. automodule:: pytket.passes.script
:members: compilation_pass_from_script, compilation_pass_grammar

pytket.passes.auto_rebase
~~~~~~~~~~~~~~~~~~~~~~~~~
.. automodule:: pytket.passes.auto_rebase
:members: auto_rebase_pass, auto_squash_pass
24 changes: 12 additions & 12 deletions pytket/docs/poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions pytket/pytket/_tket/circuit.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -2571,6 +2571,11 @@ class Circuit:
"""
A list of all qubit ids in the circuit
"""
@property
def wasm_uid(self) -> str | None:
"""
:return: the unique wasm uid of the circuit
"""
class ClBitVar:
"""
A bit variable within an expression
Expand Down
15 changes: 15 additions & 0 deletions pytket/pytket/_tket/passes.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,21 @@ class BasePass:
:param after_apply: Invoked after a pass is applied. The CompilationUnit and a summary of the pass configuration are passed into the callback.
:return: True if pass modified the circuit, else False
"""
def get_gate_set(self) -> set[pytket._tket.circuit.OpType] | None:
"""
:return: A set of allowed OpType
"""
def get_postconditions(self) -> list[pytket._tket.predicates.Predicate]:
"""
Returns the postcondition Predicates for the given pass.
:return: A list of :py:class:`Predicate`
"""
def get_preconditions(self) -> list[pytket._tket.predicates.Predicate]:
"""
Returns the precondition Predicates for the given pass.
:return: A list of Predicate
"""
def to_dict(self) -> typing.Any:
"""
:return: A JSON serializable dictionary representation of the Pass.
Expand Down
45 changes: 42 additions & 3 deletions pytket/pytket/_tket/unit_id.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ from typing import Any
from __future__ import annotations
import pytket.circuit.logic_exp
import typing
__all__ = ['Bit', 'BitRegister', 'Node', 'Qubit', 'QubitRegister', 'UnitID', 'UnitType']
__all__ = ['Bit', 'BitRegister', 'Node', 'Qubit', 'QubitRegister', 'UnitID', 'UnitType', 'WasmState']
class Bit(UnitID):
"""
A handle to a bit
Expand Down Expand Up @@ -397,7 +397,7 @@ class UnitID:
@property
def type(self) -> UnitType:
"""
Type of unit, either ``UnitType.qubit`` or ``UnitType.bit``
Type of unit, either ``UnitType.qubit`` or ``UnitType.bit`` or ``UnitType.wasmstate``
"""
class UnitType:
"""
Expand All @@ -407,11 +407,14 @@ class UnitType:
qubit : A single Qubit
wasmstate : A single WasmState
bit : A single classical Bit
"""
__members__: typing.ClassVar[dict[str, UnitType]] # value = {'qubit': <UnitType.qubit: 0>, 'bit': <UnitType.bit: 1>}
__members__: typing.ClassVar[dict[str, UnitType]] # value = {'qubit': <UnitType.qubit: 0>, 'wasmstate': <UnitType.wasmstate: 2>, 'bit': <UnitType.bit: 1>}
bit: typing.ClassVar[UnitType] # value = <UnitType.bit: 1>
qubit: typing.ClassVar[UnitType] # value = <UnitType.qubit: 0>
wasmstate: typing.ClassVar[UnitType] # value = <UnitType.wasmstate: 2>
@staticmethod
def _pybind11_conduit_v1_(*args, **kwargs): # type: ignore
...
Expand Down Expand Up @@ -441,6 +444,42 @@ class UnitType:
@property
def value(self) -> int:
...
class WasmState(UnitID):
"""
A handle to a wasmstate
"""
@staticmethod
def _pybind11_conduit_v1_(*args, **kwargs): # type: ignore
...
@staticmethod
def from_list(arg0: list) -> WasmState:
"""
Construct WasmState instance from JSON serializable list representation of the WasmState.
"""
def __copy__(self) -> WasmState:
...
def __deepcopy__(self, arg0: dict) -> WasmState:
...
def __eq__(self, arg0: typing.Any) -> bool:
...
def __getstate__(self) -> tuple:
...
def __hash__(self) -> int:
...
def __init__(self, index: int) -> None:
"""
Constructs an id for some index in the default wasm register
:param index: The index in the register
"""
def __setstate__(self, arg0: tuple) -> None:
...
def to_list(self) -> list:
"""
Return a JSON serializable list representation of the WasmState.
:return: list containing register name and index
"""
_DEBUG_ONE_REG_PREFIX: str = 'tk_DEBUG_ONE_REG'
_DEBUG_ZERO_REG_PREFIX: str = 'tk_DEBUG_ZERO_REG'
_TEMP_BIT_NAME: str = 'tk_SCRATCH_BIT'
Expand Down
Loading

0 comments on commit a1a06b5

Please sign in to comment.