def test_evaluate_with_aer_mode(self): """ evaluate with aer mode test """ try: # pylint: disable=import-outside-toplevel from qiskit import Aer except Exception as ex: # pylint: disable=broad-except self.skipTest( "Aer doesn't appear to be installed. Error: '{}'".format( str(ex))) return statevector_simulator = Aer.get_backend('statevector_simulator') quantum_instance_statevector = QuantumInstance(statevector_simulator, shots=1) wave_function = self.var_form.assign_parameters( np.array( aqua_globals.random.standard_normal( self.var_form.num_parameters))) circuits = self.qubit_op.construct_evaluation_circuit( wave_function=wave_function, statevector_mode=True) reference = self.qubit_op.evaluate_with_result( result=quantum_instance_statevector.execute(circuits), statevector_mode=True) circuits = self.qubit_op.construct_evaluation_circuit( wave_function=wave_function, statevector_mode=True, use_simulator_snapshot_mode=True) actual_value = self.qubit_op.evaluate_with_result( result=quantum_instance_statevector.execute(circuits), statevector_mode=True, use_simulator_snapshot_mode=True) self.assertAlmostEqual(reference[0], actual_value[0], places=10)
def test_callibration_results(self): """check that results counts are the same with/without error mitigation""" algorithm_globals.random_seed = 1679 np.random.seed(algorithm_globals.random_seed) qc = QuantumCircuit(1) qc.x(0) qc_meas = qc.copy() qc_meas.measure_all() backend = Aer.get_backend("aer_simulator") counts_array = [None, None] for idx, is_use_mitigation in enumerate([True, False]): if is_use_mitigation: quantum_instance = QuantumInstance( backend, seed_simulator=algorithm_globals.random_seed, seed_transpiler=algorithm_globals.random_seed, shots=1024, measurement_error_mitigation_cls=CompleteMeasFitter_IG, ) with self.assertWarnsRegex(DeprecationWarning, r".*ignis.*"): counts_array[idx] = quantum_instance.execute(qc_meas).get_counts() else: quantum_instance = QuantumInstance( backend, seed_simulator=algorithm_globals.random_seed, seed_transpiler=algorithm_globals.random_seed, shots=1024, ) counts_array[idx] = quantum_instance.execute(qc_meas).get_counts() self.assertEqual( counts_array[0], counts_array[1], msg="Counts different with/without fitter." )
def test_w_noise(self): """ with noise test """ # build noise model # Asymmetric readout error on qubit-0 only try: from qiskit.providers.aer.noise import NoiseModel from qiskit import Aer self.backend = Aer.get_backend('qasm_simulator') except ImportError as ex: self.skipTest("Aer doesn't appear to be installed. Error: '{}'".format(str(ex))) return probs_given0 = [0.9, 0.1] probs_given1 = [0.3, 0.7] noise_model = NoiseModel() noise_model.add_readout_error([probs_given0, probs_given1], [0]) quantum_instance = QuantumInstance(self.backend, seed_transpiler=self.random_seed, seed_simulator=self.random_seed, shots=1024, noise_model=noise_model) res_w_noise = quantum_instance.execute(self.qc).get_counts(self.qc) quantum_instance.skip_qobj_validation = True res_w_noise_skip_validation = quantum_instance.execute(self.qc).get_counts(self.qc) self.assertTrue(_compare_dict(res_w_noise, res_w_noise_skip_validation)) # BasicAer should fail: with self.assertRaises(QiskitError): _ = QuantumInstance(BasicAer.get_backend('qasm_simulator'), noise_model=noise_model) with self.assertRaises(QiskitError): quantum_instance = QuantumInstance(BasicAer.get_backend('qasm_simulator')) quantum_instance.set_config(noise_model=noise_model)
def test_measurement_error_mitigation_with_diff_qubit_order_ignis( self, fitter_str): """measurement error mitigation with different qubit order""" algorithm_globals.random_seed = 0 # build noise model noise_model = noise.NoiseModel() read_err = noise.errors.readout_error.ReadoutError([[0.9, 0.1], [0.25, 0.75]]) noise_model.add_all_qubit_readout_error(read_err) fitter_cls = (CompleteMeasFitter_IG if fitter_str == "CompleteMeasFitter" else TensoredMeasFitter_IG) backend = Aer.get_backend("aer_simulator") quantum_instance = QuantumInstance( backend=backend, seed_simulator=1679, seed_transpiler=167, shots=1000, noise_model=noise_model, measurement_error_mitigation_cls=fitter_cls, cals_matrix_refresh_period=0, ) # circuit qc1 = QuantumCircuit(2, 2) qc1.h(0) qc1.cx(0, 1) qc1.measure(0, 0) qc1.measure(1, 1) qc2 = QuantumCircuit(2, 2) qc2.h(0) qc2.cx(0, 1) qc2.measure(1, 0) qc2.measure(0, 1) if fitter_cls == TensoredMeasFitter_IG: with self.assertWarnsRegex(DeprecationWarning, r".*ignis.*"): self.assertRaisesRegex( QiskitError, "TensoredMeasFitter doesn't support subset_fitter.", quantum_instance.execute, [qc1, qc2], ) else: # this should run smoothly with self.assertWarnsRegex(DeprecationWarning, r".*ignis.*"): quantum_instance.execute([qc1, qc2]) self.assertGreater(quantum_instance.time_taken, 0.0) quantum_instance.reset_execution_results() # failure case qc3 = QuantumCircuit(3, 3) qc3.h(2) qc3.cx(1, 2) qc3.measure(2, 1) qc3.measure(1, 2) self.assertRaises(QiskitError, quantum_instance.execute, [qc1, qc3])
def test_measurement_error_mitigation_with_diff_qubit_order(self): """measurement error mitigation with different qubit order""" try: from qiskit.ignis.mitigation.measurement import CompleteMeasFitter from qiskit import Aer from qiskit.providers.aer import noise except ImportError as ex: self.skipTest( "Package doesn't appear to be installed. Error: '{}'".format( str(ex))) return algorithm_globals.random_seed = 0 # build noise model noise_model = noise.NoiseModel() read_err = noise.errors.readout_error.ReadoutError([[0.9, 0.1], [0.25, 0.75]]) noise_model.add_all_qubit_readout_error(read_err) backend = Aer.get_backend("aer_simulator") quantum_instance = QuantumInstance( backend=backend, seed_simulator=1679, seed_transpiler=167, shots=1000, noise_model=noise_model, measurement_error_mitigation_cls=CompleteMeasFitter, cals_matrix_refresh_period=0, ) # circuit qc1 = QuantumCircuit(2, 2) qc1.h(0) qc1.cx(0, 1) qc1.measure(0, 0) qc1.measure(1, 1) qc2 = QuantumCircuit(2, 2) qc2.h(0) qc2.cx(0, 1) qc2.measure(1, 0) qc2.measure(0, 1) # this should run smoothly quantum_instance.execute([qc1, qc2]) self.assertGreater(quantum_instance.time_taken, 0.0) quantum_instance.reset_execution_results() # failure case qc3 = QuantumCircuit(3, 3) qc3.h(2) qc3.cx(1, 2) qc3.measure(2, 1) qc3.measure(1, 2) self.assertRaises(QiskitError, quantum_instance.execute, [qc1, qc3])
def test_w_backend_options(self): """ with backend options test """ # run with backend_options quantum_instance = QuantumInstance(self.backend, seed_transpiler=self.random_seed, seed_simulator=self.random_seed, shots=1024, backend_options={ 'initial_statevector': [.5, .5, .5, .5]}) res_w_bo = quantum_instance.execute(self.qc).get_counts(self.qc) self.assertGreaterEqual(quantum_instance.time_taken, 0.) quantum_instance.reset_execution_results() quantum_instance.skip_qobj_validation = True res_w_bo_skip_validation = quantum_instance.execute(self.qc).get_counts(self.qc) self.assertGreaterEqual(quantum_instance.time_taken, 0.) quantum_instance.reset_execution_results() self.assertTrue(_compare_dict(res_w_bo, res_w_bo_skip_validation))
def test_wo_backend_options(self): """ without backend options test """ quantum_instance = QuantumInstance(self.backend, seed_transpiler=self.random_seed, seed_simulator=self.random_seed, shots=1024) # run without backend_options and without noise res_wo_bo = quantum_instance.execute(self.qc).get_counts(self.qc) self.assertGreaterEqual(quantum_instance.time_taken, 0.) quantum_instance.reset_execution_results() quantum_instance.skip_qobj_validation = True res_wo_bo_skip_validation = quantum_instance.execute(self.qc).get_counts(self.qc) self.assertGreaterEqual(quantum_instance.time_taken, 0.) quantum_instance.reset_execution_results() self.assertTrue(_compare_dict(res_wo_bo, res_wo_bo_skip_validation))
def test_circuit_modified(self): """tests that circuits don't get modified on QI execute with error mitigation as per issue #7449 """ algorithm_globals.random_seed = 1679 np.random.seed(algorithm_globals.random_seed) circuit = QuantumCircuit(1) circuit.x(0) circuit.measure_all() qi = QuantumInstance( Aer.get_backend("aer_simulator"), seed_simulator=algorithm_globals.random_seed, seed_transpiler=algorithm_globals.random_seed, shots=1024, measurement_error_mitigation_cls=CompleteMeasFitter, ) # The error happens on transpiled circuits since "execute" was changing the input array # Non transpiled circuits didn't have a problem because a new transpiled array was created # internally. circuits_ref = qi.transpile(circuit) # always returns a new array circuits_input = circuits_ref.copy() _ = qi.execute(circuits_input, had_transpiled=True) self.assertEqual(circuits_ref, circuits_input, msg="Transpiled circuit array modified.")
class QuantumKernel: r"""Quantum Kernel. The general task of machine learning is to find and study patterns in data. For many algorithms, the datapoints are better understood in a higher dimensional feature space, through the use of a kernel function: .. math:: K(x, y) = \langle f(x), f(y)\rangle. Here K is the kernel function, x, y are n dimensional inputs. f is a map from n-dimension to m-dimension space. :math:`\langle x, y \rangle` denotes the dot product. Usually m is much larger than n. The quantum kernel algorithm calculates a kernel matrix, given datapoints x and y and feature map f, all of n dimension. This kernel matrix can then be used in classical machine learning algorithms such as support vector classification, spectral clustering or ridge regression. """ @deprecate_arguments("0.5.0", {"user_parameters": "training_parameters"}) def __init__( self, feature_map: Optional[QuantumCircuit] = None, enforce_psd: bool = True, batch_size: int = 900, quantum_instance: Optional[Union[QuantumInstance, Backend]] = None, training_parameters: Optional[Union[ParameterVector, Sequence[Parameter]]] = None, ) -> None: """ Args: feature_map: Parameterized circuit to be used as the feature map. If None is given, the `ZZFeatureMap` is used with two qubits. enforce_psd: Project to closest positive semidefinite matrix if x = y. Only enforced when not using the state vector simulator. Default True. batch_size: Number of circuits to batch together for computation. Default 900. quantum_instance: Quantum Instance or Backend training_parameters: Iterable containing ``Parameter`` objects which correspond to quantum gates on the feature map circuit which may be tuned. If users intend to tune feature map parameters to find optimal values, this field should be set. """ # Class fields self._feature_map = None self._unbound_feature_map = None self._training_parameters = None self._training_parameter_binds = None self._enforce_psd = enforce_psd self._batch_size = batch_size self._quantum_instance = quantum_instance # Setters self.feature_map = feature_map if feature_map is not None else ZZFeatureMap( 2) if training_parameters is not None: self.training_parameters = training_parameters @property def feature_map(self) -> QuantumCircuit: """Return feature map""" return self._feature_map @feature_map.setter def feature_map(self, feature_map: QuantumCircuit) -> None: """ Set feature map. The ``unbound_feature_map`` field will be automatically updated when this field is set, and ``training_parameters`` and ``training_parameter_binds`` fields will be reset to ``None``. """ self._feature_map = feature_map self._unbound_feature_map = copy.deepcopy(self._feature_map) self._training_parameters = None self._training_parameter_binds = None @property def unbound_feature_map(self) -> QuantumCircuit: """Return unbound feature map""" return copy.deepcopy(self._unbound_feature_map) @property def quantum_instance(self) -> QuantumInstance: """Return quantum instance""" return self._quantum_instance @quantum_instance.setter def quantum_instance( self, quantum_instance: Union[Backend, QuantumInstance]) -> None: """Set quantum instance""" if isinstance(quantum_instance, Backend): self._quantum_instance = QuantumInstance(quantum_instance) else: self._quantum_instance = quantum_instance @property def training_parameters( self) -> Optional[Union[ParameterVector, Sequence[Parameter]]]: """Return the vector of training parameters.""" return copy.copy(self._training_parameters) @training_parameters.setter def training_parameters( self, training_params: Union[ParameterVector, Sequence[Parameter]]) -> None: """Set the training parameters""" self._training_parameter_binds = { training_params[i]: training_params[i] for i, _ in enumerate(training_params) } self._training_parameters = copy.deepcopy(training_params) def assign_training_parameters( self, values: Union[Mapping[Parameter, ParameterValueType], Sequence[ParameterValueType]] ) -> None: """ Assign training parameters in the ``QuantumKernel`` feature map. Args: values (dict or iterable): Either a dictionary or iterable specifying the new parameter values. If a dict, it specifies the mapping from ``current_parameter`` to ``new_parameter``, where ``new_parameter`` can be a parameter expression or a numeric value. If an iterable, the elements are assigned to the existing parameters in the order of ``QuantumKernel.training_parameters``. Raises: ValueError: Incompatible number of training parameters and values """ if self._training_parameters is None: raise ValueError(f""" The number of parameter values ({len(values)}) does not match the number of training parameters tracked by the QuantumKernel (None). """) # Get the input parameters. These should remain unaffected by assigning of training parameters. input_params = list( set(self._unbound_feature_map.parameters) - set(self._training_parameters)) # If iterable of values is passed, the length must match length of training_parameters field if isinstance(values, (Sequence, np.ndarray)): if len(values) != len(self._training_parameters): raise ValueError(f""" The number of parameter values ({len(values)}) does not match the number of training parameters tracked by the QuantumKernel ({len(self._training_parameters)}). """) values = { p: values[i] for i, p in enumerate(self._training_parameters) } else: if not isinstance(values, dict): raise ValueError(f""" 'values' must be of type Dict or Sequence. Type {type(values)} is not supported. """) # All input keys must exist in the circuit # This check actually catches some well defined assignments; # however; we throw an error to be consistent with the behavior # of QuantumCircuit's parameter binding. unknown_parameters = list( set(values.keys()) - set(self._training_parameters)) if len(unknown_parameters) > 0: raise ValueError( f"Cannot bind parameters ({unknown_parameters}) not tracked by the quantum kernel." ) # Because QuantumKernel supports parameter rebinding, entries of the `values` dictionary must # be handled differently depending on whether they represent numerical assignments or parameter # reassignments. However, re-ordering the values dictionary inherently changes the expected # behavior of parameter binding, as entries in the values dict do not commute with one another # in general. To resolve this issue, we handle each entry of the values dict one at a time. for param, bind in values.items(): if isinstance(bind, ParameterExpression): self._unbound_feature_map.assign_parameters({param: bind}, inplace=True) # Training params are all non-input params in the unbound feature map # This list comprehension ensures that self._training_parameters is ordered # in a way that is consistent with self.feature_map.parameters self._training_parameters = [ p for p in self._unbound_feature_map.parameters if (p not in input_params) ] # Remove param if it was overwritten if param not in self._training_parameters: del self._training_parameter_binds[param] # Add new parameters for sub_param in bind.parameters: if sub_param not in self._training_parameter_binds.keys(): self._training_parameter_binds[sub_param] = sub_param # If parameter is being set to expression of itself, training_parameter_binds # reflects a self-bind if param in bind.parameters: self._training_parameter_binds[param] = param # If assignment is numerical, update the param_binds elif isinstance(bind, numbers.Number): self._training_parameter_binds[param] = bind else: raise ValueError(f""" Parameters can only be bound to numeric values, Parameters, or ParameterExpressions. Type {type(bind)} is not supported. """) # Reorder dict according to self._training_parameters self._training_parameter_binds = { param: self._training_parameter_binds[param] for param in self._training_parameters } # Update feature map with numerical parameter assignments self._feature_map = self._unbound_feature_map.assign_parameters( self._training_parameter_binds) @property def training_parameter_binds(self) -> Optional[Mapping[Parameter, float]]: """Return a copy of the current training parameter mappings for the feature map circuit.""" return copy.deepcopy(self._training_parameter_binds) def bind_training_parameters( self, values: Union[Mapping[Parameter, ParameterValueType], Sequence[ParameterValueType]] ) -> None: """ Alternate function signature for ``assign_training_parameters`` """ self.assign_training_parameters(values) def get_unbound_training_parameters(self) -> List[Parameter]: """Return a list of any unbound training parameters in the feature map circuit.""" unbound_training_params = [] if self._training_parameter_binds is not None: # Get all training parameters not associated with numerical values unbound_training_params = [ val for val in self._training_parameter_binds.values() if not isinstance(val, numbers.Number) ] return unbound_training_params @property # type: ignore @deprecate_property("0.5.0", new_name="training_parameters") def user_parameters( self) -> Optional[Union[ParameterVector, Sequence[Parameter]]]: """[Deprecated property]Return the vector of training parameters.""" return self.training_parameters @user_parameters.setter # type: ignore @deprecate_property("0.5.0", new_name="training_parameters") def user_parameters( self, training_params: Union[ParameterVector, Sequence[Parameter]]) -> None: """[Deprecated property setter]Set the training parameters""" self.training_parameters = training_params @deprecate_method("0.5.0", new_name="assign_training_parameters") def assign_user_parameters( self, values: Union[Mapping[Parameter, ParameterValueType], Sequence[ParameterValueType]] ) -> None: """ [Deprecated method]Assign training parameters in the ``QuantumKernel`` feature map. Otherwise, just like ``assign_training_parameters``. """ self.assign_training_parameters(values) @property # type: ignore @deprecate_property("0.5.0", new_name="training_parameter_binds") def user_param_binds(self) -> Optional[Mapping[Parameter, float]]: """ [Deprecated property]Return a copy of the current training parameter mappings for the feature map circuit. """ return self.training_parameter_binds @deprecate_method("0.5.0", new_name="bind_training_parameters") def bind_user_parameters( self, values: Union[Mapping[Parameter, ParameterValueType], Sequence[ParameterValueType]] ) -> None: """ [Deprecated method]Alternate function signature for ``assign_training_parameters`` """ self.bind_training_parameters(values) @deprecate_method("0.5.0", new_name="get_unbound_training_parameters") def get_unbound_user_parameters(self) -> List[Parameter]: """ [Deprecated method]Return a list of any unbound training parameters in the feature map circuit. """ return self.get_unbound_training_parameters() def construct_circuit( self, x: ParameterVector, y: ParameterVector = None, measurement: bool = True, is_statevector_sim: bool = False, ) -> QuantumCircuit: r""" Construct inner product circuit for given datapoints and feature map. If using `statevector_simulator`, only construct circuit for :math:`\Psi(x)|0\rangle`, otherwise construct :math:`Psi^dagger(y) x Psi(x)|0>` If y is None and not using `statevector_simulator`, self inner product is calculated. Args: x: first data point parameter vector y: second data point parameter vector, ignored if using statevector simulator measurement: include measurement if not using statevector simulator is_statevector_sim: use state vector simulator Returns: QuantumCircuit Raises: ValueError: - x and/or y have incompatible dimension with feature map - unbound training parameters in the feature map circuit """ # Ensure all training parameters have been bound in the feature map circuit. unbound_params = self.get_unbound_training_parameters() if unbound_params: raise ValueError(f""" The feature map circuit contains unbound training parameters ({unbound_params}). All training parameters must be bound to numerical values before constructing inner product circuit. """) if len(x) != self._feature_map.num_parameters: raise ValueError( "x and class feature map incompatible dimensions.\n" f"x has {len(x)} dimensions, but feature map has {self._feature_map.num_parameters}." ) q = QuantumRegister(self._feature_map.num_qubits, "q") c = ClassicalRegister(self._feature_map.num_qubits, "c") qc = QuantumCircuit(q, c) x_dict = dict(zip(self._feature_map.parameters, x)) psi_x = self._feature_map.assign_parameters(x_dict) qc.append(psi_x.to_instruction(), qc.qubits) if not is_statevector_sim: if y is not None and len(y) != self._feature_map.num_parameters: raise ValueError( "y and class feature map incompatible dimensions.\n" f"y has {len(y)} dimensions, but feature map has {self._feature_map.num_parameters}." ) if y is None: y = x y_dict = dict(zip(self._feature_map.parameters, y)) psi_y_dag = self._feature_map.assign_parameters(y_dict) qc.append(psi_y_dag.to_instruction().inverse(), qc.qubits) if measurement: qc.barrier(q) qc.measure(q, c) return qc def _compute_overlap(self, idx, results, is_statevector_sim, measurement_basis) -> float: """ Helper function to compute overlap for given input. """ if is_statevector_sim: # |<0|Psi^dagger(y) x Psi(x)|0>|^2, take the amplitude v_a, v_b = [results[int(i)] for i in idx] tmp = np.vdot(v_a, v_b) kernel_value = np.vdot(tmp, tmp).real # pylint: disable=no-member else: result = results.get_counts(idx) kernel_value = result.get(measurement_basis, 0) / sum( result.values()) return kernel_value def evaluate(self, x_vec: np.ndarray, y_vec: np.ndarray = None) -> np.ndarray: r""" Construct kernel matrix for given data and feature map If y_vec is None, self inner product is calculated. If using `statevector_simulator`, only build circuits for :math:`\Psi(x)|0\rangle`, then perform inner product classically. Args: x_vec: 1D or 2D array of datapoints, NxD, where N is the number of datapoints, D is the feature dimension y_vec: 1D or 2D array of datapoints, MxD, where M is the number of datapoints, D is the feature dimension Returns: 2D matrix, NxM Raises: QiskitMachineLearningError: - A quantum instance or backend has not been provided ValueError: - unbound training parameters in the feature map circuit - x_vec and/or y_vec are not one or two dimensional arrays - x_vec and y_vec have have incompatible dimensions - x_vec and/or y_vec have incompatible dimension with feature map and and feature map can not be modified to match. """ # Ensure all training parameters have been bound in the feature map circuit. unbound_params = self.get_unbound_training_parameters() if unbound_params: raise ValueError(f""" The feature map circuit contains unbound training parameters ({unbound_params}). All training parameters must be bound to numerical values before evaluating the kernel matrix. """) if self._quantum_instance is None: raise QiskitMachineLearningError( "A QuantumInstance or Backend must be supplied to evaluate a quantum kernel." ) if isinstance(self._quantum_instance, Backend): self._quantum_instance = QuantumInstance(self._quantum_instance) if not isinstance(x_vec, np.ndarray): x_vec = np.asarray(x_vec) if y_vec is not None and not isinstance(y_vec, np.ndarray): y_vec = np.asarray(y_vec) if x_vec.ndim > 2: raise ValueError("x_vec must be a 1D or 2D array") if x_vec.ndim == 1: x_vec = np.reshape(x_vec, (-1, len(x_vec))) if y_vec is not None and y_vec.ndim > 2: raise ValueError("y_vec must be a 1D or 2D array") if y_vec is not None and y_vec.ndim == 1: y_vec = np.reshape(y_vec, (-1, len(y_vec))) if y_vec is not None and y_vec.shape[1] != x_vec.shape[1]: raise ValueError( "x_vec and y_vec have incompatible dimensions.\n" f"x_vec has {x_vec.shape[1]} dimensions, but y_vec has {y_vec.shape[1]}." ) if x_vec.shape[1] != self._feature_map.num_parameters: try: self._feature_map.num_qubits = x_vec.shape[1] except AttributeError: raise ValueError( "x_vec and class feature map have incompatible dimensions.\n" f"x_vec has {x_vec.shape[1]} dimensions, " f"but feature map has {self._feature_map.num_parameters}." ) from AttributeError if y_vec is not None and y_vec.shape[ 1] != self._feature_map.num_parameters: raise ValueError( "y_vec and class feature map have incompatible dimensions.\n" f"y_vec has {y_vec.shape[1]} dimensions, but feature map " f"has {self._feature_map.num_parameters}.") # determine if calculating self inner product is_symmetric = True if y_vec is None: y_vec = x_vec elif not np.array_equal(x_vec, y_vec): is_symmetric = False # initialize kernel matrix kernel = np.zeros((x_vec.shape[0], y_vec.shape[0])) # set diagonal to 1 if symmetric if is_symmetric: np.fill_diagonal(kernel, 1) # get indices to calculate if is_symmetric: mus, nus = np.triu_indices(x_vec.shape[0], k=1) # remove diagonal else: mus, nus = np.indices((x_vec.shape[0], y_vec.shape[0])) mus = np.asarray(mus.flat) nus = np.asarray(nus.flat) is_statevector_sim = self._quantum_instance.is_statevector measurement = not is_statevector_sim measurement_basis = "0" * self._feature_map.num_qubits # calculate kernel if is_statevector_sim: # using state vector simulator if is_symmetric: to_be_computed_data = x_vec else: # not symmetric to_be_computed_data = np.concatenate((x_vec, y_vec)) feature_map_params = ParameterVector( "par_x", self._feature_map.num_parameters) parameterized_circuit = self.construct_circuit( feature_map_params, feature_map_params, measurement=measurement, is_statevector_sim=is_statevector_sim, ) parameterized_circuit = self._quantum_instance.transpile( parameterized_circuit, pass_manager=self._quantum_instance.unbound_pass_manager)[0] statevectors = [] for min_idx in range(0, len(to_be_computed_data), self._batch_size): max_idx = min(min_idx + self._batch_size, len(to_be_computed_data)) circuits = [ parameterized_circuit.assign_parameters( {feature_map_params: x}) for x in to_be_computed_data[min_idx:max_idx] ] if self._quantum_instance.bound_pass_manager is not None: circuits = self._quantum_instance.transpile( circuits, pass_manager=self._quantum_instance.bound_pass_manager) results = self._quantum_instance.execute(circuits, had_transpiled=True) for j in range(max_idx - min_idx): statevectors.append(results.get_statevector(j)) offset = 0 if is_symmetric else len(x_vec) matrix_elements = [ self._compute_overlap(idx, statevectors, is_statevector_sim, measurement_basis) for idx in list(zip(mus, nus + offset)) ] for i, j, value in zip(mus, nus, matrix_elements): kernel[i, j] = value if is_symmetric: kernel[j, i] = kernel[i, j] else: # not using state vector simulator feature_map_params_x = ParameterVector( "par_x", self._feature_map.num_parameters) feature_map_params_y = ParameterVector( "par_y", self._feature_map.num_parameters) parameterized_circuit = self.construct_circuit( feature_map_params_x, feature_map_params_y, measurement=measurement, is_statevector_sim=is_statevector_sim, ) parameterized_circuit = self._quantum_instance.transpile( parameterized_circuit, pass_manager=self._quantum_instance.unbound_pass_manager)[0] for idx in range(0, len(mus), self._batch_size): to_be_computed_data_pair = [] to_be_computed_index = [] for sub_idx in range(idx, min(idx + self._batch_size, len(mus))): i = mus[sub_idx] j = nus[sub_idx] x_i = x_vec[i] y_j = y_vec[j] if not np.all(x_i == y_j): to_be_computed_data_pair.append((x_i, y_j)) to_be_computed_index.append((i, j)) circuits = [ parameterized_circuit.assign_parameters({ feature_map_params_x: x, feature_map_params_y: y }) for x, y in to_be_computed_data_pair ] if self._quantum_instance.bound_pass_manager is not None: circuits = self._quantum_instance.transpile( circuits, pass_manager=self._quantum_instance.bound_pass_manager) results = self._quantum_instance.execute(circuits, had_transpiled=True) matrix_elements = [ self._compute_overlap(circuit, results, is_statevector_sim, measurement_basis) for circuit in range(len(circuits)) ] for (i, j), value in zip(to_be_computed_index, matrix_elements): kernel[i, j] = value if is_symmetric: kernel[j, i] = kernel[i, j] if self._enforce_psd and is_symmetric: # Find the closest positive semi-definite approximation to symmetric kernel matrix. # The (symmetric) matrix should always be positive semi-definite by construction, # but this can be violated in case of noise, such as sampling noise, thus the # adjustment is only done if NOT using the statevector simulation. D, U = np.linalg.eig(kernel) # pylint: disable=invalid-name kernel = U @ np.diag(np.maximum(0, D)) @ U.transpose() return kernel
def get_output(self, quantum_instance: QuantumInstance, params: Optional[np.ndarray] = None, shots: Optional[int] = None) -> Tuple[List, List]: """ Get classical data samples from the generator. Running the quantum generator circuit results in a quantum state. To train this generator with a classical discriminator, we need to sample classical outputs by measuring the quantum state and mapping them to feature space defined by the training data. Args: quantum_instance: Quantum Instance, used to run the generator circuit. params: array or None, parameters which should be used to run the generator, if None use self._params shots: if not None use a number of shots that is different from the number set in quantum_instance Returns: generated samples, array: sample occurrence in percentage """ instance_shots = quantum_instance.run_config.shots q = QuantumRegister(sum(self._num_qubits), name='q') qc = QuantumCircuit(q) if params is None: params = cast(np.ndarray, self._bound_parameters) qc.append(self.construct_circuit(params), q) if quantum_instance.is_statevector: pass else: c = ClassicalRegister(sum(self._num_qubits), name='c') qc.add_register(c) qc.measure(q, c) if shots is not None: quantum_instance.set_config(shots=shots) result = quantum_instance.execute(qc) generated_samples = [] if quantum_instance.is_statevector: result = result.get_statevector(qc) values = np.multiply(result, np.conj(result)) values = list(values.real) keys = [] for j in range(len(values)): keys.append(np.binary_repr(j, int(sum(self._num_qubits)))) else: result = result.get_counts(qc) keys = list(result) values = list(result.values()) values = [float(v) / np.sum(values) for v in values] generated_samples_weights = values for i, _ in enumerate(keys): index = 0 temp = [] for k, p in enumerate(self._num_qubits): bin_rep = 0 j = 0 while j < p: bin_rep += int(keys[i][index]) * 2**(int(p) - j - 1) j += 1 index += 1 if len(self._num_qubits) > 1: temp.append(self._data_grid[k][int(bin_rep)]) else: temp.append(self._data_grid[int(bin_rep)]) generated_samples.append(temp) # self.generator_circuit._probabilities = generated_samples_weights if shots is not None: # Restore the initial quantum_instance configuration quantum_instance.set_config(shots=instance_shots) return generated_samples, generated_samples_weights
class TestQAOA(QiskitAlgorithmsTestCase): """Test QAOA with MaxCut.""" def setUp(self): super().setUp() self.seed = 10598 algorithm_globals.random_seed = self.seed self.qasm_simulator = QuantumInstance( BasicAer.get_backend("qasm_simulator"), shots=4096, seed_simulator=self.seed, seed_transpiler=self.seed, ) self.statevector_simulator = QuantumInstance( BasicAer.get_backend("statevector_simulator"), seed_simulator=self.seed, seed_transpiler=self.seed, ) @idata([ [W1, P1, M1, S1, False], [W2, P2, M2, S2, False], [W1, P1, M1, S1, True], [W2, P2, M2, S2, True], ]) @unpack def test_qaoa(self, w, prob, m, solutions, convert_to_matrix_op): """QAOA test""" self.log.debug("Testing %s-step QAOA with MaxCut on graph\n%s", prob, w) qubit_op, _ = self._get_operator(w) if convert_to_matrix_op: qubit_op = qubit_op.to_matrix_op() qaoa = QAOA(COBYLA(), prob, mixer=m, quantum_instance=self.statevector_simulator) result = qaoa.compute_minimum_eigenvalue(operator=qubit_op) x = self._sample_most_likely(result.eigenstate) graph_solution = self._get_graph_solution(x) self.assertIn(graph_solution, solutions) @idata([ [W1, P1, S1, False], [W2, P2, S2, False], [W1, P1, S1, True], [W2, P2, S2, True], ]) @unpack def test_qaoa_qc_mixer(self, w, prob, solutions, convert_to_matrix_op): """QAOA test with a mixer as a parameterized circuit""" self.log.debug( "Testing %s-step QAOA with MaxCut on graph with a mixer as a parameterized circuit\n%s", prob, w, ) optimizer = COBYLA() qubit_op, _ = self._get_operator(w) if convert_to_matrix_op: qubit_op = qubit_op.to_matrix_op() num_qubits = qubit_op.num_qubits mixer = QuantumCircuit(num_qubits) theta = Parameter("θ") mixer.rx(theta, range(num_qubits)) qaoa = QAOA(optimizer, prob, mixer=mixer, quantum_instance=self.statevector_simulator) result = qaoa.compute_minimum_eigenvalue(operator=qubit_op) x = self._sample_most_likely(result.eigenstate) graph_solution = self._get_graph_solution(x) self.assertIn(graph_solution, solutions) def test_qaoa_qc_mixer_many_parameters(self): """QAOA test with a mixer as a parameterized circuit with the num of parameters > 1.""" optimizer = COBYLA() qubit_op, _ = self._get_operator(W1) num_qubits = qubit_op.num_qubits mixer = QuantumCircuit(num_qubits) for i in range(num_qubits): theta = Parameter("θ" + str(i)) mixer.rx(theta, range(num_qubits)) qaoa = QAOA(optimizer, reps=2, mixer=mixer, quantum_instance=self.statevector_simulator) result = qaoa.compute_minimum_eigenvalue(operator=qubit_op) x = self._sample_most_likely(result.eigenstate) self.log.debug(x) graph_solution = self._get_graph_solution(x) self.assertIn(graph_solution, S1) def test_qaoa_qc_mixer_no_parameters(self): """QAOA test with a mixer as a parameterized circuit with zero parameters.""" qubit_op, _ = self._get_operator(W1) num_qubits = qubit_op.num_qubits mixer = QuantumCircuit(num_qubits) # just arbitrary circuit mixer.rx(np.pi / 2, range(num_qubits)) qaoa = QAOA(COBYLA(), reps=1, mixer=mixer, quantum_instance=self.statevector_simulator) result = qaoa.compute_minimum_eigenvalue(operator=qubit_op) # we just assert that we get a result, it is not meaningful. self.assertIsNotNone(result.eigenstate) def test_change_operator_size(self): """QAOA change operator size test""" qubit_op, _ = self._get_operator( np.array([[0, 1, 0, 1], [1, 0, 1, 0], [0, 1, 0, 1], [1, 0, 1, 0]])) qaoa = QAOA(COBYLA(), 1, quantum_instance=self.statevector_simulator) result = qaoa.compute_minimum_eigenvalue(operator=qubit_op) x = self._sample_most_likely(result.eigenstate) graph_solution = self._get_graph_solution(x) with self.subTest(msg="QAOA 4x4"): self.assertIn(graph_solution, {"0101", "1010"}) qubit_op, _ = self._get_operator( np.array([ [0, 1, 0, 1, 0, 1], [1, 0, 1, 0, 1, 0], [0, 1, 0, 1, 0, 1], [1, 0, 1, 0, 1, 0], [0, 1, 0, 1, 0, 1], [1, 0, 1, 0, 1, 0], ])) result = qaoa.compute_minimum_eigenvalue(operator=qubit_op) x = self._sample_most_likely(result.eigenstate) graph_solution = self._get_graph_solution(x) with self.subTest(msg="QAOA 6x6"): self.assertIn(graph_solution, {"010101", "101010"}) @idata([[W2, S2, None], [W2, S2, [0.0, 0.0]], [W2, S2, [1.0, 0.8]]]) @unpack def test_qaoa_initial_point(self, w, solutions, init_pt): """Check first parameter value used is initial point as expected""" qubit_op, _ = self._get_operator(w) first_pt = [] def cb_callback(eval_count, parameters, mean, std): nonlocal first_pt if eval_count == 1: first_pt = list(parameters) qaoa = QAOA( COBYLA(), initial_point=init_pt, callback=cb_callback, quantum_instance=self.statevector_simulator, ) result = qaoa.compute_minimum_eigenvalue(operator=qubit_op) x = self._sample_most_likely(result.eigenstate) graph_solution = self._get_graph_solution(x) with self.subTest("Initial Point"): # If None the preferred random initial point of QAOA variational form if init_pt is None: self.assertLess(result.eigenvalue, -0.97) else: self.assertListEqual(init_pt, first_pt) with self.subTest("Solution"): self.assertIn(graph_solution, solutions) @idata([[W2, None], [W2, [1.0] + 15 * [0.0]], [W2, CUSTOM_SUPERPOSITION]]) @unpack def test_qaoa_initial_state(self, w, init_state): """QAOA initial state test""" optimizer = COBYLA() qubit_op, _ = self._get_operator(w) init_pt = np.asarray([0.0, 0.0]) # Avoid generating random initial point if init_state is None: initial_state = None else: initial_state = QuantumCircuit(QuantumRegister(4, "q")) initial_state.initialize(init_state, initial_state.qubits) zero_init_state = QuantumCircuit( QuantumRegister(qubit_op.num_qubits, "q")) qaoa_zero_init_state = QAOA( optimizer=optimizer, initial_state=zero_init_state, initial_point=init_pt, quantum_instance=self.statevector_simulator, ) qaoa = QAOA( optimizer=optimizer, initial_state=initial_state, initial_point=init_pt, quantum_instance=self.statevector_simulator, ) zero_circuits = qaoa_zero_init_state.construct_circuit( init_pt, qubit_op) custom_circuits = qaoa.construct_circuit(init_pt, qubit_op) self.assertEqual(len(zero_circuits), len(custom_circuits)) for zero_circ, custom_circ in zip(zero_circuits, custom_circuits): z_length = len(zero_circ.data) c_length = len(custom_circ.data) self.assertGreaterEqual(c_length, z_length) self.assertTrue(zero_circ.data == custom_circ.data[-z_length:]) custom_init_qc = QuantumCircuit(custom_circ.num_qubits) custom_init_qc.data = custom_circ.data[0:c_length - z_length] if initial_state is None: original_init_qc = QuantumCircuit(qubit_op.num_qubits) original_init_qc.h(range(qubit_op.num_qubits)) else: original_init_qc = initial_state job_init_state = self.statevector_simulator.execute( original_init_qc) job_qaoa_init_state = self.statevector_simulator.execute( custom_init_qc) statevector_original = job_init_state.get_statevector( original_init_qc) statevector_custom = job_qaoa_init_state.get_statevector( custom_init_qc) self.assertListEqual(statevector_original.tolist(), statevector_custom.tolist()) def test_qaoa_random_initial_point(self): """QAOA random initial point""" w = rx.adjacency_matrix( rx.undirected_gnp_random_graph(5, 0.5, seed=algorithm_globals.random_seed)) qubit_op, _ = self._get_operator(w) qaoa = QAOA(optimizer=NELDER_MEAD(disp=True), reps=1, quantum_instance=self.qasm_simulator) result = qaoa.compute_minimum_eigenvalue(operator=qubit_op) self.assertLess(result.eigenvalue, -0.97) def test_qaoa_construct_circuit_update(self): """Test updating operators with QAOA construct_circuit""" qaoa = QAOA() ref = qaoa.construct_circuit([0, 0], I ^ Z)[0] circ2 = qaoa.construct_circuit([0, 0], I ^ Z)[0] self.assertEqual(circ2, ref) circ3 = qaoa.construct_circuit([0, 0], Z ^ I)[0] self.assertNotEqual(circ3, ref) circ4 = qaoa.construct_circuit([0, 0], I ^ Z)[0] self.assertEqual(circ4, ref) def test_optimizer_scipy_callable(self): """Test passing a SciPy optimizer directly as callable.""" qaoa = QAOA( optimizer=partial(scipy_minimize, method="Nelder-Mead", options={"maxiter": 2}), quantum_instance=self.statevector_simulator, ) result = qaoa.compute_minimum_eigenvalue(Z) self.assertEqual(result.cost_function_evals, 4) def _get_operator(self, weight_matrix): """Generate Hamiltonian for the max-cut problem of a graph. Args: weight_matrix (numpy.ndarray) : adjacency matrix. Returns: PauliSumOp: operator for the Hamiltonian float: a constant shift for the obj function. """ num_nodes = weight_matrix.shape[0] pauli_list = [] shift = 0 for i in range(num_nodes): for j in range(i): if weight_matrix[i, j] != 0: x_p = np.zeros(num_nodes, dtype=bool) z_p = np.zeros(num_nodes, dtype=bool) z_p[i] = True z_p[j] = True pauli_list.append( [0.5 * weight_matrix[i, j], Pauli((z_p, x_p))]) shift -= 0.5 * weight_matrix[i, j] opflow_list = [(pauli[1].to_label(), pauli[0]) for pauli in pauli_list] return PauliSumOp.from_list(opflow_list), shift def _get_graph_solution(self, x: np.ndarray) -> str: """Get graph solution from binary string. Args: x : binary string as numpy array. Returns: a graph solution as string. """ return "".join([str(int(i)) for i in 1 - x]) def _sample_most_likely(self, state_vector): """Compute the most likely binary string from state vector. Args: state_vector (numpy.ndarray or dict): state vector or counts. Returns: numpy.ndarray: binary string as numpy.ndarray of ints. """ n = int(np.log2(state_vector.shape[0])) k = np.argmax(np.abs(state_vector)) x = np.zeros(n) for i in range(n): x[i] = k % 2 k >>= 1 return x
class TestWeightedPauliOperator(QiskitOpflowTestCase): """WeightedPauliOperator tests.""" def setUp(self): super().setUp() seed = 3 aqua_globals.random_seed = seed self.num_qubits = 3 paulis = [ Pauli("".join(pauli_label)) for pauli_label in itertools.product('IXYZ', repeat=self.num_qubits) ] weights = aqua_globals.random.random(len(paulis)) self.qubit_op = WeightedPauliOperator.from_list(paulis, weights) self.var_form = EfficientSU2(self.qubit_op.num_qubits, reps=1) qasm_simulator = BasicAer.get_backend('qasm_simulator') self.quantum_instance_qasm = QuantumInstance(qasm_simulator, shots=65536, seed_simulator=seed, seed_transpiler=seed) statevector_simulator = BasicAer.get_backend('statevector_simulator') self.quantum_instance_statevector = \ QuantumInstance(statevector_simulator, shots=1, seed_simulator=seed, seed_transpiler=seed) def test_from_to_file(self): """ from to file test """ paulis = [ Pauli(x) for x in ['IXYZ', 'XXZY', 'IIZZ', 'XXYY', 'ZZXX', 'YYYY'] ] weights = [ 0.2 + -1j * 0.8, 0.6 + -1j * 0.6, 0.8 + -1j * 0.2, -0.2 + -1j * 0.8, -0.6 - -1j * 0.6, -0.8 - -1j * 0.2 ] op = WeightedPauliOperator.from_list(paulis, weights) file_path = self._get_resource_path('temp_op.json') op.to_file(file_path) self.assertTrue(os.path.exists(file_path)) try: load_op = WeightedPauliOperator.from_file(file_path) self.assertEqual(op, load_op) finally: os.remove(file_path) def test_num_qubits(self): """ num qubits test """ op = WeightedPauliOperator(paulis=[]) self.assertEqual(op.num_qubits, 0) self.assertEqual(self.qubit_op.num_qubits, self.num_qubits) def test_is_empty(self): """ is empty test """ op = WeightedPauliOperator(paulis=[]) self.assertTrue(op.is_empty()) self.assertFalse(self.qubit_op.is_empty()) def test_str(self): """ str test """ pauli_a = 'IXYZ' pauli_b = 'ZYIX' coeff_a = 0.5 coeff_b = 0.5 pauli_term_a = [coeff_a, Pauli(pauli_a)] pauli_term_b = [coeff_b, Pauli(pauli_b)] op_a = WeightedPauliOperator(paulis=[pauli_term_a]) op_b = WeightedPauliOperator(paulis=[pauli_term_b]) op_a += op_b self.assertEqual("Representation: paulis, qubits: 4, size: 2", str(op_a)) op_a = WeightedPauliOperator(paulis=[pauli_term_a], name='ABC') self.assertEqual("ABC: Representation: paulis, qubits: 4, size: 1", str(op_a)) def test_multiplication(self): """ multiplication test """ pauli_a = 'IXYZ' pauli_b = 'ZYIX' coeff_a = 0.5 coeff_b = 0.5 pauli_term_a = [coeff_a, Pauli(pauli_a)] pauli_term_b = [coeff_b, Pauli(pauli_b)] op_a = WeightedPauliOperator(paulis=[pauli_term_a]) op_b = WeightedPauliOperator(paulis=[pauli_term_b]) new_op = op_a * op_b self.assertEqual(1, len(new_op.paulis)) self.assertEqual(0.25, new_op.paulis[0][0]) self.assertEqual('-ZZYY', new_op.paulis[0][1].to_label()) new_op = -2j * new_op self.assertEqual(-0.5j, new_op.paulis[0][0]) new_op = new_op * 0.3j self.assertEqual(0.15, new_op.paulis[0][0]) def test_iadd(self): """ iadd test """ pauli_a = 'IXYZ' pauli_b = 'ZYIX' coeff_a = 0.5 coeff_b = 0.5 pauli_term_a = [coeff_a, Pauli(pauli_a)] pauli_term_b = [coeff_b, Pauli(pauli_b)] op_a = WeightedPauliOperator(paulis=[pauli_term_a]) op_b = WeightedPauliOperator(paulis=[pauli_term_b]) ori_op_a = op_a.copy() ori_op_b = op_b.copy() op_a += op_b self.assertNotEqual(op_a, ori_op_a) self.assertEqual(op_b, ori_op_b) self.assertEqual(2, len(op_a.paulis)) pauli_c = 'IXYZ' coeff_c = 0.25 pauli_term_c = [coeff_c, Pauli(pauli_c)] op_a += WeightedPauliOperator(paulis=[pauli_term_c]) self.assertEqual(2, len(op_a.paulis)) self.assertEqual(0.75, op_a.paulis[0][0]) def test_add(self): """ add test """ pauli_a = 'IXYZ' pauli_b = 'ZYIX' coeff_a = 0.5 coeff_b = 0.5 pauli_term_a = [coeff_a, Pauli(pauli_a)] pauli_term_b = [coeff_b, Pauli(pauli_b)] op_a = WeightedPauliOperator(paulis=[pauli_term_a]) op_b = WeightedPauliOperator(paulis=[pauli_term_b]) ori_op_a = op_a.copy() ori_op_b = op_b.copy() new_op = op_a + op_b self.assertEqual(op_a, ori_op_a) self.assertEqual(op_b, ori_op_b) self.assertEqual(1, len(op_a.paulis)) self.assertEqual(2, len(new_op.paulis)) pauli_c = 'IXYZ' coeff_c = 0.25 pauli_term_c = [coeff_c, Pauli(pauli_c)] new_op = new_op + WeightedPauliOperator(paulis=[pauli_term_c]) self.assertEqual(2, len(new_op.paulis)) self.assertEqual(0.75, new_op.paulis[0][0]) def test_sub(self): """ sub test """ pauli_a = 'IXYZ' pauli_b = 'ZYIX' coeff_a = 0.5 coeff_b = 0.5 pauli_term_a = [coeff_a, Pauli(pauli_a)] pauli_term_b = [coeff_b, Pauli(pauli_b)] op_a = WeightedPauliOperator(paulis=[pauli_term_a]) op_b = WeightedPauliOperator(paulis=[pauli_term_b]) ori_op_a = op_a.copy() ori_op_b = op_b.copy() new_op = op_a - op_b self.assertEqual(op_a, ori_op_a) self.assertEqual(op_b, ori_op_b) self.assertEqual(1, len(op_a.paulis)) self.assertEqual(2, len(new_op.paulis)) self.assertEqual(0.5, new_op.paulis[0][0]) self.assertEqual(-0.5, new_op.paulis[1][0]) pauli_c = 'IXYZ' coeff_c = 0.25 pauli_term_c = [coeff_c, Pauli(pauli_c)] new_op = new_op - WeightedPauliOperator(paulis=[pauli_term_c]) self.assertEqual(2, len(new_op.paulis)) self.assertEqual(0.25, new_op.paulis[0][0]) def test_isub(self): """ isub test """ pauli_a = 'IXYZ' pauli_b = 'ZYIX' coeff_a = 0.5 coeff_b = 0.5 pauli_term_a = [coeff_a, Pauli(pauli_a)] pauli_term_b = [coeff_b, Pauli(pauli_b)] op_a = WeightedPauliOperator(paulis=[pauli_term_a]) op_b = WeightedPauliOperator(paulis=[pauli_term_b]) ori_op_a = op_a.copy() ori_op_b = op_b.copy() op_a -= op_b self.assertNotEqual(op_a, ori_op_a) self.assertEqual(op_b, ori_op_b) self.assertEqual(2, len(op_a.paulis)) pauli_c = 'IXYZ' coeff_c = 0.5 pauli_term_c = [coeff_c, Pauli(pauli_c)] op_a -= WeightedPauliOperator(paulis=[pauli_term_c]) # sub does not remove zero weights. self.assertEqual(2, len(op_a.paulis)) def test_equal_operator(self): """ equal operator test """ paulis = [ Pauli(x) for x in ['IXYZ', 'XXZY', 'IIZZ', 'XXYY', 'ZZXX', 'YYYY'] ] coeffs = [0.2, 0.6, 0.8, -0.2, -0.6, -0.8] op1 = WeightedPauliOperator.from_list(paulis, coeffs) coeffs = [0.2, 0.6, 0.8, -0.2, -0.6, -0.8] op2 = WeightedPauliOperator.from_list(paulis, coeffs) coeffs = [-0.2, -0.6, -0.8, 0.2, 0.6, 0.8] op3 = WeightedPauliOperator.from_list(paulis, coeffs) coeffs = [-0.2, 0.6, 0.8, -0.2, -0.6, -0.8] op4 = WeightedPauliOperator.from_list(paulis, coeffs) self.assertEqual(op1, op2) self.assertNotEqual(op1, op3) self.assertNotEqual(op1, op4) self.assertNotEqual(op3, op4) def test_negation_operator(self): """ negation operator test """ paulis = [ Pauli(x) for x in ['IXYZ', 'XXZY', 'IIZZ', 'XXYY', 'ZZXX', 'YYYY'] ] coeffs = [0.2, 0.6, 0.8, -0.2, -0.6, -0.8] op1 = WeightedPauliOperator.from_list(paulis, coeffs) coeffs = [-0.2, -0.6, -0.8, 0.2, 0.6, 0.8] op2 = WeightedPauliOperator.from_list(paulis, coeffs) self.assertNotEqual(op1, op2) self.assertEqual(op1, -op2) self.assertEqual(-op1, op2) op1 = op1 * -1.0 self.assertEqual(op1, op2) def test_simplify(self): """ simplify test """ pauli_a = 'IXYZ' pauli_b = 'IXYZ' coeff_a = 0.5 coeff_b = -0.5 pauli_term_a = [coeff_a, Pauli(pauli_a)] pauli_term_b = [coeff_b, Pauli(pauli_b)] op_a = WeightedPauliOperator(paulis=[pauli_term_a]) op_b = WeightedPauliOperator(paulis=[pauli_term_b]) new_op = op_a + op_b new_op.simplify() self.assertEqual(0, len(new_op.paulis), "{}".format(new_op.print_details())) self.assertTrue(new_op.is_empty()) paulis = [ Pauli(x) for x in ['IXYZ', 'XXZY', 'IIZZ', 'XXYY', 'ZZXX', 'YYYY'] ] coeffs = [0.2, 0.6, 0.8, -0.2, -0.6, -0.8] op1 = WeightedPauliOperator.from_list(paulis, coeffs) for i, pauli in enumerate(paulis): tmp_op = WeightedPauliOperator(paulis=[[-coeffs[i], pauli]]) op1 += tmp_op op1.simplify() self.assertEqual(len(paulis) - (i + 1), len(op1.paulis)) def test_simplify_same_paulis(self): """ simplify same paulis test """ pauli_a = 'IXYZ' pauli_b = 'IXYZ' coeff_a = 0.5 coeff_b = 0.5 pauli_term_a = [coeff_a, Pauli(pauli_a)] pauli_term_b = [coeff_b, Pauli(pauli_b)] op_a = WeightedPauliOperator(paulis=[pauli_term_a, pauli_term_b]) self.assertEqual(1, len(op_a.paulis), "{}".format(op_a.print_details())) self.assertEqual(1, len(op_a.basis)) self.assertEqual(0, op_a.basis[0][1][0]) def test_chop_real(self): """ chop real test """ paulis = [ Pauli(x) for x in ['IXYZ', 'XXZY', 'IIZZ', 'XXYY', 'ZZXX', 'YYYY'] ] coeffs = [0.2, 0.6, 0.8, -0.2, -0.6, -0.8] op = WeightedPauliOperator.from_list(paulis, coeffs) ori_op = op.copy() for threshold, num_paulis in zip([0.4, 0.7, 0.9], [4, 2, 0]): op = ori_op.copy() op1 = op.chop(threshold=threshold, copy=True) self.assertEqual(len(op.paulis), 6, "\n{}".format(op.print_details())) self.assertEqual(len(op1.paulis), num_paulis, "\n{}".format(op1.print_details())) op1 = op.chop(threshold=threshold, copy=False) self.assertEqual(len(op.paulis), num_paulis, "\n{}".format(op.print_details())) self.assertEqual(len(op1.paulis), num_paulis, "\n{}".format(op1.print_details())) def test_chop_complex(self): """ chop complex test """ paulis = [ Pauli(x) for x in ['IXYZ', 'XXZY', 'IIZZ', 'XXYY', 'ZZXX', 'YYYY'] ] coeffs = [ 0.2 + -0.5j, 0.6 - 0.3j, 0.8 - 0.6j, -0.5 + -0.2j, -0.3 + 0.6j, -0.6 + 0.8j ] op = WeightedPauliOperator.from_list(paulis, coeffs) ori_op = op.copy() for threshold, num_paulis in zip([0.4, 0.7, 0.9], [6, 2, 0]): op = ori_op.copy() op1 = op.chop(threshold=threshold, copy=True) self.assertEqual(len(op.paulis), 6, "\n{}".format(op.print_details())) self.assertEqual(len(op1.paulis), num_paulis, "\n{}".format(op1.print_details())) op1 = op.chop(threshold=threshold, copy=False) self.assertEqual(len(op.paulis), num_paulis, "\n{}".format(op.print_details())) self.assertEqual(len(op1.paulis), num_paulis, "\n{}".format(op1.print_details())) def test_evaluate_single_pauli_qasm(self): """ evaluate single pauli qasm test """ # X op = WeightedPauliOperator.from_list([Pauli('X')]) qr = QuantumRegister(1, name='q') wave_function = QuantumCircuit(qr) # + 1 eigenstate wave_function.h(qr[0]) circuits = op.construct_evaluation_circuit(wave_function=wave_function, statevector_mode=False) result = self.quantum_instance_qasm.execute(circuits) actual_value = op.evaluate_with_result(result=result, statevector_mode=False) self.assertAlmostEqual(1.0, actual_value[0].real, places=5) # - 1 eigenstate wave_function = QuantumCircuit(qr) wave_function.x(qr[0]) wave_function.h(qr[0]) circuits = op.construct_evaluation_circuit(wave_function=wave_function, statevector_mode=False) result = self.quantum_instance_qasm.execute(circuits) actual_value = op.evaluate_with_result(result=result, statevector_mode=False) self.assertAlmostEqual(-1.0, actual_value[0].real, places=5) # Y op = WeightedPauliOperator.from_list([Pauli('Y')]) qr = QuantumRegister(1, name='q') wave_function = QuantumCircuit(qr) # + 1 eigenstate wave_function.h(qr[0]) wave_function.s(qr[0]) circuits = op.construct_evaluation_circuit(wave_function=wave_function, statevector_mode=False) result = self.quantum_instance_qasm.execute(circuits) actual_value = op.evaluate_with_result(result=result, statevector_mode=False) self.assertAlmostEqual(1.0, actual_value[0].real, places=5) # - 1 eigenstate wave_function = QuantumCircuit(qr) wave_function.x(qr[0]) wave_function.h(qr[0]) wave_function.s(qr[0]) circuits = op.construct_evaluation_circuit(wave_function=wave_function, statevector_mode=False) result = self.quantum_instance_qasm.execute(circuits) actual_value = op.evaluate_with_result(result=result, statevector_mode=False) self.assertAlmostEqual(-1.0, actual_value[0].real, places=5) # Z op = WeightedPauliOperator.from_list([Pauli('Z')]) qr = QuantumRegister(1, name='q') wave_function = QuantumCircuit(qr) # + 1 eigenstate circuits = op.construct_evaluation_circuit(wave_function=wave_function, statevector_mode=False) result = self.quantum_instance_qasm.execute(circuits) actual_value = op.evaluate_with_result(result=result, statevector_mode=False) self.assertAlmostEqual(1.0, actual_value[0].real, places=5) # - 1 eigenstate wave_function = QuantumCircuit(qr) wave_function.x(qr[0]) circuits = op.construct_evaluation_circuit(wave_function=wave_function, statevector_mode=False) result = self.quantum_instance_qasm.execute(circuits) actual_value = op.evaluate_with_result(result=result, statevector_mode=False) self.assertAlmostEqual(-1.0, actual_value[0].real, places=5) def test_evaluate_single_pauli_statevector(self): """ evaluate single pauli statevector test """ # X op = WeightedPauliOperator.from_list([Pauli('X')]) qr = QuantumRegister(1, name='q') wave_function = QuantumCircuit(qr) # + 1 eigenstate wave_function.h(qr[0]) circuits = op.construct_evaluation_circuit(wave_function=wave_function, statevector_mode=True) result = self.quantum_instance_statevector.execute(circuits) actual_value = op.evaluate_with_result(result=result, statevector_mode=True) self.assertAlmostEqual(1.0, actual_value[0].real, places=5) # - 1 eigenstate wave_function = QuantumCircuit(qr) wave_function.x(qr[0]) wave_function.h(qr[0]) circuits = op.construct_evaluation_circuit(wave_function=wave_function, statevector_mode=True) result = self.quantum_instance_statevector.execute(circuits) actual_value = op.evaluate_with_result(result=result, statevector_mode=True) self.assertAlmostEqual(-1.0, actual_value[0].real, places=5) # Y op = WeightedPauliOperator.from_list([Pauli('Y')]) qr = QuantumRegister(1, name='q') wave_function = QuantumCircuit(qr) # + 1 eigenstate wave_function.h(qr[0]) wave_function.s(qr[0]) circuits = op.construct_evaluation_circuit(wave_function=wave_function, statevector_mode=True) result = self.quantum_instance_statevector.execute(circuits) actual_value = op.evaluate_with_result(result=result, statevector_mode=True) self.assertAlmostEqual(1.0, actual_value[0].real, places=5) # - 1 eigenstate wave_function = QuantumCircuit(qr) wave_function.x(qr[0]) wave_function.h(qr[0]) wave_function.s(qr[0]) circuits = op.construct_evaluation_circuit(wave_function=wave_function, statevector_mode=True) result = self.quantum_instance_statevector.execute(circuits) actual_value = op.evaluate_with_result(result=result, statevector_mode=True) self.assertAlmostEqual(-1.0, actual_value[0].real, places=5) # Z op = WeightedPauliOperator.from_list([Pauli('Z')]) qr = QuantumRegister(1, name='q') wave_function = QuantumCircuit(qr) # + 1 eigenstate circuits = op.construct_evaluation_circuit(wave_function=wave_function, statevector_mode=True) result = self.quantum_instance_statevector.execute(circuits) actual_value = op.evaluate_with_result(result=result, statevector_mode=True) self.assertAlmostEqual(1.0, actual_value[0].real, places=5) # - 1 eigenstate wave_function = QuantumCircuit(qr) wave_function.x(qr[0]) circuits = op.construct_evaluation_circuit(wave_function=wave_function, statevector_mode=True) result = self.quantum_instance_statevector.execute(circuits) actual_value = op.evaluate_with_result(result=result, statevector_mode=True) self.assertAlmostEqual(-1.0, actual_value[0].real, places=5) def test_evaluate_qasm_mode(self): """ evaluate qasm mode test """ wave_function = self.var_form.assign_parameters( np.array( aqua_globals.random.standard_normal( self.var_form.num_parameters))) circuits = self.qubit_op.construct_evaluation_circuit( wave_function=wave_function, statevector_mode=True) reference = self.qubit_op.evaluate_with_result( result=self.quantum_instance_statevector.execute(circuits), statevector_mode=True) circuits = self.qubit_op.construct_evaluation_circuit( wave_function=wave_function, statevector_mode=False) result = self.quantum_instance_qasm.execute(circuits) actual_value = self.qubit_op.evaluate_with_result( result=result, statevector_mode=False) self.assertGreaterEqual( reference[0].real, actual_value[0].real - 3 * actual_value[1].real) self.assertLessEqual(reference[0].real, actual_value[0].real + 3 * actual_value[1].real) def test_evaluate_statevector_mode(self): """ evaluate statevector mode test """ wave_function = self.var_form.assign_parameters( np.array( aqua_globals.random.standard_normal( self.var_form.num_parameters))) wave_fn_statevector = \ self.quantum_instance_statevector.execute(wave_function).get_statevector(wave_function) # use matrix operator as reference: reference = self.qubit_op.evaluate_with_statevector( wave_fn_statevector) circuits = self.qubit_op.construct_evaluation_circuit( wave_function=wave_function, statevector_mode=True) actual_value = self.qubit_op.evaluate_with_result( result=self.quantum_instance_statevector.execute(circuits), statevector_mode=True) self.assertAlmostEqual(reference[0], actual_value[0], places=10) def test_evaluate_with_aer_mode(self): """ evaluate with aer mode test """ try: # pylint: disable=import-outside-toplevel from qiskit import Aer except Exception as ex: # pylint: disable=broad-except self.skipTest( "Aer doesn't appear to be installed. Error: '{}'".format( str(ex))) return statevector_simulator = Aer.get_backend('statevector_simulator') quantum_instance_statevector = QuantumInstance(statevector_simulator, shots=1) wave_function = self.var_form.assign_parameters( np.array( aqua_globals.random.standard_normal( self.var_form.num_parameters))) circuits = self.qubit_op.construct_evaluation_circuit( wave_function=wave_function, statevector_mode=True) reference = self.qubit_op.evaluate_with_result( result=quantum_instance_statevector.execute(circuits), statevector_mode=True) circuits = self.qubit_op.construct_evaluation_circuit( wave_function=wave_function, statevector_mode=True, use_simulator_snapshot_mode=True) actual_value = self.qubit_op.evaluate_with_result( result=quantum_instance_statevector.execute(circuits), statevector_mode=True, use_simulator_snapshot_mode=True) self.assertAlmostEqual(reference[0], actual_value[0], places=10) def test_simplification(self): """ Test Hamiltonians produce same result after simplification by constructor """ q = QuantumRegister(2, name='q') qc = QuantumCircuit(q) qc.rx(10.9891251356965, 0) qc.rx(6.286692023269373, 1) qc.rz(7.848801398269382, 0) qc.rz(9.42477796076938, 1) qc.cx(0, 1) def eval_op(op): from qiskit import execute backend = BasicAer.get_backend('qasm_simulator') evaluation_circuits = op.construct_evaluation_circuit(qc, False) job = execute(evaluation_circuits, backend, shots=1024) return op.evaluate_with_result(job.result(), False) pauli_string = [[1.0, Pauli('XX')], [-1.0, Pauli('YY')], [-1.0, Pauli('ZZ')]] wpo = WeightedPauliOperator(pauli_string) expectation_value, _ = eval_op(wpo) self.assertAlmostEqual(expectation_value, -3.0, places=2) # Half each coefficient value but double up (6 Paulis total) pauli_string = [[0.5, Pauli('XX')], [-0.5, Pauli('YY')], [-0.5, Pauli('ZZ')]] pauli_string *= 2 wpo2 = WeightedPauliOperator(pauli_string) expectation_value, _ = eval_op(wpo2) self.assertAlmostEqual(expectation_value, -3.0, places=2) def test_to_opflow(self): """Test for to_opflow() in WeightedPauliOperator""" pauli_a = 'IXYZ' pauli_b = 'ZYIX' coeff_a = 0.5 + 1j coeff_b = -0.5 - 1j pauli_term_a = [coeff_a, Pauli(pauli_a)] pauli_term_b = [coeff_b, Pauli(pauli_b)] op_a = WeightedPauliOperator(paulis=[pauli_term_a]) op_b = WeightedPauliOperator(paulis=[pauli_term_b]) legacy_op = op_a + op_b op = coeff_a * (I ^ X ^ Y ^ Z) + coeff_b * (Z ^ Y ^ I ^ X) self.assertEqual(op, legacy_op.to_opflow())
def get_kernel_matrix(quantum_instance: QuantumInstance, feature_map: QuantumCircuit, x1_vec: np.ndarray, x2_vec: Optional[np.ndarray] = None, enforce_psd: bool = True) -> np.ndarray: """ Construct kernel matrix, if x2_vec is None, self-innerproduct is conducted. Notes: When using `statevector_simulator`, we only build the circuits for Psi(x1)|0> rather than Psi(x2)^dagger Psi(x1)|0>, and then we perform the inner product classically. That is, for `statevector_simulator`, the total number of circuits will be O(N) rather than O(N^2) for `qasm_simulator`. Args: quantum_instance: quantum backend with all settings feature_map: a feature map that maps data to feature space x1_vec: data points, 2-D array, N1xD, where N1 is the number of data, D is the feature dimension x2_vec: data points, 2-D array, N2xD, where N2 is the number of data, D is the feature dimension enforce_psd: enforces that the kernel matrix is positive semi-definite by setting negative eigenvalues to zero. This is only applied in the symmetric case, i.e., if `x2_vec == None`. Returns: 2-D matrix, N1xN2 """ use_parameterized_circuits = True if x2_vec is None: is_symmetric = True x2_vec = x1_vec else: is_symmetric = False is_statevector_sim = quantum_instance.is_statevector measurement = not is_statevector_sim measurement_basis = '0' * feature_map.num_qubits mat = np.ones((x1_vec.shape[0], x2_vec.shape[0])) # get all indices if is_symmetric: mus, nus = np.triu_indices(x1_vec.shape[0], k=1) # remove diagonal term else: mus, nus = np.indices((x1_vec.shape[0], x2_vec.shape[0])) mus = np.asarray(mus.flat) nus = np.asarray(nus.flat) if is_statevector_sim: if is_symmetric: to_be_computed_data = x1_vec else: to_be_computed_data = np.concatenate((x1_vec, x2_vec)) if use_parameterized_circuits: # build parameterized circuits, it could be slower for building circuit # but overall it should be faster since it only transpile one circuit feature_map_params = ParameterVector( 'x', feature_map.feature_dimension) parameterized_circuit = QSVM._construct_circuit( (feature_map_params, feature_map_params), feature_map, measurement, is_statevector_sim=is_statevector_sim) parameterized_circuit = quantum_instance.transpile( parameterized_circuit)[0] circuits = [ parameterized_circuit.assign_parameters( {feature_map_params: x}) for x in to_be_computed_data ] else: # the second x is redundant to_be_computed_data_pair = [(x, x) for x in to_be_computed_data] if logger.isEnabledFor(logging.DEBUG): logger.debug("Building circuits:") TextProgressBar(sys.stderr) circuits = parallel_map( QSVM._construct_circuit, to_be_computed_data_pair, task_args=(feature_map, measurement, is_statevector_sim), num_processes=algorithm_globals.num_processes) results = quantum_instance.execute( circuits, had_transpiled=use_parameterized_circuits) if logger.isEnabledFor(logging.DEBUG): logger.debug("Calculating overlap:") TextProgressBar(sys.stderr) offset = 0 if is_symmetric else len(x1_vec) matrix_elements = parallel_map( QSVM._compute_overlap, list(zip(mus, nus + offset)), task_args=(results, is_statevector_sim, measurement_basis), num_processes=algorithm_globals.num_processes) for i, j, value in zip(mus, nus, matrix_elements): mat[i, j] = value if is_symmetric: mat[j, i] = mat[i, j] else: for idx in range(0, len(mus), QSVM.BATCH_SIZE): to_be_computed_data_pair = [] to_be_computed_index = [] for sub_idx in range(idx, min(idx + QSVM.BATCH_SIZE, len(mus))): i = mus[sub_idx] j = nus[sub_idx] x1 = x1_vec[i] x2 = x2_vec[j] if not np.all(x1 == x2): to_be_computed_data_pair.append((x1, x2)) to_be_computed_index.append((i, j)) if use_parameterized_circuits: # build parameterized circuits, it could be slower for building circuit # but overall it should be faster since it only transpile one circuit feature_map_params_x = ParameterVector( 'x', feature_map.feature_dimension) feature_map_params_y = ParameterVector( 'y', feature_map.feature_dimension) parameterized_circuit = QSVM._construct_circuit( (feature_map_params_x, feature_map_params_y), feature_map, measurement, is_statevector_sim=is_statevector_sim) parameterized_circuit = quantum_instance.transpile( parameterized_circuit)[0] circuits = [ parameterized_circuit.assign_parameters({ feature_map_params_x: x, feature_map_params_y: y }) for x, y in to_be_computed_data_pair ] else: if logger.isEnabledFor(logging.DEBUG): logger.debug("Building circuits:") TextProgressBar(sys.stderr) circuits = parallel_map( QSVM._construct_circuit, to_be_computed_data_pair, task_args=(feature_map, measurement), num_processes=algorithm_globals.num_processes) results = quantum_instance.execute( circuits, had_transpiled=use_parameterized_circuits) if logger.isEnabledFor(logging.DEBUG): logger.debug("Calculating overlap:") TextProgressBar(sys.stderr) matrix_elements = parallel_map( QSVM._compute_overlap, range(len(circuits)), task_args=(results, is_statevector_sim, measurement_basis), num_processes=algorithm_globals.num_processes) for (i, j), value in zip(to_be_computed_index, matrix_elements): mat[i, j] = value if is_symmetric: mat[j, i] = mat[i, j] if enforce_psd and is_symmetric and not is_statevector_sim: # Find the closest positive semi-definite approximation to kernel matrix, in case it is # symmetric. The (symmetric) matrix should always be positive semi-definite by # construction, but this can be violated in case of noise, such as sampling noise, thus, # the adjustment is only done if NOT using the statevector simulation. D, U = np.linalg.eig(mat) mat = U @ np.diag(np.maximum(0, D)) @ U.transpose() return mat
class QuantumKernel: r"""Quantum Kernel. The general task of machine learning is to find and study patterns in data. For many algorithms, the datapoints are better understood in a higher dimensional feature space, through the use of a kernel function: .. math:: K(x, y) = \langle f(x), f(y)\rangle. Here K is the kernel function, x, y are n dimensional inputs. f is a map from n-dimension to m-dimension space. :math:`\langle x, y \rangle` denotes the dot product. Usually m is much larger than n. The quantum kernel algorithm calculates a kernel matrix, given datapoints x and y and feature map f, all of n dimension. This kernel matrix can then be used in classical machine learning algorithms such as support vector classification, spectral clustering or ridge regression. """ def __init__(self, feature_map: Optional[QuantumCircuit] = None, enforce_psd: bool = True, batch_size: int = 1000, quantum_instance: Optional[ Union[QuantumInstance, BaseBackend, Backend]] = None) -> None: """ Args: feature_map: Parameterized circuit to be used as the feature map. If None is given, the `ZZFeatureMap` is used with two qubits. enforce_psd: Project to closest positive semidefinite matrix if x = y. Only enforced when not using the state vector simulator. Default True. batch_size: Number of circuits to batch together for computation. Default 1000. quantum_instance: Quantum Instance or Backend """ self._feature_map = feature_map if feature_map else ZZFeatureMap(2) self._enforce_psd = enforce_psd self._batch_size = batch_size self._quantum_instance = quantum_instance @property def feature_map(self) -> QuantumCircuit: """ Returns feature map """ return self._feature_map @feature_map.setter def feature_map(self, feature_map: QuantumCircuit): """ Sets feature map """ self._feature_map = feature_map @property def quantum_instance(self) -> QuantumInstance: """ Returns quantum instance """ return self._quantum_instance @quantum_instance.setter def quantum_instance(self, quantum_instance: Union[Backend, BaseBackend, QuantumInstance]) -> None: """ Sets quantum instance """ if isinstance(quantum_instance, (BaseBackend, Backend)): self._quantum_instance = QuantumInstance(quantum_instance) else: self._quantum_instance = quantum_instance def construct_circuit(self, x: ParameterVector, y: ParameterVector = None, measurement: bool = True, is_statevector_sim: bool = False) -> QuantumCircuit: r""" Construct inner product circuit for given datapoints and feature map. If using `statevector_simulator`, only construct circuit for :math:`\Psi(x)|0\rangle`, otherwise construct :math:`Psi^dagger(y) x Psi(x)|0>` If y is None and not using `statevector_simulator`, self inner product is calculated. Args: x: first data point parameter vector y: second data point parameter vector, ignored if using statevector simulator measurement: include measurement if not using statevector simulator is_statevector_sim: use state vector simulator Returns: QuantumCircuit Raises: ValueError: - x and/or y have incompatible dimension with feature map """ if len(x) != self._feature_map.num_parameters: raise ValueError("x and class feature map incompatible dimensions.\n" + "x has %s dimensions, but feature map has %s." % (len(x), self._feature_map.num_parameters)) q = QuantumRegister(self._feature_map.num_qubits, 'q') c = ClassicalRegister(self._feature_map.num_qubits, 'c') qc = QuantumCircuit(q, c) x_dict = dict(zip(self._feature_map.parameters, x)) psi_x = self._feature_map.assign_parameters(x_dict) qc.append(psi_x.to_instruction(), qc.qubits) if not is_statevector_sim: if y is not None and len(y) != self._feature_map.num_parameters: raise ValueError("y and class feature map incompatible dimensions.\n" + "y has %s dimensions, but feature map has %s." % (len(y), self._feature_map.num_parameters)) if y is None: y = x y_dict = dict(zip(self._feature_map.parameters, y)) psi_y_dag = self._feature_map.assign_parameters(y_dict) qc.append(psi_y_dag.to_instruction().inverse(), qc.qubits) if measurement: qc.barrier(q) qc.measure(q, c) return qc def _compute_overlap(self, idx, results, is_statevector_sim, measurement_basis): """ Helper function to compute overlap for given input. """ if is_statevector_sim: # |<0|Psi^dagger(y) x Psi(x)|0>|^2, take the amplitude v_a, v_b = [results.get_statevector(int(i)) for i in idx] tmp = np.vdot(v_a, v_b) kernel_value = np.vdot(tmp, tmp).real # pylint: disable=no-member else: result = results.get_counts(idx) kernel_value = result.get(measurement_basis, 0) / sum(result.values()) return kernel_value def evaluate(self, x_vec: np.ndarray, y_vec: np.ndarray = None) -> np.ndarray: r""" Construct kernel matrix for given data and feature map If y_vec is None, self inner product is calculated. If using `statevector_simulator`, only build circuits for :math:`\Psi(x)|0\rangle`, then perform inner product classically. Args: x_vec: 1D or 2D array of datapoints, NxD, where N is the number of datapoints, D is the feature dimension y_vec: 1D or 2D array of datapoints, MxD, where M is the number of datapoints, D is the feature dimension Returns: 2D matrix, NxM Raises: QiskitMachineLearningError: - A quantum instance or backend has not been provided ValueError: - x_vec and/or y_vec are not one or two dimensional arrays - x_vec and y_vec have have incompatible dimensions - x_vec and/or y_vec have incompatible dimension with feature map and and feature map can not be modified to match. """ if self._quantum_instance is None: raise QiskitMachineLearningError( "A QuantumInstance or Backend must be supplied to evaluate a quantum kernel.") if isinstance(self._quantum_instance, (BaseBackend, Backend)): self._quantum_instance = QuantumInstance(self._quantum_instance) if not isinstance(x_vec, np.ndarray): x_vec = np.asarray(x_vec) if y_vec is not None and not isinstance(y_vec, np.ndarray): y_vec = np.asarray(y_vec) if x_vec.ndim > 2: raise ValueError("x_vec must be a 1D or 2D array") if x_vec.ndim == 1: x_vec = np.reshape(x_vec, (-1, 2)) if y_vec is not None and y_vec.ndim > 2: raise ValueError("y_vec must be a 1D or 2D array") if y_vec is not None and y_vec.ndim == 1: y_vec = np.reshape(y_vec, (-1, 2)) if y_vec is not None and y_vec.shape[1] != x_vec.shape[1]: raise ValueError("x_vec and y_vec have incompatible dimensions.\n" + "x_vec has %s dimensions, but y_vec has %s." % (x_vec.shape[1], y_vec.shape[1])) if x_vec.shape[1] != self._feature_map.num_parameters: try: self._feature_map.num_qubits = x_vec.shape[1] except AttributeError: raise ValueError( "x_vec and class feature map have incompatible dimensions.\n" + "x_vec has %s dimensions, but feature map has %s." % (x_vec.shape[1], self._feature_map.num_parameters)) from AttributeError if y_vec is not None and y_vec.shape[1] != self._feature_map.num_parameters: raise ValueError("y_vec and class feature map have incompatible dimensions.\n" + "y_vec has %s dimensions, but feature map has %s." % (y_vec.shape[1], self._feature_map.num_parameters)) # determine if calculating self inner product is_symmetric = True if y_vec is None: y_vec = x_vec elif not np.array_equal(x_vec, y_vec): is_symmetric = False # initialize kernel matrix kernel = np.zeros((x_vec.shape[0], y_vec.shape[0])) # set diagonal to 1 if symmetric if is_symmetric: np.fill_diagonal(kernel, 1) # get indices to calculate if is_symmetric: mus, nus = np.triu_indices(x_vec.shape[0], k=1) # remove diagonal else: mus, nus = np.indices((x_vec.shape[0], y_vec.shape[0])) mus = np.asarray(mus.flat) nus = np.asarray(nus.flat) is_statevector_sim = self._quantum_instance.is_statevector measurement = not is_statevector_sim measurement_basis = '0' * self._feature_map.num_qubits # calculate kernel if is_statevector_sim: # using state vector simulator if is_symmetric: to_be_computed_data = x_vec else: # not symmetric to_be_computed_data = np.concatenate((x_vec, y_vec)) feature_map_params = ParameterVector('par_x', self._feature_map.num_parameters) parameterized_circuit = self.construct_circuit( feature_map_params, feature_map_params, measurement=measurement, is_statevector_sim=is_statevector_sim) parameterized_circuit = self._quantum_instance.transpile(parameterized_circuit)[0] circuits = [parameterized_circuit.assign_parameters({feature_map_params: x}) for x in to_be_computed_data] results = self._quantum_instance.execute(circuits) offset = 0 if is_symmetric else len(x_vec) matrix_elements = [self._compute_overlap(idx, results, is_statevector_sim, measurement_basis) for idx in list(zip(mus, nus + offset))] for i, j, value in zip(mus, nus, matrix_elements): kernel[i, j] = value if is_symmetric: kernel[j, i] = kernel[i, j] else: # not using state vector simulator feature_map_params_x = ParameterVector('par_x', self._feature_map.num_parameters) feature_map_params_y = ParameterVector('par_y', self._feature_map.num_parameters) parameterized_circuit = self.construct_circuit( feature_map_params_x, feature_map_params_y, measurement=measurement, is_statevector_sim=is_statevector_sim) parameterized_circuit = self._quantum_instance.transpile(parameterized_circuit)[0] for idx in range(0, len(mus), self._batch_size): to_be_computed_data_pair = [] to_be_computed_index = [] for sub_idx in range(idx, min(idx + self._batch_size, len(mus))): i = mus[sub_idx] j = nus[sub_idx] x_i = x_vec[i] y_j = y_vec[j] if not np.all(x_i == y_j): to_be_computed_data_pair.append((x_i, y_j)) to_be_computed_index.append((i, j)) circuits = [parameterized_circuit.assign_parameters({feature_map_params_x: x, feature_map_params_y: y}) for x, y in to_be_computed_data_pair] results = self._quantum_instance.execute(circuits) matrix_elements = [self._compute_overlap(circuit, results, is_statevector_sim, measurement_basis) for circuit in range(len(circuits))] for (i, j), value in zip(to_be_computed_index, matrix_elements): kernel[i, j] = value if is_symmetric: kernel[j, i] = kernel[i, j] if self._enforce_psd and is_symmetric: # Find the closest positive semi-definite approximation to symmetric kernel matrix. # The (symmetric) matrix should always be positive semi-definite by construction, # but this can be violated in case of noise, such as sampling noise, thus the # adjustment is only done if NOT using the statevector simulation. D, U = np.linalg.eig(kernel) # pylint: disable=invalid-name kernel = U @ np.diag(np.maximum(0, D)) @ U.transpose() return kernel
class TestTPBGroupedWeightedPauliOperator(QiskitOpflowTestCase): """TPBGroupedWeightedPauliOperator tests.""" def setUp(self): super().setUp() seed = 1 aqua_globals.random_seed = seed self.num_qubits = 3 paulis = [Pauli.from_label(pauli_label) for pauli_label in itertools.product('IXYZ', repeat=self.num_qubits)] weights = aqua_globals.random.random(len(paulis)) self.qubit_op = WeightedPauliOperator.from_list(paulis, weights) self.var_form = EfficientSU2(self.qubit_op.num_qubits, reps=1) qasm_simulator = BasicAer.get_backend('qasm_simulator') self.quantum_instance_qasm = QuantumInstance(qasm_simulator, shots=65536, seed_simulator=seed, seed_transpiler=seed) statevector_simulator = BasicAer.get_backend('statevector_simulator') self.quantum_instance_statevector = \ QuantumInstance(statevector_simulator, shots=1, seed_simulator=seed, seed_transpiler=seed) def test_sorted_grouping(self): """Test with color grouping approach.""" num_qubits = 2 paulis = [Pauli.from_label(pauli_label) for pauli_label in itertools.product('IXYZ', repeat=num_qubits)] weights = aqua_globals.random.random(len(paulis)) op = WeightedPauliOperator.from_list(paulis, weights) grouped_op = op_converter.to_tpb_grouped_weighted_pauli_operator( op, TPBGroupedWeightedPauliOperator.sorted_grouping) # check all paulis are still existed. for g_p in grouped_op.paulis: passed = False for pauli in op.paulis: if pauli[1] == g_p[1]: passed = pauli[0] == g_p[0] break self.assertTrue(passed, "non-existed paulis in grouped_paulis: {}".format(g_p[1].to_label())) # check the number of basis of grouped # one should be less than and equal to the original one. self.assertGreaterEqual(len(op.basis), len(grouped_op.basis)) def test_unsorted_grouping(self): """Test with normal grouping approach.""" num_qubits = 4 paulis = [Pauli.from_label(pauli_label) for pauli_label in itertools.product('IXYZ', repeat=num_qubits)] weights = aqua_globals.random.random(len(paulis)) op = WeightedPauliOperator.from_list(paulis, weights) grouped_op = op_converter.to_tpb_grouped_weighted_pauli_operator( op, TPBGroupedWeightedPauliOperator.unsorted_grouping) for g_p in grouped_op.paulis: passed = False for pauli in op.paulis: if pauli[1] == g_p[1]: passed = pauli[0] == g_p[0] break self.assertTrue(passed, "non-existed paulis in grouped_paulis: {}".format(g_p[1].to_label())) self.assertGreaterEqual(len(op.basis), len(grouped_op.basis)) def test_chop(self): """ chop test """ paulis = [Pauli.from_label(x) for x in ['IIXX', 'ZZXX', 'ZZZZ', 'XXZZ', 'XXXX', 'IXXX']] coeffs = [0.2, 0.3, 0.4, 0.5, 0.6, 0.7] op = WeightedPauliOperator.from_list(paulis, coeffs) grouped_op = op_converter.to_tpb_grouped_weighted_pauli_operator( op, TPBGroupedWeightedPauliOperator.sorted_grouping) original_num_basis = len(grouped_op.basis) chopped_grouped_op = grouped_op.chop(0.35, copy=True) self.assertLessEqual(len(chopped_grouped_op.basis), 3) self.assertLessEqual(len(chopped_grouped_op.basis), original_num_basis) # ZZXX group is remove for b, _ in chopped_grouped_op.basis: self.assertFalse(b.to_label() == 'ZZXX') chopped_grouped_op = grouped_op.chop(0.55, copy=True) self.assertLessEqual(len(chopped_grouped_op.basis), 1) self.assertLessEqual(len(chopped_grouped_op.basis), original_num_basis) for b, _ in chopped_grouped_op.basis: self.assertFalse(b.to_label() == 'ZZXX') self.assertFalse(b.to_label() == 'ZZZZ') self.assertFalse(b.to_label() == 'XXZZ') def test_evaluate_qasm_mode(self): """ evaluate qasm mode test """ wave_function = self.var_form.assign_parameters( np.array(aqua_globals.random.standard_normal(self.var_form.num_parameters))) wave_fn_statevector = \ self.quantum_instance_statevector.execute(wave_function).get_statevector(wave_function) reference = self.qubit_op.copy().evaluate_with_statevector(wave_fn_statevector) shots = 65536 // len(self.qubit_op.paulis) self.quantum_instance_qasm.set_config(shots=shots) circuits = self.qubit_op.construct_evaluation_circuit(wave_function=wave_function, statevector_mode=False) result = self.quantum_instance_qasm.execute(circuits) pauli_value = self.qubit_op.evaluate_with_result(result=result, statevector_mode=False) grouped_op = op_converter.to_tpb_grouped_weighted_pauli_operator( self.qubit_op, TPBGroupedWeightedPauliOperator.sorted_grouping) shots = 65536 // grouped_op.num_groups self.quantum_instance_qasm.set_config(shots=shots) circuits = grouped_op.construct_evaluation_circuit(wave_function=wave_function, statevector_mode=False) grouped_pauli_value = grouped_op.evaluate_with_result( result=self.quantum_instance_qasm.execute(circuits), statevector_mode=False) self.assertGreaterEqual(reference[0].real, grouped_pauli_value[0].real - 3 * grouped_pauli_value[1].real) self.assertLessEqual(reference[0].real, grouped_pauli_value[0].real + 3 * grouped_pauli_value[1].real) # this check assure the std of grouped pauli is # less than pauli mode under a fixed amount of total shots self.assertLessEqual(grouped_pauli_value[1].real, pauli_value[1].real) def test_equal(self): """ equal test """ gop_1 = op_converter.to_tpb_grouped_weighted_pauli_operator( self.qubit_op, TPBGroupedWeightedPauliOperator.sorted_grouping) gop_2 = op_converter.to_tpb_grouped_weighted_pauli_operator( self.qubit_op, TPBGroupedWeightedPauliOperator.unsorted_grouping) self.assertEqual(gop_1, gop_2)
def test_measurement_error_mitigation_with_diff_qubit_order( self, fitter_str, mit_pattern, fails, ): """measurement error mitigation with different qubit order""" algorithm_globals.random_seed = 0 # build noise model noise_model = noise.NoiseModel() read_err = noise.errors.readout_error.ReadoutError([[0.9, 0.1], [0.25, 0.75]]) noise_model.add_all_qubit_readout_error(read_err) fitter_cls = (CompleteMeasFitter if fitter_str == "CompleteMeasFitter" else TensoredMeasFitter) backend = Aer.get_backend("aer_simulator") quantum_instance = QuantumInstance( backend=backend, seed_simulator=1679, seed_transpiler=167, shots=1000, noise_model=noise_model, measurement_error_mitigation_cls=fitter_cls, cals_matrix_refresh_period=0, mit_pattern=mit_pattern, ) # circuit qc1 = QuantumCircuit(2, 2) qc1.h(0) qc1.cx(0, 1) qc1.measure(0, 0) qc1.measure(1, 1) qc2 = QuantumCircuit(2, 2) qc2.h(0) qc2.cx(0, 1) qc2.measure(1, 0) qc2.measure(0, 1) if fails: self.assertRaisesRegex( QiskitError, "Each element in the mit pattern should have length 1.", quantum_instance.execute, [qc1, qc2], ) else: quantum_instance.execute([qc1, qc2]) self.assertGreater(quantum_instance.time_taken, 0.0) quantum_instance.reset_execution_results() # failure case qc3 = QuantumCircuit(3, 3) qc3.h(2) qc3.cx(1, 2) qc3.measure(2, 1) qc3.measure(1, 2) self.assertRaises(QiskitError, quantum_instance.execute, [qc1, qc3])