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

BarrierOp and Circuit._add_conditional_barrier #988

Merged
merged 43 commits into from
Sep 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
e1c85b7
Add new `BarrierOp`
sjdilkes Aug 23, 2023
58d7f55
Add Circuit::add_conditional_gate
sjdilkes Aug 23, 2023
6ee6d09
Add tests to confirm DAG is wired suitably for conditional barrier gates
sjdilkes Aug 24, 2023
cbd62a1
Add tests and case handling for conditional barrier over different ci…
sjdilkes Aug 25, 2023
6a682c3
add `_add_conditonal_barrier` method to Circuit class
sjdilkes Aug 29, 2023
a7b7d4c
bump
sjdilkes Aug 29, 2023
a5e4a09
black format
sjdilkes Aug 29, 2023
847a425
Update test_Circ.cpp
sjdilkes Aug 30, 2023
e5a9e8a
Update add_op.cpp
sjdilkes Aug 30, 2023
94e28fb
Merge branch 'develop' into conditional-barrier
sjdilkes Aug 30, 2023
bc570fc
bump
sjdilkes Aug 30, 2023
431ddbf
Update add_op.cpp
sjdilkes Aug 30, 2023
394ee56
Merge branch 'develop' into conditional-barrier
sjdilkes Aug 30, 2023
175d69b
Expose BarrierOp to python
sjdilkes Aug 31, 2023
65994f2
Make `barr` signature accurately reflect internal UnitID, add test
sjdilkes Aug 31, 2023
fe39c11
reformat tests
sjdilkes Aug 31, 2023
016ae57
Merge branch 'develop' into conditional-barrier
sjdilkes Aug 31, 2023
5f86354
Update main.cpp
sjdilkes Aug 31, 2023
4f02bd5
Merge branch 'develop' into conditional-barrier
sjdilkes Aug 31, 2023
0ae0af9
Update qasm.py
sjdilkes Aug 31, 2023
9c1119f
Update circuit.pyi
sjdilkes Aug 31, 2023
7e2349a
Update test_Circ.cpp
sjdilkes Aug 31, 2023
d792bf6
requested changes from review
sjdilkes Sep 4, 2023
89551b4
Merge branch 'develop' into conditional-barrier
sjdilkes Sep 4, 2023
66f826f
fix types
sjdilkes Sep 4, 2023
3c03668
Update basic_circ_manip.cpp
sjdilkes Sep 4, 2023
3a06eaa
Update basic_circ_manip.cpp
sjdilkes Sep 4, 2023
04c0c9f
Update qasm_test.py
sjdilkes Sep 4, 2023
c1f1828
Update qasm_test.py
sjdilkes Sep 4, 2023
1b94d6f
Update circuit.pyi
sjdilkes Sep 4, 2023
ed13e99
Add symbolic substituion test for barrier op
sjdilkes Sep 5, 2023
a6a491b
Update serialization/deserialization for MetaOp/BarrierOp
sjdilkes Sep 5, 2023
2cbebb8
Merge branch 'develop' into conditional-barrier
sjdilkes Sep 5, 2023
312b7f1
add MetaOp::is_equal
sjdilkes Sep 5, 2023
c3bfbdd
Add back data type
sjdilkes Sep 5, 2023
011b7e7
Update add_op.cpp
sjdilkes Sep 5, 2023
7fcfefa
bump
sjdilkes Sep 5, 2023
ddd64a3
Merge branch 'develop' into conditional-barrier
sjdilkes Sep 5, 2023
1456359
bump
sjdilkes Sep 5, 2023
bd908d2
Merge branch 'develop' into conditional-barrier
sjdilkes Sep 6, 2023
6deb21d
bump
sjdilkes Sep 6, 2023
ef91614
update with requested changes
sjdilkes Sep 6, 2023
d26666c
Merge branch 'develop' into conditional-barrier
sjdilkes Sep 6, 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 @@ -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
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
3 changes: 2 additions & 1 deletion pytket/docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ Unreleased

Minor new features:

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

Fixes:

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
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(
cqc-alec marked this conversation as resolved.
Show resolved Hide resolved
"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
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
21 changes: 19 additions & 2 deletions tket/include/tket/Circuit/Circuit.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -836,9 +836,9 @@ class Circuit {
Vertex add_op(
OpType type, const std::vector<Expr> &params, const std::vector<ID> &args,
std::optional<std::string> opgroup = std::nullopt) {
if (is_metaop_type(type)) {
if (is_metaop_type(type) || is_barrier_type(type)) {
throw CircuitInvalidity(
"Cannot add metaop. Please use `add_barrier` to add a "
"Cannot add metaop or barrier. Please use `add_barrier` to add a "
"barrier.");
}
return add_op(get_op_ptr(type, params, args.size()), args, opgroup);
Expand Down Expand Up @@ -904,6 +904,11 @@ class Circuit {
if (is_metaop_type(type)) {
throw CircuitInvalidity("Cannot add a conditional metaop.");
}
if (is_barrier_type(type)) {
throw CircuitInvalidity(
"Please use 'add_conditional_barrier' to add a conditional barrier "
"gate.");
}
Op_ptr cond = std::make_shared<Conditional>(
get_op_ptr(type, params, (unsigned)args.size()), (unsigned)bits.size(),
value);
Expand All @@ -918,6 +923,18 @@ class Circuit {

Vertex add_barrier(const unit_vector_t &args, const std::string &_data = "");

Vertex add_conditional_barrier(
const std::vector<unsigned> &barrier_qubits,
const std::vector<unsigned> &barrier_bits,
const std::vector<unsigned> &condition_bits, unsigned value,
const std::string &_data,
std::optional<std::string> opgroup = std::nullopt);

Vertex add_conditional_barrier(
const unit_vector_t &barrier_args, const bit_vector_t &condition_bits,
unsigned value, const std::string &_data,
std::optional<std::string> opgroup = std::nullopt);

/**
* Add a postfix to a classical register name if the register exists
* Example: tket_c results in tket_c_2 if tket_c and tket_c_1 both exist
Expand Down
8 changes: 4 additions & 4 deletions tket/include/tket/Gate/OpPtrFunctions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ namespace tket {
*
* @param chosen_type operation type
* @param param operation parameter
* @param n_qubits number of qubits (only necessary for gates and metaops
* with variable quantum arity)
* @param n_qubits number of qubits (only necessary for gates, barrier
* and metaops with variable quantum arity)
*/
Op_ptr get_op_ptr(OpType chosen_type, const Expr &param, unsigned n_qubits = 0);

Expand All @@ -35,8 +35,8 @@ Op_ptr get_op_ptr(OpType chosen_type, const Expr &param, unsigned n_qubits = 0);
*
* @param chosen_type operation type
* @param params operation parameters
* @param n_qubits number of qubits (only necessary for gates and metaops
* with variable quantum arity)
* @param n_qubits number of qubits (only necessary for gates, barrier
* and metaops with variable quantum arity)
*/
Op_ptr get_op_ptr(
OpType chosen_type, const std::vector<Expr> &params = {},
Expand Down
6 changes: 5 additions & 1 deletion tket/include/tket/OpType/OpDesc.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,12 @@ class OpDesc {
/** Number of classical bits written to */
OptUInt n_classical() const;

/** Whether the 'operation' is actually an input or output or barrier */
/** Whether the 'operation' is actually an input or output */
bool is_meta() const;

/** Whether the operation is a Barrier */
bool is_barrier() const;

/** Whether the operation is a box of some kind */
bool is_box() const;

Expand Down Expand Up @@ -111,6 +114,7 @@ class OpDesc {
const OpType type_;
const OpTypeInfo info_;
const bool is_meta_;
const bool is_barrier_;
const bool is_box_;
const bool is_gate_;
const bool is_flowop_;
Expand Down
5 changes: 4 additions & 1 deletion tket/include/tket/OpType/OpTypeFunctions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,12 @@ const OpTypeSet &all_controlled_gate_types();
/** Set of all classical gates */
const OpTypeSet &all_classical_types();

/** Test for initial, final and barrier "ops" */
/** Test for initial and final "ops" */
bool is_metaop_type(OpType optype);

/** Test for Barrier "ops" */
bool is_barrier_type(OpType optype);

/** Test for input or creation quantum "ops" */
bool is_initial_q_type(OpType optype);

Expand Down
Loading
Loading