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

Unitary Synthesis of ChoiMixTableau for Diagonalisation (Again) #1320

Merged
merged 8 commits into from
Apr 4, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
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.108@tket/stable")
self.requires("tket/1.2.109@tket/stable")
self.requires("tklog/0.3.3@tket/stable")
self.requires("tkrng/0.3.3@tket/stable")
self.requires("tkassert/0.3.4@tket/stable")
Expand Down
2 changes: 0 additions & 2 deletions tket/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,6 @@ target_sources(tket
src/ZX/MBQCRewrites.cpp
src/ZX/ZXRWSequences.cpp
src/Converters/ChoiMixTableauConverters.cpp
src/Converters/PauliGadget.cpp
src/Converters/PauliGraphConverters.cpp
src/Converters/Gauss.cpp
src/Converters/PhasePoly.cpp
Expand Down Expand Up @@ -379,7 +378,6 @@ target_sources(tket
include/tket/ZX/ZXGenerator.hpp
include/tket/Converters/Converters.hpp
include/tket/Converters/Gauss.hpp
include/tket/Converters/PauliGadget.hpp
include/tket/Converters/PhasePoly.hpp
include/tket/Converters/UnitaryTableauBox.hpp
include/tket/Placement/NeighbourPlacements.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.108"
version = "1.2.109"
package_type = "library"
license = "Apache 2"
homepage = "https://github.com/CQCL/tket"
Expand Down
26 changes: 21 additions & 5 deletions tket/include/tket/Circuit/CircUtils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -111,13 +111,13 @@ std::pair<Circuit, Complex> decompose_2cx_DV(const Eigen::Matrix4cd& U);
* Construct a phase gadget
*
* @param n_qubits number of qubits
* @param t phase parameter
* @param angle phase parameter
* @param cx_config CX configuration
*
* @return phase gadget implementation wrapped in a ConjugationBox
*/
Circuit phase_gadget(
unsigned n_qubits, const Expr& t,
unsigned n_qubits, const Expr& angle,
CXConfigType cx_config = CXConfigType::Snake);

/**
Expand All @@ -127,13 +127,29 @@ Circuit phase_gadget(
* \f$ e^{-\frac12 i \pi t \sigma_0 \otimes \sigma_1 \otimes \cdots} \f$
* where \f$ \sigma_i \in \{I,X,Y,Z\} \f$ are the Pauli operators.
*
* @param paulis Pauli operators
* @param t angle in half-turns
* @param pauli Pauli operators; coefficient gives rotation angle in half-turns
* @param cx_config CX configuration
* @return Pauli gadget implementation wrapped in a ConjugationBox
*/
Circuit pauli_gadget(
const std::vector<Pauli>& paulis, const Expr& t,
SpSymPauliTensor pauli, CXConfigType cx_config = CXConfigType::Snake);

/**
* Construct a circuit realising a pair of Pauli gadgets with the fewest
* two-qubit gates.
*
* The returned circuit implements the unitary e^{-i pi angle1 paulis1 / 2}
* e^{-i pi angle0 paulis0 / 2}, i.e. a gadget of angle0 about paulis0 followed
* by a gadget of angle1 about paulis1.
*
* @param paulis0 Pauli operators for first gadget; coefficient gives rotation
* angle in half-turns
* @param paulis1 Pauli operators for second gadget; coefficient gives rotation
* angle in half-turns
* @param cx_config CX configuration
*/
Circuit pauli_gadget_pair(
SpSymPauliTensor paulis0, SpSymPauliTensor paulis1,
CXConfigType cx_config = CXConfigType::Snake);

/**
Expand Down
42 changes: 42 additions & 0 deletions tket/include/tket/Circuit/PauliExpBoxes.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -275,4 +275,46 @@ class TermSequenceBox : public Box {
CXConfigType cx_configuration_;
};

/**
* Constructs a PauliExpBox for a single pauli gadget and appends it to a
* circuit.
*
* @param circ The circuit to append the box to
* @param pauli The pauli operator of the gadget; coefficient gives the rotation
* angle in half-turns
* @param cx_config The CX configuration to be used during synthesis
*/
void append_single_pauli_gadget_as_pauli_exp_box(
Circuit &circ, const SpSymPauliTensor &pauli, CXConfigType cx_config);

/**
* Constructs a PauliExpPairBox for a pair of pauli gadgets and appends it to a
* circuit. The pauli gadgets may or may not commute, so the ordering matters.
*
* @param circ The circuit to append the box to
* @param pauli0 The pauli operator of the first gadget; coefficient gives the
* rotation angle in half-turns
* @param pauli1 The pauli operator of the second gadget; coefficient gives the
* rotation angle in half-turns
* @param cx_config The CX configuration to be used during synthesis
*/
void append_pauli_gadget_pair_as_box(
Circuit &circ, const SpSymPauliTensor &pauli0,
const SpSymPauliTensor &pauli1, CXConfigType cx_config);

/**
* Constructs a PauliExpCommutingSetBox for a set of mutually commuting pauli
* gadgets and appends it to a circuit. As the pauli gadgets all commute, the
* ordering does not matter semantically, but may yield different synthesised
* circuits.
*
* @param circ The circuit to append the box to
* @param gadgets Description of the pauli gadgets; coefficients give the
* rotation angles in half-turns
* @param cx_config The CX configuration to be used during synthesis
*/
void append_commuting_pauli_gadget_set_as_box(
Circuit &circ, const std::list<SpSymPauliTensor> &gadgets,
CXConfigType cx_config);

} // namespace tket
21 changes: 18 additions & 3 deletions tket/include/tket/Clifford/ChoiMixTableau.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ class ChoiMixTableau {
* When mapped to a sparse readable representation, independent
* SpPauliStabiliser objects are used for each segment, so we no longer expect
* their individual phases to be +-1, instead only requiring this on their
* product.
* product. get_row() will automatically transpose the input segment term so
* it is presented as RxS s.t. SCR = C.
*
* Columns of the tableau are indexed by pair of Qubit id and a tag to
* distinguish input vs output. Rows are not maintained in any particular
Expand Down Expand Up @@ -93,6 +94,7 @@ class ChoiMixTableau {
* Construct a tableau directly from its rows.
* Each row is represented as a product of SpPauliStabilisers where the first
* is over the input qubits and the second is over the outputs.
* A row RxS is a pair s.t. SCR = C
*/
explicit ChoiMixTableau(const std::list<row_tensor_t>& rows);
/**
Expand Down Expand Up @@ -122,13 +124,23 @@ class ChoiMixTableau {
* Get the number of boundaries representing outputs from the process.
*/
unsigned get_n_outputs() const;
/**
* Get all qubit names present in the input segment.
*/
qubit_vector_t input_qubits() const;
/**
* Get all qubit names present in the output segment.
*/
qubit_vector_t output_qubits() const;

/**
* Read off a row as a Pauli string
* Read off a row as a Pauli string.
* Returns a pair of Pauli strings RxS such that SCR = C
*/
row_tensor_t get_row(unsigned i) const;
/**
* Combine rows into a single row
* Combine rows into a single row.
* Returns a pair of Pauli strings RxS such that SCR = C
*/
row_tensor_t get_row_product(const std::vector<unsigned>& rows) const;

Expand All @@ -142,7 +154,10 @@ class ChoiMixTableau {
* outputs.
*/
void apply_S(const Qubit& qb, TableauSegment seg = TableauSegment::Output);
void apply_Z(const Qubit& qb, TableauSegment seg = TableauSegment::Output);
void apply_V(const Qubit& qb, TableauSegment seg = TableauSegment::Output);
void apply_X(const Qubit& qb, TableauSegment seg = TableauSegment::Output);
void apply_H(const Qubit& qb, TableauSegment seg = TableauSegment::Output);
void apply_CX(
const Qubit& control, const Qubit& target,
TableauSegment seg = TableauSegment::Output);
Expand Down
41 changes: 8 additions & 33 deletions tket/include/tket/Clifford/SymplecticTableau.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,6 @@

namespace tket {

// Forward declare friend classes for converters
class ChoiMixTableau;
class UnitaryTableau;
class UnitaryRevTableau;
class Circuit;

/**
* Boolean encoding of Pauli
* <x, z> = <false, false> ==> I
Expand Down Expand Up @@ -136,10 +130,13 @@ class SymplecticTableau {
void row_mult(unsigned ra, unsigned rw, Complex coeff = 1.);

/**
* Applies an S/V/CX gate to the given qubit(s)
* Applies a chosen gate to the given qubit(s)
*/
void apply_S(unsigned qb);
void apply_Z(unsigned qb);
void apply_V(unsigned qb);
void apply_X(unsigned qb);
void apply_H(unsigned qb);
void apply_CX(unsigned qc, unsigned qt);
void apply_gate(OpType type, const std::vector<unsigned> &qbs);

Expand Down Expand Up @@ -173,29 +170,19 @@ class SymplecticTableau {
*/
void gaussian_form();

private:
/**
* Number of rows
*/
unsigned n_rows_;

/**
* Number of qubits in each row
*/
unsigned n_qubits_;

/**
* Tableau contents
*/
MatrixXb xmat_;
MatrixXb zmat_;
VectorXb phase_;
MatrixXb xmat;
MatrixXb zmat;
VectorXb phase;

/**
* Complex conjugate of the state by conjugating rows
*/
SymplecticTableau conjugate() const;

private:
/**
* Helper methods for manipulating the tableau when applying gates
*/
Expand All @@ -206,18 +193,6 @@ class SymplecticTableau {
void col_mult(
const MatrixXb::ColXpr &a, const MatrixXb::ColXpr &b, bool flip,
MatrixXb::ColXpr &w, VectorXb &pw);

friend class UnitaryTableau;
friend class ChoiMixTableau;
friend Circuit unitary_tableau_to_circuit(const UnitaryTableau &tab);
friend std::pair<Circuit, unit_map_t> cm_tableau_to_circuit(
const ChoiMixTableau &tab);
friend std::ostream &operator<<(std::ostream &os, const UnitaryTableau &tab);
friend std::ostream &operator<<(
std::ostream &os, const UnitaryRevTableau &tab);

friend void to_json(nlohmann::json &j, const SymplecticTableau &tab);
friend void from_json(const nlohmann::json &j, SymplecticTableau &tab);
};

JSON_DECL(SymplecticTableau)
Expand Down
12 changes: 12 additions & 0 deletions tket/include/tket/Clifford/UnitaryTableau.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,14 @@ class UnitaryTableau {
*/
void apply_S_at_front(const Qubit& qb);
void apply_S_at_end(const Qubit& qb);
void apply_Z_at_front(const Qubit& qb);
void apply_Z_at_end(const Qubit& qb);
void apply_V_at_front(const Qubit& qb);
void apply_V_at_end(const Qubit& qb);
void apply_X_at_front(const Qubit& qb);
void apply_X_at_end(const Qubit& qb);
void apply_H_at_front(const Qubit& qb);
void apply_H_at_end(const Qubit& qb);
void apply_CX_at_front(const Qubit& control, const Qubit& target);
void apply_CX_at_end(const Qubit& control, const Qubit& target);
void apply_gate_at_front(OpType type, const qubit_vector_t& qbs);
Expand Down Expand Up @@ -244,8 +250,14 @@ class UnitaryRevTableau {
*/
void apply_S_at_front(const Qubit& qb);
void apply_S_at_end(const Qubit& qb);
void apply_Z_at_front(const Qubit& qb);
void apply_Z_at_end(const Qubit& qb);
void apply_V_at_front(const Qubit& qb);
void apply_V_at_end(const Qubit& qb);
void apply_X_at_front(const Qubit& qb);
void apply_X_at_end(const Qubit& qb);
void apply_H_at_front(const Qubit& qb);
void apply_H_at_end(const Qubit& qb);
void apply_CX_at_front(const Qubit& control, const Qubit& target);
void apply_CX_at_end(const Qubit& control, const Qubit& target);
void apply_gate_at_front(OpType type, const qubit_vector_t& qbs);
Expand Down
83 changes: 79 additions & 4 deletions tket/include/tket/Converters/Converters.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,88 @@ ChoiMixTableau circuit_to_cm_tableau(const Circuit &circ);

/**
* Constructs a circuit producing the same effect as a ChoiMixTableau.
* Uses a naive synthesis method until we develop a good heuristic.
* Since Circuit does not support distinct qubit addresses for inputs and
* outputs, also returns a map from the output qubit IDs in the tableau to their
* corresponding outputs in the circuit
* corresponding outputs in the circuit.
*
* The circuit produced will be the (possibly non-unitary) channel whose
* stabilisers are exactly those of the tableau and no more, using
* initialisations, post-selections, discards, resets, and collapses to ensure
* this. It will automatically reuse qubits so no more qubits will be needed
* than max(tab.get_n_inputs(), tab.get_n_outputs()).
*
* Example 1:
* ZXI -> ()
* YYZ -> ()
* This becomes a diagonalisation circuit followed by post-selections.
*
* Example 2:
* Z -> ZZ
* X -> IY
* Z -> -XX
* Combining the first and last rows reveals an initialisation is required for I
* -> YY. Since there are two output qubits, at least one of them does not
* already exist in the input fragment so we can freely add an extra qubit on
* the input side, initialise it and apply a unitary mapping IZ -> YY.
*
* Example 3:
* ZX -> IZ
* II -> ZI
* We require an initialised qubit for the final row, but both input and output
* spaces only have q[0] and q[1], of which both inputs need to be open for the
* first row. We can obtain an initialised qubit by resetting a qubit after
* reducing the first row to only a single qubit.
*/
std::pair<Circuit, unit_map_t> cm_tableau_to_circuit(
const ChoiMixTableau &circ);
std::pair<Circuit, qubit_map_t> cm_tableau_to_exact_circuit(
const ChoiMixTableau &tab, CXConfigType cx_config = CXConfigType::Snake);

/**
* We define a unitary extension of a ChoiMixTableau to be a unitary circuit
* whose stabilizer group contain all the rows of the ChoiMixTableau and
* possibly more. This is useful when we are treating the ChoiMixTableau as a
* means to encode a diagonalisation problem, since we are generally looking for
* a unitary as we may wish to apply the inverse afterwards (e.g. conjugating
* some rotations to implement a set of Pauli gadgets).
*
* Not every ChoiMixTableau can be extended to a unitary by just adding rows,
* e.g. if it requires any initialisation or post-selections. In this case, the
* unitary circuit is extended with additional input qubits which are assumed to
* be zero-initialised, and additional output qubits which are assumed to be
* post-selected. The synthesis guarantees that, if we take the unitary,
* initialise all designated inputs, and post-select on all designated outputs,
* every row from the original tableau is a stabiliser for the remaining
* projector. When not enough additional qubit names are provided, an error is
* thrown.
*
*
* Example 1:
* ZXI -> ()
* YYZ -> ()
* Since, in exact synthesis, at least two post-selections would be required, we
* pick two names from post_names. This is then a diagonalisation circuit which
* maps each row to an arbitrary diagonal string over post_names.
*
* Example 2:
* Z -> ZZ
* X -> IY
* Z -> -XX
* Combining the first and last rows reveals an initialisation is required for I
* -> YY. We extend the inputs with a qubit from init_names. The initialisation
* can manifest as either altering the first row to ZZ -> ZZ or the last row to
* ZZ -> -XX.
*
* Example 3:
* ZX -> IZ
* II -> ZI
* We require an initialised qubit for the final row, but both input and output
* spaces only have q[0] and q[1], of which both inputs need to be open for the
* first row. Unlike exact synthesis, we cannot reuse qubits, so the returned
* circuit will be over 3 qubits, extending with a name from init_names.
*/
std::pair<Circuit, qubit_map_t> cm_tableau_to_unitary_extension_circuit(
const ChoiMixTableau &tab, const std::vector<Qubit> &init_names = {},
const std::vector<Qubit> &post_names = {},
CXConfigType cx_config = CXConfigType::Snake);

PauliGraph circuit_to_pauli_graph(const Circuit &circ);

Expand Down
Loading
Loading