Skip to content

Commit 8f4ccc3

Browse files
authored
MAHOUT-681: Implement T-gate (π/8 gate) across all backends (#693)
* Implement T-gate (π/8 gate) across all backends - Add apply_t_gate() method to QuMat class with validation - Implement T-gate support in Qiskit, Cirq, and Amazon Braket backends - Restore T-gate documentation in basic_gates.md to match implementation Fixes #681 * Refine T-gate docs and add coverage * chore: satisfy pre-commit hooks
1 parent 51da117 commit 8f4ccc3

File tree

6 files changed

+142
-1
lines changed

6 files changed

+142
-1
lines changed

docs/basic_gates.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ The Pauli Z gate introduces a phase flip without changing the qubit's state. It
4040

4141
It's used for measuring the phase of a qubit.
4242

43-
## T-Gate (π/8 Gate) (New Addition)
43+
## T-Gate (π/8 Gate)
4444
The T-Gate applies a **π/4 phase shift** to the qubit. It is essential for quantum computing because it, along with the Hadamard and CNOT gates, allows for **universal quantum computation**. Mathematically:
4545

4646
\[ T|0⟩ = |0⟩ \]

qumat/amazon_braket_backend.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,10 @@ def apply_pauli_z_gate(circuit, qubit_index):
8181
circuit.z(qubit_index)
8282

8383

84+
def apply_t_gate(circuit, qubit_index):
85+
circuit.t(qubit_index)
86+
87+
8488
def execute_circuit(circuit, backend, backend_config):
8589
shots = backend_config["backend_options"].get("shots", 1)
8690
parameter_values = backend_config.get("parameter_values", {})

qumat/cirq_backend.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,11 @@ def apply_pauli_z_gate(circuit, qubit_index):
9595
circuit.append(cirq.Z(qubit))
9696

9797

98+
def apply_t_gate(circuit, qubit_index):
99+
qubit = cirq.LineQubit(qubit_index)
100+
circuit.append(cirq.T(qubit))
101+
102+
98103
def execute_circuit(circuit, backend, backend_config):
99104
# handle 0-qubit circuits before adding measurements
100105
if not circuit.all_qubits():

qumat/qiskit_backend.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,11 @@ def apply_pauli_z_gate(circuit, qubit_index):
8585
circuit.z(qubit_index)
8686

8787

88+
def apply_t_gate(circuit, qubit_index):
89+
# Apply a T gate (π/8 gate) on the specified qubit
90+
circuit.t(qubit_index)
91+
92+
8893
def execute_circuit(circuit, backend, backend_config):
8994
# Add measurements if they are not already present
9095
# Check if circuit already has measurement operations

qumat/qumat.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,21 @@ def apply_pauli_z_gate(self, qubit_index):
251251
self._validate_qubit_index(qubit_index)
252252
self.backend_module.apply_pauli_z_gate(self.circuit, qubit_index)
253253

254+
def apply_t_gate(self, qubit_index):
255+
"""Apply a T-gate (π/8 gate) to the specified qubit.
256+
257+
Applies a relative pi/4 phase (multiplies the |1> state by e^{i*pi/4}).
258+
Essential for universal quantum computation when combined with
259+
Hadamard and CNOT gates.
260+
261+
:param qubit_index: Index of the qubit.
262+
:type qubit_index: int
263+
:raises RuntimeError: If the circuit has not been initialized.
264+
"""
265+
self._ensure_circuit_initialized()
266+
self._validate_qubit_index(qubit_index)
267+
self.backend_module.apply_t_gate(self.circuit, qubit_index)
268+
254269
def execute_circuit(self, parameter_values=None):
255270
"""Execute the quantum circuit and return the measurement results.
256271

testing/test_single_qubit_gates.py

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -527,6 +527,118 @@ def test_pauli_z_with_hadamard(self, backend_name):
527527
)
528528

529529

530+
@pytest.mark.parametrize("backend_name", TESTING_BACKENDS)
531+
class TestTGate:
532+
"""Test class for T gate functionality."""
533+
534+
@pytest.mark.parametrize(
535+
"initial_state, expected_state",
536+
[
537+
("0", "0"), # T leaves |0> unchanged
538+
("1", "1"), # T applies phase to |1>, measurement unchanged
539+
],
540+
)
541+
def test_t_gate_preserves_basis_states(
542+
self, backend_name, initial_state, expected_state
543+
):
544+
"""T gate should preserve computational basis measurement outcomes."""
545+
backend_config = get_backend_config(backend_name)
546+
qumat = QuMat(backend_config)
547+
qumat.create_empty_circuit(num_qubits=1)
548+
549+
if initial_state == "1":
550+
qumat.apply_pauli_x_gate(0)
551+
552+
qumat.apply_t_gate(0)
553+
results = qumat.execute_circuit()
554+
555+
prob = get_state_probability(
556+
results, expected_state, num_qubits=1, backend_name=backend_name
557+
)
558+
assert prob > 0.95, (
559+
f"Backend: {backend_name}, expected |{expected_state}> after T, "
560+
f"got probability {prob:.4f}"
561+
)
562+
563+
def test_t_gate_phase_visible_via_hzh(self, backend_name):
564+
"""T^4 = Z; H-Z-H should act like X and flip |0> to |1>."""
565+
backend_config = get_backend_config(backend_name)
566+
qumat = QuMat(backend_config)
567+
qumat.create_empty_circuit(num_qubits=1)
568+
569+
qumat.apply_hadamard_gate(0)
570+
for _ in range(4):
571+
qumat.apply_t_gate(0)
572+
qumat.apply_hadamard_gate(0)
573+
574+
results = qumat.execute_circuit()
575+
prob = get_state_probability(
576+
results, "1", num_qubits=1, backend_name=backend_name
577+
)
578+
assert prob > 0.95, (
579+
f"Backend: {backend_name}, expected |1> after H-T^4-H, "
580+
f"got probability {prob:.4f}"
581+
)
582+
583+
def test_t_gate_eight_applications_identity(self, backend_name):
584+
"""T^8 should be identity."""
585+
backend_config = get_backend_config(backend_name)
586+
qumat = QuMat(backend_config)
587+
qumat.create_empty_circuit(num_qubits=1)
588+
589+
for _ in range(8):
590+
qumat.apply_t_gate(0)
591+
592+
results = qumat.execute_circuit()
593+
prob = get_state_probability(
594+
results, "0", num_qubits=1, backend_name=backend_name
595+
)
596+
assert prob > 0.95, (
597+
f"Backend: {backend_name}, expected |0> after T^8, "
598+
f"got probability {prob:.4f}"
599+
)
600+
601+
602+
@pytest.mark.parametrize(
603+
"phase_applications",
604+
[
605+
1, # single T
606+
2, # T^2 = S
607+
4, # T^4 = Z
608+
],
609+
)
610+
def test_t_gate_cross_backend_consistency(phase_applications):
611+
"""T gate should behave consistently across all backends."""
612+
results_dict = {}
613+
614+
for backend_name in TESTING_BACKENDS:
615+
backend_config = get_backend_config(backend_name)
616+
qumat = QuMat(backend_config)
617+
qumat.create_empty_circuit(num_qubits=1)
618+
619+
# Use H ... H sandwich to turn phase into amplitude when needed
620+
qumat.apply_hadamard_gate(0)
621+
for _ in range(phase_applications):
622+
qumat.apply_t_gate(0)
623+
qumat.apply_hadamard_gate(0)
624+
625+
results = qumat.execute_circuit()
626+
prob_one = get_state_probability(
627+
results, "1", num_qubits=1, backend_name=backend_name
628+
)
629+
results_dict[backend_name] = prob_one
630+
631+
backends = list(results_dict.keys())
632+
for i in range(len(backends)):
633+
for j in range(i + 1, len(backends)):
634+
b1, b2 = backends[i], backends[j]
635+
diff = abs(results_dict[b1] - results_dict[b2])
636+
assert diff < 0.05, (
637+
f"T gate inconsistent between {b1} and {b2} for T^{phase_applications}: "
638+
f"{results_dict[b1]:.4f} vs {results_dict[b2]:.4f}"
639+
)
640+
641+
530642
@pytest.mark.parametrize("backend_name", TESTING_BACKENDS)
531643
class TestSingleQubitGatesEdgeCases:
532644
"""Test class for edge cases of single-qubit gates."""

0 commit comments

Comments
 (0)