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.construct_circuit( np.array(aqua_globals.random.randn(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_w_noise(self): # build noise model # Asymetric readout error on qubit-0 only try: from qiskit.providers.aer.noise import NoiseModel except Exception as e: self.skipTest( "Aer doesn't appear to be installed. Error: '{}'".format( str(e))) 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, circuit_caching=False) 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))
def test_w_noise(self): """ with noise test """ # build noise model # Asymmetric readout error on qubit-0 only try: # pylint: disable=import-outside-toplevel from qiskit.providers.aer.noise import NoiseModel from qiskit import Aer self.backend = Aer.get_backend('qasm_simulator') except Exception as ex: # pylint: disable=broad-except 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))
def test_wo_backend_options(self): quantum_instance = QuantumInstance(self.backend, seed_mapper=self.random_seed, run_config=self.run_config) # run without backend_options and without noise res_wo_bo = quantum_instance.execute(self.qc).get_counts(self.qc) quantum_instance.skip_qobj_validation = True res_wo_bo_skip_validation = quantum_instance.execute( self.qc).get_counts(self.qc) self.assertTrue(_compare_dict(res_wo_bo, res_wo_bo_skip_validation))
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) quantum_instance.skip_qobj_validation = True res_w_bo_skip_validation = quantum_instance.execute(self.qc).get_counts(self.qc) self.assertTrue(_compare_dict(res_w_bo, res_w_bo_skip_validation))
def test_measurement_error_mitigation_with_diff_qubit_order(self): """ measurement error mitigation with different qubit order""" # pylint: disable=import-outside-toplevel try: from qiskit import Aer from qiskit.providers.aer import noise except ImportError as ex: # pylint: disable=broad-except self.skipTest( "Aer doesn't appear to be installed. Error: '{}'".format( str(ex))) return aqua_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('qasm_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.) 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(AquaError, quantum_instance.execute, [qc1, qc3])
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) quantum_instance.skip_qobj_validation = True res_wo_bo_skip_validation = quantum_instance.execute(self.qc).get_counts(self.qc) self.assertTrue(_compare_dict(res_wo_bo, res_wo_bo_skip_validation))
def test_w_backend_options(self): # run with backend_options quantum_instance = QuantumInstance( self.backend, seed_mapper=self.random_seed, run_config=self.run_config, backend_options={'initial_statevector': [.5, .5, .5, .5]}) res_w_bo = quantum_instance.execute(self.qc).get_counts(self.qc) quantum_instance.skip_qobj_validation = True res_w_bo_skip_validation = quantum_instance.execute( self.qc).get_counts(self.qc) self.assertTrue(_compare_dict(res_w_bo, res_w_bo_skip_validation))
def test_w_backend_options(self): """ with backend options test """ # run with backend_options os.environ.pop('QISKIT_AQUA_CIRCUIT_CACHE', None) 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]}, circuit_caching=False) res_w_bo = quantum_instance.execute(self.qc).get_counts(self.qc) quantum_instance.skip_qobj_validation = True res_w_bo_skip_validation = quantum_instance.execute(self.qc).get_counts(self.qc) self.assertTrue(_compare_dict(res_w_bo, res_w_bo_skip_validation))
def test_wo_backend_options(self): os.environ.pop('QISKIT_AQUA_CIRCUIT_CACHE', None) quantum_instance = QuantumInstance(self.backend, seed_transpiler=self.random_seed, seed_simulator=self.random_seed, shots=1024, circuit_caching=False) # run without backend_options and without noise res_wo_bo = quantum_instance.execute(self.qc).get_counts(self.qc) quantum_instance.skip_qobj_validation = True res_wo_bo_skip_validation = quantum_instance.execute( self.qc).get_counts(self.qc) self.assertTrue(_compare_dict(res_wo_bo, res_wo_bo_skip_validation))
def test_saving_and_loading_one_circ(self): """ Saving and Loading one Circ test """ with tempfile.NamedTemporaryFile(suffix='.inp', delete=True) as cache_tmp_file: cache_tmp_file_name = cache_tmp_file.name var_form = RYRZ(num_qubits=4, depth=5) backend = BasicAer.get_backend('statevector_simulator') params0 = aqua_globals.random.random_sample( var_form.num_parameters) circ0 = var_form.construct_circuit(params0) qi0 = QuantumInstance(backend, circuit_caching=True, cache_file=cache_tmp_file_name, skip_qobj_deepcopy=True, skip_qobj_validation=True, seed_simulator=self.seed, seed_transpiler=self.seed) _ = qi0.execute([circ0]) with open(cache_tmp_file_name, "rb") as cache_handler: saved_cache = pickle.load(cache_handler, encoding="ASCII") self.assertIn('qobjs', saved_cache) self.assertIn('mappings', saved_cache) qobjs = [Qobj.from_dict(qob) for qob in saved_cache['qobjs']] self.assertTrue(isinstance(qobjs[0], Qobj)) self.assertGreaterEqual(len(saved_cache['mappings'][0][0]), 50) qi1 = QuantumInstance(backend, circuit_caching=True, cache_file=cache_tmp_file_name, skip_qobj_deepcopy=True, skip_qobj_validation=True, seed_simulator=self.seed, seed_transpiler=self.seed) params1 = aqua_globals.random.random_sample( var_form.num_parameters) circ1 = var_form.construct_circuit(params1) qobj1 = qi1.circuit_cache.load_qobj_from_cache( [circ1], 0, run_config=qi1.run_config) self.assertTrue(isinstance(qobj1, Qobj)) _ = qi1.execute([circ1]) self.assertEqual(qi0.circuit_cache.mappings, qi1.circuit_cache.mappings) self.assertLessEqual(qi1.circuit_cache.misses, 0)
def calc_expectataion(self, pauli_str, sub_circuit): qubit_op = WeightedPauliOperator([[1, Pauli.from_label(pauli_str)]]) sv_mode = False qi = QuantumInstance(backend=Aer.get_backend('qasm_simulator'), shots=1000, seed_simulator=100, seed_transpiler=2) if qi.is_statevector: sv_mode = True # Make sure that the eval quantum/ classical registers in the circuit are named 'q'/'c' qc = qubit_op.construct_evaluation_circuit( statevector_mode=sv_mode, wave_function=sub_circuit, qr=find_regs_by_name(sub_circuit, 'q'), use_simulator_operator_mode=True) result = qi.execute(qc) avg, std = qubit_op.evaluate_with_result( statevector_mode=sv_mode, result=result, use_simulator_operator_mode=True) return avg
def test_measurement_error_mitigation_with_diff_qubit_order(self): """ measurement error mitigation with dedicated shots test """ # pylint: disable=import-outside-toplevel from qiskit import Aer from qiskit.providers.aer import noise aqua_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('qasm_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]) # failure case qc3 = QuantumCircuit(3, 3) qc3.h(2) qc3.cx(1, 2) qc3.measure(2, 1) qc3.measure(1, 2) self.assertRaises(AquaError, quantum_instance.execute, [qc1, qc3])
def test_w_noise(self): # build noise model # Asymetric readout error on qubit-0 only 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_mapper=self.random_seed, run_config=self.run_config, 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))
def test_evaluate_with_aer_mode(self): try: from qiskit import Aer except Exception as e: self.skipTest( "Aer doesn't appear to be installed. Error: '{}'".format( str(e))) return statevector_simulator = Aer.get_backend('statevector_simulator') quantum_instance_statevector = QuantumInstance(statevector_simulator, shots=1) wave_function = self.var_form.construct_circuit( np.array(np.random.randn(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_operator_mode=True) extra_args = { 'expectation': { 'params': [self.qubit_op.aer_paulis], 'num_qubits': self.qubit_op.num_qubits } } actual_value = self.qubit_op.evaluate_with_result( result=quantum_instance_statevector.execute( circuits, **extra_args), statevector_mode=True, use_simulator_operator_mode=True) self.assertAlmostEqual(reference[0], actual_value[0], places=10)
def multi_circuit_eval(circuit: QuantumCircuit, op_list: List[BaseOperator], qi: QuantumInstance, drop_dups: bool = True): num_processes = 8 kwargs = {'statevector_mode': qi.is_statevector} logger.info('Constructing evaluation circuits...') start = time.time() print('about to get eval circs') total_evaluation_circuits = list( parallel_map(_circ_eval, op_list, task_kwargs={ **kwargs, 'wave_function': circuit }, num_processes=num_processes)) total_evaluation_circuits = [ item for sublist in total_evaluation_circuits for item in sublist ] logger.info('Removing duplicate circuits') start = time.time() print('about to drop dups') if drop_dups: final_circs = [] for circ in total_evaluation_circuits: if not fast_circuit_inclusion(circ, final_circs): final_circs.append(circ) logger.info('Finished removing duplicate circuits') else: final_circs = deepcopy(total_evaluation_circuits) del total_evaluation_circuits evals = len(final_circs) #will added logger.debug('Executing {} circuits for evaluation...'.format( len(final_circs))) start = time.time() #print('about to execute multi circuit evals') result = qi.execute(final_circs) #print('finished executing multi circuit evals') logger.debug('Computing {} expectations...'.format(len(op_list))) start = time.time() print('about to compute expecs') exp_vals = list( parallel_map(_compute_grad, op_list, task_kwargs={ **kwargs, 'result': result }, num_processes=num_processes)) logger.debug('Computed expectations: {}'.format(exp_vals)) return exp_vals, evals #will added
class TestBernoulli(QiskitAquaTestCase): """Tests based on the Bernoulli A operator. This class tests * the estimation result * the constructed circuits """ def setUp(self): super().setUp() self._statevector = QuantumInstance( backend=BasicAer.get_backend('statevector_simulator'), seed_simulator=2, seed_transpiler=2) self._unitary = QuantumInstance( backend=BasicAer.get_backend('unitary_simulator'), shots=1, seed_simulator=42, seed_transpiler=91) def qasm(shots=100): return QuantumInstance( backend=BasicAer.get_backend('qasm_simulator'), shots=shots, seed_simulator=2, seed_transpiler=2) self._qasm = qasm @idata( [[0.2, AmplitudeEstimation(2), { 'estimation': 0.5, 'mle': 0.2 }], [0.4, AmplitudeEstimation(4), { 'estimation': 0.30866, 'mle': 0.4 }], [0.82, AmplitudeEstimation(5), { 'estimation': 0.85355, 'mle': 0.82 }], [0.49, AmplitudeEstimation(3), { 'estimation': 0.5, 'mle': 0.49 }], [0.2, MaximumLikelihoodAmplitudeEstimation(2), { 'estimation': 0.2 }], [0.4, MaximumLikelihoodAmplitudeEstimation(4), { 'estimation': 0.4 }], [0.82, MaximumLikelihoodAmplitudeEstimation(5), { 'estimation': 0.82 }], [0.49, MaximumLikelihoodAmplitudeEstimation(3), { 'estimation': 0.49 }], [0.2, IterativeAmplitudeEstimation(0.1, 0.1), { 'estimation': 0.2 }], [ 0.4, IterativeAmplitudeEstimation(0.00001, 0.01), { 'estimation': 0.4 } ], [ 0.82, IterativeAmplitudeEstimation(0.00001, 0.05), { 'estimation': 0.82 } ], [ 0.49, IterativeAmplitudeEstimation(0.001, 0.01), { 'estimation': 0.49 } ]]) @unpack def test_statevector(self, prob, qae, expect): """ statevector test """ # construct factories for A and Q qae.a_factory = BernoulliAFactory(prob) qae.q_factory = BernoulliQFactory(qae.a_factory) result = qae.run(self._statevector) for key, value in expect.items(): self.assertAlmostEqual(value, result[key], places=3, msg="estimate `{}` failed".format(key)) @idata([[ 0.2, 100, AmplitudeEstimation(4), { 'estimation': 0.14644, 'mle': 0.193888 } ], [0.0, 1000, AmplitudeEstimation(2), { 'estimation': 0.0, 'mle': 0.0 }], [ 0.8, 10, AmplitudeEstimation(7), { 'estimation': 0.79784, 'mle': 0.801612 } ], [ 0.2, 100, MaximumLikelihoodAmplitudeEstimation(4), { 'estimation': 0.199606 } ], [ 0.4, 1000, MaximumLikelihoodAmplitudeEstimation(6), { 'estimation': 0.399488 } ], [ 0.8, 10, MaximumLikelihoodAmplitudeEstimation(7), { 'estimation': 0.800926 } ], [ 0.2, 100, IterativeAmplitudeEstimation(0.0001, 0.01), { 'estimation': 0.199987 } ], [ 0.4, 1000, IterativeAmplitudeEstimation(0.001, 0.05), { 'estimation': 0.400071 } ], [ 0.8, 10, IterativeAmplitudeEstimation(0.1, 0.05), { 'estimation': 0.811711 } ]]) @unpack def test_qasm(self, prob, shots, qae, expect): """ qasm test """ # construct factories for A and Q qae.a_factory = BernoulliAFactory(prob) qae.q_factory = BernoulliQFactory(qae.a_factory) result = qae.run(self._qasm(shots)) for key, value in expect.items(): self.assertAlmostEqual(value, result[key], places=3, msg="estimate `{}` failed".format(key)) @idata([[True], [False]]) @unpack def test_qae_circuit(self, efficient_circuit): """Test circuits resulting from canonical amplitude estimation. Build the circuit manually and from the algorithm and compare the resulting unitaries. """ prob = 0.5 for m in range(2, 7): qae = AmplitudeEstimation(m, a_factory=BernoulliAFactory(prob)) angle = 2 * np.arcsin(np.sqrt(prob)) # manually set up the inefficient AE circuit q_ancilla = QuantumRegister(m, 'a') q_objective = QuantumRegister(1, 'q') circuit = QuantumCircuit(q_ancilla, q_objective) # initial Hadamard gates for i in range(m): circuit.h(q_ancilla[i]) # A operator circuit.ry(angle, q_objective) if efficient_circuit: qae.q_factory = BernoulliQFactory(qae.a_factory) for power in range(m): circuit.cry(2 * 2**power * angle, q_ancilla[power], q_objective[0]) else: q_factory = QFactory(qae.a_factory, i_objective=0) for power in range(m): for _ in range(2**power): q_factory.build_controlled(circuit, q_objective, q_ancilla[power]) # fourier transform iqft = Standard(m) circuit = iqft.construct_circuit(qubits=q_ancilla, circuit=circuit, do_swaps=False) expected_unitary = self._unitary.execute(circuit).get_unitary() actual_circuit = qae.construct_circuit(measurement=False) actual_unitary = self._unitary.execute( actual_circuit).get_unitary() diff = np.sum(np.abs(actual_unitary - expected_unitary)) self.assertAlmostEqual(diff, 0) @idata([[True], [False]]) @unpack def test_iqae_circuits(self, efficient_circuit): """Test circuits resulting from iterative amplitude estimation. Build the circuit manually and from the algorithm and compare the resulting unitaries. """ prob = 0.5 for k in range(2, 7): qae = IterativeAmplitudeEstimation( 0.01, 0.05, a_factory=BernoulliAFactory(prob)) angle = 2 * np.arcsin(np.sqrt(prob)) # manually set up the inefficient AE circuit q_objective = QuantumRegister(1, 'q') circuit = QuantumCircuit(q_objective) # A operator circuit.ry(angle, q_objective) if efficient_circuit: qae.q_factory = BernoulliQFactory(qae.a_factory) # for power in range(k): # circuit.ry(2 ** power * angle, q_objective[0]) circuit.ry(2 * k * angle, q_objective[0]) else: q_factory = QFactory(qae.a_factory, i_objective=0) for _ in range(k): q_factory.build(circuit, q_objective) expected_unitary = self._unitary.execute(circuit).get_unitary() actual_circuit = qae.construct_circuit(k, measurement=False) actual_unitary = self._unitary.execute( actual_circuit).get_unitary() diff = np.sum(np.abs(actual_unitary - expected_unitary)) self.assertAlmostEqual(diff, 0) @idata([[True], [False]]) @unpack def test_mlae_circuits(self, efficient_circuit): """ Test the circuits constructed for MLAE """ prob = 0.5 for k in range(1, 7): qae = MaximumLikelihoodAmplitudeEstimation( k, a_factory=BernoulliAFactory(prob)) angle = 2 * np.arcsin(np.sqrt(prob)) # compute all the circuits used for MLAE circuits = [] # 0th power q_objective = QuantumRegister(1, 'q') circuit = QuantumCircuit(q_objective) circuit.ry(angle, q_objective) circuits += [circuit] # powers of 2 for power in range(k): q_objective = QuantumRegister(1, 'q') circuit = QuantumCircuit(q_objective) # A operator circuit.ry(angle, q_objective) # Q^(2^j) operator if efficient_circuit: qae.q_factory = BernoulliQFactory(qae.a_factory) circuit.ry(2 * 2**power * angle, q_objective[0]) else: q_factory = QFactory(qae.a_factory, i_objective=0) for _ in range(2**power): q_factory.build(circuit, q_objective) actual_circuits = qae.construct_circuits(measurement=False) for actual, expected in zip(actual_circuits, circuits): expected_unitary = self._unitary.execute( expected).get_unitary() actual_unitary = self._unitary.execute(actual).get_unitary() diff = np.sum(np.abs(actual_unitary - expected_unitary)) self.assertAlmostEqual(diff, 0)
class GroverOptimizer(OptimizationAlgorithm): """Uses Grover Adaptive Search (GAS) to find the minimum of a QUBO function.""" def __init__(self, num_value_qubits: int, num_iterations: int = 3, quantum_instance: Optional[Union[BaseBackend, QuantumInstance]] = None) -> None: """ Args: num_value_qubits: The number of value qubits. num_iterations: The number of iterations the algorithm will search with no improvement. quantum_instance: Instance of selected backend, defaults to Aer's statevector simulator. """ self._num_value_qubits = num_value_qubits self._n_iterations = num_iterations self._quantum_instance = None if quantum_instance is not None: self.quantum_instance = quantum_instance @property def quantum_instance(self) -> QuantumInstance: """The quantum instance to run the circuits. Returns: The quantum instance used in the algorithm. """ return self._quantum_instance @quantum_instance.setter def quantum_instance(self, quantum_instance: Union[BaseBackend, QuantumInstance]) -> None: """Set the quantum instance used to run the circuits. Args: quantum_instance: The quantum instance to be used in the algorithm. """ if isinstance(quantum_instance, BaseBackend): self._quantum_instance = QuantumInstance(quantum_instance) else: self._quantum_instance = quantum_instance def get_compatibility_msg(self, problem: QuadraticProgram) -> str: """Checks whether a given problem can be solved with this optimizer. Checks whether the given problem is compatible, i.e., whether the problem can be converted to a QUBO, and otherwise, returns a message explaining the incompatibility. Args: problem: The optimization problem to check compatibility. Returns: A message describing the incompatibility. """ return QuadraticProgramToQubo.get_compatibility_msg(problem) def solve(self, problem: QuadraticProgram) -> OptimizationResult: """Tries to solves the given problem using the grover optimizer. Runs the optimizer to try to solve the optimization problem. If the problem cannot be, converted to a QUBO, this optimizer raises an exception due to incompatibility. Args: problem: The problem to be solved. Returns: The result of the optimizer applied to the problem. Raises: AttributeError: If the quantum instance has not been set. QiskitOptimizationError: If the problem is incompatible with the optimizer. """ if self.quantum_instance is None: raise AttributeError('The quantum instance or backend has not been set.') # check compatibility and raise exception if incompatible msg = self.get_compatibility_msg(problem) if len(msg) > 0: raise QiskitOptimizationError('Incompatible problem: {}'.format(msg)) # convert problem to QUBO qubo_converter = QuadraticProgramToQubo() problem_ = qubo_converter.encode(problem) # Variables for tracking the optimum. optimum_found = False optimum_key = math.inf optimum_value = math.inf threshold = 0 n_key = len(problem_.variables) n_value = self._num_value_qubits # Variables for tracking the solutions encountered. num_solutions = 2**n_key keys_measured = [] # Variables for result object. func_dict = {} operation_count = {} iteration = 0 # Variables for stopping if we've hit the rotation max. rotations = 0 max_rotations = int(np.ceil(100*np.pi/4)) # Initialize oracle helper object. orig_constant = problem_.objective.constant measurement = not self._quantum_instance.is_statevector opt_prob_converter = QuadraticProgramToNegativeValueOracle(n_value, measurement) while not optimum_found: m = 1 improvement_found = False # Get oracle O and the state preparation operator A for the current threshold. problem_.objective.constant = orig_constant - threshold a_operator, oracle, func_dict = opt_prob_converter.encode(problem_) # Iterate until we measure a negative. loops_with_no_improvement = 0 while not improvement_found: # Determine the number of rotations. loops_with_no_improvement += 1 rotation_count = int(np.ceil(random.uniform(0, m-1))) rotations += rotation_count # Apply Grover's Algorithm to find values below the threshold. if rotation_count > 0: # TODO: Utilize Grover's incremental feature - requires changes to Grover. grover = Grover(oracle, init_state=a_operator, num_iterations=rotation_count) circuit = grover.construct_circuit( measurement=self._quantum_instance.is_statevector) else: circuit = a_operator._circuit # Get the next outcome. outcome = self._measure(circuit, n_key, n_value) k = int(outcome[0:n_key], 2) v = outcome[n_key:n_key + n_value] int_v = self._bin_to_int(v, n_value) + threshold v = self._twos_complement(int_v, n_value) logger.info('Outcome: %s', outcome) logger.info('Value: %s = %s', v, int_v) # If the value is an improvement, we update the iteration parameters (e.g. oracle). if int_v < optimum_value: optimum_key = k optimum_value = int_v logger.info('Current Optimum Key: %s', optimum_key) logger.info('Current Optimum Value: %s', optimum_value) if v.startswith('1'): improvement_found = True threshold = optimum_value else: # Using Durr and Hoyer method, increase m. m = int(np.ceil(min(m * 8/7, 2**(n_key / 2)))) logger.info('No Improvement. M: %s', m) # Check if we've already seen this value. if k not in keys_measured: keys_measured.append(k) # Assume the optimal if any of the stop parameters are true. if loops_with_no_improvement >= self._n_iterations or \ len(keys_measured) == num_solutions or rotations >= max_rotations: improvement_found = True optimum_found = True # Track the operation count. operations = circuit.count_ops() operation_count[iteration] = operations iteration += 1 logger.info('Operation Count: %s\n', operations) # Get original key and value pairs. func_dict[-1] = orig_constant solutions = self._get_qubo_solutions(func_dict, n_key) # If the constant is 0 and we didn't find a negative, the answer is likely 0. if optimum_value >= 0 and orig_constant == 0: optimum_key = 0 opt_x = [1 if s == '1' else 0 for s in ('{0:%sb}' % n_key).format(optimum_key)] # Build the results object. grover_results = GroverOptimizationResults(operation_count, n_key, n_value, func_dict) result = OptimizationResult(x=opt_x, fval=solutions[optimum_key], results={"grover_results": grover_results, "qubo_converter": qubo_converter}) # cast binaries back to integers result = qubo_converter.decode(result) return result def _measure(self, circuit: QuantumCircuit, n_key: int, n_value: int) -> str: """Get probabilities from the given backend, and picks a random outcome.""" probs = self._get_probs(n_key, n_value, circuit) freq = sorted(probs.items(), key=lambda x: x[1], reverse=True) # Pick a random outcome. freq[len(freq)-1] = (freq[len(freq)-1][0], 1 - sum([x[1] for x in freq[0:len(freq)-1]])) idx = np.random.choice(len(freq), 1, p=[x[1] for x in freq])[0] logger.info('Frequencies: %s', freq) return freq[idx][0] def _get_probs(self, n_key: int, n_value: int, qc: QuantumCircuit) -> Dict[str, float]: """Gets probabilities from a given backend.""" # Execute job and filter results. result = self._quantum_instance.execute(qc) if self._quantum_instance.is_statevector: state = np.round(result.get_statevector(qc), 5) keys = [bin(i)[2::].rjust(int(np.log2(len(state))), '0')[::-1] for i in range(0, len(state))] probs = [np.round(abs(a)*abs(a), 5) for a in state] f_hist = dict(zip(keys, probs)) hist = {} for key in f_hist: new_key = key[:n_key] + key[n_key:n_key+n_value][::-1] + key[n_key+n_value:] hist[new_key] = f_hist[key] else: state = result.get_counts(qc) shots = self._quantum_instance.run_config.shots hist = {} for key in state: hist[key[:n_key] + key[n_key:n_key+n_value][::-1] + key[n_key+n_value:]] = \ state[key] / shots hist = dict(filter(lambda p: p[1] > 0, hist.items())) return hist @staticmethod def _twos_complement(v: int, n_bits: int) -> str: """Converts an integer into a binary string of n bits using two's complement.""" assert -2**n_bits <= v < 2**n_bits if v < 0: v += 2**n_bits bin_v = bin(v)[2:] else: format_string = '{0:0'+str(n_bits)+'b}' bin_v = format_string.format(v) return bin_v @staticmethod def _bin_to_int(v: str, num_value_bits: int) -> int: """Converts a binary string of n bits using two's complement to an integer.""" if v.startswith("1"): int_v = int(v, 2) - 2 ** num_value_bits else: int_v = int(v, 2) return int_v @staticmethod def _get_qubo_solutions(function_dict: Dict[Union[int, Tuple[int, int]], int], n_key: int, print_solutions: Optional[bool] = False): """ Calculates all of the outputs of a QUBO function representable by n key qubits. Args: function_dict: A dictionary representation of the function, where the keys correspond to a variable, and the values are the corresponding coefficients. n_key: The number of key qubits. print_solutions: If true, the solutions will be formatted and printed. Returns: dict: A dictionary of the inputs (keys) and outputs (values) of the QUBO function. """ # Determine constant. constant = 0 if -1 in function_dict: constant = function_dict[-1] format_string = '{0:0'+str(n_key)+'b}' # Iterate through every key combination. if print_solutions: print("QUBO Solutions:") print("==========================") solutions = {} for i in range(2**n_key): solution = constant # Convert int to a list of binary variables. bin_key = format_string.format(i) bin_list = [int(bin_key[j]) for j in range(len(bin_key))] # Handle the linear terms. for k in range(len(bin_key)): if bin_list[k] == 1 and k in function_dict: solution += function_dict[k] # Handle the quadratic terms. for j in range(len(bin_key)): for q in range(len(bin_key)): if (j, q) in function_dict and j != q and bin_list[j] == 1 and bin_list[q] == 1: solution += function_dict[(j, q)] # Print row. if print_solutions: spacer = "" if i >= 10 else " " value_spacer = " " if solution < 0 else " " print(spacer + str(i), "=", bin_key, "->" + value_spacer + str(round(solution, 4))) # Record solution. solutions[i] = solution if print_solutions: print() return solutions
class TestWeightedPauliOperator(QiskitAquaTestCase): """WeightedPauliOperator tests.""" def setUp(self): super().setUp() seed = 0 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_sample(len(paulis)) self.qubit_op = WeightedPauliOperator.from_list(paulis, weights) self.var_form = RYRZ(self.qubit_op.num_qubits, 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.from_label(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)) load_op = WeightedPauliOperator.from_file(file_path) self.assertEqual(op, load_op) 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.from_label(pauli_a)] pauli_term_b = [coeff_b, Pauli.from_label(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.from_label(pauli_a)] pauli_term_b = [coeff_b, Pauli.from_label(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.from_label(pauli_a)] pauli_term_b = [coeff_b, Pauli.from_label(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.from_label(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.from_label(pauli_a)] pauli_term_b = [coeff_b, Pauli.from_label(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.from_label(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.from_label(pauli_a)] pauli_term_b = [coeff_b, Pauli.from_label(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.from_label(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.from_label(pauli_a)] pauli_term_b = [coeff_b, Pauli.from_label(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.from_label(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.from_label(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.from_label(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.from_label(pauli_a)] pauli_term_b = [coeff_b, Pauli.from_label(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.from_label(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.from_label(pauli_a)] pauli_term_b = [coeff_b, Pauli.from_label(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.from_label(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.from_label(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.from_label('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.from_label('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.from_label('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.from_label('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.from_label('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.from_label('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.construct_circuit( np.array(aqua_globals.random.randn(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.construct_circuit( np.array(aqua_globals.random.randn(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.construct_circuit( np.array(aqua_globals.random.randn(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) @parameterized.expand([['trotter', 1, 3], ['suzuki', 1, 3]]) def test_evolve(self, expansion_mode, evo_time, num_time_slices): """ evolve test """ expansion_orders = [1, 2, 3, 4] if expansion_mode == 'suzuki' else [1] num_qubits = 2 paulis = [ Pauli.from_label(pauli_label) for pauli_label in itertools.product('IXYZ', repeat=num_qubits) ] weights = aqua_globals.random.random_sample(len(paulis)) pauli_op = WeightedPauliOperator.from_list(paulis, weights) matrix_op = op_converter.to_matrix_operator(pauli_op) state_in = Custom(num_qubits, state='random') # get the exact state_out from raw matrix multiplication state_out_exact = matrix_op.evolve( state_in=state_in.construct_circuit('vector'), evo_time=evo_time, num_time_slices=0) # self.log.debug('exact:\n%s', state_out_exact) self.log.debug('Under %s expansion mode:', expansion_mode) for expansion_order in expansion_orders: # assure every time the operator from the original one if expansion_mode == 'suzuki': self.log.debug('With expansion order %s:', expansion_order) state_out_matrix = matrix_op.evolve( state_in=state_in.construct_circuit('vector'), evo_time=evo_time, num_time_slices=num_time_slices, expansion_mode=expansion_mode, expansion_order=expansion_order) quantum_registers = QuantumRegister(pauli_op.num_qubits, name='q') qc = QuantumCircuit(quantum_registers) qc += state_in.construct_circuit('circuit', quantum_registers) qc += pauli_op.copy().evolve( evo_time=evo_time, num_time_slices=num_time_slices, quantum_registers=quantum_registers, expansion_mode=expansion_mode, expansion_order=expansion_order, ) state_out_circuit = self.quantum_instance_statevector.execute( qc).get_statevector(qc) self.log.debug('The fidelity between exact and matrix: %s', state_fidelity(state_out_exact, state_out_matrix)) self.log.debug('The fidelity between exact and circuit: %s', state_fidelity(state_out_exact, state_out_circuit)) f_mc = state_fidelity(state_out_matrix, state_out_circuit) self.log.debug('The fidelity between matrix and circuit: %s', f_mc) self.assertAlmostEqual(f_mc, 1)
class TestTPBGroupedWeightedPauliOperator(QiskitAquaTestCase): """TPBGroupedWeightedPauliOperator tests.""" def setUp(self): super().setUp() seed = 0 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) warnings.filterwarnings('ignore', category=DeprecationWarning) self.var_form = RYRZ(self.qubit_op.num_qubits, 1) warnings.filterwarnings('always', category=DeprecationWarning) 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.construct_circuit( 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)
class CircuitSampler(ConverterBase): """ The CircuitSampler traverses an Operator and converts any CircuitStateFns into approximations of the state function by a DictStateFn or VectorStateFn using a quantum backend. Note that in order to approximate the value of the CircuitStateFn, it must 1) send state function through a depolarizing channel, which will destroy all phase information and 2) replace the sampled frequencies with **square roots** of the frequency, rather than the raw probability of sampling (which would be the equivalent of sampling the **square** of the state function, per the Born rule. The CircuitSampler aggressively caches transpiled circuits to handle re-parameterization of the same circuit efficiently. If you are converting multiple different Operators, you are better off using a different CircuitSampler for each Operator to avoid cache thrashing. """ def __init__(self, backend: Union[BaseBackend, QuantumInstance] = None, statevector: Optional[bool] = None, param_qobj: bool = False, attach_results: bool = False) -> None: """ Args: backend: The quantum backend or QuantumInstance to use to sample the circuits. statevector: If backend is a statevector backend, whether to replace the CircuitStateFns with DictStateFns (from the counts) or VectorStateFns (from the statevector). ``None`` will set this argument automatically based on the backend. param_qobj: (TODO, not yet available) Whether to use Aer's parameterized Qobj capability to avoid re-assembling the circuits. attach_results: Whether to attach the data from the backend ``Results`` object for a given ``CircuitStateFn``` to an ``execution_results`` field added the converted ``DictStateFn`` or ``VectorStateFn``. Raises: ValueError: Set statevector or param_qobj True when not supported by backend. """ self._quantum_instance = backend if isinstance(backend, QuantumInstance) else\ QuantumInstance(backend=backend) self._statevector = statevector if statevector is not None \ else self.quantum_instance.is_statevector self._param_qobj = param_qobj self._attach_results = attach_results self._check_quantum_instance_and_modes_consistent() # Object state variables self._last_op = None self._reduced_op_cache = None self._circuit_ops_cache = {} self._transpiled_circ_cache = None self._transpile_before_bind = True self._binding_mappings = None def _check_quantum_instance_and_modes_consistent(self) -> None: """ Checks whether the statevector and param_qobj settings are compatible with the backend Raises: ValueError: statevector or param_qobj are True when not supported by backend. """ if self._statevector and not is_statevector_backend( self.quantum_instance.backend): raise ValueError( 'Statevector mode for circuit sampling requires statevector ' 'backend, not {}.'.format(self.quantum_instance.backend)) if self._param_qobj and not is_aer_provider( self.quantum_instance.backend): raise ValueError('Parameterized Qobj mode requires Aer ' 'backend, not {}.'.format( self.quantum_instance.backend)) @property def backend(self) -> BaseBackend: """ Returns the backend. Returns: The backend used by the CircuitSampler """ return self.quantum_instance.backend @backend.setter def backend(self, backend: BaseBackend): """ Sets backend without additional configuration. """ self.set_backend(backend) def set_backend(self, backend: BaseBackend, **kwargs) -> None: """ Sets backend with configuration. Raises: ValueError: statevector or param_qobj are True when not supported by backend. """ self.quantum_instance = QuantumInstance(backend) self.quantum_instance.set_config(**kwargs) @property def quantum_instance(self) -> QuantumInstance: """ Returns the quantum instance. Returns: The QuantumInstance used by the CircuitSampler """ return self._quantum_instance @quantum_instance.setter def quantum_instance( self, quantum_instance: Union[QuantumInstance, BaseBackend]) -> None: """ Sets the QuantumInstance. Raises: ValueError: statevector or param_qobj are True when not supported by backend. """ if isinstance(quantum_instance, BaseBackend): quantum_instance = QuantumInstance(quantum_instance) self._quantum_instance = quantum_instance self._check_quantum_instance_and_modes_consistent() # pylint: disable=arguments-differ def convert( self, operator: OperatorBase, params: Optional[Dict[Union[ParameterExpression, ParameterVector], Union[float, List[float], List[List[float]]]]] = None ) -> OperatorBase: r""" Converts the Operator to one in which the CircuitStateFns are replaced by DictStateFns or VectorStateFns. Extracts the CircuitStateFns out of the Operator, caches them, calls ``sample_circuits`` below to get their converted replacements, and replaces the CircuitStateFns in operator with the replacement StateFns. Args: operator: The Operator to convert params: A dictionary mapping parameters to either single binding values or lists of binding values. The dictionary can also contain pairs of ParameterVectors with lists of parameters or lists of lists of parameters to bind to them. Returns: The converted Operator with CircuitStateFns replaced by DictStateFns or VectorStateFns. """ if self._last_op is None or id(operator) != id(self._last_op): # Clear caches self._last_op = operator self._reduced_op_cache = None self._circuit_ops_cache = None self._transpiled_circ_cache = None self._transpile_before_bind = True if not self._reduced_op_cache: operator_dicts_replaced = operator.to_circuit_op() self._reduced_op_cache = operator_dicts_replaced.reduce() if not self._circuit_ops_cache: self._circuit_ops_cache = {} self._extract_circuitstatefns(self._reduced_op_cache) if params: num_parameterizations = len(list(params.values())[0]) param_bindings = [{ param: value_list[i] for (param, value_list) in params.items() } for i in range(num_parameterizations)] else: param_bindings = None num_parameterizations = 1 # Don't pass circuits if we have in the cache, the sampling function knows to use the cache circs = list(self._circuit_ops_cache.values() ) if not self._transpiled_circ_cache else None sampled_statefn_dicts = self.sample_circuits( circuit_sfns=circs, param_bindings=param_bindings) def replace_circuits_with_dicts(operator, param_index=0): if isinstance(operator, CircuitStateFn): return sampled_statefn_dicts[id(operator)][param_index] elif isinstance(operator, ListOp): return operator.traverse( partial(replace_circuits_with_dicts, param_index=param_index)) else: return operator if params: return ListOp([ replace_circuits_with_dicts(self._reduced_op_cache, param_index=i) for i in range(num_parameterizations) ]) else: return replace_circuits_with_dicts(self._reduced_op_cache, param_index=0) def _extract_circuitstatefns(self, operator: OperatorBase) -> None: r""" Recursively extract the ``CircuitStateFns`` contained in operator into the ``_circuit_ops_cache`` field. """ if isinstance(operator, CircuitStateFn): self._circuit_ops_cache[id(operator)] = operator elif isinstance(operator, ListOp): for op in operator.oplist: self._extract_circuitstatefns(op) def sample_circuits( self, circuit_sfns: Optional[List[CircuitStateFn]] = None, param_bindings: Optional[List[Dict[ParameterExpression, List[float]]]] = None ) -> Dict[int, Union[StateFn, List[StateFn]]]: r""" Samples the CircuitStateFns and returns a dict associating their ``id()`` values to their replacement DictStateFn or VectorStateFn. If param_bindings is provided, the CircuitStateFns are broken into their parameterizations, and a list of StateFns is returned in the dict for each circuit ``id()``. Note that param_bindings is provided here in a different format than in ``convert``, and lists of parameters within the dict is not supported, and only binding dicts which are valid to be passed into Terra can be included in this list. Args: circuit_sfns: The list of CircuitStateFns to sample. param_bindings: The parameterizations to bind to each CircuitStateFn. Returns: The dictionary mapping ids of the CircuitStateFns to their replacement StateFns. """ if circuit_sfns or not self._transpiled_circ_cache: if self._statevector: circuits = [ op_c.to_circuit(meas=False) for op_c in circuit_sfns ] else: circuits = [ op_c.to_circuit(meas=True) for op_c in circuit_sfns ] try: self._transpiled_circ_cache = self.quantum_instance.transpile( circuits) except QiskitError: logger.debug( r'CircuitSampler failed to transpile circuits with unbound ' r'parameters. Attempting to transpile only when circuits are bound ' r'now, but this can hurt performance due to repeated transpilation.' ) self._transpile_before_bind = False self._transpiled_circ_cache = circuits else: circuit_sfns = list(self._circuit_ops_cache.values()) if param_bindings is not None: if self._param_qobj: ready_circs = self._transpiled_circ_cache self._prepare_parameterized_run_config(param_bindings) else: ready_circs = [ circ.assign_parameters(binding) for circ in self._transpiled_circ_cache for binding in param_bindings ] else: ready_circs = self._transpiled_circ_cache results = self.quantum_instance.execute( ready_circs, had_transpiled=self._transpile_before_bind) # Wipe parameterizations, if any # self.quantum_instance._run_config.parameterizations = None sampled_statefn_dicts = {} for i, op_c in enumerate(circuit_sfns): # Taking square root because we're replacing a statevector # representation of probabilities. reps = len(param_bindings) if param_bindings is not None else 1 c_statefns = [] for j in range(reps): circ_index = (i * reps) + j circ_results = results.data(circ_index) if 'expval_measurement' in circ_results.get( 'snapshots', {}).get('expectation_value', {}): snapshot_data = results.data(circ_index)['snapshots'] avg = snapshot_data['expectation_value'][ 'expval_measurement'][0]['value'] if isinstance(avg, (list, tuple)): # Aer versions before 0.4 use a list snapshot format # which must be converted to a complex value. avg = avg[0] + 1j * avg[1] # Will be replaced with just avg when eval is called later num_qubits = circuit_sfns[0].num_qubits result_sfn = (Zero ^ num_qubits).adjoint() * avg elif self._statevector: result_sfn = StateFn(op_c.coeff * results.get_statevector(circ_index)) else: shots = self.quantum_instance._run_config.shots result_sfn = StateFn({ b: (v * op_c.coeff / shots)**.5 for (b, v) in results.get_counts(circ_index).items() }) if self._attach_results: result_sfn.execution_results = circ_results c_statefns.append(result_sfn) sampled_statefn_dicts[id(op_c)] = c_statefns return sampled_statefn_dicts # TODO build Aer re-parameterized Qobj. def _prepare_parameterized_run_config(self, param_bindings: dict) -> None: raise NotImplementedError
class CircuitSampler(ConverterBase): """ The CircuitSampler traverses an Operator and converts any CircuitStateFns into approximations of the state function by a DictStateFn or VectorStateFn using a quantum backend. Note that in order to approximate the value of the CircuitStateFn, it must 1) send state function through a depolarizing channel, which will destroy all phase information and 2) replace the sampled frequencies with **square roots** of the frequency, rather than the raw probability of sampling (which would be the equivalent of sampling the **square** of the state function, per the Born rule. The CircuitSampler aggressively caches transpiled circuits to handle re-parameterization of the same circuit efficiently. If you are converting multiple different Operators, you are better off using a different CircuitSampler for each Operator to avoid cache thrashing. """ def __init__(self, backend: Union[Backend, BaseBackend, QuantumInstance], statevector: Optional[bool] = None, param_qobj: bool = False, attach_results: bool = False) -> None: """ Args: backend: The quantum backend or QuantumInstance to use to sample the circuits. statevector: If backend is a statevector backend, whether to replace the CircuitStateFns with DictStateFns (from the counts) or VectorStateFns (from the statevector). ``None`` will set this argument automatically based on the backend. attach_results: Whether to attach the data from the backend ``Results`` object for a given ``CircuitStateFn``` to an ``execution_results`` field added the converted ``DictStateFn`` or ``VectorStateFn``. param_qobj: Whether to use Aer's parameterized Qobj capability to avoid re-assembling the circuits. Raises: ValueError: Set statevector or param_qobj True when not supported by backend. """ self._quantum_instance = backend if isinstance(backend, QuantumInstance) else\ QuantumInstance(backend=backend) self._statevector = statevector if statevector is not None \ else self.quantum_instance.is_statevector self._param_qobj = param_qobj self._attach_results = attach_results self._check_quantum_instance_and_modes_consistent() # Object state variables self._last_op = None self._reduced_op_cache = None self._circuit_ops_cache = {} # type: Dict[int, CircuitStateFn] self._transpiled_circ_cache = None # type: Optional[List[Any]] self._transpiled_circ_templates = None # type: Optional[List[Any]] self._transpile_before_bind = True self._binding_mappings = None def _check_quantum_instance_and_modes_consistent(self) -> None: """ Checks whether the statevector and param_qobj settings are compatible with the backend Raises: ValueError: statevector or param_qobj are True when not supported by backend. """ if self._statevector and not is_statevector_backend( self.quantum_instance.backend): raise ValueError( 'Statevector mode for circuit sampling requires statevector ' 'backend, not {}.'.format(self.quantum_instance.backend)) if self._param_qobj and not is_aer_provider( self.quantum_instance.backend): raise ValueError('Parameterized Qobj mode requires Aer ' 'backend, not {}.'.format( self.quantum_instance.backend)) @property def backend(self) -> Union[Backend, BaseBackend]: """ Returns the backend. Returns: The backend used by the CircuitSampler """ return self.quantum_instance.backend @backend.setter def backend(self, backend: Union[Backend, BaseBackend]): """ Sets backend without additional configuration. """ self.set_backend(backend) def set_backend(self, backend: Union[Backend, BaseBackend], **kwargs) -> None: """ Sets backend with configuration. Raises: ValueError: statevector or param_qobj are True when not supported by backend. """ self.quantum_instance = QuantumInstance(backend) self.quantum_instance.set_config(**kwargs) @property def quantum_instance(self) -> QuantumInstance: """ Returns the quantum instance. Returns: The QuantumInstance used by the CircuitSampler """ return self._quantum_instance @quantum_instance.setter def quantum_instance( self, quantum_instance: Union[QuantumInstance, Backend, BaseBackend]) -> None: """ Sets the QuantumInstance. Raises: ValueError: statevector or param_qobj are True when not supported by backend. """ if isinstance(quantum_instance, (Backend, BaseBackend)): quantum_instance = QuantumInstance(quantum_instance) self._quantum_instance = quantum_instance self._check_quantum_instance_and_modes_consistent() # pylint: disable=arguments-differ def convert( self, operator: OperatorBase, params: Optional[Dict[Parameter, Union[float, List[float], List[List[float]]]]] = None ) -> OperatorBase: r""" Converts the Operator to one in which the CircuitStateFns are replaced by DictStateFns or VectorStateFns. Extracts the CircuitStateFns out of the Operator, caches them, calls ``sample_circuits`` below to get their converted replacements, and replaces the CircuitStateFns in operator with the replacement StateFns. Args: operator: The Operator to convert params: A dictionary mapping parameters to either single binding values or lists of binding values. Returns: The converted Operator with CircuitStateFns replaced by DictStateFns or VectorStateFns. Raises: AquaError: if extracted circuits are empty. """ if self._last_op is None or id(operator) != id(self._last_op): # Clear caches self._last_op = operator self._reduced_op_cache = None self._circuit_ops_cache = None self._transpiled_circ_cache = None self._transpile_before_bind = True if not self._reduced_op_cache: operator_dicts_replaced = operator.to_circuit_op() self._reduced_op_cache = operator_dicts_replaced.reduce() if not self._circuit_ops_cache: self._circuit_ops_cache = {} self._extract_circuitstatefns(self._reduced_op_cache) if not self._circuit_ops_cache: raise AquaError( 'Circuits are empty. ' 'Check that the operator is an instance of CircuitStateFn or its ListOp.' ) if params is not None and len(params.keys()) > 0: p_0 = list(params.values())[0] # type: ignore if isinstance(p_0, (list, np.ndarray)): num_parameterizations = len(cast(List, p_0)) param_bindings = [ { param: value_list[i] # type: ignore for (param, value_list) in params.items() } for i in range(num_parameterizations) ] else: num_parameterizations = 1 param_bindings = [params] # type: ignore else: param_bindings = None num_parameterizations = 1 # Don't pass circuits if we have in the cache, the sampling function knows to use the cache circs = list(self._circuit_ops_cache.values() ) if not self._transpiled_circ_cache else None p_b = cast(List[Dict[Parameter, float]], param_bindings) sampled_statefn_dicts = self.sample_circuits(circuit_sfns=circs, param_bindings=p_b) def replace_circuits_with_dicts(operator, param_index=0): if isinstance(operator, CircuitStateFn): return sampled_statefn_dicts[id(operator)][param_index] elif isinstance(operator, ListOp): return operator.traverse( partial(replace_circuits_with_dicts, param_index=param_index)) else: return operator if params: return ListOp([ replace_circuits_with_dicts(self._reduced_op_cache, param_index=i) for i in range(num_parameterizations) ]) else: return replace_circuits_with_dicts(self._reduced_op_cache, param_index=0) def _extract_circuitstatefns(self, operator: OperatorBase) -> None: r""" Recursively extract the ``CircuitStateFns`` contained in operator into the ``_circuit_ops_cache`` field. """ if isinstance(operator, CircuitStateFn): self._circuit_ops_cache[id(operator)] = operator elif isinstance(operator, ListOp): for op in operator.oplist: self._extract_circuitstatefns(op) def sample_circuits( self, circuit_sfns: Optional[List[CircuitStateFn]] = None, param_bindings: Optional[List[Dict[Parameter, float]]] = None ) -> Dict[int, Union[StateFn, List[StateFn]]]: r""" Samples the CircuitStateFns and returns a dict associating their ``id()`` values to their replacement DictStateFn or VectorStateFn. If param_bindings is provided, the CircuitStateFns are broken into their parameterizations, and a list of StateFns is returned in the dict for each circuit ``id()``. Note that param_bindings is provided here in a different format than in ``convert``, and lists of parameters within the dict is not supported, and only binding dicts which are valid to be passed into Terra can be included in this list. Args: circuit_sfns: The list of CircuitStateFns to sample. param_bindings: The parameterizations to bind to each CircuitStateFn. Returns: The dictionary mapping ids of the CircuitStateFns to their replacement StateFns. Raises: AquaError: if extracted circuits are empty. """ if not circuit_sfns and not self._transpiled_circ_cache: raise AquaError('CircuitStateFn is empty and there is no cache.') if circuit_sfns: self._transpiled_circ_templates = None if self._statevector: circuits = [ op_c.to_circuit(meas=False) for op_c in circuit_sfns ] else: circuits = [ op_c.to_circuit(meas=True) for op_c in circuit_sfns ] try: self._transpiled_circ_cache = self.quantum_instance.transpile( circuits) except QiskitError: logger.debug( r'CircuitSampler failed to transpile circuits with unbound ' r'parameters. Attempting to transpile only when circuits are bound ' r'now, but this can hurt performance due to repeated transpilation.' ) self._transpile_before_bind = False self._transpiled_circ_cache = circuits else: circuit_sfns = list(self._circuit_ops_cache.values()) if param_bindings is not None: if self._param_qobj: start_time = time() ready_circs = self._prepare_parameterized_run_config( param_bindings) end_time = time() logger.debug('Parameter conversion %.5f (ms)', (end_time - start_time) * 1000) else: start_time = time() ready_circs = [ circ.assign_parameters(_filter_params(circ, binding)) for circ in self._transpiled_circ_cache for binding in param_bindings ] end_time = time() logger.debug('Parameter binding %.5f (ms)', (end_time - start_time) * 1000) else: ready_circs = self._transpiled_circ_cache results = self.quantum_instance.execute( ready_circs, had_transpiled=self._transpile_before_bind) if param_bindings is not None and self._param_qobj: self._clean_parameterized_run_config() # Wipe parameterizations, if any # self.quantum_instance._run_config.parameterizations = None sampled_statefn_dicts = {} for i, op_c in enumerate(circuit_sfns): # Taking square root because we're replacing a statevector # representation of probabilities. reps = len(param_bindings) if param_bindings is not None else 1 c_statefns = [] for j in range(reps): circ_index = (i * reps) + j circ_results = results.data(circ_index) if 'expval_measurement' in circ_results.get( 'snapshots', {}).get('expectation_value', {}): snapshot_data = results.data(circ_index)['snapshots'] avg = snapshot_data['expectation_value'][ 'expval_measurement'][0]['value'] if isinstance(avg, (list, tuple)): # Aer versions before 0.4 use a list snapshot format # which must be converted to a complex value. avg = avg[0] + 1j * avg[1] # Will be replaced with just avg when eval is called later num_qubits = circuit_sfns[0].num_qubits result_sfn = DictStateFn( '0' * num_qubits, is_measurement=op_c.is_measurement) * avg elif self._statevector: result_sfn = StateFn(op_c.coeff * results.get_statevector(circ_index), is_measurement=op_c.is_measurement) else: shots = self.quantum_instance._run_config.shots result_sfn = StateFn( { b: (v / shots)**0.5 * op_c.coeff for (b, v) in results.get_counts(circ_index).items() }, is_measurement=op_c.is_measurement) if self._attach_results: result_sfn.execution_results = circ_results c_statefns.append(result_sfn) sampled_statefn_dicts[id(op_c)] = c_statefns return sampled_statefn_dicts def _build_aer_params(self, circuit: QuantumCircuit, building_param_tables: Dict[Tuple[int, int], List[float]], input_params: Dict[Parameter, float]) -> None: def resolve_param(inst_param): if not isinstance(inst_param, ParameterExpression): return None param_mappings = {} for param in inst_param._parameter_symbols.keys(): if param not in input_params: raise ValueError('unexpected parameter: {0}'.format(param)) param_mappings[param] = input_params[param] return float(inst_param.bind(param_mappings)) gate_index = 0 for inst, _, _ in circuit.data: param_index = 0 for inst_param in inst.params: val = resolve_param(inst_param) if val is not None: param_key = (gate_index, param_index) if param_key in building_param_tables: building_param_tables[param_key].append(val) else: building_param_tables[param_key] = [val] param_index += 1 gate_index += 1 def _prepare_parameterized_run_config( self, param_bindings: List[Dict[Parameter, float]]) -> List[Any]: self.quantum_instance._run_config.parameterizations = [] if self._transpiled_circ_templates is None \ or len(self._transpiled_circ_templates) != len(self._transpiled_circ_cache): # temporally resolve parameters of self._transpiled_circ_cache # They will be overridden in Aer from the next iterations self._transpiled_circ_templates = [ circ.assign_parameters(_filter_params(circ, param_bindings[0])) for circ in self._transpiled_circ_cache ] for circ in self._transpiled_circ_cache: building_param_tables = { } # type: Dict[Tuple[int, int], List[float]] for param_binding in param_bindings: self._build_aer_params(circ, building_param_tables, param_binding) param_tables = [] for gate_and_param_indices in building_param_tables: gate_index = gate_and_param_indices[0] param_index = gate_and_param_indices[1] param_tables.append([[gate_index, param_index], building_param_tables[(gate_index, param_index)]]) self.quantum_instance._run_config.parameterizations.append( param_tables) return self._transpiled_circ_templates def _clean_parameterized_run_config(self) -> None: self.quantum_instance._run_config.parameterizations = []