30 std::unique_ptr<AbstractSquasher> squasher,
Circuit &circ,
bool reversed,
31 bool always_squash_symbols)
32 : squasher_(
std::move(squasher)),
35 always_squash_symbols_(always_squash_symbols) {}
38 : squasher_(other.squasher_->clone()),
40 reversed_(other.reversed_),
41 always_squash_symbols_(other.always_squash_symbols_) {}
47 for (
unsigned i = 0; i < circ_.
n_qubits(); ++i) {
64 std::vector<Gate_ptr> single_chain;
67 Condition condition = {};
70 OpType v_type = v_op->get_type();
71 bool move_to_next_vertex =
false;
72 bool reset_search =
false;
73 Condition this_condition = {};
77 this_condition = get_condition(v);
80 v_type = v_op->get_type();
83 if (single_chain.empty()) {
84 condition = this_condition;
91 if (e != out && condition == this_condition && is_squashable) {
93 squasher_->append(
as_gate_ptr(reversed_ ? v_op->dagger() : v_op));
94 move_to_next_vertex =
true;
98 if (single_chain.empty()) {
100 move_to_next_vertex =
true;
103 std::optional<Pauli> commutation_colour = std::nullopt;
106 move_to_next_vertex =
true;
108 auto pair = squasher_->flush(commutation_colour);
110 Gate_ptr left_over_gate = pair.second;
111 if (left_over_gate !=
nullptr) {
112 if (commute_ok(e, condition)) {
114 insert_left_over_gate(left_over_gate, next_edge(v, e), condition);
117 sub.
add_op<
unsigned>(left_over_gate, {0});
119 left_over_gate =
nullptr;
127 if (sub_is_better(sub, single_chain)) {
128 substitute(sub, bin, e, condition);
133 if (e == out || is_last_optype(v_type)) {
137 if (move_to_next_vertex) {
147 single_chain.clear();
155void SingleQubitSquash::substitute(
157 const Condition &condition) {
159 VertPort backup = {next_vertex(e), next_port(e)};
161 if (!condition.empty()) {
168 VertexSet{single_chain.begin(), single_chain.end()},
172 e = prev_edge(backup);
175bool SingleQubitSquash::path_exists(
178 while (!frontier.empty()) {
179 if (std::any_of(vs.begin(), vs.end(), [&frontier](
const Vertex &v1) {
180 return frontier.contains(v1);
185 for (
const Vertex &v1 : frontier) {
188 new_frontier.insert(succs.begin(), succs.end());
191 new_frontier.insert(preds.begin(), preds.end());
194 frontier = std::move(new_frontier);
199bool SingleQubitSquash::commute_ok(
200 const Edge &e,
const Condition &condition)
const {
202 for (
const std::pair<std::list<VertPort>,
unsigned> &cond : condition) {
203 for (
const VertPort &vp : cond.first) {
207 if (vs.empty())
return true;
211 return !path_exists(circ_.
source(e), vs);
216 return !path_exists(circ_.
target(e), vs);
220void SingleQubitSquash::insert_left_over_gate(
221 Op_ptr left_over,
const Edge &e,
const Condition &condition) {
223 left_over = left_over->dagger();
229 for (
auto c_rit = condition.rbegin(); c_rit != condition.rend(); ++c_rit) {
230 left_over = std::make_shared<Conditional>(
231 left_over, (
unsigned)c_rit->first.size(), c_rit->second);
235 for (
const std::pair<std::list<VertPort>,
unsigned> &c : condition) {
236 for (
const VertPort &vp : c.first) {
243 circ_.
rewire(new_v, preds, sigs);
246bool SingleQubitSquash::sub_is_better(
247 const Circuit &sub,
const std::vector<Gate_ptr> chain)
const {
248 const unsigned n_gates = sub.n_gates();
249 if (n_gates > chain.size()) {
252 if (!sub.is_symbolic() || always_squash_symbols_) {
253 return n_gates < chain.size() || !is_equal(sub, chain, reversed_);
258 return std::accumulate(
259 sub.begin(), sub.end(), std::size_t{0},
260 [](std::size_t a,
const Command &cmd) {
261 return a + cmd.get_op_ptr()->get_name().size();
264 chain.begin(), chain.end(), std::size_t{0},
265 [](std::size_t a,
const Gate_ptr &gpr) {
266 return a + gpr->get_name().size();
271SingleQubitSquash::Condition SingleQubitSquash::get_condition(
Vertex v)
const {
273 OpType v_type = v_op->get_type();
275 throw BadOpType(
"Cannot get condition from non-conditional OpType", v_type);
277 Condition conds = {};
278 unsigned port_offset = 0;
282 std::list<VertPort> bool_ports;
283 for (
port_t p = 0; p < cond_op.get_width(); ++p) {
284 Edge in_p = ins.at(port_offset + p);
286 bool_ports.push_back(vp);
288 conds.push_back({bool_ports, cond_op.get_value()});
289 v_op = cond_op.get_op();
290 v_type = v_op->get_type();
296Vertex SingleQubitSquash::next_vertex(
const Edge &e)
const {
300port_t SingleQubitSquash::next_port(
const Edge &e)
const {
304Edge SingleQubitSquash::prev_edge(
const VertPort &pair)
const {
309Edge SingleQubitSquash::next_edge(
const Vertex &v,
const Edge &e)
const {
313bool SingleQubitSquash::is_last_optype(
OpType type)
const {
318bool SingleQubitSquash::is_equal(
319 const Circuit &circ,
const std::vector<Gate_ptr> &gates,
bool reversed) {
321 return is_equal(circ, {gates.rbegin(), gates.rend()});
323 if (circ.n_qubits() != 1) {
324 throw CircuitInvalidity(
"Only circuits with one qubit are supported");
327 auto it1 = circ.begin();
328 auto it2 = gates.cbegin();
330 while (it1 != circ.end() && it2 != gates.end()) {
333 if (!(*op1 == *op2)) {
340 if (it1 != circ.end() || it2 != gates.cend()) {
Edge get_nth_in_edge(const Vertex &vert_to, const port_t &n) const
Get the edge targeting the nth input port at vert_to.
VertexVec get_predecessors(const Vertex &vert) const
unsigned n_qubits() const
const Op_ptr get_Op_ptr_from_Vertex(const Vertex &vert) const
Edge get_next_edge(const Vertex &vert, const Edge &in_edge) const
Given a Quantum or Classical in-edge to vert, returns the corresponding out-edge, tracing the resourc...
void substitute_conditional(Circuit to_insert, const Vertex &to_replace, VertexDeletion vertex_deletion=VertexDeletion::Yes, OpGroupTransfer opgroup_transfer=OpGroupTransfer::Disallow)
Replace a conditional vertex with a new circuit.
VertexVec q_outputs() const
port_t get_target_port(const Edge &edge) const
std::optional< Pauli > commuting_basis(const Vertex &vert, port_t port) const
Which Pauli, if any, commutes with the operation at a given vertex and port.
void substitute(const Circuit &to_insert, const Subcircuit &to_replace, VertexDeletion vertex_deletion=VertexDeletion::Yes, OpGroupTransfer opgroup_transfer=OpGroupTransfer::Disallow)
Replace a subcircuit with a new circuit.
void remove_vertices(const VertexSet &surplus, GraphRewiring graph_rewiring, VertexDeletion vertex_deletion)
VertexVec q_inputs() const
Vertex source(const Edge &e) const
Edge get_nth_out_edge(const Vertex &vert_from, const port_t &n) const
Get the edge originated from the nth output port at vert_from.
Vertex add_op(const Op_ptr &op, const std::vector< ID > &args, std::optional< std::string > opgroup=std::nullopt)
Append an operation to the circuit.
unsigned n_in_edges_of_type(const Vertex &vert, EdgeType et) const
Number of inward edges of a specific type.
Edge get_last_edge(const Vertex &vert, const Edge &out_edge) const
void rewire(const Vertex &new_vert, const EdgeVec &preds, const op_signature_t &types)
Rewires linear resources (Quantum or Classical) in place of pred Adds new edges for Boolean O(n alpha...
port_t get_source_port(const Edge &edge) const
VertexVec get_successors(const Vertex &vert) const
EdgeVec get_in_edges(const Vertex &vert) const
All inward edges, ordered by port number Every port has a single in-edge and they are numbered contig...
Vertex add_vertex(const Op_ptr op_ptr, std::optional< std::string > opgroup=std::nullopt)
Add a vertex to the DAG.
Vertex target(const Edge &e) const
Decorates another op, adding a QASM-style classical condition.
Squashes single qubit gates using given Squasher.
bool squash_between(const Edge &in, const Edge &out)
Squash everything between in-edge and out-edge.
bool squash()
Squash entire circuit, one qubit at a time.
SingleQubitSquash(std::unique_ptr< AbstractSquasher > squasher, Circuit &circ, bool reversed=false, bool always_squash_symbols=false)
Construct a new Single Qubit Squash object.
Defines tket::DeviceCharacterisation, used in NoiseAwarePlacement and in commute_SQ_gates_through_SWA...
boost::graph_traits< DAG >::vertex_descriptor Vertex
Gate_ptr as_gate_ptr(Op_ptr op)
Cast a general Op (of gate type) to a Gate.
OpType
Named operation types.
@ Conditional
See Conditional.
std::shared_ptr< const Gate > Gate_ptr
std::unordered_set< Vertex > VertexSet
bool is_initial_q_type(OpType optype)
Test for input or creation quantum "ops".
@ Quantum
A wire carrying quantum information, corresponding to some allocated Qubit.
@ Boolean
A wire carrying a bit of classical information from a classical output port of one op to a classical ...
bool is_gate_type(OpType optype)
Test for elementary gates.
std::pair< Vertex, port_t > VertPort
boost::graph_traits< DAG >::edge_descriptor Edge
std::shared_ptr< const Op > Op_ptr
std::vector< Edge > EdgeVec
unsigned port_t
A specific entry or exit port of a vertex.
std::vector< EdgeType > op_signature_t
bool is_final_q_type(OpType optype)
Test for output or discard quantum "ops".
std::vector< Vertex > VertexVec