Skip to content

Commit

Permalink
add deterministic colors to gates + improve readme (#43)
Browse files Browse the repository at this point in the history
* add deterministic colors to gates + improve readme

* ux improvements

* add a string representation of the circuit to recreate it

* Enhance Gates class with detailed docstrings for methods and add size calculation fix in QubitSimulator tests

* Update project description in setup.py for clarity
  • Loading branch information
splch authored Jan 16, 2025
1 parent 666eec2 commit d91a8e8
Show file tree
Hide file tree
Showing 5 changed files with 414 additions and 183 deletions.
20 changes: 10 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,43 +29,43 @@ Create a simulator with a specified number of qubits:
```python
from qubit_simulator import QubitSimulator

simulator = QubitSimulator(3)
sim = QubitSimulator(num_qubits=2)
```

### Applying Gates

Apply various quantum gates to the qubits:

```python
simulator.h(0) # Hadamard gate
simulator.t(1) # π/8 gate
simulator.cx(0, 2) # Controlled-Not gate
sim.h(0) # Hadamard gate
sim.t(0) # π/8 gate
sim.cx(0, 1) # Controlled-Not gate
```

### Custom Gates

Define and apply custom gates using angles:

```python
simulator.u(0.3, 0.4, 0.5, 2) # Generic gate
sim.u(1.05, 1.57, 3.14, 1) # Arbitrary single-qubit gate
```

### Circuit Drawing

Get a drawing of the circuit:

```python
simulator.draw()
sim.draw()
```

![Circuit Drawing](https://github.com/user-attachments/assets/7dda252d-c931-4120-b4af-d75bfa1d3ea9)
![Circuit Drawing](https://github.com/user-attachments/assets/2e6dbbc3-39e0-4d4f-8c43-c6f2ba83e121)

### Measurements

Measure the state of the qubits:

```python
print(simulator.run(shots=100))
print(sim.run(shots=100))
```

```plaintext
Expand All @@ -77,10 +77,10 @@ print(simulator.run(shots=100))
Show the amplitude and phase of all quantum states:

```python
simulator.plot_state()
sim.state()
```

![Statevector Bar Chart](https://github.com/user-attachments/assets/3cdb0f17-e384-416f-b29d-f2bc6f5faaab)
![Statevector Bar Chart](https://github.com/user-attachments/assets/f883b77f-1dc5-4236-8aed-0d67f8305e12)

## Testing

Expand Down
71 changes: 56 additions & 15 deletions qubit_simulator/gates.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@

class Gates:
"""
Minimal collection of common gates and helper methods.
A utility class containing standard quantum gate matrices (as NumPy arrays)
and static methods for generating parametric gates, controlled gates,
and multi-qubit gate matrices.
"""

# Single-qubit gates (2x2)
Expand All @@ -17,30 +19,46 @@ class Gates:
@staticmethod
def U(theta: float, phi: float, lam: float) -> np.ndarray:
"""
General single-qubit rotation gate:
U(θ, φ, λ) = [[ cos(θ/2), -e^{iλ} sin(θ/2)],
[ e^{iφ} sin(θ/2), e^{i(φ+λ)} cos(θ/2)]]
Create a single-qubit U(θ, φ, λ) gate matrix according to the standard
Euler-angle parameterization.
Args:
theta (float): Rotation angle.
phi (float): Phase angle for the rotation axis.
lam (float): Additional phase angle.
Returns:
np.ndarray: A 2x2 complex matrix representing U(θ, φ, λ).
"""
c, s = np.cos(theta / 2), np.sin(theta / 2)
e = np.exp
return np.array(
[
[np.cos(theta / 2), -np.exp(1j * lam) * np.sin(theta / 2)],
[
np.exp(1j * phi) * np.sin(theta / 2),
np.exp(1j * (phi + lam)) * np.cos(theta / 2),
],
],
[[c, -e(1j * lam) * s], [e(1j * phi) * s, e(1j * (phi + lam)) * c]],
dtype=complex,
)

# Two-qubit gates (4x4)
@staticmethod
def SWAP_matrix() -> np.ndarray:
"""
Return the 4x4 matrix for the SWAP gate, which exchanges the states of two qubits.
Returns:
np.ndarray: A 4x4 complex SWAP gate matrix.
"""
return np.array(
[[1, 0, 0, 0], [0, 0, 1, 0], [0, 1, 0, 0], [0, 0, 0, 1]], dtype=complex
)

@staticmethod
def iSWAP_matrix() -> np.ndarray:
"""
Return the 4x4 matrix for the iSWAP gate, which swaps the two qubits and
introduces a phase of i for the |01> <-> |10> transitions.
Returns:
np.ndarray: A 4x4 complex iSWAP gate matrix.
"""
return np.array(
[[1, 0, 0, 0], [0, 0, 1j, 0], [0, 1j, 0, 0], [0, 0, 0, 1]], dtype=complex
)
Expand All @@ -49,9 +67,14 @@ def iSWAP_matrix() -> np.ndarray:
@staticmethod
def Toffoli_matrix() -> np.ndarray:
"""
Flip the 3rd qubit if first two are |1>.
Return the 8x8 matrix for the Toffoli (CCX) gate, which flips the target bit
only if both control bits are 1.
Returns:
np.ndarray: An 8x8 complex matrix for the Toffoli gate.
"""
m = np.eye(8, dtype=complex)
# Swap the last two diagonal elements to apply the X on the 111 state
m[[6, 7], [6, 7]] = 0
m[6, 7] = 1
m[7, 6] = 1
Expand All @@ -60,9 +83,14 @@ def Toffoli_matrix() -> np.ndarray:
@staticmethod
def Fredkin_matrix() -> np.ndarray:
"""
Swap the last two qubits if the first is |1>.
Return the 8x8 matrix for the Fredkin (CSWAP) gate, which swaps the two target
bits if the control bit is 1.
Returns:
np.ndarray: An 8x8 complex matrix for the Fredkin gate.
"""
m = np.eye(8, dtype=complex)
# Swap indices 5 (101) and 6 (110) to reflect the conditional swap
m[[5, 6], [5, 6]] = 0
m[5, 6] = 1
m[6, 5] = 1
Expand All @@ -71,14 +99,27 @@ def Fredkin_matrix() -> np.ndarray:
@staticmethod
def inverse_gate(U: np.ndarray) -> np.ndarray:
"""
Returns the inverse of a unitary matrix (conjugate transpose).
Return the inverse of a given unitary gate U.
Args:
U (np.ndarray): A unitary matrix.
Returns:
np.ndarray: The inverse (conjugate transpose) of U.
"""
return U.conjugate().T

@staticmethod
def controlled_gate(U: np.ndarray) -> np.ndarray:
"""
Make a 2-qubit controlled version of a single-qubit gate U.
Create a 4x4 controlled version of a 2x2 gate U. The resulting matrix
applies U only when the control qubit is |1>.
Args:
U (np.ndarray): A 2x2 single-qubit gate matrix.
Returns:
np.ndarray: A 4x4 matrix representing the controlled gate.
"""
c = np.zeros((4, 4), dtype=complex)
c[:2, :2] = np.eye(2)
Expand Down
Loading

0 comments on commit d91a8e8

Please sign in to comment.