diff --git a/unitary/alpha/quantum_world.py b/unitary/alpha/quantum_world.py index d1b34c70..5cd70617 100644 --- a/unitary/alpha/quantum_world.py +++ b/unitary/alpha/quantum_world.py @@ -19,6 +19,8 @@ from unitary.alpha.quantum_object import QuantumObject from unitary.alpha.sparse_vector_simulator import PostSelectOperation, SparseSimulator from unitary.alpha.qudit_state_transform import qudit_to_qubit_unitary, num_bits +import numpy as np +import itertools class QuantumWorld: @@ -484,6 +486,36 @@ def get_histogram( histogram[idx][cast(int, result[idx])] += 1 return histogram + def get_correlated_histogram( + self, objects: Optional[Sequence[QuantumObject]] = None, count: int = 100 + ) -> Dict[Tuple[int], int]: + """Creates histogram of the whole quantum world (or `objects` if specified) + based on measurements (peeks) carried out. Comparing to get_histogram(), + this statistics contains entanglement information accross quantum objects. + + Parameters: + objects: List of QuantumObjects + count: Number of measurements + + Returns: + A dictionary, with the keys being tuples representing each possible state of + the whole quantum world (or, if `objects` is specified, the key is a tuple of + the results of each object in `objects` and in the order of `objects`), and + the values being the count of that state. + """ + if not objects: + objects = self.public_objects + peek_results = self.peek(objects=objects, convert_to_enum=False, count=count) + histogram = {} + for result in peek_results: + # Convert the list to tuple so that it could be the key of a dictionary. + key = tuple(result) + if key not in histogram: + histogram[key] = 1 + else: + histogram[key] += 1 + return histogram + def get_probabilities( self, objects: Optional[Sequence[QuantumObject]] = None, count: int = 100 ) -> List[Dict[int, float]]: diff --git a/unitary/alpha/quantum_world_test.py b/unitary/alpha/quantum_world_test.py index 75dc9b56..30b02941 100644 --- a/unitary/alpha/quantum_world_test.py +++ b/unitary/alpha/quantum_world_test.py @@ -656,6 +656,8 @@ def test_get_histogram_and_get_probabilities_one_binary_qobject( ) histogram = world.get_histogram() assert histogram == [{0: 0, 1: 100}] + histogram = world.get_correlated_histogram() + assert histogram == {(1,): 100} probs = world.get_probabilities() assert probs == [{0: 0.0, 1: 1.0}] bin_probs = world.get_binary_probabilities() @@ -663,6 +665,8 @@ def test_get_histogram_and_get_probabilities_one_binary_qobject( alpha.Flip()(l1) histogram = world.get_histogram() assert histogram == [{0: 100, 1: 0}] + histogram = world.get_correlated_histogram() + assert histogram == {(0,): 100} probs = world.get_probabilities() assert probs == [{0: 1.0, 1: 0.0}] bin_probs = world.get_binary_probabilities() @@ -673,6 +677,10 @@ def test_get_histogram_and_get_probabilities_one_binary_qobject( assert len(histogram[0]) == 2 assert histogram[0][0] > 10 assert histogram[0][1] > 10 + histogram = world.get_correlated_histogram() + assert len(histogram) == 2 + assert histogram[(0,)] > 10 + assert histogram[(1,)] > 10 probs = world.get_probabilities() assert len(probs) == 1 assert len(probs[0]) == 2 @@ -700,6 +708,8 @@ def test_get_histogram_and_get_probabilities_one_trinary_qobject( ) histogram = world.get_histogram() assert histogram == [{0: 0, 1: 100, 2: 0}] + histogram = world.get_correlated_histogram() + assert histogram == {(1,): 100} probs = world.get_probabilities() assert probs == [{0: 0.0, 1: 1.0, 2: 0.0}] bin_probs = world.get_binary_probabilities() @@ -723,13 +733,45 @@ def test_get_histogram_and_get_probabilities_two_qobjects(simulator, compile_to_ ) histogram = world.get_histogram() assert histogram == [{0: 0, 1: 100}, {0: 0, 1: 100, 2: 0}] + histogram = world.get_correlated_histogram() + assert histogram == {(1, 1): 100} probs = world.get_probabilities() assert probs == [{0: 0.0, 1: 1.0}, {0: 0.0, 1: 1.0, 2: 0.0}] bin_probs = world.get_binary_probabilities() assert bin_probs == [1.0, 1.0] histogram = world.get_histogram(objects=[l2], count=1000) assert histogram == [{0: 0, 1: 1000, 2: 0}] + histogram = world.get_correlated_histogram(objects=[l2], count=1000) + assert histogram == {(1,): 1000} probs = world.get_probabilities(objects=[l2], count=1000) assert probs == [{0: 0.0, 1: 1.0, 2: 0.0}] bin_probs = world.get_binary_probabilities(objects=[l2], count=1000) assert bin_probs == [1.0] + + +@pytest.mark.parametrize( + ("simulator", "compile_to_qubits"), + [ + (cirq.Simulator, False), + (cirq.Simulator, True), + # Cannot use SparseSimulator without `compile_to_qubits` due to issue #78. + (alpha.SparseSimulator, True), + ], +) +def test_get_correlated_histogram_with_entangled_qobjects(simulator, compile_to_qubits): + light1 = alpha.QuantumObject("l1", Light.GREEN) + light2 = alpha.QuantumObject("l2", Light.RED) + light3 = alpha.QuantumObject("l3", Light.RED) + light4 = alpha.QuantumObject("l4", Light.GREEN) + light5 = alpha.QuantumObject("l5", Light.RED) + + world = alpha.QuantumWorld( + [light1, light2, light3, light4, light5], + sampler=simulator(), + compile_to_qubits=compile_to_qubits, + ) + alpha.Split()(light1, light2, light3) + alpha.quantum_if(light2).equals(1).apply(alpha.Move())(light4, light5) + + histogram = world.get_correlated_histogram() + assert histogram.keys() == {(0, 0, 1, 1, 0), (0, 1, 0, 0, 1)}