Skip to content

Commit

Permalink
Merge pull request #566 from qiboteam/probsumtol
Browse files Browse the repository at this point in the history
Relax probability sum check in `UnitaryChannel`
  • Loading branch information
scarrazza authored Mar 30, 2022
2 parents 351dcc3 + d939a67 commit 0154fc7
Show file tree
Hide file tree
Showing 7 changed files with 41 additions and 8 deletions.
4 changes: 0 additions & 4 deletions src/qibo/abstractions/gates.py
Original file line number Diff line number Diff line change
Expand Up @@ -1467,10 +1467,6 @@ def __init__(self, p, ops, seed=None):
self.name = "UnitaryChannel"
self.probs = p
self.psum = sum(p)
if self.psum > 1 or self.psum <= 0:
raise_error(ValueError, "UnitaryChannel probability sum should be "
"between 0 and 1 but is {}."
"".format(self.psum))
self.seed = seed
self.density_matrix = False
self.init_args = [p, self.gates]
Expand Down
6 changes: 6 additions & 0 deletions src/qibo/backends/abstract.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ def __init__(self):
self.available_platforms = [None]

self.precision = 'double'
from qibo.config import PRECISION_TOL_DOUBLE
self.precision_tol = PRECISION_TOL_DOUBLE
self._dtypes = {'DTYPEINT': 'int64', 'DTYPE': 'float64',
'DTYPECPX': 'complex128'}

Expand Down Expand Up @@ -56,11 +58,15 @@ def dtypes(self, name):

def set_precision(self, dtype):
if dtype == 'single':
from qibo.config import PRECISION_TOL_SINGLE
self._dtypes['DTYPE'] = 'float32'
self._dtypes['DTYPECPX'] = 'complex64'
self.precision_tol = PRECISION_TOL_SINGLE
elif dtype == 'double':
from qibo.config import PRECISION_TOL_DOUBLE
self._dtypes['DTYPE'] = 'float64'
self._dtypes['DTYPECPX'] = 'complex128'
self.precision_tol = PRECISION_TOL_DOUBLE
else:
raise_error(ValueError, f'dtype {dtype} not supported.')
self.precision = dtype
Expand Down
4 changes: 4 additions & 0 deletions src/qibo/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@
# Eigenvalues smaller than this cut-off are ignored in entropy calculation
EIGVAL_CUTOFF = 1e-14

# Tolerance for the probability sum check in the unitary channel
PRECISION_TOL_SINGLE = 1e-8
PRECISION_TOL_DOUBLE = 1e-12

# Batch size for sampling shots in measurement frequencies calculation
SHOT_BATCH_SIZE = 2 ** 18

Expand Down
4 changes: 4 additions & 0 deletions src/qibo/core/gates.py
Original file line number Diff line number Diff line change
Expand Up @@ -927,6 +927,10 @@ def __init__(self, p: List[float], ops: List["Gate"],
seed: Optional[int] = None):
BackendGate.__init__(self)
abstract_gates.UnitaryChannel.__init__(self, p, ops, seed=seed)
if self.psum > 1 + K.precision_tol or self.psum <= 0:
raise_error(ValueError, "UnitaryChannel probability sum should be "
"between 0 and 1 but is {}."
"".format(self.psum))
self.set_seed()

def calculate_inverse_gates(self):
Expand Down
2 changes: 0 additions & 2 deletions src/qibo/tests/test_abstract_gates.py
Original file line number Diff line number Diff line change
Expand Up @@ -247,8 +247,6 @@ def test_unitary_channel_init():
gate = gates.UnitaryChannel(2 * [0.1], ops)
with pytest.raises(ValueError):
gate = gates.UnitaryChannel(4 * [-0.1], ops)
with pytest.raises(ValueError):
gate = gates.UnitaryChannel(4 * [0.5], ops)


def test_pauli_noise_channel_init():
Expand Down
3 changes: 3 additions & 0 deletions src/qibo/tests/test_backends_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,13 @@ def test_set_precision(backend, precision):
backends.set_precision(precision)
if precision == "single":
expected_dtype = K.backend.complex64
expected_tol = 1e-8
else:
expected_dtype = K.backend.complex128
expected_tol = 1e-12
assert backends.get_precision() == precision
assert K.dtypes('DTYPECPX') == expected_dtype
assert K.precision_tol == expected_tol
# Test that circuits use proper precision
circuit = models.Circuit(2)
circuit.add([gates.H(0), gates.H(1)])
Expand Down
26 changes: 24 additions & 2 deletions src/qibo/tests/test_core_gates.py
Original file line number Diff line number Diff line change
Expand Up @@ -423,7 +423,7 @@ def test_general_channel(backend):
initial_rho = random_density_matrix(2)
gate = gates.KrausChannel([((1,), a1), ((0, 1), a2)])
assert gate.target_qubits == (0, 1)
final_rho = gate(np.copy(initial_rho))
final_rho = gate(K.cast(np.copy(initial_rho)))
m1 = np.kron(np.eye(2), K.to_numpy(a1))
m2 = K.to_numpy(a2)
target_rho = (m1.dot(initial_rho).dot(m1.conj().T) +
Expand Down Expand Up @@ -476,6 +476,28 @@ def test_unitary_channel(backend):
K.assert_allclose(final_state, target_state)


@pytest.mark.parametrize("precision", ["double", "single"])
def test_unitary_channel_probability_tolerance(backend, precision):
"""Create ``UnitaryChannel`` with probability sum within tolerance (see #562)."""
import qibo
original_precision = qibo.get_precision()
qibo.set_precision(precision)
nqubits = 2
param = 0.006
num_terms = 2 ** (2 * nqubits)
max_param = num_terms / (num_terms - 1)
prob_identity = 1 - param / max_param
prob_pauli = param / num_terms
probs = [prob_identity] + [prob_pauli] * (num_terms - 1)
if precision == "double":
probs = np.array(probs, dtype="float64")
else:
probs = np.array(probs, dtype="float32")
matrices = len(probs) * [((0, 1), np.random.random((4, 4)))]
gate = gates.UnitaryChannel(probs, matrices)
qibo.set_precision(original_precision)


def test_unitary_channel_errors():
"""Check errors raised by ``gates.UnitaryChannel``."""
a1 = np.array([[0, 1], [1, 0]])
Expand All @@ -488,7 +510,7 @@ def test_unitary_channel_errors():
# Probability > 1
with pytest.raises(ValueError):
gate = gates.UnitaryChannel([1.1, 0.2], matrices)
# Probability sum = 0
# Probability sum < 0
with pytest.raises(ValueError):
gate = gates.UnitaryChannel([0.0, 0.0], matrices)

Expand Down

0 comments on commit 0154fc7

Please sign in to comment.