def test_amplitudes_and_probs_output_type(self, wf: Wavefunction): if len(wf.free_symbols) > 0: assert wf.amplitudes.dtype == object assert wf.probabilities().dtype == object else: assert wf.amplitudes.dtype == np.complex128 assert wf.probabilities().dtype == np.float64
def test_eq_returns_true_for_objects_with_equal_wavefunctions( self, symbolic_wf: Wavefunction, numeric_wf: Wavefunction): test_wf = Wavefunction(symbolic_wf._amplitude_vector) assert symbolic_wf == test_wf test_wf = Wavefunction(numeric_wf._amplitude_vector) np.testing.assert_array_equal(numeric_wf, test_wf)
def test_string_output_of_numeric_wavefunction(self): wf = Wavefunction([1j, 0]) wf_str = wf.__str__() assert "j" in wf_str assert wf_str.endswith("])") assert wf_str.startswith("Wavefunction([")
def test_string_output_of_symbolic_wavefunction(self): wf = Wavefunction([Symbol("alpha"), 0]) wf_str = wf.__str__() assert "alpha" in wf_str assert wf_str.endswith("])") assert wf_str.startswith("Wavefunction([")
def test_get_outcome_probs(self, wf_vec): wf = Wavefunction(wf_vec) probs_dict = wf.get_outcome_probs() assert all([len(key) == wf.n_qubits for key in probs_dict.keys()]) for key in probs_dict.keys(): assert len(key) == wf.n_qubits assert wf.probabilities()[int(key, 2)] == probs_dict[key]
class TestRepresentations: def test_string_output_of_symbolic_wavefunction(self): wf = Wavefunction([Symbol("alpha"), 0]) wf_str = wf.__str__() assert "alpha" in wf_str assert wf_str.endswith("])") assert wf_str.startswith("Wavefunction([") def test_string_output_of_numeric_wavefunction(self): wf = Wavefunction([1j, 0]) wf_str = wf.__str__() assert "j" in wf_str assert wf_str.endswith("])") assert wf_str.startswith("Wavefunction([") @pytest.mark.parametrize( "wf", [Wavefunction.init_system(2), Wavefunction([Symbol("alpha"), 0.0])]) def test_amplitudes_and_probs_output_type(self, wf: Wavefunction): if len(wf.free_symbols) > 0: assert wf.amplitudes.dtype == object assert wf.probabilities().dtype == object else: assert wf.amplitudes.dtype == np.complex128 assert wf.probabilities().dtype == np.float64 @pytest.mark.parametrize( "wf_vec", [ [1.0, 0.0], [0.5, 0.5, 0.5, 0.5], [1 / sqrt(2), 0, 0, 0, 0, 0, 0, 1 / sqrt(2)], ], ) def test_get_outcome_probs(self, wf_vec): wf = Wavefunction(wf_vec) probs_dict = wf.get_outcome_probs() assert all([len(key) == wf.n_qubits for key in probs_dict.keys()]) for key in probs_dict.keys(): assert len(key) == wf.n_qubits assert wf.probabilities()[int(key, 2)] == probs_dict[key]
def test_binding_all_symbols_returns_numpy_array( self, symbolic_wf: Wavefunction): assert isinstance( symbolic_wf.bind({ "alpha": 0.5, "beta": 0.5 })._amplitude_vector, np.ndarray)
def sample_from_wavefunction( wavefunction: Wavefunction, n_samples: int, seed: Optional[int] = None, ) -> List[Tuple[int, ...]]: """Sample bitstrings from a wavefunction. Args: wavefunction: the wavefunction to sample from. n_samples: the number of samples taken. Needs to be greater than 0. seed: the seed of the sampler Returns: List[Tuple[int]]: A list of tuples where the each tuple is a sampled bitstring. """ assert isinstance(n_samples, int) and n_samples > 0 rng = np.random.default_rng(seed) outcomes_str, probabilities_np = zip( *wavefunction.get_outcome_probs().items()) probabilities = [ x[0] if isinstance(x, (list, np.ndarray)) else x for x in list(probabilities_np) ] samples_ndarray = rng.choice(a=outcomes_str, size=n_samples, p=probabilities) samples = [ tuple(int(y) for y in list(x)[::-1]) for x in list(samples_ndarray) ] return samples
def test_sample_from_wavefunction_fails_for_invalid_n_samples(n_samples): n_qubits = 4 amplitudes = [0] * (2**n_qubits) amplitudes[1] = 1 wavefunction = Wavefunction(amplitudes) with pytest.raises(AssertionError): sample_from_wavefunction(wavefunction, n_samples)
def get_wavefunction( self, circuit: Circuit, initial_state: Optional[StateVector] = None) -> Wavefunction: """Returns a wavefunction representing quantum state produced by a circuit Args: circuit: quantum circuit to be executed. initial_state: a state from which the simulation starts. If not provided, the default |0...0> is used. """ if initial_state is None: state = np.zeros(2**circuit.n_qubits) state[0] = 1 else: state = initial_state for is_supported, subcircuit in split_circuit( circuit, self.is_natively_supported): # Native subcircuits are passed through to the underlying simulator. # They also count towards number of circuits and number of jobs run. if is_supported: self.number_of_circuits_run += 1 self.number_of_jobs_run += 1 state = self._get_wavefunction_from_native_circuit( subcircuit, state) else: for operation in subcircuit.operations: state = operation.apply(state) return Wavefunction(state)
class TestGates: @pytest.fixture def simulator(self) -> SymbolicSimulator: return SymbolicSimulator() @pytest.mark.parametrize( "circuit, expected_wavefunction", [ ( Circuit([RX(Symbol("theta"))(0)]), Wavefunction([ 1.0 * cos(Symbol("theta") / 2), -1j * sin(Symbol("theta") / 2) ]), ), ( Circuit([X(0), RY(Symbol("theta"))(0)]), Wavefunction([ -1.0 * sin(Symbol("theta") / 2), 1.0 * cos(Symbol("theta") / 2), ]), ), ( Circuit([ H(0), U3(Symbol("theta"), Symbol("phi"), Symbol("lambda"))(0) ]), Wavefunction([ cos(Symbol("theta") / 2) / sqrt(2) + -exp(I * Symbol("lambda")) * sin(Symbol("theta") / 2) / sqrt(2), exp(I * Symbol("phi")) * sin(Symbol("theta") / 2) / sqrt(2) + exp(I * (Symbol("lambda") + Symbol("phi"))) * cos(Symbol("theta") / 2) / sqrt(2), ]), ), ], ) def test_wavefunction_works_as_expected_with_symbolic_circuits( self, simulator: SymbolicSimulator, circuit: Circuit, expected_wavefunction: Wavefunction, ): returned_wavefunction = simulator.get_wavefunction(circuit) assert returned_wavefunction == expected_wavefunction
def _get_wavefunction_from_native_circuit(self, circuit: Circuit, initial_state) -> Wavefunction: state = initial_state for operation in circuit.operations: state = operation.apply(state) return Wavefunction(state)
def test_sample_from_wavefunction_column_vector(): n_qubits = 4 expected_bitstring = (0, 0, 0, 1) amplitudes = np.array([0] * (2**n_qubits)).reshape(2**n_qubits, 1) amplitudes[1] = 1 # |0001> will be measured in all cases. wavefunction = Wavefunction(amplitudes) sample = set(sample_from_wavefunction(wavefunction, 500)) assert len(sample) == 1 assert sample.pop() == expected_bitstring
def test_sample_from_wavefunction_list(): n_qubits = 4 expected_bitstring = (0, 0, 0, 1) amplitudes = [0] * (2**n_qubits) amplitudes[1] = 1 # |0001> will be measured in all cases. wavefunction = Wavefunction(amplitudes) sample = set(sample_from_wavefunction(wavefunction, 500)) assert len(sample) == 1 assert sample.pop() == expected_bitstring
def create_random_wavefunction(n_qubits, seed=None): if seed: np.random.seed(seed) random_vector = [ complex(a, b) for a, b in zip(np.random.rand(2**n_qubits), np.random.rand(2**n_qubits)) ] normalization_factor = np.sqrt(np.sum(np.abs(random_vector)**2)) random_vector /= normalization_factor return Wavefunction(random_vector)
def test_get_expectation_value(self): """Check <Z0> and <Z1> for the state |100>""" # Given wf = Wavefunction([0, 1, 0, 0, 0, 0, 0, 0]) op1 = QubitOperator("Z0") op2 = QubitOperator("Z1") # When exp_op1 = get_expectation_value(op1, wf, True) exp_op2 = get_expectation_value(op2, wf, True) # Then self.assertAlmostEqual(-1, exp_op1) self.assertAlmostEqual(1, exp_op2)
def test_init_system_returns_expected_wavefunction_size(self, n_qubits): wavefunction = Wavefunction.init_system(n_qubits=n_qubits) # Check length assert len(wavefunction) == 2**n_qubits # Check internal property assert wavefunction.n_qubits == n_qubits # Check amplitude of zero state assert wavefunction[0] == 1.0 # Check amplitude of the rest of the states assert not np.any(wavefunction[1:])
def load_wavefunction(file: LoadSource) -> Wavefunction: """Load a qubit wavefunction from a file. Args: file (str or file-like object): the name of the file, or a file-like object. Returns: wavefunction (zquantum.core.Wavefunction): the wavefunction object """ if isinstance(file, str): with open(file, "r") as f: data = json.load(f) else: data = json.load(file) wavefunction = Wavefunction(convert_dict_to_array(data["amplitudes"])) return wavefunction
def jw_get_ground_state_at_particle_number( particle_number: int, qubit_operator: Union[str, SymbolicOperator]): """Get the ground state wavefunction of the operator for the input particle number. Outputs are serialized to JSON within the files: "ground-state.json" and "value-estimate.json". Args: particle_number: The given number of particles in the system qubit_operator: The operator for which to find the ground state """ if isinstance(qubit_operator, str): qubit_operator = load_qubit_operator(qubit_operator) sparse_matrix = qubit_operator_sparse(qubit_operator) ground_energy, ground_state_amplitudes = _jw_get_ground_state_at_particle_number( sparse_matrix, particle_number) ground_state = Wavefunction(ground_state_amplitudes) value_estimate = ValueEstimate(ground_energy) save_wavefunction(ground_state, "ground-state.json") save_value_estimate(value_estimate, "value-estimate.json")
def test_imag_wavefunction_io(): wf = Wavefunction([0, 1j, 0, 0, 0, 0, 0, 0]) save_wavefunction(wf, "wavefunction.json") loaded_wf = load_wavefunction("wavefunction.json") assert np.allclose(wf.amplitudes, loaded_wf.amplitudes) remove_file_if_exists("wavefunction.json")
def test_init_system_returns_numpy_array(self): wf = Wavefunction.init_system(2) assert isinstance(wf._amplitude_vector, np.ndarray)
def test_constructor_returns_sympy_matrix_for_free_symbols(self): wf = Wavefunction([0.25, 0, Symbol("alpha"), 0]) assert isinstance(wf._amplitude_vector, Matrix)
def symbolic_wf(self) -> Wavefunction: return Wavefunction([Symbol("alpha"), 0.5, Symbol("beta"), 0.5])
def test_init_system_fails_on_invalid_params(self, n_qubits): with pytest.raises(ValueError): Wavefunction.init_system(n_qubits=n_qubits)
def test_init_system_raises_warning_for_non_ints(self): with pytest.warns(UserWarning): Wavefunction.init_system(1.234)
def test_init_fails_when_passed_list_has_free_symbols_and_exceeds_unity( self, input_list): with pytest.raises(ValueError): Wavefunction(input_list)
def test_flipped_wavefunction_comprises_expected_amplitudes( input_amplitudes, expected_output_amplitudes): np.testing.assert_array_equal( flip_wavefunction(Wavefunction(input_amplitudes)).amplitudes, expected_output_amplitudes, )
def numeric_wf(self) -> Wavefunction: return Wavefunction([0.5, 0.5, 0.5, 0.5])
def test_init_fails_when_len_of_passed_list_is_not_power_of_two( self, input_list): with pytest.raises(ValueError): Wavefunction(input_list)
def test_constructor_returns_numpy_array_for_no_symbols(self): wf = Wavefunction([1.0, 0, 0, 0]) assert isinstance(wf._amplitude_vector, np.ndarray)