1010
1111#pragma once
1212
13+ #include < llvm/ADT/ArrayRef.h>
14+ #include < mlir/Support/LLVM.h>
15+
1316#include < array>
1417#include < complex>
1518#include < cstddef>
@@ -26,6 +29,8 @@ using Complex = std::complex<double>;
2629inline constexpr double MATRIX_TOLERANCE = 1e-14 ;
2730
2831class DynamicMatrix ;
32+ struct Matrix4x4 ;
33+ struct SymmetricEigen4 ;
2934
3035/* *
3136 * @brief 1x1 matrix for global-phase gates.
@@ -183,6 +188,12 @@ struct Matrix2x2 {
183188 */
184189 [[nodiscard]] Matrix2x2 adjoint () const ;
185190
191+ /* *
192+ * @brief Returns the (non-conjugate) transpose of this matrix.
193+ * @return Transposed matrix `A^T`.
194+ */
195+ [[nodiscard]] Matrix2x2 transpose () const ;
196+
186197 /* *
187198 * @brief Returns the trace of this matrix.
188199 * @return Sum of diagonal entries.
@@ -195,6 +206,13 @@ struct Matrix2x2 {
195206 */
196207 [[nodiscard]] Complex determinant () const ;
197208
209+ /* *
210+ * @brief Checks whether this matrix is approximately the identity.
211+ * @param tol Maximum allowed complex modulus of each entry difference.
212+ * @return True if every entry is within @p tol of the identity.
213+ */
214+ [[nodiscard]] bool isIdentity (double tol = MATRIX_TOLERANCE ) const ;
215+
198216 /* *
199217 * @brief Checks approximate equality using an absolute entry-wise tolerance.
200218 *
@@ -215,6 +233,30 @@ struct Matrix2x2 {
215233 * @return `true` when @p src is 2x2.
216234 */
217235 [[nodiscard]] bool assignFrom (const DynamicMatrix& src);
236+
237+ /* *
238+ * @brief Embed this single-qubit matrix into an @p numQubits-qubit Hilbert
239+ * space.
240+ *
241+ * Wire @p qubitIndex uses the same MSB-first convention as @ref
242+ * Matrix4x4::kron (high bit first operand, low bit second). For each basis
243+ * pair whose untouched wires match, copies this matrix at the target qubit's
244+ * row/column bits.
245+ *
246+ * @param numQubits Number of qubits in the target Hilbert space.
247+ * @param qubitIndex Wire index to act on.
248+ * @return Embedded unitary as a dynamic matrix.
249+ */
250+ [[nodiscard]] DynamicMatrix embedInNqubit (std::size_t numQubits,
251+ std::size_t qubitIndex) const ;
252+
253+ /* *
254+ * @brief Embed this single-qubit matrix into a two-qubit Hilbert space.
255+ *
256+ * @param qubitIndex Wire index (`0` = high bit / MSB, `1` = low bit).
257+ * @return The `4x4` embedded unitary.
258+ */
259+ [[nodiscard]] Matrix4x4 embedInTwoQubit (std::size_t qubitIndex) const ;
218260};
219261
220262/* *
@@ -322,6 +364,12 @@ struct Matrix4x4 {
322364 */
323365 [[nodiscard]] Matrix4x4 adjoint () const ;
324366
367+ /* *
368+ * @brief Returns the (non-conjugate) transpose of this matrix.
369+ * @return Transposed matrix `A^T`.
370+ */
371+ [[nodiscard]] Matrix4x4 transpose () const ;
372+
325373 /* *
326374 * @brief Returns the trace of this matrix.
327375 * @return Sum of diagonal entries.
@@ -334,6 +382,81 @@ struct Matrix4x4 {
334382 */
335383 [[nodiscard]] Complex determinant () const ;
336384
385+ /* *
386+ * @brief Checks whether this matrix is approximately the identity.
387+ * @param tol Maximum allowed complex modulus of each entry difference.
388+ * @return True if every entry is within @p tol of the identity.
389+ */
390+ [[nodiscard]] bool isIdentity (double tol = MATRIX_TOLERANCE ) const ;
391+
392+ /* *
393+ * @brief Returns the four diagonal entries `(m00, m11, m22, m33)`.
394+ * @return Array of diagonal entries.
395+ */
396+ [[nodiscard]] std::array<Complex, K_ROWS > diagonal () const ;
397+
398+ /* *
399+ * @brief Builds a diagonal matrix from four diagonal entries.
400+ * @param diagonalEntries Diagonal entries `(m00, m11, m22, m33)`; must have
401+ * length `K_ROWS`.
402+ * @return Diagonal matrix with the given entries.
403+ */
404+ [[nodiscard]] static Matrix4x4
405+ fromDiagonal (ArrayRef<Complex> diagonalEntries);
406+
407+ /* *
408+ * @brief Kronecker product `lhs (x) rhs` of two single-qubit matrices.
409+ *
410+ * Uses the computational-basis bit order where the first operand labels the
411+ * high bit, matching `UnitaryOpInterface::getUnitaryMatrix4x4`.
412+ *
413+ * @param lhs Left factor (acts on the high bit / qubit 0).
414+ * @param rhs Right factor (acts on the low bit / qubit 1).
415+ * @return The `4x4` Kronecker product.
416+ */
417+ [[nodiscard]] static Matrix4x4 kron (const Matrix2x2& lhs,
418+ const Matrix2x2& rhs);
419+
420+ /* *
421+ * @brief Returns the entries of column @p col, top to bottom.
422+ * @param col Column index in `[0, K_COLS)`.
423+ * @return Array of the four column entries.
424+ */
425+ [[nodiscard]] std::array<Complex, K_ROWS > column (std::size_t col) const ;
426+
427+ /* *
428+ * @brief Overwrites column @p col with @p values.
429+ * @param col Column index in `[0, K_COLS)`.
430+ * @param values New column entries, top to bottom; must have length `K_ROWS`.
431+ */
432+ void setColumn (std::size_t col, ArrayRef<Complex> values);
433+
434+ /* *
435+ * @brief Returns the entries of row @p row, left to right.
436+ * @param row Row index in `[0, K_ROWS)`.
437+ * @return View over the four row entries.
438+ */
439+ [[nodiscard]] ArrayRef<const Complex> row (std::size_t row) const ;
440+
441+ /* *
442+ * @brief Overwrites row @p row with @p values.
443+ * @param row Row index in `[0, K_ROWS)`.
444+ * @param values New row entries, left to right; must have length `K_COLS`.
445+ */
446+ void setRow (std::size_t row, ArrayRef<Complex> values);
447+
448+ /* *
449+ * @brief Returns the element-wise real parts in row-major order.
450+ * @return Real parts of all entries.
451+ */
452+ [[nodiscard]] std::array<double , K_SIZE_AT_COMPILE_TIME > realPart () const ;
453+
454+ /* *
455+ * @brief Returns the element-wise imaginary parts in row-major order.
456+ * @return Imaginary parts of all entries.
457+ */
458+ [[nodiscard]] std::array<double , K_SIZE_AT_COMPILE_TIME > imagPart () const ;
459+
337460 /* *
338461 * @brief Checks approximate equality using an absolute entry-wise tolerance.
339462 *
@@ -354,6 +477,58 @@ struct Matrix4x4 {
354477 * @return `true` when @p src is 4x4.
355478 */
356479 [[nodiscard]] bool assignFrom (const DynamicMatrix& src);
480+
481+ /* *
482+ * @brief Embed this two-qubit matrix into an @p numQubits-qubit Hilbert
483+ * space.
484+ *
485+ * Operand 0 labels the high bit of the pair and acts on @p q0Index; operand 1
486+ * labels the low bit and acts on @p q1Index. For each basis pair whose other
487+ * wires match, copies this matrix at the packed two-qubit row/column indices.
488+ *
489+ * @param numQubits Number of qubits in the target Hilbert space.
490+ * @param q0Index Wire index of operand 0.
491+ * @param q1Index Wire index of operand 1.
492+ * @return Embedded unitary as a dynamic matrix.
493+ */
494+ [[nodiscard]] DynamicMatrix embedInNqubit (std::size_t numQubits,
495+ std::size_t q0Index,
496+ std::size_t q1Index) const ;
497+
498+ /* *
499+ * @brief Reorder this matrix to act on qubits `{0, 1}`.
500+ *
501+ * @param q0Index Wire index of operand 0; @p q1Index wire index of operand 1.
502+ * @return Reordered copy of this matrix.
503+ */
504+ [[nodiscard]] Matrix4x4 reorderForQubits (std::size_t q0Index,
505+ std::size_t q1Index) const ;
506+
507+ /* *
508+ * @brief Computes the eigendecomposition of this real symmetric matrix.
509+ *
510+ * @copydoc Matrix4x4::symmetricEigen4(const std::array<double, 16>&)
511+ *
512+ * @pre Entries are real (imaginary parts must be negligible). The real parts
513+ * must form a symmetric matrix; imaginary parts are ignored.
514+ */
515+ [[nodiscard]] SymmetricEigen4 symmetricEigen4 () const ;
516+
517+ /* *
518+ * @brief Computes the eigendecomposition of a real symmetric `4x4` matrix.
519+ *
520+ * Uses Householder tridiagonalization (EISPACK `tred2`) followed by implicit
521+ * QL iteration (`tql2`) on the tridiagonal form.
522+ *
523+ * @pre @p symmetric is real and symmetric: `symmetric[i,j] == symmetric[j,i]`
524+ * for all `i, j`. Only the lower triangle (including the diagonal) is read,
525+ * but supplying a non-symmetric matrix yields undefined numerical results.
526+ *
527+ * @param symmetric Row-major real symmetric `4x4` matrix.
528+ * @return Ascending eigenvalues and matching eigenvectors (as columns).
529+ */
530+ [[nodiscard]] static SymmetricEigen4
531+ symmetricEigen4 (const std::array<double , 16 >& symmetric);
357532};
358533
359534/* *
@@ -539,6 +714,41 @@ class DynamicMatrix {
539714 [[nodiscard]] bool isApprox (const DynamicMatrix& other,
540715 double tol = MATRIX_TOLERANCE ) const ;
541716
717+ /* *
718+ * @brief Returns the trace of this matrix.
719+ * @return Sum of diagonal entries.
720+ */
721+ [[nodiscard]] Complex trace () const ;
722+
723+ /* *
724+ * @brief Matrix product `*this * rhs`.
725+ * @param rhs Right-hand factor.
726+ * @return Product of the two matrices.
727+ */
728+ [[nodiscard]] DynamicMatrix operator *(const DynamicMatrix& rhs) const ;
729+
730+ /* *
731+ * @brief Element-wise scaling by a complex scalar.
732+ * @param scalar Factor applied to every matrix entry.
733+ * @return Scaled copy of this matrix.
734+ */
735+ [[nodiscard]] DynamicMatrix operator *(const Complex& scalar) const ;
736+
737+ /* *
738+ * @brief Element-wise in-place scaling by a complex scalar.
739+ * @param scalar Factor applied to every matrix entry.
740+ * @return Reference to this matrix.
741+ */
742+ DynamicMatrix& operator *=(const Complex& scalar);
743+
744+ /* *
745+ * @brief Checks whether this matrix is approximately the identity.
746+ * @param tol Maximum allowed complex modulus of each off-diagonal entry and
747+ * each diagonal deviation from one.
748+ * @return True when the matrix is close to the identity.
749+ */
750+ [[nodiscard]] bool isIdentity (double tol = MATRIX_TOLERANCE ) const ;
751+
542752private:
543753 struct Impl ;
544754 std::unique_ptr<Impl> impl_;
@@ -558,4 +768,31 @@ inline constexpr bool
558768 std::disjunction_v<std::is_same<T, Matrix1x1>, std::is_same<T, Matrix2x2>,
559769 std::is_same<T, Matrix4x4>,
560770 std::is_same<T, DynamicMatrix>>;
771+
772+ // / Scalar-on-the-left multiply `scalar * matrix` (commutes with the member
773+ // / `matrix * scalar`). Provided so generic code can scale a matrix from
774+ // / either side.
775+ [[nodiscard]] Matrix2x2 operator *(const Complex& scalar,
776+ const Matrix2x2& matrix);
777+ // / @copydoc operator*(const Complex&, const Matrix2x2&)
778+ [[nodiscard]] Matrix4x4 operator *(const Complex& scalar,
779+ const Matrix4x4& matrix);
780+ // / @copydoc operator*(const Complex&, const Matrix2x2&)
781+ [[nodiscard]] DynamicMatrix operator *(const Complex& scalar,
782+ const DynamicMatrix& matrix);
783+
784+ /* *
785+ * @brief Eigenvalues and eigenvectors of a real symmetric `4x4` matrix.
786+ *
787+ * `eigenvalues` are sorted ascending and `eigenvectors` holds the
788+ * corresponding orthonormal eigenvectors as columns (column `j` is the
789+ * eigenvector for `eigenvalues[j]`).
790+ */
791+ struct SymmetricEigen4 {
792+ // / Eigenvalues in ascending order.
793+ std::array<double , 4 > eigenvalues{};
794+ // / Orthonormal eigenvectors as columns (column `j` matches `eigenvalues[j]`).
795+ Matrix4x4 eigenvectors{};
796+ };
797+
561798} // namespace mlir::qco
0 commit comments