Skip to content

Commit

Permalink
Merge branch 'develop' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
cqc-alec authored Sep 7, 2023
2 parents 1589be9 + 99debd3 commit e168343
Show file tree
Hide file tree
Showing 46 changed files with 618 additions and 115 deletions.
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 @@ -173,6 +173,28 @@ void init_circuit_add_op(py::class_<Circuit, std::shared_ptr<Circuit>> &c) {
"\n\n:param data: additional data stored in the barrier"
"\n:return: the new :py:class:`Circuit`",
py::arg("qubits"), py::arg("bits") = no_bits, py::arg("data") = "")
.def(
"add_conditional_barrier",
[](Circuit *circ, const std::vector<unsigned> &barrier_qubits,
const std::vector<unsigned> &barrier_bits,
const std::vector<unsigned> &condition_bits, unsigned value,
const std::string &_data) {
circ->add_conditional_barrier(
barrier_qubits, barrier_bits, condition_bits, value, _data);
return circ;
},
"Append a Conditional Barrier on the given barrier qubits and "
"barrier bits, conditioned on the given condition bits."
"\n\n:param barrier_qubits: Qubit in Barrier operation."
"\n:param barrier_bits: Bit in Barrier operation."
"\n:param condition_bits: Bit covering classical control condition "
"of barrier operation."
"\n:param value: Value that classical condition must have to "
"hold (little-endian)."
"\n:param data: Additional data stored in Barrier operation."
"\n:return: the new :py:class:`Circuit`",
py::arg("barrier_qubits"), py::arg("barrier_bits"),
py::arg("condition_bits"), py::arg("value"), py::arg("data") = "")
.def(
"add_circbox",
[](Circuit *circ, const CircBox &box,
Expand Down Expand Up @@ -415,6 +437,26 @@ void init_circuit_add_op(py::class_<Circuit, std::shared_ptr<Circuit>> &c) {
"\n\n:param data: additional data stored in the barrier"
"\n:return: the new :py:class:`Circuit`",
py::arg("units"), py::arg("data") = "")
.def(
"add_conditional_barrier",
[](Circuit *circ, const unit_vector_t &barrier_args,
const bit_vector_t &condition_bits, unsigned value,
const std::string &_data) {
circ->add_conditional_barrier(
barrier_args, condition_bits, value, _data);
return circ;
},
"Append a Conditional Barrier on the given barrier qubits and "
"barrier bits, conditioned on the given condition bits."
"\n\n:param barrier_args: Qubit and Bit in Barrier operation."
"\n:param condition_bits: Bit covering classical control "
" condition of barrier operation."
"\n:param value: Value that classical condition must have to "
"hold (little-endian)."
"\n:param data: Additional data stored in Barrier operation."
"\n:return: the new :py:class:`Circuit`",
py::arg("barrier_args"), py::arg("condition_bits"), py::arg("value"),
py::arg("data") = "")
.def(
"add_circbox",
[](Circuit *circ, const CircBox &box, const unit_vector_t &args,
Expand Down
14 changes: 13 additions & 1 deletion pytket/binders/circuit/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include "tket/Gate/Gate.hpp"
#include "tket/Gate/OpPtrFunctions.hpp"
#include "tket/Gate/SymTable.hpp"
#include "tket/Ops/BarrierOp.hpp"
#include "tket/Ops/MetaOp.hpp"
#include "tket/Ops/Op.hpp"
#include "tket/Utils/Constants.hpp"
Expand Down Expand Up @@ -615,7 +616,7 @@ PYBIND11_MODULE(circuit, m) {
":return: set of symbolic parameters for the command");

py::class_<MetaOp, std::shared_ptr<MetaOp>, Op>(
m, "MetaOp", "Meta operation, for example used as barrier")
m, "MetaOp", "Meta operation, such as input or output vertices.")
.def(
py::init<OpType, op_signature_t, const std::string &>(),
"Construct MetaOp with optype, signature and additional data string"
Expand All @@ -625,6 +626,17 @@ PYBIND11_MODULE(circuit, m) {
py::arg("type"), py::arg("signature"), py::arg("data"))
.def_property_readonly("data", &MetaOp::get_data, "Get data from MetaOp");

py::class_<BarrierOp, std::shared_ptr<BarrierOp>, Op>(
m, "BarrierOp", "Barrier operations.")
.def(
py::init<op_signature_t, const std::string &>(),
"Construct BarrierOp with signature and additional data string"
"\n:param signature: signature for the op"
"\n:param data: additional string stored in the op",
py::arg("signature"), py::arg("data"))
.def_property_readonly(
"data", &BarrierOp::get_data, "Get data from BarrierOp");

auto pyCircuit = py::class_<Circuit, std::shared_ptr<Circuit>>(
m, "Circuit", py::dynamic_attr(),
"Encapsulates a quantum circuit using a DAG representation.\n\n>>> "
Expand Down
7 changes: 5 additions & 2 deletions pytket/binders/include/add_gate.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,12 @@ static Circuit *add_gate_method(
Circuit *circ, const Op_ptr &op, const std::vector<ID> &args,
const py::kwargs &kwargs) {
if (op->get_desc().is_meta()) {
throw CircuitInvalidity("Cannot add metaop to a circuit.");
}
if (op->get_desc().is_barrier()) {
throw CircuitInvalidity(
"Cannot add metaop. Please use `add_barrier` to add a "
"barrier.");
"Please use `add_barrier` to add a "
"barrier to a circuit.");
}
static const std::set<std::string> allowed_kwargs = {
"opgroup", "condition", "condition_bits", "condition_value"};
Expand Down
27 changes: 21 additions & 6 deletions pytket/binders/tailoring.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,14 @@ namespace py = pybind11;

namespace tket {

QubitPauliString apply_clifford_basis_change(
QubitPauliTensor apply_clifford_basis_change_tensor(
const QubitPauliTensor &in_pauli, const Circuit &circ) {
UnitaryRevTableau tab = circuit_to_unitary_rev_tableau(circ);
QubitPauliTensor new_operator = tab.get_row_product(in_pauli);
return new_operator;
}

QubitPauliString apply_clifford_basis_change_string(
const QubitPauliString &in_pauli, const Circuit &circ) {
UnitaryRevTableau tab = circuit_to_unitary_rev_tableau(circ);
QubitPauliTensor new_operator =
Expand Down Expand Up @@ -163,13 +170,21 @@ PYBIND11_MODULE(tailoring, m) {
py::arg("circuit"), py::arg("samples"))
.def("__repr__", &UniversalFrameRandomisation::to_string);
m.def(
"apply_clifford_basis_change", &apply_clifford_basis_change,
"apply_clifford_basis_change", &apply_clifford_basis_change_string,
"Given Pauli operator P and Clifford circuit C, "
"returns C_dagger.P.C in multiplication order. This ignores any -1 "
"phase that could be introduced.\n\n:param pauli: Pauli "
"operator being transformed. \n:param circuit: "
"Clifford circuit acting on Pauli operator.\n"
":return: :py:class:`QubitPauliString` for new operator",
"phase that could be introduced. "
"\n\n:param pauli: Pauli operator being transformed. "
"\n:param circuit: Clifford circuit acting on Pauli operator. "
"\n:return: :py:class:`QubitPauliString` for new operator",
py::arg("pauli"), py::arg("circuit"));
m.def(
"apply_clifford_basis_change_tensor", &apply_clifford_basis_change_tensor,
"Given Pauli operator P and Clifford circuit C, "
"returns C_dagger.P.C in multiplication order"
"\n\n:param pauli: Pauli operator being transformed."
"\n:param circuit: Clifford circuit acting on Pauli operator. "
"\n:return: :py:class:`QubitPauliTensor` for new operator",
py::arg("pauli"), py::arg("circuit"));
}
} // namespace tket
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.39@tket/stable")
self.requires("tket/1.2.40@tket/stable")
self.requires("tklog/0.3.3@tket/stable")
self.requires("tkrng/0.3.3@tket/stable")
self.requires("tkassert/0.3.3@tket/stable")
Expand Down
5 changes: 5 additions & 0 deletions pytket/docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ Changelog
1.19.1 (September 2023)
-----------------------

Minor new features:

* ``Circuit.add_conditional_barrier``
* Add ``apply_clifford_basis_change_tensor`` method

Fixes:

* Fix `RebaseCustom()` rebasing of `TK2` gates.
Expand Down
9 changes: 9 additions & 0 deletions pytket/pytket/_tket/circuit.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ import pytket._tket.pauli
import pytket._tket.unit_id
import sympy

class BarrierOp(Op):
def __init__(self, signature: List[EdgeType], data: str) -> None: ...
@property
def data(self) -> str: ...

class BasisOrder:
__members__: ClassVar[dict] = ... # read-only
__entries: ClassVar[dict] = ...
Expand Down Expand Up @@ -348,6 +353,10 @@ class Circuit:
def add_classicalexpbox_bit(self, expression: object, target: List[pytket._tket.unit_id.Bit], **kwargs: Any) -> Circuit: ...
def add_classicalexpbox_register(self, expression: object, target: List[pytket._tket.unit_id.Bit], **kwargs: Any) -> Circuit: ...
@overload
def add_conditional_barrier(self, barrier_qubits: List[int], barrier_bits: List[int], condition_bits: List[int], value: int, data: str = ...) -> Circuit: ...
@overload
def add_conditional_barrier(self, barrier_args: List[pytket._tket.unit_id.UnitID], condition_bits: List[pytket._tket.unit_id.Bit], value: int, data: str = ...) -> Circuit: ...
@overload
def add_conjugation_box(self, box: ConjugationBox, args: List[pytket._tket.unit_id.UnitID], **kwargs: Any) -> Circuit: ...
@overload
def add_conjugation_box(self, box: ConjugationBox, args: List[int], **kwargs: Any) -> Circuit: ...
Expand Down
1 change: 1 addition & 0 deletions pytket/pytket/_tket/tailoring.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,4 @@ class UniversalFrameRandomisation:
def sample_circuits(self, circuit: pytket._tket.circuit.Circuit, samples: int) -> List[pytket._tket.circuit.Circuit]: ...

def apply_clifford_basis_change(pauli: pytket._tket.pauli.QubitPauliString, circuit: pytket._tket.circuit.Circuit) -> pytket._tket.pauli.QubitPauliString: ...
def apply_clifford_basis_change_tensor(pauli: pytket._tket.pauli.QubitPauliTensor, circuit: pytket._tket.circuit.Circuit) -> pytket._tket.pauli.QubitPauliTensor: ...
2 changes: 0 additions & 2 deletions pytket/pytket/backends/backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,6 @@ def valid_circuit(self, circuit: Circuit) -> bool:
def _check_all_circuits(
self, circuits: Iterable[Circuit], nomeasure_warn: Optional[bool] = None
) -> bool:

if nomeasure_warn is None:
nomeasure_warn = not (
self._supports_state
Expand Down Expand Up @@ -267,7 +266,6 @@ def process_circuits(
valid_check: bool = True,
**kwargs: KwargTypes,
) -> List[ResultHandle]:

"""
Submit circuits to the backend for running. The results will be stored
in the backend's result cache to be retrieved by the corresponding
Expand Down
2 changes: 1 addition & 1 deletion pytket/pytket/circuit/decompose_classical.py
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,7 @@ def _decompose_expressions(circ: Circuit) -> Tuple[Circuit, bool]:
modified = True
continue
if optype == OpType.Barrier:
# add_gate doesn't work for metaops like barrier
# add_gate doesn't work for metaops
newcirc.add_barrier(args)
else:
newcirc.add_gate(op, args, **kwargs)
Expand Down
17 changes: 13 additions & 4 deletions pytket/pytket/qasm/qasm.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
MultiBitOp,
WASMOp,
CustomGate,
MetaOp,
BarrierOp,
)
from pytket._tket.unit_id import _TEMP_BIT_NAME
from pytket.circuit import (
Expand Down Expand Up @@ -362,9 +362,19 @@ def meas(self, tree: List[Token]) -> Iterable[CommandDict]:

def barr(self, tree: List[Arg]) -> Iterable[CommandDict]:
args = [q for qs in self.unroll_all_args(tree[0]) for q in qs]
signature: List[str] = []
for arg in args:
if arg[0] in self.c_registers:
signature.append("C")
elif arg[0] in self.q_registers:
signature.append("Q")
else:
raise QASMParseError(
"UnitID " + str(arg) + " in Barrier arguments is not declared."
)
yield {
"args": args,
"op": {"signature": ["Q"] * len(args), "type": "Barrier"},
"op": {"signature": signature, "type": "Barrier"},
}

def reset(self, tree: List[Token]) -> Iterable[CommandDict]:
Expand All @@ -379,7 +389,6 @@ def mixedcall(self, tree: List) -> Iterator[CommandDict]:

optoken = next(child_iter)
opstr = optoken.value

next_tree = next(child_iter)
try:
args = next(child_iter)
Expand Down Expand Up @@ -1321,7 +1330,7 @@ def circuit_to_qasm_io(
param = -2 + param
params = [param] # type: ignore
elif optype == OpType.Barrier and header == "hqslib1_dev":
assert isinstance(op, MetaOp)
assert isinstance(op, BarrierOp)
if op.data == "":
opstr = _tk_to_qasm_noparams[optype]
else:
Expand Down
16 changes: 15 additions & 1 deletion pytket/tests/characterisation_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@
PauliFrameRandomisation,
UniversalFrameRandomisation,
apply_clifford_basis_change,
apply_clifford_basis_change_tensor,
)
from pytket.pauli import Pauli, QubitPauliString
from pytket.pauli import Pauli, QubitPauliString, QubitPauliTensor

import pytest # type: ignore

Expand Down Expand Up @@ -116,15 +117,24 @@ def test_apply_clifford_basis_change() -> None:
z_op = QubitPauliString(Qubit(0), Pauli.Z)
x_op = QubitPauliString(Qubit(0), Pauli.X)
assert apply_clifford_basis_change(z_op, circ_0) == x_op
t = apply_clifford_basis_change_tensor(QubitPauliTensor(z_op), circ_0)
assert t.string == x_op
assert t.coeff == 1

circ_1 = Circuit(2).CX(0, 1)
zz_op = QubitPauliString([Qubit(0), Qubit(1)], [Pauli.Z, Pauli.Z])
iz_op = QubitPauliString([Qubit(0), Qubit(1)], [Pauli.I, Pauli.Z])
assert apply_clifford_basis_change(zz_op, circ_1) == iz_op
t = apply_clifford_basis_change_tensor(QubitPauliTensor(zz_op), circ_1)
assert t.string == iz_op
assert t.coeff == 1

circ_2 = Circuit(2).H(0).CX(0, 1).S(1).X(0)
zz_op = QubitPauliString([Qubit(0), Qubit(1)], [Pauli.Z, Pauli.Z])
assert apply_clifford_basis_change(zz_op, circ_2) == iz_op
t = apply_clifford_basis_change_tensor(QubitPauliTensor(zz_op), circ_2)
assert t.string == iz_op
assert t.coeff == -1

circ_3 = (
Circuit(4).H(0).H(1).H(2).H(3).CX(0, 1).CX(1, 2).CX(2, 3).S(0).Y(1).Z(2).X(3)
Expand All @@ -136,6 +146,10 @@ def test_apply_clifford_basis_change() -> None:
[Qubit(0), Qubit(1), Qubit(2), Qubit(3)], [Pauli.Y, Pauli.X, Pauli.Y, Pauli.I]
)
assert apply_clifford_basis_change(yxzi_op, circ_3) == yxyi_op
t = apply_clifford_basis_change_tensor(QubitPauliTensor(yxzi_op), circ_3)

assert t.string == yxyi_op
assert t.coeff == 1


if __name__ == "__main__":
Expand Down
1 change: 0 additions & 1 deletion pytket/tests/classical_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -494,7 +494,6 @@ def qubit_register(
index=strategies.integers(min_value=0, max_value=32),
)
def test_registers(reg: Union[BitRegister, QubitRegister], index: int) -> None:

unit_type = Qubit if type(reg) is QubitRegister else Bit
if index < reg.size:
assert reg[index] == unit_type(reg.name, index)
Expand Down
18 changes: 17 additions & 1 deletion pytket/tests/qasm_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -216,13 +216,29 @@ def test_conditional_gates() -> None:
circ.Measure(0, 0)
circ.Measure(1, 1)
circ.Z(0, condition_bits=[0, 1], condition_value=2)
circ.add_conditional_barrier([0, 1], [], [0, 1], 1)
circ.Measure(0, 0, condition_bits=[0, 1], condition_value=1)
qasm_out = str(curr_file_path / "qasm_test_files/testout5.qasm")
circuit_to_qasm(circ, qasm_out)
c2 = circuit_from_qasm(qasm_out)
assert circ == c2


def test_named_conditional_barrier() -> None:
circ = Circuit(2, 2)
circ.add_bit(Bit("test", 3))
circ.Z(0, condition_bits=[0, 1], condition_value=2)
circ.add_conditional_barrier(
[Qubit("q", 0), Bit("test", 3)],
[Bit("c", 0), Bit("c", 1)],
0,
data="cond_barrier",
)
qs_str: str = circuit_to_qasm_str(circ)
c_from_qs: Circuit = circuit_from_qasm_str(qs_str)
assert qs_str == circuit_to_qasm_str(c_from_qs)


def test_hqs_conditional() -> None:
c = Circuit(1)
a = c.add_c_register("a", 8)
Expand Down Expand Up @@ -273,7 +289,6 @@ def test_barrier() -> None:
c.H(0)
c.H(2)
c.add_barrier([0], [0], "comment")

result = """OPENQASM 2.0;\ninclude "hqslib1_dev.inc";\n\nqreg q[3];
creg c[3];\nh q[0];\nh q[2];\ncomment q[0],c[0];\n"""
assert result == circuit_to_qasm_str(c, header="hqslib1_dev")
Expand Down Expand Up @@ -773,6 +788,7 @@ def test_rxxyyzz_conversion() -> None:
test_extended_qasm()
test_register_commands()
test_conditional_gates()
test_named_conditional_barrier()
test_hqs_conditional()
test_hqs_conditional_params()
test_barrier()
Expand Down
2 changes: 2 additions & 0 deletions tket/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ target_sources(tket
src/Clifford/ChoiMixTableau.cpp
src/Clifford/SymplecticTableau.cpp
src/Clifford/UnitaryTableau.cpp
src/Ops/BarrierOp.cpp
src/Ops/FlowOp.cpp
src/Ops/MetaOp.cpp
src/Ops/Op.cpp
Expand Down Expand Up @@ -278,6 +279,7 @@ target_sources(tket
include/tket/Clifford/SymplecticTableau.hpp
include/tket/Clifford/UnitaryTableau.hpp
include/tket/Ops/ClassicalOps.hpp
include/tket/Ops/BarrierOp.hpp
include/tket/Ops/FlowOp.hpp
include/tket/Ops/MetaOp.hpp
include/tket/Ops/Op.hpp
Expand Down
2 changes: 1 addition & 1 deletion tket/conanfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@

class TketConan(ConanFile):
name = "tket"
version = "1.2.39"
version = "1.2.40"
package_type = "library"
license = "Apache 2"
homepage = "https://github.com/CQCL/tket"
Expand Down
Loading

0 comments on commit e168343

Please sign in to comment.