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

[CQT-30] Print out available information instead of "anonymous-gate" #253

Merged
Merged
26 changes: 11 additions & 15 deletions docs/tutorial.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,20 +152,24 @@ except Exception as e:

## Modifying a circuit

### Merging gates
### Merging single qubit gates

OpenSquirrel can merge consecutive quantum gates. Currently, this is only done for single-qubit gates. The resulting gate is labeled as an "anonymous gate". Since those gates have no name, the placeholder `<anonymous-gate>` is used instead.
All single-qubit gates appearing in a circuit can be merged by applying `merge_single_qubit_gates()` to the circuit.
Note that multi-qubit gates remain untouched and single-qubit gates are not merged across any multi-qubit gates.
The gate that results from the merger of single-qubit gates will, in general, comprise an arbitrary rotation and, therefore, not be a known gate.
In OpenSquirrel an unrecognized gate is deemed _anonymous_.
When a circuit that contains anonymous gates is written to a cQASM string, the semantic representation of the anonymous gate is exported.
Note that the semantic representation of an anonymous gate is not compliant cQASM.

```python
import math

builder = CircuitBuilder(qubit_register_size=1)
for i in range(16):
builder.rx(Qubit(0), Float(math.pi / 16))
builder = CircuitBuilder(1)
for i in range(4):
builder.Rx(Qubit(0), Float(math.pi / 4))

circuit = builder.to_circuit()

# Merge single qubit gates
circuit.merge_single_qubit_gates()
circuit
```
Expand All @@ -174,15 +178,7 @@ circuit

qubit[1] q

<anonymous-gate>

You can inspect what the gate has become in terms of the Bloch sphere rotation it represents:

```python
circuit.ir.statements[0]
```

BlochSphereRotation(Qubit[0], axis=[1. 0. 0.], angle=3.141592653589795, phase=0.0)
BlochSphereRotation(Qubit[0], axis=[1. 0. 0.], angle=3.14159, phase=0.0)

In the above example, OpenSquirrel has merged all the Rx gates together. Yet, for now, OpenSquirrel does not recognize that this results in a single Rx over the cumulated angle of the individual rotations. Moreover, it does not recognize that the result corresponds to the X-gate (up to a global phase difference). At a later stage, we may want OpenSquirrel to recognize the resultant gate in the case it is part of the set of known gates.

Expand Down
14 changes: 11 additions & 3 deletions opensquirrel/ir.py
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,9 @@ def get_qubit_operands(self) -> list[Qubit]:


class Gate(Statement, ABC):

elenbaasc marked this conversation as resolved.
Show resolved Hide resolved
_significant_digits_repr = 5
rturrado marked this conversation as resolved.
Show resolved Hide resolved

def __init__(
self,
generator: Callable[..., Gate] | None = None,
Expand All @@ -265,7 +268,9 @@ def __eq__(self, other: object) -> bool:

@property
def name(self) -> str:
return self.generator.__name__ if self.generator else "<anonymous-gate>"
if self.generator:
return self.generator.__name__
return self.__repr__()

@property
def is_anonymous(self) -> bool:
Expand Down Expand Up @@ -309,7 +314,10 @@ def identity(q: Qubit) -> BlochSphereRotation:
return BlochSphereRotation(qubit=q, axis=(1, 0, 0), angle=0, phase=0)

def __repr__(self) -> str:
return f"BlochSphereRotation({self.qubit}, axis={self.axis}, angle={self.angle}, phase={self.phase})"
axis = np.round(self.axis, self._significant_digits_repr)
angle = np.round(self.angle, self._significant_digits_repr)
phase = np.round(self.phase, self._significant_digits_repr)
rturrado marked this conversation as resolved.
Show resolved Hide resolved
return f"BlochSphereRotation({self.qubit}, axis={axis}, angle={angle}, phase={phase})"

def __eq__(self, other: object) -> bool:
if not isinstance(other, BlochSphereRotation):
Expand Down Expand Up @@ -355,7 +363,7 @@ def __init__(
self.operands = operands

def __repr__(self) -> str:
return f"MatrixGate(qubits={self.operands}, matrix={self.matrix})"
return f"MatrixGate(qubits={self.operands}, matrix={np.round(self.matrix, self._significant_digits_repr)})"
juanboschero marked this conversation as resolved.
Show resolved Hide resolved

def accept(self, visitor: IRVisitor) -> Any:
visitor.visit_gate(self)
Expand Down
Empty file added test/docs/__init__.py
Empty file.
25 changes: 25 additions & 0 deletions test/docs/test_tutorial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import math

from opensquirrel import CircuitBuilder
from opensquirrel.ir import Float, Qubit


def test_anonymous_gate():

builder = CircuitBuilder(1)
for i in range(4):
builder.Rx(Qubit(0), Float(math.pi / 4))

circuit = builder.to_circuit()

circuit.merge_single_qubit_gates()

assert (
elenbaasc marked this conversation as resolved.
Show resolved Hide resolved
str(circuit)
== """version 3.0

qubit[1] q

BlochSphereRotation(Qubit[0], axis=[1. 0. 0.], angle=3.14159, phase=0.0)
"""
)
2 changes: 1 addition & 1 deletion test/writer/test_cqasm_lite_writer.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ def test_anonymous_gate() -> None:
qubit[2] q

CR(1.234) q[0], q[1]
<anonymous-gate>
BlochSphereRotation(Qubit[0], axis=[0.57735 0.57735 0.57735], angle=1.23, phase=0.0)
CR(1.234) q[0], q[1]
"""
)
Expand Down
30 changes: 19 additions & 11 deletions test/writer/test_writer.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import numpy as np

from opensquirrel import CircuitBuilder
from opensquirrel.circuit import Circuit
from opensquirrel.default_gates import CR, H
from opensquirrel.ir import IR, BlochSphereRotation, Comment, Float, Qubit
from opensquirrel.ir import IR, BlochSphereRotation, Comment, ControlledGate, Float, MatrixGate, Qubit
from opensquirrel.register_manager import QubitRegister, RegisterManager
from opensquirrel.writer import writer

Expand Down Expand Up @@ -36,21 +39,26 @@ def test_write() -> None:


def test_anonymous_gate() -> None:
register_manager = RegisterManager(QubitRegister(2))
ir = IR()
ir.add_gate(CR(Qubit(0), Qubit(1), Float(1.234)))
ir.add_gate(BlochSphereRotation(Qubit(0), axis=(1, 1, 1), angle=1.23))
ir.add_gate(CR(Qubit(0), Qubit(1), Float(1.234)))
circuit = Circuit(register_manager, ir)

qc = CircuitBuilder(2, 2)
qc.H(Qubit(0))
qc.ir.add_gate(BlochSphereRotation(Qubit(0), axis=(1, 1, 1), angle=1.23))
qc.ir.add_gate(ControlledGate(Qubit(0), BlochSphereRotation(Qubit(0), axis=(1, 1, 1), angle=1.23)))
qc.ir.add_gate(MatrixGate(np.array([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0]]), [Qubit(0), Qubit(1)]))
qc.CR(Qubit(0), Qubit(1), Float(1.234))
assert (
writer.circuit_to_string(circuit)
writer.circuit_to_string(qc.to_circuit())
elenbaasc marked this conversation as resolved.
Show resolved Hide resolved
== """version 3.0

qubit[2] q
bit[2] b

CR(1.234) q[0], q[1]
<anonymous-gate>
H q[0]
BlochSphereRotation(Qubit[0], axis=[0.57735 0.57735 0.57735], angle=1.23, phase=0.0)
ControlledGate(control_qubit=Qubit[0], BlochSphereRotation(Qubit[0], axis=[0.57735 0.57735 0.57735], angle=1.23, phase=0.0))
MatrixGate(qubits=[Qubit[0], Qubit[1]], matrix=[[1 0 0 0]
juanboschero marked this conversation as resolved.
Show resolved Hide resolved
[0 1 0 0]
[0 0 0 1]
[0 0 1 0]])
CR(1.234) q[0], q[1]
"""
)
Expand Down
Loading