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 convenience method Circuit.add_clexpr_from_logicexp() #1681

Merged
merged 4 commits into from
Nov 19, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
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
29 changes: 29 additions & 0 deletions pytket/binders/circuit/Circuit/add_op.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -513,6 +513,35 @@ void init_circuit_add_op(py::class_<Circuit, std::shared_ptr<Circuit>> &c) {
":param args: The bits to apply the expression to\n"
":return: the new :py:class:`Circuit`",
py::arg("expr"), py::arg("args"))
.def(
"add_clexpr_from_logicexp",
[](Circuit *circ, const py::tket_custom::LogicExpression &exp,
const py::tket_custom::SequenceVec<Bit> &output_bits,
const py::kwargs &kwargs) {
py::list outputs;
for (const auto &bit : output_bits) {
outputs.append(bit);
}
py::module clexpr = py::module::import("pytket.circuit.clexpr");
py::object add_op =
clexpr.attr("_add_clexpr_to_circuit_from_logicexp");
add_op(circ, exp, outputs, **kwargs);
return circ;
},
"Append a :py:class:`ClExprOp` defined in terms of a logical "
"expression.\n\n"
"Example:\n"
">>> c = Circuit()\n"
">>> x_reg = c.add_c_register('x', 3)\n"
">>> y_reg = c.add_c_register('y', 3)\n"
">>> z_reg = c.add_c_register('z', 3)\n"
">>> c.add_clexpr_from_logicexp(x_reg | y_reg, z_reg.to_list())\n"
">>> [ClExpr x[0], x[1], x[2], y[0], y[1], y[2], z[0], z[1], z[2]; "
"]\n\n"
":param exp: logical expression\n"
":param output_bits: list of bits in output\n"
":return: the updated circuit",
py::arg("exp"), py::arg("output_bits"))
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
py::arg("exp"), py::arg("output_bits"))
py::arg("exp"), py::arg("output"))

Copy link
Contributor

Choose a reason for hiding this comment

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

And allow sequence[bits] and register?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I think I prefer output_bits as it is more explicit.

Any sequence of bits should work here.

In principle the output bits do not need to form a register (though that would be the case usually), so I'm not sure it's worth adding an overload.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think the question is, would this work, and if not do we want it to?

c = Circuit()
x_reg = c.add_c_register('x', 3)
y_reg = c.add_c_register('y', 3)
z_reg = c.add_c_register('z', 3)
c.add_clexpr_from_logicexp(x_reg | y_reg, z_reg)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes that does work; the register is coerced to a list.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

(ISTR mypy doesn't like it though.)

Copy link
Contributor

Choose a reason for hiding this comment

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

Could we add an additional stubs for that, to make mypy happy?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I'm not sure how to do that?

Copy link
Contributor

Choose a reason for hiding this comment

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

I think we would need a second c++ function in the binders for that, but not sure if there might be a easier way?
If you think that is not worth the effort happy to approve this as it is.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yeah, I don't think it's worth the effort (and maintenance) of an extra binder just to fix a mypy problem.

.def(
"add_custom_gate",
[](Circuit *circ, const composite_def_ptr_t &definition,
Expand Down
1 change: 1 addition & 0 deletions pytket/docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ Features:
* Use `ClExprOp` by default when converting from QASM.
* Extend `DecomposeClassicalExp` to handle `ClExprOp` as well as
`ClassicalExpBox`.
* Add convenience method `Circuit.add_clecpr_from_logicexp()`.

Deprecations:

Expand Down
16 changes: 16 additions & 0 deletions pytket/pytket/_tket/circuit.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -1461,6 +1461,22 @@ class Circuit:
:param args: The bits to apply the expression to
:return: the new :py:class:`Circuit`
"""
def add_clexpr_from_logicexp(self, exp: pytket.circuit.logic_exp.LogicExp, output_bits: typing.Sequence[pytket._tket.unit_id.Bit], **kwargs: Any) -> Circuit:
"""
Append a :py:class:`ClExprOp` defined in terms of a logical expression.

Example:
>>> c = Circuit()
>>> x_reg = c.add_c_register('x', 3)
>>> y_reg = c.add_c_register('y', 3)
>>> z_reg = c.add_c_register('z', 3)
>>> c.add_clexpr_from_logicexp(x_reg | y_reg, z_reg.to_list())
>>> [ClExpr x[0], x[1], x[2], y[0], y[1], y[2], z[0], z[1], z[2]; ]

:param exp: logical expression
:param output_bits: list of bits in output
:return: the updated circuit
"""
@typing.overload
def add_conditional_barrier(self, barrier_qubits: typing.Sequence[int], barrier_bits: typing.Sequence[int], condition_bits: typing.Sequence[int], value: int, data: str = '') -> Circuit:
"""
Expand Down
8 changes: 8 additions & 0 deletions pytket/pytket/circuit/clexpr.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
# limitations under the License.

from dataclasses import dataclass
from typing import Any

from pytket.circuit import (
Bit,
Expand Down Expand Up @@ -194,3 +195,10 @@ def check_register_alignments(circ: Circuit) -> bool:
):
return False
return True


def _add_clexpr_to_circuit_from_logicexp(
circ: Circuit, exp: LogicExp, output_bits: list[Bit], **kwargs: Any
) -> None:
wexpr, args = wired_clexpr_from_logic_exp(exp, output_bits)
circ.add_clexpr(wexpr, args, **kwargs)
20 changes: 20 additions & 0 deletions pytket/tests/clexpr_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -235,3 +235,23 @@ def test_circbox() -> None:
c2 = c1.copy()
c2.flatten_registers()
assert c1 == c2


def test_add_logicexp_as_clexpr() -> None:
c = Circuit()
a_reg = c.add_c_register("a", 3)
b_reg = c.add_c_register("b", 3)
c_reg = c.add_c_register("c", 3)
c.add_clexpr_from_logicexp(a_reg | b_reg, c_reg.to_list())
qasm = circuit_to_qasm_str(c, header="hqslib1")
assert (
qasm
== """OPENQASM 2.0;
include "hqslib1.inc";

creg a[3];
creg b[3];
creg c[3];
c = (a | b);
"""
)
Loading