Skip to content

Commit

Permalink
Merge branch 'master' into restrict_topology
Browse files Browse the repository at this point in the history
  • Loading branch information
renatomello committed Jan 10, 2024
2 parents 12c31cd + ea72695 commit 38aa9bd
Show file tree
Hide file tree
Showing 10 changed files with 1,026 additions and 162 deletions.
4 changes: 4 additions & 0 deletions doc/source/api-reference/qibo.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1420,6 +1420,10 @@ passing a symplectic matrix to the constructor.
symplectic_matrix = backend.zero_state(nqubits=3)
clifford = Clifford(symplectic_matrix, engine=NumpyBackend())
# The initialization above is equivalent to the initialization below
circuit = Circuit(nqubits=3)
clifford = Clifford(circuit, engine=NumpyBackend())

The generators of the stabilizers can be extracted with the
:meth:`qibo.quantum_info.clifford.Clifford.generators` method,
or the complete set of :math:`d = 2^{n}` stabilizers operators can be extracted through the
Expand Down
4 changes: 3 additions & 1 deletion src/qibo/backends/clifford.py
Original file line number Diff line number Diff line change
Expand Up @@ -627,13 +627,15 @@ def execute_circuit(self, circuit, initial_state=None, nshots: int = 1000):
for gate in circuit.queue:
state = gate.apply_clifford(self, state, nqubits)

return Clifford(
clifford = Clifford(
state,
measurements=circuit.measurements,
nshots=nshots,
engine=self.engine,
)

return clifford

except self.oom_error: # pragma: no cover
raise_error(
RuntimeError,
Expand Down
2 changes: 1 addition & 1 deletion src/qibo/hamiltonians/hamiltonians.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ def expectation_from_samples(self, freq, qubit_map=None):
for i in qubit_map:
index += int(k[qubit_map.index(i)]) * 2 ** (size - 1 - i)
expval += obs[index, index] * counts[j]
return expval
return np.real(expval)

def eye(self, dim: Optional[int] = None):
"""Generate Identity matrix with dimension ``dim``"""
Expand Down
83 changes: 73 additions & 10 deletions src/qibo/models/circuit.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import collections
import copy
from typing import Dict, List, Tuple, Union
from typing import Dict, List, Optional, Tuple, Union

import numpy as np

Expand Down Expand Up @@ -119,7 +119,8 @@ class Circuit:
- *nqubits*
- *accelerators*
- *density_matrix*.
- *density_matrix*
- *wire_names*.
queue (_Queue): List that holds the queue of gates of a circuit.
parametrized_gates (_ParametrizedGates): List of parametric gates.
Expand All @@ -129,18 +130,30 @@ class Circuit:
compiled (CompiledExecutor): Circuit executor. Defaults to ``None``.
repeated_execution (bool): If `True`, the circuit would be re-executed when sampling.
Defaults to ``False``.
density_matrix (bool): If `True`, the circuit would evolve density matrices.
density_matrix (bool, optional): If `True`, the circuit would evolve density matrices.
If ``False``, defaults to statevector simulation.
Defaults to ``False``.
accelerators (dict): Dictionary that maps device names to the number of times each
accelerators (dict, optional): Dictionary that maps device names to the number of times each
device will be used. Defaults to ``None``.
wire_names (list or dict, optional): Names for qubit wires.
If ``None``, defaults to (``q0``, ``q1``... ``qn``).
If ``list`` is passed, length of ``list`` must match ``nqubits``.
If ``dict`` is passed, the keys should match the default pattern.
Defaults to ``None``.
ndevices (int): Total number of devices. Defaults to ``None``.
nglobal (int): Base two logarithm of the number of devices. Defaults to ``None``.
nlocal (int): Total number of available qubits in each device. Defaults to ``None``.
queues (DistributedQueues): Gate queues for each accelerator device.
Defaults to ``None``.
"""

def __init__(self, nqubits, accelerators=None, density_matrix=False):
def __init__(
self,
nqubits: int,
accelerators=None,
density_matrix: bool = False,
wire_names: Optional[Union[list, dict]] = None,
):
if not isinstance(nqubits, int):
raise_error(
TypeError,
Expand All @@ -152,10 +165,12 @@ def __init__(self, nqubits, accelerators=None, density_matrix=False):
f"Number of qubits must be positive but is {nqubits}.",
)
self.nqubits = nqubits
self.wire_names = wire_names
self.init_kwargs = {
"nqubits": nqubits,
"accelerators": accelerators,
"density_matrix": density_matrix,
"wire_names": wire_names,
}
self.queue = _Queue(nqubits)
# Keep track of parametrized gates for the ``set_parameters`` method
Expand Down Expand Up @@ -259,6 +274,52 @@ def __add__(self, circuit):

return newcircuit

@property
def wire_names(self):
return self._wire_names

@wire_names.setter
def wire_names(self, wire_names: Union[list, dict]):
if not isinstance(wire_names, (list, dict, type(None))):
raise_error(
TypeError,
f"``wire_names`` must be type ``list`` or ``dict``, but is {type(wire_names)}.",
)

if isinstance(wire_names, list):
if len(wire_names) != self.nqubits:
raise_error(
ValueError,
"Number of wire names must be equal to the number of qubits, "
f"but is {len(wire_names)}.",
)

if any([not isinstance(name, str) for name in wire_names]):
raise_error(ValueError, "all wire names must be type ``str``.")

self._wire_names = wire_names
elif isinstance(wire_names, dict):
if len(wire_names.keys()) > self.nqubits:
raise_error(
ValueError,
"number of elements in the ``wire_names`` dictionary "
+ "cannot be bigger than ``nqubits``.",
)

if any([not isinstance(name, str) for name in wire_names.keys()]) or any(
[not isinstance(name, str) for name in wire_names.values()]
):
raise_error(
ValueError,
"all keys and values in the ``wire_names`` dictionary must be type ``str``.",
)

self._wire_names = [
wire_names.get(f"q{i}", f"q{i}") for i in range(self.nqubits)
]
else:
self._wire_names = [f"q{i}" for i in range(self.nqubits)]

@property
def repeated_execution(self):
return self.has_collapse or (
Expand Down Expand Up @@ -340,6 +401,7 @@ def light_cone(self, *qubits):
kwargs = dict(self.init_kwargs)
kwargs["nqubits"] = len(qubits)
circuit = self.__class__(**kwargs)
circuit.wire_names = [self.wire_names[q] for q in list(sorted(qubits))]
circuit.add(gate.on_qubits(qubit_map) for gate in reversed(list_of_gates))
return circuit, qubit_map

Expand Down Expand Up @@ -1416,11 +1478,12 @@ def draw(self, line_wrap=70, legend=False) -> str:
matrix[row][col] += "─" * (1 + maxlen - len(matrix[row][col]))

# Print to terminal
max_name_len = max(len(name) for name in self.wire_names)
output = ""
for q in range(self.nqubits):
output += (
f"q{q}"
+ " " * (len(str(self.nqubits)) - len(str(q)))
self.wire_names[q]
+ " " * (max_name_len - len(self.wire_names[q]))
+ ": ─"
+ "".join(matrix[q])
+ "\n"
Expand Down Expand Up @@ -1453,7 +1516,7 @@ def chunkstring(string, length):

for row in range(self.nqubits):
chunks, nchunks = chunkstring(
loutput[row][3 + len(str(self.nqubits)) :], line_wrap
loutput[row][3 + max_name_len - 1 :], line_wrap
)
if nchunks == 1:
loutput = None
Expand All @@ -1462,8 +1525,8 @@ def chunkstring(string, length):
loutput += ["" for _ in range(self.nqubits)]
suffix = " ...\n"
prefix = (
f"q{row}"
+ " " * (len(str(self.nqubits)) - len(str(row)))
self.wire_names[row]
+ " " * (max_name_len - len(self.wire_names[row]))
+ ": "
)
if i == 0:
Expand Down
Loading

0 comments on commit 38aa9bd

Please sign in to comment.