def test_post_measurement_bitflips_on_circuit_result(backend): """Check bitflip errors on ``CircuitResult`` objects.""" original_backend = qibo.get_backend() qibo.set_backend(backend) thetas = np.random.random(4) c = models.Circuit(4) c.add((gates.RX(i, theta=t) for i, t in enumerate(thetas))) c.add(gates.M(0, 1, register_name="a")) c.add(gates.M(3, register_name="b")) result = c(nshots=30) samples = result.samples() K.set_seed(123) noisy_result = result.copy().apply_bitflips({0: 0.2, 1: 0.4, 3: 0.3}) noisy_samples = noisy_result.samples(binary=True) register_samples = noisy_result.samples(binary=True, registers=True) K.set_seed(123) sprobs = np.array(K.random_uniform(samples.shape)) flipper = sprobs < np.array([0.2, 0.4, 0.3]) target_samples = (samples + flipper) % 2 np.testing.assert_allclose(noisy_samples, target_samples) # Check register samples np.testing.assert_allclose(register_samples["a"], target_samples[:, :2]) np.testing.assert_allclose(register_samples["b"], target_samples[:, 2:]) qibo.set_backend(original_backend)
def test_measurementresult_apply_bitflips_random_samples(backend, probs): qubits = tuple(range(4)) samples = np.random.randint(0, 2, (20, 4)) result = measurements.MeasurementResult(qubits) result.binary = K.cast(np.copy(samples)) K.set_seed(123) noisy_result = result.apply_bitflips(probs) K.set_seed(123) if isinstance(probs, dict): probs = np.array([probs[q] for q in qubits]) sprobs = K.to_numpy(K.random_uniform(samples.shape)) target_samples = (samples + (sprobs < probs)) % 2 K.assert_allclose(noisy_result.samples(), target_samples)
def test_measurementresult_apply_bitflips_random_samples_asymmetric(backend): qubits = tuple(range(4)) samples = np.random.randint(0, 2, (20, 4)) result = measurements.MeasurementResult(qubits) result.binary = K.cast(np.copy(samples)) p1_map = {0: 0.2, 1: 0.0, 2: 0.0, 3: 0.1} K.set_seed(123) noisy_result = result.apply_bitflips(p0=0.2, p1=p1_map) p0 = 0.2 * np.ones(4) p1 = np.array([0.2, 0.0, 0.0, 0.1]) K.set_seed(123) sprobs = K.to_numpy(K.random_uniform(samples.shape)) target_samples = np.copy(samples).ravel() ids = (np.where(target_samples == 0)[0], np.where(target_samples == 1)[0]) target_samples[ids[0]] = samples.ravel()[ids[0]] + (sprobs < p0).ravel()[ids[0]] target_samples[ids[1]] = samples.ravel()[ids[1]] - (sprobs < p1).ravel()[ids[1]] target_samples = target_samples.reshape(samples.shape) K.assert_allclose(noisy_result.samples(), target_samples)
def apply_bitflips(self, p0: ProbsType, p1: Optional[ProbsType] = None): """Applies bitflip noise to the measured samples. Args: p0: Bitflip probability map. Can be: A dictionary that maps each measured qubit to the probability that it is flipped, a list or tuple that has the same length as the tuple of measured qubits or a single float number. If a single float is given the same probability will be used for all qubits. p1: Probability of asymmetric bitflip. If ``p1`` is given, ``p0`` will be used as the probability for 0->1 and ``p1`` as the probability for 1->0. If ``p1`` is ``None`` the same probability ``p0`` will be used for both bitflips. Returns: A new :class:`qibo.core.measurements.MeasurementResult` object that holds the noisy samples. """ if p1 is None: probs = 2 * (M._get_bitflip_tuple(self.qubits, p0),) else: probs = (M._get_bitflip_tuple(self.qubits, p0), M._get_bitflip_tuple(self.qubits, p1)) # Calculate noisy samples noiseless_samples = self.samples() fprobs = K.cast(probs, dtype='DTYPE') sprobs = K.random_uniform(noiseless_samples.shape) flip0 = K.cast(sprobs < fprobs[0], dtype=noiseless_samples.dtype) flip1 = K.cast(sprobs < fprobs[1], dtype=noiseless_samples.dtype) noisy_samples = noiseless_samples + (1 - noiseless_samples) * flip0 noisy_samples = noisy_samples - noiseless_samples * flip1 noisy_result = self.__class__(self.qubits) noisy_result.binary = noisy_samples return noisy_result