def _validate(self, qobj): """Semantic validations of the qobj which cannot be done via schemas. Some of these may later move to backend schemas. 1. Set shots=1. 2. Check number of qubits will fit in local memory. """ n_qubits = qobj.config.n_qubits max_qubits = self.configuration().n_qubits if n_qubits > max_qubits: raise AerError('Number of qubits ({}) '.format(n_qubits) + 'is greater than maximum ({}) '.format(max_qubits) + 'for "{}" '.format(self.name()) + 'with {} GB system memory.'.format(int(local_hardware_info()['memory']))) if qobj.config.shots != 1: logger.info('"%s" only supports 1 shot. Setting shots=1.', self.name()) qobj.config.shots = 1 for experiment in qobj.experiments: name = experiment.header.name if getattr(experiment.config, 'shots', 1) != 1: logger.info('"%s" only supports 1 shot. ' 'Setting shots=1 for circuit "%s".', self.name(), name) experiment.config.shots = 1
def _validate(self, qobj, backend_options, noise_model): """Semantic validations of the qobj which cannot be done via schemas. Some of these may later move to backend schemas. 1. Set shots=1 2. No measurements or reset 3. Check number of qubits will fit in local memory. """ name = self.name() if noise_model is not None: logger.error("{} cannot be run with a noise.".format(name)) raise AerError("{} does not support noise.".format(name)) n_qubits = qobj.config.n_qubits max_qubits = self.configuration().n_qubits if n_qubits > max_qubits: raise AerError('Number of qubits ({}) '.format(n_qubits) + 'is greater than maximum ({}) '.format(max_qubits) + 'for "{}" '.format(name) + 'with {} GB system memory.'.format( int(local_hardware_info()['memory']))) if qobj.config.shots != 1: logger.info('"%s" only supports 1 shot. Setting shots=1.', name) qobj.config.shots = 1 for experiment in qobj.experiments: exp_name = experiment.header.name if getattr(experiment.config, 'shots', 1) != 1: logger.info( '"%s" only supports 1 shot. ' 'Setting shots=1 for circuit "%s".', name, exp_name) experiment.config.shots = 1 for operation in experiment.instructions: if operation.name in ['measure', 'reset']: raise AerError( 'Unsupported "%s" instruction "%s" ' + 'in circuit "%s" ', name, operation.name, exp_name)
def _validate(self, qobj, backend_options, noise_model): """Semantic validations of the qobj which cannot be done via schemas. 1. Check number of qubits will fit in local memory. 2. warn if no classical registers or measurements in circuit. """ clifford_instructions = [ "id", "x", "y", "z", "h", "s", "sdg", "CX", "cx", "cz", "swap", "barrier", "reset", "measure" ] # Check if noise model is Clifford: method = "automatic" if backend_options and "method" in backend_options: method = backend_options["method"] clifford_noise = not (method == "statevector") if clifford_noise: if method != "stabilizer" and noise_model: for error in noise_model.as_dict()['errors']: if error['type'] == 'qerror': for circ in error["instructions"]: for instr in circ: if instr not in clifford_instructions: clifford_noise = False break # Check to see if experiments are clifford for experiment in qobj.experiments: name = experiment.header.name # Check for classical bits if experiment.config.memory_slots == 0: logger.warning( 'No classical registers in circuit "%s": ' 'result data will not contain counts.', name) # Check if Clifford circuit or if measure opts missing no_measure = True clifford = False if method == "statevector" else clifford_noise for op in experiment.instructions: if not clifford and not no_measure: break # we don't need to check any more ops if clifford and op.name not in clifford_instructions: clifford = False if no_measure and op.name == "measure": no_measure = False # Print warning if clbits but no measure if no_measure: logger.warning( 'No measurements in circuit "%s": ' 'count data will return all zeros.', name) # Check qubits for statevector simulation if not clifford: n_qubits = experiment.config.n_qubits max_qubits = self.configuration().n_qubits if n_qubits > max_qubits: system_memory = int(local_hardware_info()['memory']) raise AerError( 'Number of qubits ({}) '.format(n_qubits) + 'is greater than maximum ({}) '.format(max_qubits) + 'for "{}" (method=statevector) '.format(self.name()) + 'with {} GB system memory.'.format(system_memory))
def _format_qobj_str(self, qobj, backend_options, noise_model): """Format qobj string for qiskit aer controller""" # Save original qobj config so we can revert our modification # after execution original_config = qobj.config # Convert to dictionary and add new parameters # from noise model and backend options config = original_config.as_dict() if backend_options is not None: for key, val in backend_options.items(): config[key] = val if not "available_memory" in config: available_mb = int(local_hardware_info()['memory'] * 1024) config['available_memory'] = available_mb # Add noise model if noise_model is not None: config["noise_model"] = noise_model # Add runtime config config['library_dir'] = self.configuration().library_dir qobj.config = QobjConfig.from_dict(config) # Get the JSON serialized string output = json.dumps(qobj, cls=AerJSONEncoder).encode('UTF-8') # Revert original qobj qobj.config = original_config # Return output return output
class QiskitAquaGlobals(object): """Aqua class for global properties.""" CPU_COUNT = local_hardware_info()['cpus'] def __init__(self): self._random_seed = None self._num_processes = QiskitAquaGlobals.CPU_COUNT self._random = None @property def random_seed(self): """Return random seed.""" return self._random_seed @random_seed.setter def random_seed(self, seed): """Set random seed.""" self._random_seed = seed self._random = None @property def num_processes(self): """Return num processes.""" return self._num_processes @num_processes.setter def num_processes(self, num_processes): """Set num processes.""" if num_processes < 1: raise AquaError('Invalid Number of Processes {}.'.format(num_processes)) if num_processes > QiskitAquaGlobals.CPU_COUNT: raise AquaError('Number of Processes {} cannot be greater than cpu count {}.'.format(num_processes, QiskitAquaGlobals._CPU_COUNT)) self._num_processes = num_processes # TODO: change Terra CPU_COUNT until issue gets resolved: https://github.com/Qiskit/qiskit-terra/issues/1963 try: qiskit.tools.parallel.CPU_COUNT = self.num_processes except Exception as e: logger.warning("Failed to set qiskit.tools.parallel.CPU_COUNT to value: '{}': Error: '{}'".format(self.num_processes, str(e))) @property def random(self): """Return a numpy random.""" if self._random is None: if self._random_seed is None: self._random = np.random else: self._random = np.random.RandomState(self._random_seed) return self._random
def _validate(self, qobj): """Semantic validations of the qobj which cannot be done via schemas. 1. Check number of qubits will fit in local memory. 2. warn if no classical registers or measurements in circuit. """ n_qubits = qobj.config.n_qubits max_qubits = self.configuration().n_qubits if n_qubits > max_qubits: raise AerError('Number of qubits ({}) '.format(n_qubits) + 'is greater than maximum ({}) '.format(max_qubits) + 'for "{}" '.format(self.name()) + 'with {} GB system memory.'.format(int(local_hardware_info()['memory']))) for experiment in qobj.experiments: name = experiment.header.name if experiment.config.memory_slots == 0: logger.warning('No classical registers in circuit "%s": ' 'result data will not contain counts.', name) elif 'measure' not in [op.name for op in experiment.instructions]: logger.warning('No measurements in circuit "%s": ' 'count data will return all zeros.', name)
class StatevectorSimulatorPy(QasmSimulatorPy): """Python statevector simulator.""" MAX_QUBITS_MEMORY = int(log2(local_hardware_info()['memory'] * (1024 ** 3) / 16)) DEFAULT_CONFIGURATION = { 'backend_name': 'statevector_simulator', 'backend_version': '1.0.0', 'n_qubits': min(24, MAX_QUBITS_MEMORY), 'url': 'https://github.com/Qiskit/qiskit-terra', 'simulator': True, 'local': True, 'conditional': True, 'open_pulse': False, 'memory': True, 'max_shots': 65536, 'description': 'A Python statevector simulator for qobj files', 'basis_gates': ['u1', 'u2', 'u3', 'cx', 'id', 'snapshot'], 'gates': [ { 'name': 'u1', 'parameters': ['lambda'], 'qasm_def': 'gate u1(lambda) q { U(0,0,lambda) q; }' }, { 'name': 'u2', 'parameters': ['phi', 'lambda'], 'qasm_def': 'gate u2(phi,lambda) q { U(pi/2,phi,lambda) q; }' }, { 'name': 'u3', 'parameters': ['theta', 'phi', 'lambda'], 'qasm_def': 'gate u3(theta,phi,lambda) q { U(theta,phi,lambda) q; }' }, { 'name': 'cx', 'parameters': ['c', 't'], 'qasm_def': 'gate cx c,t { CX c,t; }' }, { 'name': 'id', 'parameters': ['a'], 'qasm_def': 'gate id a { U(0,0,0) a; }' }, { 'name': 'snapshot', 'parameters': ['slot'], 'qasm_def': 'gate snapshot(slot) q { TODO }' } ] } # Override base class value to return the final state vector SHOW_FINAL_STATE = True def __init__(self, configuration=None, provider=None): super().__init__(configuration=(configuration or BackendConfiguration.from_dict(self.DEFAULT_CONFIGURATION)), provider=provider) def run(self, qobj, backend_options=None): """Run qobj asynchronously. Args: qobj (Qobj): payload of the experiment backend_options (dict): backend options Returns: SimulatorsJob: derived from BaseJob Additional Information: backend_options: Is a dict of options for the backend. It may contain * "initial_statevector": vector_like * "chop_threshold": double The "initial_statevector" option specifies a custom initial initial statevector for the simulator to be used instead of the all zero state. This size of this vector must be correct for the number of qubits in all experiments in the qobj. The "chop_threshold" option specifies a trunctation value for setting small values to zero in the output statevector. The default value is 1e-15. Example: backend_options = { "initial_statevector": np.array([1, 0, 0, 1j]) / np.sqrt(2), "chop_threshold": 1e-15 } """ return super().run(qobj, backend_options=backend_options) def _validate(self, qobj): """Semantic validations of the qobj which cannot be done via schemas. Some of these may later move to backend schemas. 1. No shots 2. No measurements in the middle """ n_qubits = qobj.config.n_qubits max_qubits = self.configuration().n_qubits if n_qubits > max_qubits: raise SimulatorError('Number of qubits {} '.format(n_qubits) + 'is greater than maximum ({}) '.format(max_qubits) + 'for "{}".'.format(self.name())) if qobj.config.shots != 1: logger.info('"%s" only supports 1 shot. Setting shots=1.', self.name()) qobj.config.shots = 1 for experiment in qobj.experiments: name = experiment.header.name if getattr(experiment.config, 'shots', 1) != 1: logger.info('"{}" only supports 1 shot. ' + 'Setting shots=1 for circuit "{}".', self.name(), name) experiment.config.shots = 1
class UnitarySimulator(AerBackend): """Unitary circuit simulator. Backend options: The following backend options may be used with in the `backend_options` kwarg diction for `UnitarySimulator.run` or `qiskit.execute` * "initial_unitary" (matrix_like): Sets a custom initial unitary matrix for the simulation instead of identity (Default: None). * "chop_threshold" (double): Sets the threshold for truncating small values to zero in the Result data (Default: 1e-15) * "max_parallel_threads" (int): Sets the maximum number of CPU cores used by OpenMP for parallelization. If set to 0 the maximum will be set to the number of CPU cores (Default: 0). * "max_parallel_experiments" (int): Sets the maximum number of qobj experiments that may be executed in parallel up to the max_parallel_threads value. If set to 1 parallel circuit execution will be disabled. If set to 0 the maximum will be automatically set to max_parallel_threads (Default: 1). * "unitary_parallel_threshold" (int): Sets the threshold that "n_qubits" must be greater than to enable OpenMP parallelization for matrix multiplication during execution of an experiment. If parallel circuit execution is enabled this will only use unallocated CPU cores up to max_parallel_threads. Note that setting this too low can reduce performance (Default: 6). """ MAX_QUBITS_MEMORY = int( log2(sqrt(local_hardware_info()['memory'] * (1024**3) / 16))) DEFAULT_CONFIGURATION = { 'backend_name': 'unitary_simulator', 'backend_version': __version__, 'n_qubits': MAX_QUBITS_MEMORY, 'url': 'https://github.com/Qiskit/qiskit-aer', 'simulator': True, 'local': True, 'conditional': False, 'open_pulse': False, 'memory': False, 'max_shots': 1, 'description': 'A Python simulator for computing the unitary' + 'matrix for experiments in qobj files', 'basis_gates': [ 'u1', 'u2', 'u3', 'cx', 'cz', 'id', 'x', 'y', 'z', 'h', 's', 'sdg', 't', 'tdg', 'ccx', 'swap', 'snapshot', 'unitary' ], 'gates': [{ 'name': 'TODO', 'parameters': [], 'qasm_def': 'TODO' }] } def __init__(self, configuration=None, provider=None): super().__init__(unitary_controller_execute, BackendConfiguration.from_dict( self.DEFAULT_CONFIGURATION), provider=provider) def run(self, qobj, backend_options=None): """Run a qobj on the backend. Args: qobj (Qobj): a Qobj. backend_options (dict): backend configuration options. Returns: AerJob: the simulation job. """ return super().run(qobj, backend_options=backend_options) def _validate(self, qobj): """Semantic validations of the qobj which cannot be done via schemas. Some of these may later move to backend schemas. 1. No shots 2. No measurements or reset """ if qobj.config.shots != 1: logger.info("UnitarySimulator only supports 1 shot. " "Setting shots=1.") qobj.config.shots = 1 for experiment in qobj.experiments: # Check for measure or reset operations for pos, instr in reversed(list(enumerate( experiment.instructions))): if instr.name == "measure": raise AerError( "UnitarySimulator: circuit contains measure.") if instr.name == "reset": raise AerError("UnitarySimulator: circuit contains reset.") # Set shots to 1 if getattr(experiment.config, 'shots', 1) != 1: logger.info( "UnitarySimulator only supports 1 shot. " "Setting shots=1 for circuit %s.", experiment.header.name) experiment.config.shots = 1 # Set memory slots to 0 if getattr(experiment.config, 'memory_slots', 0) != 0: experiment.config.memory_slots = 0
class UnitarySimulator(AerBackend): """Unitary circuit simulator. Backend options: The following backend options may be used with in the `backend_options` kwarg diction for `UnitarySimulator.run` or `qiskit.execute` * "initial_unitary" (matrix_like): Sets a custom initial unitary matrix for the simulation instead of identity (Default: None). * "chop_threshold" (double): Sets the threshold for truncating small values to zero in the Result data (Default: 1e-15) * "max_parallel_threads" (int): Sets the maximum number of CPU cores used by OpenMP for parallelization. If set to 0 the maximum will be set to the number of CPU cores (Default: 0). * "max_parallel_experiments" (int): Sets the maximum number of qobj experiments that may be executed in parallel up to the max_parallel_threads value. If set to 1 parallel circuit execution will be disabled. If set to 0 the maximum will be automatically set to max_parallel_threads (Default: 1). * "unitary_parallel_threshold" (int): Sets the threshold that "n_qubits" must be greater than to enable OpenMP parallelization for matrix multiplication during execution of an experiment. If parallel circuit execution is enabled this will only use unallocated CPU cores up to max_parallel_threads. Note that setting this too low can reduce performance (Default: 6). """ MAX_QUBIT_MEMORY = int( log2(sqrt(local_hardware_info()['memory'] * (1024**3) / 16))) DEFAULT_CONFIGURATION = { 'backend_name': 'unitary_simulator', 'backend_version': __version__, 'n_qubits': MAX_QUBIT_MEMORY, 'url': 'https://github.com/Qiskit/qiskit-aer', 'simulator': True, 'local': True, 'conditional': False, 'open_pulse': False, 'memory': False, 'max_shots': 1, 'description': 'A Python simulator for computing the unitary' + 'matrix for experiments in qobj files', 'coupling_map': None, 'basis_gates': [ 'u1', 'u2', 'u3', 'cx', 'cz', 'id', 'x', 'y', 'z', 'h', 's', 'sdg', 't', 'tdg', 'ccx', 'swap', 'snapshot', 'unitary' ], 'gates': [{ 'name': 'TODO', 'parameters': [], 'qasm_def': 'TODO' }], # Location where we put external libraries that will be loaded at runtime # by the simulator extension 'library_dir': os.path.dirname(__file__) } def __init__(self, configuration=None, provider=None): super().__init__(unitary_controller_execute, BackendConfiguration.from_dict( self.DEFAULT_CONFIGURATION), provider=provider) def _validate(self, qobj, backend_options, noise_model): """Semantic validations of the qobj which cannot be done via schemas. Some of these may later move to backend schemas. 1. Set shots=1 2. No measurements or reset 3. Check number of qubits will fit in local memory. """ name = self.name() if noise_model is not None: logger.error("{} cannot be run with a noise.".format(name)) raise AerError("{} does not support noise.".format(name)) n_qubits = qobj.config.n_qubits max_qubits = self.configuration().n_qubits if n_qubits > max_qubits: raise AerError('Number of qubits ({}) '.format(n_qubits) + 'is greater than maximum ({}) '.format(max_qubits) + 'for "{}" '.format(name) + 'with {} GB system memory.'.format( int(local_hardware_info()['memory']))) if qobj.config.shots != 1: logger.info('"%s" only supports 1 shot. Setting shots=1.', name) qobj.config.shots = 1 for experiment in qobj.experiments: exp_name = experiment.header.name if getattr(experiment.config, 'shots', 1) != 1: logger.info( '"%s" only supports 1 shot. ' 'Setting shots=1 for circuit "%s".', name, exp_name) experiment.config.shots = 1 for operation in experiment.instructions: if operation.name in ['measure', 'reset']: raise AerError( 'Unsupported "%s" instruction "%s" ' + 'in circuit "%s" ', name, operation.name, exp_name)
class QasmSimulator(BaseBackend): """C++ quantum circuit simulator with realistic noise""" DEFAULT_CONFIGURATION = { 'backend_name': 'qasm_simulator', 'backend_version': '1.0.0', 'n_qubits': int(log2(local_hardware_info()['memory'] * (1024**3) / 16)), 'url': 'https://github.com/Qiskit/qiskit-terra/src/qasm-simulator-cpp', 'simulator': True, 'local': True, 'conditional': True, 'open_pulse': False, 'memory': True, 'max_shots': 65536, 'description': 'A C++ realistic noise simulator for qasm experiments', 'basis_gates': [ 'u0', 'u1', 'u2', 'u3', 'cx', 'cz', 'id', 'x', 'y', 'z', 'h', 's', 'sdg', 't', 'tdg', 'rzz', 'snapshot', 'wait', 'noise', 'save', 'load' ], 'gates': [{ 'name': 'TODO', 'parameters': [], 'qasm_def': 'TODO' }] } def __init__(self, configuration=None, provider=None): super().__init__(configuration=(configuration or BackendConfiguration.from_dict( self.DEFAULT_CONFIGURATION)), provider=provider) # Try to use the default executable if not specified. if 'exe' in self._configuration: paths = [self._configuration.exe] else: paths = DEFAULT_SIMULATOR_PATHS # Ensure that the executable is available. try: self._configuration.exe = next( path for path in paths if (os.path.exists(path) and os.path.getsize(path) > 100)) except StopIteration: raise FileNotFoundError( 'Simulator executable not found (using %s)' % getattr(self._configuration, 'exe', 'default locations')) def properties(self): """Return backend properties""" properties = { 'backend_name': self.name(), 'backend_version': self.configuration().backend_version, 'last_update_date': '2000-01-01 00:00:00Z', 'qubits': [[{ 'name': 'TODO', 'date': '2000-01-01 00:00:00Z', 'unit': 'TODO', 'value': 0 }]], 'gates': [{ 'qubits': [0], 'gate': 'TODO', 'parameters': [{ 'name': 'TODO', 'date': '2000-01-01 00:00:00Z', 'unit': 'TODO', 'value': 0 }] }], 'general': [] } return BackendProperties.from_dict(properties) def run(self, qobj): """Run a qobj on the backend.""" job_id = str(uuid.uuid4()) aer_job = AerJob(self, job_id, self._run_job, qobj) aer_job.submit() return aer_job def _run_job(self, job_id, qobj): self._validate(qobj) result = run(qobj, self._configuration.exe) result['job_id'] = job_id copy_qasm_from_qobj_into_result(qobj, result) return result_from_old_style_dict(result) def _validate(self, qobj): for experiment in qobj.experiments: if 'measure' not in [op.name for op in experiment.instructions]: logger.warning( "no measurements in circuit '%s', " "classical register will remain all zeros.", experiment.header.name)
class QasmSimulator(AerBackend): """Aer quantum circuit simulator Backend options: The following backend options may be used with in the `backend_options` kwarg diction for `QasmSimulator.run` or `qiskit.execute` * "method" (str): Set the simulation method. Allowed values are: * "statevector": Uses a dense statevector simulation. * "stabilizer": uses a Clifford stabilizer state simulator that is only valid for Clifford circuits and noise models. * "ch": Uses an approximate simulator that decomposes circuits into stabilizer state terms, the number of which grows with the number of non-Clifford gates. * "automatic": automatically run on stabilizer simulator if the circuit and noise model supports it. If there is enough available memory, uses the statevector method. Otherwise, uses the ch method (Default: "automatic"). * "available_memory" (int): Set the amount of memory (in MB) the simulator has access to (Default: Maximum available) * "initial_statevector" (vector_like): Sets a custom initial statevector for the simulation instead of the all zero initial state (Default: None). * "chop_threshold" (double): Sets the threshold for truncating small values to zero in the Result data (Default: 1e-15) * "max_parallel_threads" (int): Sets the maximum number of CPU cores used by OpenMP for parallelization. If set to 0 the maximum will be set to the number of CPU cores (Default: 0). * "max_parallel_experiments" (int): Sets the maximum number of qobj experiments that may be executed in parallel up to the max_parallel_threads value. If set to 1 parallel circuit execution will be disabled. If set to 0 the maximum will be automatically set to max_parallel_threads (Default: 1). * "max_parallel_shots" (int): Sets the maximum number of shots that may be executed in parallel during each experiment execution, up to the max_parallel_threads value. If set to 1 parallel shot execution wil be disabled. If set to 0 the maximum will be automatically set to max_parallel_threads. Note that this cannot be enabled at the same time as parallel experiment execution (Default: 1). * "statevector_parallel_threshold" (int): Sets the threshold that "n_qubits" must be greater than to enable OpenMP parallelization for matrix multiplication during execution of an experiment. If parallel circuit or shot execution is enabled this will only use unallocated CPU cores up to max_parallel_threads. Note that setting this too low can reduce performance (Default: 12). * "statevector_sample_measure_opt" (int): Sets the threshold that the number of qubits must be greater than to enable a large qubit optimized implementation of measurement sampling. Note that setting this two low can reduce performance (Default: 10) * "statevector_hpc_gate_opt" (bool): If set to True this enables a different optimzied gate application routine that can increase performance on systems with a large number of CPU cores. For systems with a small number of cores it enabling can reduce performance (Default: False). * "ch_approximation_error" (double): Set the error in the approximation for the ch method. A smaller error needs more memory and computational time. (Default: 0.05) * "ch_disable_measurement_opt" (bool): Force the simulator to re-run the monte-carlo step for every measurement. Enabling this will improve the sampling accuracy if the output distribution is strongly peaked, but requires more computational time. (Default: False) * "ch_mixing_time" (int): Set how long the monte-carlo method runs before performing measurements. If the output distribution is strongly peaked, this can be decreased alongside setting ch_disable_measurement_opt to True. (Default: 7000) * "ch_norm_estimation_samples" (int): Number of samples used to compute the correct normalisation for a statevector snapshot. (Default: 100) * "ch_parallel_threshold" (int): Set the minimum size of the ch decomposition before we enable OpenMP parallelisation. If parallel circuit or shot execution is enabled this will only use unallocated CPU cores up to max_parallel_threads. (Default: 100) """ MAX_QUBIT_MEMORY = int( log2(local_hardware_info()['memory'] * (1024**3) / 16)) DEFAULT_CONFIGURATION = { 'backend_name': 'qasm_simulator', 'backend_version': __version__, 'n_qubits': MAX_QUBIT_MEMORY, 'url': 'https://github.com/Qiskit/qiskit-aer', 'simulator': True, 'local': True, 'conditional': True, 'open_pulse': False, 'memory': True, 'max_shots': 100000, 'description': 'A C++ simulator with realistic for qobj files', 'basis_gates': [ 'u1', 'u2', 'u3', 'cx', 'cz', 'id', 'x', 'y', 'z', 'h', 's', 'sdg', 't', 'tdg', 'ccx', 'swap', 'snapshot', 'unitary' ], 'gates': [{ 'name': 'TODO', 'parameters': [], 'qasm_def': 'TODO' }], # Location where we put external libraries that will be loaded at runtime # by the simulator extension 'library_dir': os.path.dirname(__file__) } def __init__(self, configuration=None, provider=None): super().__init__(qasm_controller_execute, BackendConfiguration.from_dict( self.DEFAULT_CONFIGURATION), provider=provider) def _validate(self, qobj, backend_options, noise_model): """Semantic validations of the qobj which cannot be done via schemas. 1. Check number of qubits will fit in local memory. 2. warn if no classical registers or measurements in circuit. """ clifford_instructions = [ "id", "x", "y", "z", "h", "s", "sdg", "CX", "cx", "cz", "swap", "barrier", "reset", "measure" ] unsupported_ch_instructions = ["u2", "u3"] # Check if noise model is Clifford: method = "automatic" if backend_options and "method" in backend_options: method = backend_options["method"] clifford_noise = not (method == "statevector") if clifford_noise: if method != "stabilizer" and noise_model: for error in noise_model.as_dict()['errors']: if error['type'] == 'qerror': for circ in error["instructions"]: for instr in circ: if instr not in clifford_instructions: clifford_noise = False break # Check to see if experiments are clifford for experiment in qobj.experiments: name = experiment.header.name # Check for classical bits if experiment.config.memory_slots == 0: logger.warning( 'No classical registers in circuit "%s": ' 'result data will not contain counts.', name) # Check if Clifford circuit or if measure opts missing no_measure = True ch_supported = False ch_supported = method in ["ch", "automatic"] clifford = False if method == "statevector" else clifford_noise for op in experiment.instructions: if not clifford and not no_measure: break # we don't need to check any more ops if clifford and op.name not in clifford_instructions: clifford = False if no_measure and op.name == "measure": no_measure = False if ch_supported and op.name in unsupported_ch_instructions: ch_supported = False # Print warning if clbits but no measure if no_measure: logger.warning( 'No measurements in circuit "%s": ' 'count data will return all zeros.', name) # Check qubits for statevector simulation if not clifford and method != "ch": n_qubits = experiment.config.n_qubits max_qubits = self.configuration().n_qubits if n_qubits > max_qubits: system_memory = int(local_hardware_info()['memory']) err_string = ('Number of qubits ({}) is greater than ' 'maximum ({}) for "{}" (method=statevector) ' 'with {} GB system memory') err_string = err_string.format(n_qubits, max_qubits, self.name(), system_memory) if method != "automatic": raise AerError(err_string + '.') else: if n_qubits > 63: raise AerError(err_string + ', and has too many ' + 'qubits to fall back to the ' + 'CH simulator.') if not ch_supported: raise AerError(err_string + ', and contains ' + 'instructions not supported by ' + 'the CH simulator.') logger.info('The QasmSimulator will automatically ' 'switch to the CH backend, based on ' 'the memory requirements.')
class StatevectorSimulatorPy(QasmSimulatorPy): """Python statevector simulator.""" DEFAULT_CONFIGURATION = { 'backend_name': 'statevector_simulator_py', 'backend_version': '1.0.0', 'n_qubits': int(log2(local_hardware_info()['memory'] * (1024**3)/16)), 'url': 'https://github.com/Qiskit/qiskit-terra', 'simulator': True, 'local': True, 'conditional': False, 'open_pulse': False, 'memory': False, 'max_shots': 65536, 'description': 'A Python statevector simulator for qobj files', 'basis_gates': ['u1', 'u2', 'u3', 'cx', 'id', 'snapshot'], 'gates': [ { 'name': 'u1', 'parameters': ['lambda'], 'qasm_def': 'gate u1(lambda) q { U(0,0,lambda) q; }' }, { 'name': 'u2', 'parameters': ['phi', 'lambda'], 'qasm_def': 'gate u2(phi,lambda) q { U(pi/2,phi,lambda) q; }' }, { 'name': 'u3', 'parameters': ['theta', 'phi', 'lambda'], 'qasm_def': 'gate u3(theta,phi,lambda) q { U(theta,phi,lambda) q; }' }, { 'name': 'cx', 'parameters': ['c', 't'], 'qasm_def': 'gate cx c,t { CX c,t; }' }, { 'name': 'id', 'parameters': ['a'], 'qasm_def': 'gate id a { U(0,0,0) a; }' }, { 'name': 'snapshot', 'parameters': ['slot'], 'qasm_def': 'gate snapshot(slot) q { TODO }' } ] } def __init__(self, configuration=None, provider=None): super().__init__(configuration=(configuration or BackendConfiguration.from_dict(self.DEFAULT_CONFIGURATION)), provider=provider) def run(self, qobj): """Run qobj asynchronously. Args: qobj (dict): job description Returns: AerJob: derived from BaseJob """ job_id = str(uuid.uuid4()) aer_job = AerJob(self, job_id, self._run_job, qobj) aer_job.submit() return aer_job def _run_job(self, job_id, qobj): """Run a Qobj on the backend.""" self._validate(qobj) final_state_key = 32767 # Internal key for final state snapshot # Add final snapshots to circuits for experiment in qobj.experiments: experiment.instructions.append( QobjInstruction(name='snapshot', params=[final_state_key], label='MISSING', type='MISSING') ) result = super()._run_job(job_id, qobj) # Remove added snapshot from qobj for experiment in qobj.experiments: del experiment.instructions[-1] # Extract final state snapshot and move to 'statevector' data field for experiment_result in result.results: snapshots = experiment_result.data.snapshots.to_dict() if str(final_state_key) in snapshots: final_state_key = str(final_state_key) # Pop off final snapshot added above final_state = snapshots.pop(final_state_key, None) final_state = final_state['statevector'][0] # Add final state to results data experiment_result.data.statevector = final_state # Remove snapshot dict if empty if snapshots == {}: delattr(experiment_result.data, 'snapshots') return result def _validate(self, qobj): """Semantic validations of the qobj which cannot be done via schemas. Some of these may later move to backend schemas. 1. No shots 2. No measurements in the middle """ if qobj.config.shots != 1: logger.info("statevector simulator only supports 1 shot. " "Setting shots=1.") qobj.config.shots = 1 for experiment in qobj.experiments: if getattr(experiment.config, 'shots', 1) != 1: logger.info("statevector simulator only supports 1 shot. " "Setting shots=1 for circuit %s.", experiment.name) experiment.config.shots = 1 for op in experiment.instructions: if op.name in ['measure', 'reset']: raise SimulatorError( "In circuit {}: statevector simulator does not support " "measure or reset.".format(experiment.header.name))
class UnitarySimulatorPy(BaseBackend): """Python implementation of a unitary simulator.""" MAX_QUBITS_MEMORY = int( log2(sqrt(local_hardware_info()['memory'] * (1024**3) / 16))) DEFAULT_CONFIGURATION = { 'backend_name': 'unitary_simulator', 'backend_version': '1.0.0', 'n_qubits': min(24, MAX_QUBITS_MEMORY), 'url': 'https://github.com/Qiskit/qiskit-terra', 'simulator': True, 'local': True, 'conditional': False, 'open_pulse': False, 'memory': False, 'max_shots': 65536, 'description': 'A python simulator for unitary matrix corresponding to a circuit', 'basis_gates': ['u1', 'u2', 'u3', 'cx', 'id'], 'gates': [{ 'name': 'u1', 'parameters': ['lambda'], 'qasm_def': 'gate u1(lambda) q { U(0,0,lambda) q; }' }, { 'name': 'u2', 'parameters': ['phi', 'lambda'], 'qasm_def': 'gate u2(phi,lambda) q { U(pi/2,phi,lambda) q; }' }, { 'name': 'u3', 'parameters': ['theta', 'phi', 'lambda'], 'qasm_def': 'gate u3(theta,phi,lambda) q { U(theta,phi,lambda) q; }' }, { 'name': 'cx', 'parameters': ['c', 't'], 'qasm_def': 'gate cx c,t { CX c,t; }' }, { 'name': 'id', 'parameters': ['a'], 'qasm_def': 'gate id a { U(0,0,0) a; }' }] } DEFAULT_OPTIONS = {"initial_unitary": None, "chop_threshold": 1e-15} def __init__(self, configuration=None, provider=None): super().__init__(configuration=(configuration or BackendConfiguration.from_dict( self.DEFAULT_CONFIGURATION)), provider=provider) # Define attributes inside __init__. self._unitary = None self._number_of_qubits = 0 self._initial_unitary = None self._chop_threshold = 1e-15 def _add_unitary_single(self, gate, qubit): """Apply an arbitrary 1-qubit unitary matrix. Args: gate (matrix_like): a single qubit gate matrix qubit (int): the qubit to apply gate to """ # Convert to complex rank-2 tensor gate_tensor = np.array(gate, dtype=complex) # Compute einsum index string for 1-qubit matrix multiplication indexes = einsum_matmul_index([qubit], self._number_of_qubits) # Apply matrix multiplication self._unitary = np.einsum(indexes, gate_tensor, self._unitary, dtype=complex, casting='no') def _add_unitary_two(self, gate, qubit0, qubit1): """Apply a two-qubit unitary matrix. Args: gate (matrix_like): a the two-qubit gate matrix qubit0 (int): gate qubit-0 qubit1 (int): gate qubit-1 """ # Convert to complex rank-4 tensor gate_tensor = np.reshape(np.array(gate, dtype=complex), 4 * [2]) # Compute einsum index string for 2-qubit matrix multiplication indexes = einsum_matmul_index([qubit0, qubit1], self._number_of_qubits) # Apply matrix multiplication self._unitary = np.einsum(indexes, gate_tensor, self._unitary, dtype=complex, casting='no') def _validate_initial_unitary(self): """Validate an initial unitary matrix""" # If initial unitary isn't set we don't need to validate if self._initial_unitary is None: return # Check unitary is correct length for number of qubits shape = np.shape(self._initial_unitary) required_shape = (2**self._number_of_qubits, 2**self._number_of_qubits) if shape != required_shape: raise SimulatorError('initial unitary is incorrect shape: ' + '{} != 2 ** {}'.format(shape, required_shape)) def _set_options(self, qobj_config=None, backend_options=None): """Set the backend options for all experiments in a qobj""" # Reset default options self._initial_unitary = self.DEFAULT_OPTIONS["initial_unitary"] self._chop_threshold = self.DEFAULT_OPTIONS["chop_threshold"] if backend_options is None: backend_options = {} # Check for custom initial statevector in backend_options first, # then config second if 'initial_unitary' in backend_options: self._initial_unitary = np.array( backend_options['initial_unitary'], dtype=complex) elif hasattr(qobj_config, 'initial_unitary'): self._initial_unitary = np.array(qobj_config.initial_unitary, dtype=complex) if self._initial_unitary is not None: # Check the initial unitary is actually unitary shape = np.shape(self._initial_unitary) if len(shape) != 2 or shape[0] != shape[1]: raise SimulatorError("initial unitary is not a square matrix") iden = np.eye(len(self._initial_unitary)) u_dagger_u = np.dot(self._initial_unitary.T.conj(), self._initial_unitary) norm = np.linalg.norm(u_dagger_u - iden) if round(norm, 10) != 0: raise SimulatorError("initial unitary is not unitary") # Check the initial statevector is normalized # Check for custom chop threshold # Replace with custom options if 'chop_threshold' in backend_options: self._chop_threshold = backend_options['chop_threshold'] elif hasattr(qobj_config, 'chop_threshold'): self._chop_threshold = qobj_config.chop_threshold def _initialize_unitary(self): """Set the initial unitary for simulation""" self._validate_initial_unitary() if self._initial_unitary is None: # Set to identity matrix self._unitary = np.eye(2**self._number_of_qubits, dtype=complex) else: self._unitary = self._initial_unitary.copy() # Reshape to rank-N tensor self._unitary = np.reshape(self._unitary, self._number_of_qubits * [2, 2]) def _get_unitary(self): """Return the current unitary in JSON Result spec format""" unitary = np.reshape(self._unitary, 2 * [2**self._number_of_qubits]) # Expand complex numbers unitary = np.stack((unitary.real, unitary.imag), axis=-1) # Truncate small values unitary[abs(unitary) < self._chop_threshold] = 0.0 return unitary def run(self, qobj, backend_options=None): """Run qobj asynchronously. Args: qobj (Qobj): payload of the experiment backend_options (dict): backend options Returns: SimulatorsJob: derived from BaseJob Additional Information:: backend_options: Is a dict of options for the backend. It may contain * "initial_unitary": matrix_like * "chop_threshold": double The "initial_unitary" option specifies a custom initial unitary matrix for the simulator to be used instead of the identity matrix. This size of this matrix must be correct for the number of qubits inall experiments in the qobj. The "chop_threshold" option specifies a trunctation value for setting small values to zero in the output unitary. The default value is 1e-15. Example:: backend_options = { "initial_unitary": np.array([[1, 0, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0], [0, 1, 0, 0]]) "chop_threshold": 1e-15 } """ self._set_options(qobj_config=qobj.config, backend_options=backend_options) job_id = str(uuid.uuid4()) job = SimulatorsJob(self, job_id, self._run_job, qobj) job.submit() return job def _run_job(self, job_id, qobj): """Run experiments in qobj. Args: job_id (str): unique id for the job. qobj (Qobj): job description Returns: Result: Result object """ self._validate(qobj) result_list = [] start = time.time() for experiment in qobj.experiments: result_list.append(self.run_experiment(experiment)) end = time.time() result = { 'backend_name': self.name(), 'backend_version': self._configuration.backend_version, 'qobj_id': qobj.qobj_id, 'job_id': job_id, 'results': result_list, 'status': 'COMPLETED', 'success': True, 'time_taken': (end - start), 'header': qobj.header.as_dict() } return Result.from_dict(result) def run_experiment(self, experiment): """Run an experiment (circuit) and return a single experiment result. Args: experiment (QobjExperiment): experiment from qobj experiments list Returns: dict: A result dictionary which looks something like:: { "name": name of this experiment (obtained from qobj.experiment header) "seed": random seed used for simulation "shots": number of shots used in the simulation "data": { "unitary": [[[0.0, 0.0], [1.0, 0.0]], [[1.0, 0.0], [0.0, 0.0]]] }, "status": status string for the simulation "success": boolean "time taken": simulation time of this single experiment } Raises: SimulatorError: if the number of qubits in the circuit is greater than 24. Note that the practical qubit limit is much lower than 24. """ start = time.time() self._number_of_qubits = experiment.header.n_qubits # Validate the dimension of initial unitary if set self._validate_initial_unitary() self._initialize_unitary() for operation in experiment.instructions: # Check if single gate if operation.name in ('U', 'u1', 'u2', 'u3'): params = getattr(operation, 'params', None) qubit = operation.qubits[0] gate = single_gate_matrix(operation.name, params) self._add_unitary_single(gate, qubit) elif operation.name in ('id', 'u0'): pass # Check if CX gate elif operation.name in ('CX', 'cx'): qubit0 = operation.qubits[0] qubit1 = operation.qubits[1] gate = cx_gate_matrix() self._add_unitary_two(gate, qubit0, qubit1) # Check if barrier elif operation.name == 'barrier': pass else: backend = self.name() err_msg = '{0} encountered unrecognized operation "{1}"' raise SimulatorError(err_msg.format(backend, operation.name)) # Add final state to data data = {'unitary': self._get_unitary()} end = time.time() return { 'name': experiment.header.name, 'shots': 1, 'data': data, 'status': 'DONE', 'success': True, 'time_taken': (end - start), 'header': experiment.header.as_dict() } def _validate(self, qobj): """Semantic validations of the qobj which cannot be done via schemas. Some of these may later move to backend schemas. 1. No shots 2. No measurements in the middle """ n_qubits = qobj.config.n_qubits max_qubits = self.configuration().n_qubits if n_qubits > max_qubits: raise SimulatorError( 'Number of qubits {} '.format(n_qubits) + 'is greater than maximum ({}) '.format(max_qubits) + 'for "{}".'.format(self.name())) if qobj.config.shots != 1: logger.info('"%s" only supports 1 shot. Setting shots=1.', self.name()) qobj.config.shots = 1 for experiment in qobj.experiments: name = experiment.header.name if getattr(experiment.config, 'shots', 1) != 1: logger.info( '"%s" only supports 1 shot. ' + 'Setting shots=1 for circuit "%s".', self.name(), name) experiment.config.shots = 1 for operation in experiment.instructions: if operation.name in ['measure', 'reset']: raise SimulatorError( 'Unsupported "%s" instruction "%s" ' + 'in circuit "%s" ', self.name(), operation.name, name)
class UnitarySimulatorPy(BaseBackend): """Python implementation of a unitary simulator.""" max_qubits = int( log2(sqrt(local_hardware_info()['memory'] * (1024**3)) / 16)) DEFAULT_CONFIGURATION = { 'backend_name': 'unitary_simulator_py', 'backend_version': '1.0.0', 'n_qubits': max_qubits, 'url': 'https://github.com/Qiskit/qiskit-terra', 'simulator': True, 'local': True, 'conditional': False, 'open_pulse': False, 'memory': False, 'max_shots': 65536, 'description': 'A python simulator for unitary matrix corresponding to a circuit', 'basis_gates': ['u1', 'u2', 'u3', 'cx', 'id'], 'gates': [{ 'name': 'u1', 'parameters': ['lambda'], 'qasm_def': 'gate u1(lambda) q { U(0,0,lambda) q; }' }, { 'name': 'u2', 'parameters': ['phi', 'lambda'], 'qasm_def': 'gate u2(phi,lambda) q { U(pi/2,phi,lambda) q; }' }, { 'name': 'u3', 'parameters': ['theta', 'phi', 'lambda'], 'qasm_def': 'gate u3(theta,phi,lambda) q { U(theta,phi,lambda) q; }' }, { 'name': 'cx', 'parameters': ['c', 't'], 'qasm_def': 'gate cx c,t { CX c,t; }' }, { 'name': 'id', 'parameters': ['a'], 'qasm_def': 'gate id a { U(0,0,0) a; }' }] } def __init__(self, configuration=None, provider=None): super().__init__(configuration=(configuration or BackendConfiguration.from_dict( self.DEFAULT_CONFIGURATION)), provider=provider) # Define attributes inside __init__. self._unitary_state = None self._number_of_qubits = 0 def _add_unitary_single(self, gate, qubit): """Apply the single-qubit gate. gate is the single-qubit gate. qubit is the qubit to apply it on counts from 0 and order is q_{n-1} ... otimes q_1 otimes q_0. number_of_qubits is the number of qubits in the system. """ # Convert to complex rank-2 tensor gate_tensor = np.array(gate, dtype=complex) # Compute einsum index string for 1-qubit matrix multiplication indexes = einsum_matmul_index([qubit], self._number_of_qubits) # Apply matrix multiplication self._unitary_state = np.einsum(indexes, gate_tensor, self._unitary_state, dtype=complex, casting='no') def _add_unitary_two(self, gate, qubit0, qubit1): """Apply the two-qubit gate. gate is the two-qubit gate qubit0 is the first qubit (control) counts from 0 qubit1 is the second qubit (target) returns a complex numpy array """ # Convert to complex rank-4 tensor gate_tensor = np.reshape(np.array(gate, dtype=complex), 4 * [2]) # Compute einsum index string for 2-qubit matrix multiplication indexes = einsum_matmul_index([qubit0, qubit1], self._number_of_qubits) # Apply matrix multiplication self._unitary_state = np.einsum(indexes, gate_tensor, self._unitary_state, dtype=complex, casting='no') def run(self, qobj): """Run qobj asynchronously. Args: qobj (dict): job description Returns: AerJob: derived from BaseJob """ job_id = str(uuid.uuid4()) aer_job = AerJob(self, job_id, self._run_job, qobj) aer_job.submit() return aer_job def _run_job(self, job_id, qobj): """Run experiments in qobj. Args: job_id (str): unique id for the job. qobj (Qobj): job description Returns: Result: Result object """ self._validate(qobj) result_list = [] start = time.time() for experiment in qobj.experiments: result_list.append(self.run_experiment(experiment)) end = time.time() result = { 'backend_name': self.name(), 'backend_version': self._configuration.backend_version, 'qobj_id': qobj.qobj_id, 'job_id': job_id, 'results': result_list, 'status': 'COMPLETED', 'success': True, 'time_taken': (end - start), 'header': qobj.header.as_dict() } return Result.from_dict(result) def run_experiment(self, experiment): """Run an experiment (circuit) and return a single experiment result. Args: experiment (QobjExperiment): experiment from qobj experiments list Returns: dict: A dictionary of results. dict: A result dictionary which looks something like:: { "name": name of this experiment (obtained from qobj.experiment header) "seed": random seed used for simulation "shots": number of shots used in the simulation "data": { "unitary": [[0.2, 0.6, j+0.1, 0.2j], [0, 0.9+j, 0.5, 0.7], [-j, -0.1, -3.14, 0], [0, 0, 0.5j, j-0.5]] }, "status": status string for the simulation "success": boolean "time taken": simulation time of this single experiment } Raises: SimulatorError: if the number of qubits in the circuit is greater than 24. Note that the practical qubit limit is much lower than 24. """ self._number_of_qubits = experiment.header.n_qubits if self._number_of_qubits > self.max_qubits: raise SimulatorError( "np.einsum implementation limits unitary_simulator_py" + " to 24 qubit circuits.") result = { 'data': {}, 'name': experiment.header.name, 'header': experiment.header.as_dict() } # Initialize unitary as rank 2*N tensor self._unitary_state = np.reshape( np.eye(2**self._number_of_qubits, dtype=complex), self._number_of_qubits * [2, 2]) for operation in experiment.instructions: if operation.name in ('U', 'u1', 'u2', 'u3'): params = getattr(operation, 'params', None) qubit = operation.qubits[0] gate = single_gate_matrix(operation.name, params) self._add_unitary_single(gate, qubit) elif operation.name in ('id', 'u0'): pass elif operation.name in ('CX', 'cx'): qubit0 = operation.qubits[0] qubit1 = operation.qubits[1] gate = np.array([[1, 0, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0], [0, 1, 0, 0]]) self._add_unitary_two(gate, qubit0, qubit1) elif operation.name == 'barrier': pass else: result['status'] = 'ERROR' return result # Reshape unitary rank-2n tensor back to a matrix tmp = np.reshape(self._unitary_state, 2 * [2**self._number_of_qubits]) # Convert complex numbers to pair of (real, imag) result['data']['unitary'] = np.stack((tmp.real, tmp.imag), axis=-1) result['status'] = 'DONE' result['success'] = True result['shots'] = 1 return result def _validate(self, qobj): """Semantic validations of the qobj which cannot be done via schemas. Some of these may later move to backend schemas. 1. No shots 2. No measurements in the middle """ if qobj.config.shots != 1: logger.info("unitary simulator only supports 1 shot. " "Setting shots=1.") qobj.config.shots = 1 for experiment in qobj.experiments: if getattr(experiment.config, 'shots', 1) != 1: logger.info( "unitary simulator only supports 1 shot. " "Setting shots=1 for circuit %s.", experiment.name) experiment.config.shots = 1 for operation in experiment.instructions: if operation.name in ['measure', 'reset']: raise SimulatorError( "In circuit {}: unitary simulator does not support " "measure or reset.".format(experiment.header.name))
def test_local_hardware_none_cpu_count(self, cpu_count_mock, vmem_mock, platform_mock): """Test cpu count fallback to 1 when true value can't be determined""" # pylint: disable=unused-argument result = _util.local_hardware_info() self.assertEqual(1, result['cpus'])
class QasmSimulator(AerBackend): """Aer quantum circuit simulator Backend options: The following backend options may be used with in the `backend_options` kwarg diction for `QasmSimulator.run` or `qiskit.execute` * "method" (str): Set the simulation method. Allowed values are: * "statevector": Uses a dense statevector simulation. * "stabilizer": uses a Clifford stabilizer state simulator that is only valid for Clifford circuits and noise models. * "automatic": automatically run on stabilizer simulator if the circuit and noise model supports it, otherwise use the statevector method (Default: "automatic"). * "initial_statevector" (vector_like): Sets a custom initial statevector for the simulation instead of the all zero initial state (Default: None). * "chop_threshold" (double): Sets the threshold for truncating small values to zero in the Result data (Default: 1e-15) * "max_parallel_threads" (int): Sets the maximum number of CPU cores used by OpenMP for parallelization. If set to 0 the maximum will be set to the number of CPU cores (Default: 0). * "max_parallel_experiments" (int): Sets the maximum number of qobj experiments that may be executed in parallel up to the max_parallel_threads value. If set to 1 parallel circuit execution will be disabled. If set to 0 the maximum will be automatically set to max_parallel_threads (Default: 1). * "max_parallel_shots" (int): Sets the maximum number of shots that may be executed in parallel during each experiment execution, up to the max_parallel_threads value. If set to 1 parallel shot execution wil be disabled. If set to 0 the maximum will be automatically set to max_parallel_threads. Note that this cannot be enabled at the same time as parallel experiment execution (Default: 1). * "statevector_parallel_threshold" (int): Sets the threshold that "n_qubits" must be greater than to enable OpenMP parallelization for matrix multiplication during execution of an experiment. If parallel circuit or shot execution is enabled this will only use unallocated CPU cores up to max_parallel_threads. Note that setting this too low can reduce performance (Default: 12). * "statevector_sample_measure_opt" (int): Sets the threshold that the number of qubits must be greater than to enable a large qubit optimized implementation of measurement sampling. Note that setting this two low can reduce performance (Default: 10) * "statevector_hpc_gate_opt" (bool): If set to True this enables a different optimzied gate application routine that can increase performance on systems with a large number of CPU cores. For systems with a small number of cores it enabling can reduce performance (Default: False). """ MAX_QUBIT_MEMORY = int( log2(local_hardware_info()['memory'] * (1024**3) / 16)) DEFAULT_CONFIGURATION = { 'backend_name': 'qasm_simulator', 'backend_version': __version__, 'n_qubits': MAX_QUBIT_MEMORY, 'url': 'https://github.com/Qiskit/qiskit-aer', 'simulator': True, 'local': True, 'conditional': True, 'open_pulse': False, 'memory': True, 'max_shots': 100000, 'description': 'A C++ simulator with realistic for qobj files', 'basis_gates': [ 'u1', 'u2', 'u3', 'cx', 'cz', 'id', 'x', 'y', 'z', 'h', 's', 'sdg', 't', 'tdg', 'ccx', 'swap', 'snapshot', 'unitary' ], 'gates': [{ 'name': 'TODO', 'parameters': [], 'qasm_def': 'TODO' }] } def __init__(self, configuration=None, provider=None): super().__init__(qasm_controller_execute, BackendConfiguration.from_dict( self.DEFAULT_CONFIGURATION), provider=provider) def _validate(self, qobj, backend_options, noise_model): """Semantic validations of the qobj which cannot be done via schemas. 1. Check number of qubits will fit in local memory. 2. warn if no classical registers or measurements in circuit. """ clifford_instructions = [ "id", "x", "y", "z", "h", "s", "sdg", "CX", "cx", "cz", "swap", "barrier", "reset", "measure" ] # Check if noise model is Clifford: method = "automatic" if backend_options and "method" in backend_options: method = backend_options["method"] clifford_noise = not (method == "statevector") if clifford_noise: if method != "stabilizer" and noise_model: for error in noise_model.as_dict()['errors']: if error['type'] == 'qerror': for circ in error["instructions"]: for instr in circ: if instr not in clifford_instructions: clifford_noise = False break # Check to see if experiments are clifford for experiment in qobj.experiments: name = experiment.header.name # Check for classical bits if experiment.config.memory_slots == 0: logger.warning( 'No classical registers in circuit "%s": ' 'result data will not contain counts.', name) # Check if Clifford circuit or if measure opts missing no_measure = True clifford = False if method == "statevector" else clifford_noise for op in experiment.instructions: if not clifford and not no_measure: break # we don't need to check any more ops if clifford and op.name not in clifford_instructions: clifford = False if no_measure and op.name == "measure": no_measure = False # Print warning if clbits but no measure if no_measure: logger.warning( 'No measurements in circuit "%s": ' 'count data will return all zeros.', name) # Check qubits for statevector simulation if not clifford: n_qubits = experiment.config.n_qubits max_qubits = self.configuration().n_qubits if n_qubits > max_qubits: system_memory = int(local_hardware_info()['memory']) raise AerError( 'Number of qubits ({}) '.format(n_qubits) + 'is greater than maximum ({}) '.format(max_qubits) + 'for "{}" (method=statevector) '.format(self.name()) + 'with {} GB system memory.'.format(system_memory))
class UnitarySimulatorPy(BaseBackend): """Python implementation of a unitary simulator.""" DEFAULT_CONFIGURATION = { 'backend_name': 'unitary_simulator_py', 'backend_version': '1.0.0', 'n_qubits': int(log2(sqrt(local_hardware_info()['memory'] * (1024**3)) / 16)), 'url': 'https://github.com/Qiskit/qiskit-terra', 'simulator': True, 'local': True, 'conditional': False, 'open_pulse': False, 'memory': False, 'max_shots': 65536, 'description': 'A python simulator for unitary matrix corresponding to a circuit', 'basis_gates': ['u1', 'u2', 'u3', 'cx', 'id'], 'gates': [{ 'name': 'TODO', 'parameters': [], 'qasm_def': 'TODO' }] } def __init__(self, configuration=None, provider=None): super().__init__(configuration=(configuration or BackendConfiguration.from_dict( self.DEFAULT_CONFIGURATION)), provider=provider) # Define attributes inside __init__. self._unitary_state = None self._number_of_qubits = 0 def properties(self): """Return backend properties""" properties = { 'backend_name': self.name(), 'backend_version': self.configuration().backend_version, 'last_update_date': '2000-01-01 00:00:00Z', 'qubits': [[{ 'name': 'TODO', 'date': '2000-01-01 00:00:00Z', 'unit': 'TODO', 'value': 0 }]], 'gates': [{ 'qubits': [0], 'gate': 'TODO', 'parameters': [{ 'name': 'TODO', 'date': '2000-01-01 00:00:00Z', 'unit': 'TODO', 'value': 0 }] }], 'general': [] } return BackendProperties.from_dict(properties) def _add_unitary_single(self, gate, qubit): """Apply the single-qubit gate. gate is the single-qubit gate. qubit is the qubit to apply it on counts from 0 and order is q_{n-1} ... otimes q_1 otimes q_0. number_of_qubits is the number of qubits in the system. """ # Convert to complex rank-2 tensor gate_tensor = np.array(gate, dtype=complex) # Compute einsum index string for 1-qubit matrix multiplication indexes = einsum_matmul_index([qubit], self._number_of_qubits) # Apply matrix multiplication self._unitary_state = np.einsum(indexes, gate_tensor, self._unitary_state, dtype=complex, casting='no') def _add_unitary_two(self, gate, qubit0, qubit1): """Apply the two-qubit gate. gate is the two-qubit gate qubit0 is the first qubit (control) counts from 0 qubit1 is the second qubit (target) returns a complex numpy array """ # Convert to complex rank-4 tensor gate_tensor = np.reshape(np.array(gate, dtype=complex), 4 * [2]) # Compute einsum index string for 2-qubit matrix multiplication indexes = einsum_matmul_index([qubit0, qubit1], self._number_of_qubits) # Apply matrix multiplication self._unitary_state = np.einsum(indexes, gate_tensor, self._unitary_state, dtype=complex, casting='no') def run(self, qobj): """Run qobj asynchronously. Args: qobj (dict): job description Returns: AerJob: derived from BaseJob """ job_id = str(uuid.uuid4()) aer_job = AerJob(self, job_id, self._run_job, qobj) aer_job.submit() return aer_job def _run_job(self, job_id, qobj): """Run qobj. This is a blocking call. Args: job_id (str): unique id for the job. qobj (Qobj): job description Returns: Result: Result object """ result_list = [] start = time.time() for circuit in qobj.experiments: result_list.append(self.run_circuit(circuit)) end = time.time() result = { 'backend': self.name(), 'id': qobj.qobj_id, 'job_id': job_id, 'result': result_list, 'status': 'COMPLETED', 'success': True, 'time_taken': (end - start) } copy_qasm_from_qobj_into_result(qobj, result) return result_from_old_style_dict(result) def run_circuit(self, circuit): """Apply the single-qubit gate. Args: circuit (QobjExperiment): experiment from qobj experiments list Returns: dict: A dictionary of results. Raises: QiskitError: if the number of qubits in the circuit is greater than 24. Note that the practical qubit limit is much lower than 24. """ self._number_of_qubits = circuit.header.number_of_qubits if self._number_of_qubits > 24: raise QiskitError( "np.einsum implementation limits unitary_simulator_py" + " to 24 qubit circuits.") result = {'data': {}, 'name': circuit.header.name} # Initialize unitary as rank 2*N tensor self._unitary_state = np.reshape( np.eye(2**self._number_of_qubits, dtype=complex), self._number_of_qubits * [2, 2]) for operation in circuit.instructions: if operation.name in ('U', 'u1', 'u2', 'u3'): params = getattr(operation, 'params', None) qubit = operation.qubits[0] gate = single_gate_matrix(operation.name, params) self._add_unitary_single(gate, qubit) elif operation.name in ('id', 'u0'): pass elif operation.name in ('CX', 'cx'): qubit0 = operation.qubits[0] qubit1 = operation.qubits[1] gate = np.array([[1, 0, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0], [0, 1, 0, 0]]) self._add_unitary_two(gate, qubit0, qubit1) elif operation.name == 'measure': logger.info('Warning have dropped measure from unitary ' 'simulator') elif operation.name == 'reset': logger.info('Warning have dropped reset from unitary ' 'simulator') elif operation.name == 'barrier': pass else: result['status'] = 'ERROR' return result # Reshape unitary rank-2n tensor back to a matrix tmp = np.reshape(self._unitary_state, 2 * [2**self._number_of_qubits]) # Convert complex numbers to pair of (real, imag) result['data']['unitary'] = np.stack((tmp.real, tmp.imag), axis=-1) result['status'] = 'DONE' result['success'] = True result['shots'] = 1 return result
class CliffordSimulator(BaseBackend): """"C++ Clifford circuit simulator with realistic noise.""" DEFAULT_CONFIGURATION = { 'backend_name': 'clifford_simulator', 'backend_version': '1.0.0', 'n_qubits': int(log2(local_hardware_info()['memory'] * (1024**3) / 16)), 'url': 'https://github.com/Qiskit/qiskit-terra/src/qasm-simulator-cpp', 'simulator': True, 'local': True, 'conditional': True, 'open_pulse': False, 'memory': False, 'max_shots': 65536, 'description': 'A C++ Clifford simulator with approximate noise', 'basis_gates': [ 'cx', 'id', 'x', 'y', 'z', 'h', 's', 'sdg', 'snapshot', 'wait', 'noise', 'save', 'load' ], 'gates': [{ 'name': 'cx', 'parameters': ['c', 't'], 'qasm_def': 'gate cx c,t { CX c,t; }' }, { 'name': 'id', 'parameters': ['a'], 'qasm_def': 'gate id a { U(0,0,0) a; }' }, { 'name': 'x', 'parameters': ['a'], 'qasm_def': 'gate x a { u3(pi,0,pi) a; }' }, { 'name': 'y', 'parameters': ['a'], 'qasm_def': 'gate y a { u3(pi,pi/2,pi/2) a; }' }, { 'name': 'z', 'parameters': ['z'], 'qasm_def': 'gate z a { u1(pi) a; }' }, { 'name': 'h', 'parameters': ['a'], 'qasm_def': 'gate h a { u2(0,pi) a; }' }, { 'name': 's', 'parameters': ['a'], 'qasm_def': 'gate s a { u1(pi/2) a; }' }, { 'name': 'sdg', 'parameters': ['a'], 'qasm_def': 'gate sdg a { u1(-pi/2) a; }' }, { 'name': 'snapshot', 'parameters': ['slot'], 'qasm_def': 'gate snapshot(slot) q { TODO }' }, { 'name': 'wait', 'parameters': ['t'], 'qasm_def': 'gate wait(t) q { TODO }' }, { 'name': 'noise', 'parameters': ['switch'], 'qasm_def': 'gate noise(switch) q { TODO }' }, { 'name': 'save', 'parameters': ['slot'], 'qasm_def': 'gate save(slot) q { TODO }' }, { 'name': 'load', 'parameters': ['slot'], 'qasm_def': 'gate load(slot) q { TODO }' }] } def __init__(self, configuration=None, provider=None): super().__init__(configuration=(configuration or BackendConfiguration.from_dict( self.DEFAULT_CONFIGURATION)), provider=provider) # Try to use the default executable if not specified. if 'exe' in self._configuration: paths = [self._configuration.exe] else: paths = DEFAULT_SIMULATOR_PATHS # Ensure that the executable is available. try: self._configuration.exe = next( path for path in paths if (os.path.exists(path) and os.path.getsize(path) > 100)) except StopIteration: raise FileNotFoundError( 'Simulator executable not found (using %s)' % getattr(self._configuration, 'exe', 'default locations')) def run(self, qobj): """Run a Qobj on the backend. Args: qobj (dict): job description Returns: AerJob: derived from BaseJob """ job_id = str(uuid.uuid4()) aer_job = AerJob(self, job_id, self._run_job, qobj) aer_job.submit() return aer_job def _run_job(self, job_id, qobj): qobj_dict = qobj.as_dict() self._validate() # set backend to Clifford simulator if 'config' in qobj_dict: qobj_dict['config']['simulator'] = 'clifford' else: qobj_dict['config'] = {'simulator': 'clifford'} result = run(qobj_dict, self._configuration.exe) result['job_id'] = job_id # Ensure that the required results fields are present, even if the # job failed. result['results'] = result.get('results', []) result['qobj_id'] = result.get('qobj_id', 'unavailable') result['backend_name'] = result.get('backend_name', self.name()) result['backend_version'] = result.get( 'backend_version', self.configuration().backend_version) return Result.from_dict(result) def _validate(self): return
class QasmSimulatorPy(BaseBackend): """Python implementation of a qasm simulator.""" DEFAULT_CONFIGURATION = { 'backend_name': 'qasm_simulator_py', 'backend_version': '2.0.0', 'n_qubits': int(log2(local_hardware_info()['memory'] * (1024**3)/16)), 'url': 'https://github.com/Qiskit/qiskit-terra', 'simulator': True, 'local': True, 'conditional': True, 'open_pulse': False, 'memory': True, 'max_shots': 65536, 'description': 'A python simulator for qasm experiments', 'basis_gates': ['u1', 'u2', 'u3', 'cx', 'id', 'snapshot'], 'gates': [ { 'name': 'u1', 'parameters': ['lambda'], 'qasm_def': 'gate u1(lambda) q { U(0,0,lambda) q; }' }, { 'name': 'u2', 'parameters': ['phi', 'lambda'], 'qasm_def': 'gate u2(phi,lambda) q { U(pi/2,phi,lambda) q; }' }, { 'name': 'u3', 'parameters': ['theta', 'phi', 'lambda'], 'qasm_def': 'gate u3(theta,phi,lambda) q { U(theta,phi,lambda) q; }' }, { 'name': 'cx', 'parameters': ['c', 't'], 'qasm_def': 'gate cx c,t { CX c,t; }' }, { 'name': 'id', 'parameters': ['a'], 'qasm_def': 'gate id a { U(0,0,0) a; }' }, { 'name': 'snapshot', 'parameters': ['slot'], 'qasm_def': 'gate snapshot(slot) q { TODO }' } ] } def __init__(self, configuration=None, provider=None): super().__init__(configuration=(configuration or BackendConfiguration.from_dict(self.DEFAULT_CONFIGURATION)), provider=provider) self._local_random = random.Random() # Define attributes in __init__. self._classical_state = 0 self._statevector = 0 self._snapshots = {} self._number_of_cbits = 0 self._number_of_qubits = 0 self._shots = 0 self._memory = False self._qobj_config = None def _add_qasm_single(self, gate, qubit): """Apply an arbitrary 1-qubit operator to a qubit. Gate is the single qubit applied. qubit is the qubit the gate is applied to. """ psi = self._statevector bit = 1 << qubit for k1 in range(0, 1 << self._number_of_qubits, 1 << (qubit+1)): for k2 in range(0, 1 << qubit, 1): k = k1 | k2 cache0 = psi[k] cache1 = psi[k | bit] psi[k] = gate[0, 0] * cache0 + gate[0, 1] * cache1 psi[k | bit] = gate[1, 0] * cache0 + gate[1, 1] * cache1 def _add_qasm_cx(self, q0, q1): """Optimized ideal CX on two qubits. q0 is the first qubit (control) counts from 0. q1 is the second qubit (target). """ psi = self._statevector for k in range(0, 1 << (self._number_of_qubits - 2)): # first bit is control, second is target ind1 = index2(1, q0, 0, q1, k) # swap target if control is 1 ind3 = index2(1, q0, 1, q1, k) cache0 = psi[ind1] cache1 = psi[ind3] psi[ind3] = cache0 psi[ind1] = cache1 def _add_qasm_decision(self, qubit): """Apply the decision of measurement/reset qubit gate. qubit is the qubit that is measured/reset """ probability_zero = 0 random_number = self._local_random.random() for ii in range(1 << self._number_of_qubits): if ii & (1 << qubit) == 0: probability_zero += np.abs(self._statevector[ii])**2 if random_number <= probability_zero: outcome = '0' norm = np.sqrt(probability_zero) else: outcome = '1' norm = np.sqrt(1-probability_zero) return (outcome, norm) def _add_qasm_measure(self, qubit, cbit): """Apply the measurement qubit gate. qubit is the qubit measured. cbit is the classical bit the measurement is assigned to. """ outcome, norm = self._add_qasm_decision(qubit) for ii in range(1 << self._number_of_qubits): # update quantum state if (ii >> qubit) & 1 == int(outcome): self._statevector[ii] = self._statevector[ii]/norm else: self._statevector[ii] = 0 # update classical state bit = 1 << cbit self._classical_state = (self._classical_state & (~bit)) | (int(outcome) << cbit) def _add_qasm_reset(self, qubit): """Apply the reset to the qubit. This is done by doing a measurement and if 0 do nothing and if 1 flip the qubit. qubit is the qubit that is reset. """ # TODO: slow, refactor later outcome, norm = self._add_qasm_decision(qubit) temp = np.copy(self._statevector) self._statevector.fill(0.0) # measurement for ii in range(1 << self._number_of_qubits): if (ii >> qubit) & 1 == int(outcome): temp[ii] = temp[ii]/norm else: temp[ii] = 0 # reset if outcome == '1': for ii in range(1 << self._number_of_qubits): iip = (~ (1 << qubit)) & ii # bit number qubit set to zero self._statevector[iip] += temp[ii] else: self._statevector = temp def _add_qasm_snapshot(self, slot): """Snapshot instruction to record simulator's internal representation of quantum statevector. Args: slot (string): a label to identify the recorded snapshot. """ self._snapshots.setdefault(str(slot), {}).setdefault("statevector", []).append(np.copy(self._statevector)) def run(self, qobj): """Run qobj asynchronously. Args: qobj (Qobj): payload of the experiment Returns: AerJob: derived from BaseJob """ job_id = str(uuid.uuid4()) aer_job = AerJob(self, job_id, self._run_job, qobj) aer_job.submit() return aer_job def _run_job(self, job_id, qobj): """Run experiments in qobj Args: job_id (str): unique id for the job. qobj (Qobj): job description Returns: Result: Result object """ self._validate(qobj) result_list = [] self._shots = qobj.config.shots self._memory = qobj.config.memory self._qobj_config = qobj.config start = time.time() for experiment in qobj.experiments: result_list.append(self.run_experiment(experiment)) end = time.time() result = {'backend_name': self.name(), 'backend_version': self._configuration.backend_version, 'qobj_id': qobj.qobj_id, 'job_id': job_id, 'results': result_list, 'status': 'COMPLETED', 'success': True, 'time_taken': (end - start), 'header': qobj.header.as_dict()} return Result.from_dict(result) def run_experiment(self, experiment): """Run an experiment (circuit) and return a single experiment result. Args: experiment (QobjExperiment): experiment from qobj experiments list Returns: dict: A result dictionary which looks something like:: { "name": name of this experiment (obtained from qobj.experiment header) "seed": random seed used for simulation "shots": number of shots used in the simulation "data": { "memory": ['0x9', '0xF', '0x1D', ..., '0x9'] "snapshots": { '1': [0.7, 0, 0, 0.7], '2': [0.5, 0.5, 0.5, 0.5] } }, "status": status string for the simulation "success": boolean "time_taken": simulation time of this single experiment } Raises: SimulatorError: if an error occurred. """ self._number_of_qubits = experiment.config.n_qubits self._number_of_cbits = experiment.config.memory_slots self._statevector = 0 self._classical_state = 0 self._snapshots = {} # Get the seed looking in circuit, qobj, and then random. if hasattr(experiment, 'config') and hasattr(experiment.config, 'seed'): seed = experiment.config.seed elif hasattr(self._qobj_config, 'seed'): seed = self._qobj_config.seed else: seed = random.getrandbits(32) self._local_random.seed(seed) outcomes = [] start = time.time() for _ in range(self._shots): self._statevector = np.zeros(1 << self._number_of_qubits, dtype=complex) self._statevector[0] = 1 self._classical_state = 0 for operation in experiment.instructions: if getattr(operation, 'conditional', None): mask = int(operation.conditional.mask, 16) if mask > 0: value = self._classical_state & mask while (mask & 0x1) == 0: mask >>= 1 value >>= 1 if value != int(operation.conditional.val, 16): continue # Check if single gate if operation.name in ('U', 'u1', 'u2', 'u3'): params = getattr(operation, 'params', None) qubit = operation.qubits[0] gate = single_gate_matrix(operation.name, params) self._add_qasm_single(gate, qubit) # Check if CX gate elif operation.name in ('id', 'u0'): pass elif operation.name in ('CX', 'cx'): qubit0 = operation.qubits[0] qubit1 = operation.qubits[1] self._add_qasm_cx(qubit0, qubit1) # Check if measure elif operation.name == 'measure': qubit = operation.qubits[0] cbit = operation.memory[0] self._add_qasm_measure(qubit, cbit) # Check if reset elif operation.name == 'reset': qubit = operation.qubits[0] self._add_qasm_reset(qubit) # Check if barrier elif operation.name == 'barrier': pass # Check if snapshot command elif operation.name == 'snapshot': params = operation.params self._add_qasm_snapshot(params[0]) else: backend = self.name() err_msg = '{0} encountered unrecognized operation "{1}"' raise SimulatorError(err_msg.format(backend, operation.name)) # Turn classical_state (int) into bit string and pad zero for unused cbits outcome = bin(self._classical_state)[2:] # Return a compact hexadecimal outcomes.append(hex(int(outcome, 2))) data = { 'counts': dict(Counter(outcomes)), 'snapshots': self._snapshots } if self._memory: data['memory'] = outcomes end = time.time() return {'name': experiment.header.name, 'seed': seed, 'shots': self._shots, 'data': data, 'status': 'DONE', 'success': True, 'time_taken': (end-start), 'header': experiment.header.as_dict()} def _validate(self, qobj): for experiment in qobj.experiments: if 'measure' not in [op.name for op in experiment.instructions]: logger.warning("no measurements in circuit '%s', " "classical register will remain all zeros.", experiment.header.name)
class StatevectorSimulator(AerBackend): """Aer statevector simulator Backend options: The following backend options may be used with in the `backend_options` kwarg diction for `StatevectorSimulator.run` or `qiskit.execute` * "initial_statevector" (vector_like): Sets a custom initial statevector for the simulation instead of the all zero initial state (Default: None). * "chop_threshold" (double): Sets the threshold for truncating small values to zero in the Result data (Default: 1e-15) * "max_parallel_threads" (int): Sets the maximum number of CPU cores used by OpenMP for parallelization. If set to 0 the maximum will be set to the number of CPU cores (Default: 0). * "max_parallel_experiments" (int): Sets the maximum number of qobj experiments that may be executed in parallel up to the max_parallel_threads value. If set to 1 parallel circuit execution will be disabled. If set to 0 the maximum will be automatically set to max_parallel_threads (Default: 1). * "statevector_parallel_threshold" (int): Sets the threshold that "n_qubits" must be greater than to enable OpenMP parallelization for matrix multiplication during execution of an experiment. If parallel circuit or shot execution is enabled this will only use unallocated CPU cores up to max_parallel_threads. Note that setting this too low can reduce performance (Default: 12). * "statevector_hpc_gate_opt" (bool): If set to True this enables a different optimzied gate application routine that can increase performance on systems with a large number of CPU cores. For systems with a small number of cores it enabling can reduce performance (Default: False). """ MAX_QUBIT_MEMORY = int(log2(local_hardware_info()['memory'] * (1024 ** 3) / 16)) DEFAULT_CONFIGURATION = { 'backend_name': 'statevector_simulator', 'backend_version': __version__, 'n_qubits': MAX_QUBIT_MEMORY, 'url': 'https://github.com/Qiskit/qiskit-aer', 'simulator': True, 'local': True, 'conditional': True, 'open_pulse': False, 'memory': True, 'max_shots': 1, 'description': 'A C++ statevector simulator for qobj files', 'basis_gates': ['u1', 'u2', 'u3', 'cx', 'cz', 'id', 'x', 'y', 'z', 'h', 's', 'sdg', 't', 'tdg', 'ccx', 'swap', 'snapshot', 'unitary'], 'gates': [ { 'name': 'TODO', 'parameters': [], 'qasm_def': 'TODO' } ] } def __init__(self, configuration=None, provider=None): super().__init__(statevector_controller_execute, BackendConfiguration.from_dict(self.DEFAULT_CONFIGURATION), provider=provider) def run(self, qobj, backend_options=None): """Run a qobj on the backend. Args: qobj (Qobj): a Qobj. backend_options (dict): backend configuration options. Returns: AerJob: the simulation job. """ return super().run(qobj, backend_options=backend_options) def _validate(self, qobj): """Semantic validations of the qobj which cannot be done via schemas. Some of these may later move to backend schemas. 1. Set shots=1. 2. Check number of qubits will fit in local memory. """ n_qubits = qobj.config.n_qubits max_qubits = self.configuration().n_qubits if n_qubits > max_qubits: raise AerError('Number of qubits ({}) '.format(n_qubits) + 'is greater than maximum ({}) '.format(max_qubits) + 'for "{}" '.format(self.name()) + 'with {} GB system memory.'.format(int(local_hardware_info()['memory']))) if qobj.config.shots != 1: logger.info('"%s" only supports 1 shot. Setting shots=1.', self.name()) qobj.config.shots = 1 for experiment in qobj.experiments: name = experiment.header.name if getattr(experiment.config, 'shots', 1) != 1: logger.info('"%s" only supports 1 shot. ' 'Setting shots=1 for circuit "%s".', self.name(), name) experiment.config.shots = 1
def _validate(self, qobj, backend_options, noise_model): """Semantic validations of the qobj which cannot be done via schemas. 1. Check number of qubits will fit in local memory. 2. warn if no classical registers or measurements in circuit. """ clifford_instructions = [ "id", "x", "y", "z", "h", "s", "sdg", "CX", "cx", "cz", "swap", "barrier", "reset", "measure" ] unsupported_ch_instructions = ["u2", "u3"] # Check if noise model is Clifford: method = "automatic" if backend_options and "method" in backend_options: method = backend_options["method"] clifford_noise = not (method == "statevector") if clifford_noise: if method != "stabilizer" and noise_model: for error in noise_model.as_dict()['errors']: if error['type'] == 'qerror': for circ in error["instructions"]: for instr in circ: if instr not in clifford_instructions: clifford_noise = False break # Check to see if experiments are clifford for experiment in qobj.experiments: name = experiment.header.name # Check for classical bits if experiment.config.memory_slots == 0: logger.warning( 'No classical registers in circuit "%s": ' 'result data will not contain counts.', name) # Check if Clifford circuit or if measure opts missing no_measure = True ch_supported = False ch_supported = method in ["ch", "automatic"] clifford = False if method == "statevector" else clifford_noise for op in experiment.instructions: if not clifford and not no_measure: break # we don't need to check any more ops if clifford and op.name not in clifford_instructions: clifford = False if no_measure and op.name == "measure": no_measure = False if ch_supported and op.name in unsupported_ch_instructions: ch_supported = False # Print warning if clbits but no measure if no_measure: logger.warning( 'No measurements in circuit "%s": ' 'count data will return all zeros.', name) # Check qubits for statevector simulation if not clifford and method != "ch": n_qubits = experiment.config.n_qubits max_qubits = self.configuration().n_qubits if n_qubits > max_qubits: system_memory = int(local_hardware_info()['memory']) err_string = ('Number of qubits ({}) is greater than ' 'maximum ({}) for "{}" (method=statevector) ' 'with {} GB system memory') err_string = err_string.format(n_qubits, max_qubits, self.name(), system_memory) if method != "automatic": raise AerError(err_string + '.') else: if n_qubits > 63: raise AerError(err_string + ', and has too many ' + 'qubits to fall back to the ' + 'CH simulator.') if not ch_supported: raise AerError(err_string + ', and contains ' + 'instructions not supported by ' + 'the CH simulator.') logger.info('The QasmSimulator will automatically ' 'switch to the CH backend, based on ' 'the memory requirements.')
""" Routines for running Python functions in parallel using process pools from the multiprocessing library. """ import os import platform from multiprocessing import Pool from qiskit._qiskiterror import QISKitError from qiskit._util import local_hardware_info from ._receiver import receiver as rec from ._progressbar import BaseProgressBar # Number of local physical cpus CPU_COUNT = local_hardware_info()['cpus'] def parallel_map(task, values, task_args=tuple(), task_kwargs={}, # pylint: disable=W0102 num_processes=CPU_COUNT): """ Parallel execution of a mapping of `values` to the function `task`. This is functionally equivalent to:: result = [task(value, *task_args, **task_kwargs) for value in values] On Windows this function defaults to a serial implimentation to avoid the overhead from spawning processes in Windows. Args: task (func): Function that is to be called for each value in ``task_vec``.
class QasmSimulator(BaseBackend): """C++ quantum circuit simulator with realistic noise""" DEFAULT_CONFIGURATION = { 'backend_name': 'qasm_simulator', 'backend_version': '1.0.0', 'n_qubits': int(log2(local_hardware_info()['memory'] * (1024**3) / 16)), 'url': 'https://github.com/Qiskit/qiskit-terra/src/qasm-simulator-cpp', 'simulator': True, 'local': True, 'conditional': True, 'open_pulse': False, 'memory': True, 'max_shots': 65536, 'description': 'A C++ realistic noise simulator for qasm experiments', 'basis_gates': [ 'u0', 'u1', 'u2', 'u3', 'cx', 'cz', 'id', 'x', 'y', 'z', 'h', 's', 'sdg', 't', 'tdg', 'rzz', 'snapshot', 'wait', 'noise', 'save', 'load' ], 'gates': [{ 'name': 'u0', 'parameters': ['gamma'], 'qasm_def': 'gate u0(gamma) q { U(0,0,0) q; }' }, { 'name': 'u1', 'parameters': ['lambda'], 'qasm_def': 'gate u1(lambda) q { U(0,0,lambda) q; }' }, { 'name': 'u2', 'parameters': ['phi', 'lambda'], 'qasm_def': 'gate u2(phi,lambda) q { U(pi/2,phi,lambda) q; }' }, { 'name': 'u3', 'parameters': ['theta', 'phi', 'lambda'], 'qasm_def': 'gate u3(theta,phi,lambda) q { U(theta,phi,lambda) q; }' }, { 'name': 'cx', 'parameters': ['c', 't'], 'qasm_def': 'gate cx c,t { CX c,t; }' }, { 'name': 'cz', 'parameters': ['a', 'b'], 'qasm_def': 'gate cz a,b { h b; cx a,b; h b; }' }, { 'name': 'id', 'parameters': ['a'], 'qasm_def': 'gate id a { U(0,0,0) a; }' }, { 'name': 'x', 'parameters': ['a'], 'qasm_def': 'gate x a { u3(pi,0,pi) a; }' }, { 'name': 'y', 'parameters': ['a'], 'qasm_def': 'gate y a { u3(pi,pi/2,pi/2) a; }' }, { 'name': 'z', 'parameters': ['z'], 'qasm_def': 'gate z a { u1(pi) a; }' }, { 'name': 'h', 'parameters': ['a'], 'qasm_def': 'gate h a { u2(0,pi) a; }' }, { 'name': 's', 'parameters': ['a'], 'qasm_def': 'gate s a { u1(pi/2) a; }' }, { 'name': 'sdg', 'parameters': ['a'], 'qasm_def': 'gate sdg a { u1(-pi/2) a; }' }, { 'name': 't', 'parameters': ['a'], 'qasm_def': 'gate t a { u1(pi/4) a; }' }, { 'name': 'tdg', 'parameters': ['a'], 'qasm_def': 'gate tdg a { u1(-pi/4) a; }' }, { 'name': 'rzz', 'parameters': ['theta', 'a', 'b'], 'qasm_def': 'gate rzz(theta) a,b { cx a,b; u1(theta) b; cx a,b; }' }, { 'name': 'snapshot', 'parameters': ['slot'], 'qasm_def': 'gate snapshot(slot) q { TODO }' }, { 'name': 'wait', 'parameters': ['t'], 'qasm_def': 'gate wait(t) q { TODO }' }, { 'name': 'noise', 'parameters': ['switch'], 'qasm_def': 'gate noise(switch) q { TODO }' }, { 'name': 'save', 'parameters': ['slot'], 'qasm_def': 'gate save(slot) q { TODO }' }, { 'name': 'load', 'parameters': ['slot'], 'qasm_def': 'gate load(slot) q { TODO }' }] } def __init__(self, configuration=None, provider=None): super().__init__(configuration=(configuration or BackendConfiguration.from_dict( self.DEFAULT_CONFIGURATION)), provider=provider) # Try to use the default executable if not specified. if 'exe' in self._configuration: paths = [self._configuration.exe] else: paths = DEFAULT_SIMULATOR_PATHS # Ensure that the executable is available. try: self._configuration.exe = next( path for path in paths if (os.path.exists(path) and os.path.getsize(path) > 100)) except StopIteration: raise FileNotFoundError( 'Simulator executable not found (using %s)' % getattr(self._configuration, 'exe', 'default locations')) def run(self, qobj): """Run a qobj on the backend.""" job_id = str(uuid.uuid4()) aer_job = AerJob(self, job_id, self._run_job, qobj) aer_job.submit() return aer_job def _run_job(self, job_id, qobj): """Run a Qobj on the backend.""" self._validate(qobj) qobj_dict = qobj.as_dict() result = run(qobj_dict, self._configuration.exe) result['job_id'] = job_id # Ensure that the required results fields are present, even if the # job failed. result['results'] = result.get('results', []) result['qobj_id'] = result.get('qobj_id', 'unavailable') result['backend_name'] = result.get('backend_name', self.name()) result['backend_version'] = result.get( 'backend_version', self.configuration().backend_version) return Result.from_dict(result) def _validate(self, qobj): for experiment in qobj.experiments: if 'measure' not in [op.name for op in experiment.instructions]: logger.warning( "no measurements in circuit '%s', " "classical register will remain all zeros.", experiment.header.name)
class QasmSimulatorPy(BaseBackend): """Python implementation of a qasm simulator.""" DEFAULT_CONFIGURATION = { 'backend_name': 'qasm_simulator_py', 'backend_version': '2.0.0', 'n_qubits': int(log2(local_hardware_info()['memory'] * (1024**3) / 16)), 'url': 'https://github.com/Qiskit/qiskit-terra', 'simulator': True, 'local': True, 'conditional': True, 'open_pulse': False, 'memory': True, 'max_shots': 65536, 'description': 'A python simulator for qasm experiments', 'basis_gates': ['u1', 'u2', 'u3', 'cx', 'id', 'snapshot'], 'gates': [{ 'name': 'TODO', 'parameters': [], 'qasm_def': 'TODO' }] } def __init__(self, configuration=None, provider=None): super().__init__(configuration=(configuration or BackendConfiguration.from_dict( self.DEFAULT_CONFIGURATION)), provider=provider) self._local_random = random.Random() # Define attributes in __init__. self._classical_state = 0 self._statevector = 0 self._snapshots = {} self._number_of_cbits = 0 self._number_of_qubits = 0 self._shots = 0 self._qobj_config = None def properties(self): """Return backend properties""" properties = { 'backend_name': self.name(), 'backend_version': self.configuration().backend_version, 'last_update_date': '2000-01-01 00:00:00Z', 'qubits': [[{ 'name': 'TODO', 'date': '2000-01-01 00:00:00Z', 'unit': 'TODO', 'value': 0 }]], 'gates': [{ 'qubits': [0], 'gate': 'TODO', 'parameters': [{ 'name': 'TODO', 'date': '2000-01-01 00:00:00Z', 'unit': 'TODO', 'value': 0 }] }], 'general': [] } return BackendProperties.from_dict(properties) def _add_qasm_single(self, gate, qubit): """Apply an arbitrary 1-qubit operator to a qubit. Gate is the single qubit applied. qubit is the qubit the gate is applied to. """ psi = self._statevector bit = 1 << qubit for k1 in range(0, 1 << self._number_of_qubits, 1 << (qubit + 1)): for k2 in range(0, 1 << qubit, 1): k = k1 | k2 cache0 = psi[k] cache1 = psi[k | bit] psi[k] = gate[0, 0] * cache0 + gate[0, 1] * cache1 psi[k | bit] = gate[1, 0] * cache0 + gate[1, 1] * cache1 def _add_qasm_cx(self, q0, q1): """Optimized ideal CX on two qubits. q0 is the first qubit (control) counts from 0. q1 is the second qubit (target). """ psi = self._statevector for k in range(0, 1 << (self._number_of_qubits - 2)): # first bit is control, second is target ind1 = index2(1, q0, 0, q1, k) # swap target if control is 1 ind3 = index2(1, q0, 1, q1, k) cache0 = psi[ind1] cache1 = psi[ind3] psi[ind3] = cache0 psi[ind1] = cache1 def _add_qasm_decision(self, qubit): """Apply the decision of measurement/reset qubit gate. qubit is the qubit that is measured/reset """ probability_zero = 0 random_number = self._local_random.random() for ii in range(1 << self._number_of_qubits): if ii & (1 << qubit) == 0: probability_zero += np.abs(self._statevector[ii])**2 if random_number <= probability_zero: outcome = '0' norm = np.sqrt(probability_zero) else: outcome = '1' norm = np.sqrt(1 - probability_zero) return (outcome, norm) def _add_qasm_measure(self, qubit, cbit): """Apply the measurement qubit gate. qubit is the qubit measured. cbit is the classical bit the measurement is assigned to. """ outcome, norm = self._add_qasm_decision(qubit) for ii in range(1 << self._number_of_qubits): # update quantum state if (ii >> qubit) & 1 == int(outcome): self._statevector[ii] = self._statevector[ii] / norm else: self._statevector[ii] = 0 # update classical state bit = 1 << cbit self._classical_state = (self._classical_state & (~bit)) | (int(outcome) << cbit) def _add_qasm_reset(self, qubit): """Apply the reset to the qubit. This is done by doing a measruement and if 0 do nothing and if 1 flip the qubit. qubit is the qubit that is reset. """ # TODO: slow, refactor later outcome, norm = self._add_qasm_decision(qubit) temp = np.copy(self._statevector) self._statevector.fill(0.0) # measurement for ii in range(1 << self._number_of_qubits): if (ii >> qubit) & 1 == int(outcome): temp[ii] = temp[ii] / norm else: temp[ii] = 0 # reset if outcome == '1': for ii in range(1 << self._number_of_qubits): iip = (~(1 << qubit)) & ii # bit number qubit set to zero self._statevector[iip] += temp[ii] else: self._statevector = temp def _add_qasm_snapshot(self, slot): """Snapshot instruction to record simulator's internal representation of quantum statevector. slot is a string indicating a snapshot slot label. """ self._snapshots.setdefault(str(slot), {}).setdefault( "statevector", []).append(np.copy(self._statevector)) def run(self, qobj): """Run qobj asynchronously. Args: qobj (dict): job description Returns: AerJob: derived from BaseJob """ job_id = str(uuid.uuid4()) aer_job = AerJob(self, job_id, self._run_job, qobj) aer_job.submit() return aer_job def _run_job(self, job_id, qobj): """Run circuits in qobj""" self._validate(qobj) result_list = [] self._shots = qobj.config.shots self._qobj_config = qobj.config start = time.time() for circuit in qobj.experiments: result_list.append(self.run_circuit(circuit)) end = time.time() result = { 'backend': self.name(), 'id': qobj.qobj_id, 'job_id': job_id, 'result': result_list, 'status': 'COMPLETED', 'success': True, 'time_taken': (end - start) } copy_qasm_from_qobj_into_result(qobj, result) return result_from_old_style_dict(result) def run_circuit(self, circuit): """Run a circuit and return a single Result. Args: circuit (QobjExperiment): experiment from qobj experiments list Returns: dict: A dictionary of results which looks something like:: { "data": { #### DATA CAN BE A DIFFERENT DICTIONARY FOR EACH BACKEND #### "counts": {'00000': XXXX, '00001': XXXXX}, "time" : xx.xxxxxxxx }, "status": --status (string)-- } Raises: SimulatorError: if an error occurred. """ self._number_of_qubits = circuit.header.number_of_qubits self._number_of_cbits = circuit.header.number_of_clbits self._statevector = 0 self._classical_state = 0 self._snapshots = {} cl_reg_index = [] # starting bit index of classical register cl_reg_nbits = [] # number of bits in classical register cbit_index = 0 for cl_reg in circuit.header.clbit_labels: cl_reg_nbits.append(cl_reg[1]) cl_reg_index.append(cbit_index) cbit_index += cl_reg[1] # Get the seed looking in circuit, qobj, and then random. if hasattr(circuit, 'config') and hasattr(circuit.config, 'seed'): seed = circuit.config.seed elif hasattr(self._qobj_config, 'seed'): seed = self._qobj_config.seed else: seed = random.getrandbits(32) self._local_random.seed(seed) outcomes = [] start = time.time() for _ in range(self._shots): self._statevector = np.zeros(1 << self._number_of_qubits, dtype=complex) self._statevector[0] = 1 self._classical_state = 0 for operation in circuit.instructions: if getattr(operation, 'conditional', None): mask = int(operation.conditional.mask, 16) if mask > 0: value = self._classical_state & mask while (mask & 0x1) == 0: mask >>= 1 value >>= 1 if value != int(operation.conditional.val, 16): continue # Check if single gate if operation.name in ('U', 'u1', 'u2', 'u3'): params = getattr(operation, 'params', None) qubit = operation.qubits[0] gate = single_gate_matrix(operation.name, params) self._add_qasm_single(gate, qubit) # Check if CX gate elif operation.name in ('id', 'u0'): pass elif operation.name in ('CX', 'cx'): qubit0 = operation.qubits[0] qubit1 = operation.qubits[1] self._add_qasm_cx(qubit0, qubit1) # Check if measure elif operation.name == 'measure': qubit = operation.qubits[0] cbit = operation.clbits[0] self._add_qasm_measure(qubit, cbit) # Check if reset elif operation.name == 'reset': qubit = operation.qubits[0] self._add_qasm_reset(qubit) # Check if barrier elif operation.name == 'barrier': pass # Check if snapshot command elif operation.name == 'snapshot': params = operation.params self._add_qasm_snapshot(params[0]) else: backend = self.name() err_msg = '{0} encountered unrecognized operation "{1}"' raise SimulatorError( err_msg.format(backend, operation.name)) # Turn classical_state (int) into bit string outcomes.append( bin(self._classical_state)[2:].zfill(self._number_of_cbits)) # Return the results counts = dict(Counter(outcomes)) data = { 'counts': self._format_result(counts, cl_reg_index, cl_reg_nbits), 'snapshots': self._snapshots } end = time.time() return { 'name': circuit.header.name, 'seed': seed, 'shots': self._shots, 'data': data, 'status': 'DONE', 'success': True, 'time_taken': (end - start) } def _validate(self, qobj): for experiment in qobj.experiments: if 'measure' not in [op.name for op in experiment.instructions]: logger.warning( "no measurements in circuit '%s', " "classical register will remain all zeros.", experiment.header.name) def _format_result(self, counts, cl_reg_index, cl_reg_nbits): """Format the result bit string. This formats the result bit strings such that spaces are inserted at register divisions. Args: counts (dict): dictionary of counts e.g. {'1111': 1000, '0000':5} cl_reg_index (list): starting bit index of classical register cl_reg_nbits (list): total amount of bits in classical register Returns: dict: spaces inserted into dictionary keys at register boundaries. """ fcounts = {} for key, value in counts.items(): if cl_reg_nbits: new_key = [key[-cl_reg_nbits[0]:]] for index, nbits in zip(cl_reg_index[1:], cl_reg_nbits[1:]): new_key.insert(0, key[-(index + nbits):-index]) fcounts[' '.join(new_key)] = value return fcounts
class QasmSimulatorPy(BaseBackend): """Python implementation of a qasm simulator.""" MAX_QUBITS_MEMORY = int( log2(local_hardware_info()['memory'] * (1024**3) / 16)) DEFAULT_CONFIGURATION = { 'backend_name': 'qasm_simulator', 'backend_version': '2.0.0', 'n_qubits': min(24, MAX_QUBITS_MEMORY), 'url': 'https://github.com/Qiskit/qiskit-terra', 'simulator': True, 'local': True, 'conditional': True, 'open_pulse': False, 'memory': True, 'max_shots': 65536, 'description': 'A python simulator for qasm experiments', 'basis_gates': ['u1', 'u2', 'u3', 'cx', 'id', 'unitary'], 'gates': [{ 'name': 'u1', 'parameters': ['lambda'], 'qasm_def': 'gate u1(lambda) q { U(0,0,lambda) q; }' }, { 'name': 'u2', 'parameters': ['phi', 'lambda'], 'qasm_def': 'gate u2(phi,lambda) q { U(pi/2,phi,lambda) q; }' }, { 'name': 'u3', 'parameters': ['theta', 'phi', 'lambda'], 'qasm_def': 'gate u3(theta,phi,lambda) q { U(theta,phi,lambda) q; }' }, { 'name': 'cx', 'parameters': ['c', 't'], 'qasm_def': 'gate cx c,t { CX c,t; }' }, { 'name': 'id', 'parameters': ['a'], 'qasm_def': 'gate id a { U(0,0,0) a; }' }, { 'name': 'unitary', 'parameters': ['matrix'], 'qasm_def': 'unitary(matrix) q1, q2,...' }] } DEFAULT_OPTIONS = {"initial_statevector": None, "chop_threshold": 1e-15} # Class level variable to return the final state at the end of simulation # This should be set to True for the statevector simulator SHOW_FINAL_STATE = False def __init__(self, configuration=None, provider=None): super().__init__(configuration=(configuration or BackendConfiguration.from_dict( self.DEFAULT_CONFIGURATION)), provider=provider) # Define attributes in __init__. self._local_random = np.random.RandomState() self._classical_memory = 0 self._classical_register = 0 self._statevector = 0 self._number_of_cmembits = 0 self._number_of_qubits = 0 self._shots = 0 self._memory = False self._initial_statevector = self.DEFAULT_OPTIONS["initial_statevector"] self._chop_threshold = self.DEFAULT_OPTIONS["chop_threshold"] self._qobj_config = None # TEMP self._sample_measure = False def _add_unitary_single(self, gate, qubit): """Apply an arbitrary 1-qubit unitary matrix. Args: gate (matrix_like): a single qubit gate matrix qubit (int): the qubit to apply gate to """ # Compute einsum index string for 1-qubit matrix multiplication indexes = einsum_vecmul_index([qubit], self._number_of_qubits) # Convert to complex rank-2 tensor gate_tensor = np.array(gate, dtype=complex) # Apply matrix multiplication self._statevector = np.einsum(indexes, gate_tensor, self._statevector, dtype=complex, casting='no') def _add_unitary_two(self, gate, qubit0, qubit1): """Apply a two-qubit unitary matrix. Args: gate (matrix_like): a the two-qubit gate matrix qubit0 (int): gate qubit-0 qubit1 (int): gate qubit-1 """ # Compute einsum index string for 1-qubit matrix multiplication indexes = einsum_vecmul_index([qubit0, qubit1], self._number_of_qubits) # Convert to complex rank-4 tensor gate_tensor = np.reshape(np.array(gate, dtype=complex), 4 * [2]) # Apply matrix multiplication self._statevector = np.einsum(indexes, gate_tensor, self._statevector, dtype=complex, casting='no') def _get_measure_outcome(self, qubit): """Simulate the outcome of measurement of a qubit. Args: qubit (int): the qubit to measure Return: tuple: pair (outcome, probability) where outcome is '0' or '1' and probability is the probability of the returned outcome. """ # Axis for numpy.sum to compute probabilities axis = list(range(self._number_of_qubits)) axis.remove(self._number_of_qubits - 1 - qubit) probabilities = np.sum(np.abs(self._statevector)**2, axis=tuple(axis)) # Compute einsum index string for 1-qubit matrix multiplication random_number = self._local_random.rand() if random_number < probabilities[0]: return '0', probabilities[0] # Else outcome was '1' return '1', probabilities[1] def _add_sample_measure(self, measure_params, num_samples): """Generate memory samples from current statevector. Args: measure_params (list): List of (qubit, cmembit) values for measure instructions to sample. num_samples (int): The number of memory samples to generate. Returns: list: A list of memory values in hex format. """ # Get unique qubits that are actually measured measured_qubits = list({qubit for qubit, cmembit in measure_params}) num_measured = len(measured_qubits) # Axis for numpy.sum to compute probabilities axis = list(range(self._number_of_qubits)) for qubit in reversed(measured_qubits): # Remove from largest qubit to smallest so list position is correct # with respect to position from end of the list axis.remove(self._number_of_qubits - 1 - qubit) probabilities = np.reshape( np.sum(np.abs(self._statevector)**2, axis=tuple(axis)), 2**num_measured) # Generate samples on measured qubits samples = self._local_random.choice(range(2**num_measured), num_samples, p=probabilities) # Convert to bit-strings memory = [] for sample in samples: classical_memory = self._classical_memory for count, (qubit, cmembit) in enumerate(sorted(measure_params)): qubit_outcome = int((sample & (1 << count)) >> count) membit = 1 << cmembit classical_memory = (classical_memory & (~membit)) | (qubit_outcome << cmembit) value = bin(classical_memory)[2:] memory.append(hex(int(value, 2))) return memory def _add_qasm_measure(self, qubit, cmembit, cregbit=None): """Apply a measure instruction to a qubit. Args: qubit (int): qubit is the qubit measured. cmembit (int): is the classical memory bit to store outcome in. cregbit (int, optional): is the classical register bit to store outcome in. """ # get measure outcome outcome, probability = self._get_measure_outcome(qubit) # update classical state membit = 1 << cmembit self._classical_memory = (self._classical_memory & (~membit)) | (int(outcome) << cmembit) if cregbit is not None: regbit = 1 << cregbit self._classical_register = \ (self._classical_register & (~regbit)) | (int(outcome) << cregbit) # update quantum state if outcome == '0': update_diag = [[1 / np.sqrt(probability), 0], [0, 0]] else: update_diag = [[0, 0], [0, 1 / np.sqrt(probability)]] # update classical state self._add_unitary_single(update_diag, qubit) def _add_qasm_reset(self, qubit): """Apply a reset instruction to a qubit. Args: qubit (int): the qubit being rest This is done by doing a simulating a measurement outcome and projecting onto the outcome state while renormalizing. """ # get measure outcome outcome, probability = self._get_measure_outcome(qubit) # update quantum state if outcome == '0': update = [[1 / np.sqrt(probability), 0], [0, 0]] self._add_unitary_single(update, qubit) else: update = [[0, 1 / np.sqrt(probability)], [0, 0]] self._add_unitary_single(update, qubit) def _validate_initial_statevector(self): """Validate an initial statevector""" # If initial statevector isn't set we don't need to validate if self._initial_statevector is None: return # Check statevector is correct length for number of qubits length = len(self._initial_statevector) required_dim = 2**self._number_of_qubits if length != required_dim: raise BasicAerError('initial statevector is incorrect length: ' + '{} != {}'.format(length, required_dim)) def _set_options(self, qobj_config=None, backend_options=None): """Set the backend options for all experiments in a qobj""" # Reset default options self._initial_statevector = self.DEFAULT_OPTIONS["initial_statevector"] self._chop_threshold = self.DEFAULT_OPTIONS["chop_threshold"] if backend_options is None: backend_options = {} # Check for custom initial statevector in backend_options first, # then config second if 'initial_statevector' in backend_options: self._initial_statevector = np.array( backend_options['initial_statevector'], dtype=complex) elif hasattr(qobj_config, 'initial_statevector'): self._initial_statevector = np.array( qobj_config.initial_statevector, dtype=complex) if self._initial_statevector is not None: # Check the initial statevector is normalized norm = np.linalg.norm(self._initial_statevector) if round(norm, 12) != 1: raise BasicAerError('initial statevector is not normalized: ' + 'norm {} != 1'.format(norm)) # Check for custom chop threshold # Replace with custom options if 'chop_threshold' in backend_options: self._chop_threshold = backend_options['chop_threshold'] elif hasattr(qobj_config, 'chop_threshold'): self._chop_threshold = qobj_config.chop_threshold def _initialize_statevector(self): """Set the initial statevector for simulation""" if self._initial_statevector is None: # Set to default state of all qubits in |0> self._statevector = np.zeros(2**self._number_of_qubits, dtype=complex) self._statevector[0] = 1 else: self._statevector = self._initial_statevector.copy() # Reshape to rank-N tensor self._statevector = np.reshape(self._statevector, self._number_of_qubits * [2]) def _get_statevector(self): """Return the current statevector in JSON Result spec format""" vec = np.reshape(self._statevector, 2**self._number_of_qubits) # Expand complex numbers vec = np.stack([vec.real, vec.imag], axis=1) # Truncate small values vec[abs(vec) < self._chop_threshold] = 0.0 return vec def _validate_measure_sampling(self, experiment): """Determine if measure sampling is allowed for an experiment Args: experiment (QobjExperiment): a qobj experiment. """ # Check for config flag if hasattr(experiment.config, 'allows_measure_sampling'): self._sample_measure = experiment.config.allows_measure_sampling # If flag isn't found do a simple test to see if a circuit contains # no reset instructions, and no gates instructions after # the first measure. else: measure_flag = False for instruction in experiment.instructions: # If circuit contains reset operations we cannot sample if instruction.name == "reset": self._sample_measure = False return # If circuit contains a measure option then we can # sample only if all following operations are measures if measure_flag: # If we find a non-measure instruction # we cannot do measure sampling if instruction.name not in [ "measure", "barrier", "id", "u0" ]: self._sample_measure = False return elif instruction.name == "measure": measure_flag = True # If we made it to the end of the circuit without returning # measure sampling is allowed self._sample_measure = True def run(self, qobj, backend_options=None): """Run qobj asynchronously. Args: qobj (Qobj): payload of the experiment backend_options (dict): backend options Returns: BasicAerJob: derived from BaseJob Additional Information: backend_options: Is a dict of options for the backend. It may contain * "initial_statevector": vector_like The "initial_statevector" option specifies a custom initial initial statevector for the simulator to be used instead of the all zero state. This size of this vector must be correct for the number of qubits in all experiments in the qobj. Example:: backend_options = { "initial_statevector": np.array([1, 0, 0, 1j]) / np.sqrt(2), } """ self._set_options(qobj_config=qobj.config, backend_options=backend_options) job_id = str(uuid.uuid4()) job = BasicAerJob(self, job_id, self._run_job, qobj) job.submit() return job def _run_job(self, job_id, qobj): """Run experiments in qobj Args: job_id (str): unique id for the job. qobj (Qobj): job description Returns: Result: Result object """ self._validate(qobj) result_list = [] self._shots = qobj.config.shots self._memory = getattr(qobj.config, 'memory', False) self._qobj_config = qobj.config start = time.time() for experiment in qobj.experiments: result_list.append(self.run_experiment(experiment)) end = time.time() result = { 'backend_name': self.name(), 'backend_version': self._configuration.backend_version, 'qobj_id': qobj.qobj_id, 'job_id': job_id, 'results': result_list, 'status': 'COMPLETED', 'success': True, 'time_taken': (end - start), 'header': qobj.header.as_dict() } return Result.from_dict(result) def run_experiment(self, experiment): """Run an experiment (circuit) and return a single experiment result. Args: experiment (QobjExperiment): experiment from qobj experiments list Returns: dict: A result dictionary which looks something like:: { "name": name of this experiment (obtained from qobj.experiment header) "seed": random seed used for simulation "shots": number of shots used in the simulation "data": { "counts": {'0x9: 5, ...}, "memory": ['0x9', '0xF', '0x1D', ..., '0x9'] }, "status": status string for the simulation "success": boolean "time_taken": simulation time of this single experiment } Raises: BasicAerError: if an error occurred. """ start = time.time() self._number_of_qubits = experiment.config.n_qubits self._number_of_cmembits = experiment.config.memory_slots self._statevector = 0 self._classical_memory = 0 self._classical_register = 0 self._sample_measure = False # Validate the dimension of initial statevector if set self._validate_initial_statevector() # Get the seed looking in circuit, qobj, and then random. if hasattr(experiment.config, 'seed'): seed = experiment.config.seed elif hasattr(self._qobj_config, 'seed'): seed = self._qobj_config.seed else: # For compatibility on Windows force dyte to be int32 # and set the maximum value to be (2 ** 31) - 1 seed = np.random.randint(2147483647, dtype='int32') self._local_random.seed(seed=seed) # Check if measure sampling is supported for current circuit self._validate_measure_sampling(experiment) # List of final counts for all shots memory = [] # Check if we can sample measurements, if so we only perform 1 shot # and sample all outcomes from the final state vector if self._sample_measure: shots = 1 # Store (qubit, cmembit) pairs for all measure ops in circuit to # be sampled measure_sample_ops = [] else: shots = self._shots for _ in range(shots): self._initialize_statevector() # Initialize classical memory to all 0 self._classical_memory = 0 self._classical_register = 0 for operation in experiment.instructions: conditional = getattr(operation, 'conditional', None) if isinstance(conditional, int): if not self._classical_register[-conditional - 1]: continue elif conditional is not None: mask = int(operation.conditional.mask, 16) if mask > 0: value = self._classical_memory & mask while (mask & 0x1) == 0: mask >>= 1 value >>= 1 if value != int(operation.conditional.val, 16): continue # Check if single gate if operation.name in ('U', 'u1', 'u2', 'u3'): params = getattr(operation, 'params', None) qubit = operation.qubits[0] gate = single_gate_matrix(operation.name, params) self._add_unitary_single(gate, qubit) # Check if CX gate elif operation.name in ('id', 'u0'): pass elif operation.name in ('CX', 'cx'): qubit0 = operation.qubits[0] qubit1 = operation.qubits[1] gate = cx_gate_matrix() self._add_unitary_two(gate, qubit0, qubit1) # Check if reset elif operation.name == 'reset': qubit = operation.qubits[0] self._add_qasm_reset(qubit) # Check if barrier elif operation.name == 'barrier': pass # Check if measure elif operation.name == 'measure': qubit = operation.qubits[0] cmembit = operation.memory[0] cregbit = operation.register[0] if hasattr( operation, 'register') else None if self._sample_measure: # If sampling measurements record the qubit and cmembit # for this measurement for later sampling measure_sample_ops.append((qubit, cmembit)) else: # If not sampling perform measurement as normal self._add_qasm_measure(qubit, cmembit, cregbit) elif operation.name == 'bfunc': mask = int(operation.mask, 16) relation = operation.relation val = int(operation.val, 16) cregbit = operation.register cmembit = operation.memory if hasattr(operation, 'memory') else None compared = (self._classical_register & mask) - val if relation == '==': outcome = (compared == 0) elif relation == '!=': outcome = (compared != 0) elif relation == '<': outcome = (compared < 0) elif relation == '<=': outcome = (compared <= 0) elif relation == '>': outcome = (compared > 0) elif relation == '>=': outcome = (compared >= 0) else: raise BasicAerError( 'Invalid boolean function relation.') # Store outcome in register and optionally memory slot regbit = 1 << cregbit self._classical_register = \ (self._classical_register & (~regbit)) | (int(outcome) << cregbit) if cmembit is not None: membit = 1 << cmembit self._classical_memory = \ (self._classical_memory & (~membit)) | (int(outcome) << cmembit) else: backend = self.name() err_msg = '{0} encountered unrecognized operation "{1}"' raise BasicAerError(err_msg.format(backend, operation.name)) # Add final creg data to memory list if self._number_of_cmembits > 0: if self._sample_measure: # If sampling we generate all shot samples from the final statevector memory = self._add_sample_measure(measure_sample_ops, self._shots) else: # Turn classical_memory (int) into bit string and pad zero for unused cmembits outcome = bin(self._classical_memory)[2:] memory.append(hex(int(outcome, 2))) # Add data data = {'counts': dict(Counter(memory))} # Optionally add memory list if self._memory: data['memory'] = memory # Optionally add final statevector if self.SHOW_FINAL_STATE: data['statevector'] = self._get_statevector() # Remove empty counts and memory for statevector simulator if not data['counts']: data.pop('counts') if 'memory' in data and not data['memory']: data.pop('memory') end = time.time() return { 'name': experiment.header.name, 'seed': seed, 'shots': self._shots, 'data': data, 'status': 'DONE', 'success': True, 'time_taken': (end - start), 'header': experiment.header.as_dict() } def _validate(self, qobj): """Semantic validations of the qobj which cannot be done via schemas.""" n_qubits = qobj.config.n_qubits max_qubits = self.configuration().n_qubits if n_qubits > max_qubits: raise BasicAerError( 'Number of qubits {} '.format(n_qubits) + 'is greater than maximum ({}) '.format(max_qubits) + 'for "{}".'.format(self.name())) for experiment in qobj.experiments: name = experiment.header.name if experiment.config.memory_slots == 0: logger.warning( 'No classical registers in circuit "%s", ' 'counts will be empty.', name) elif 'measure' not in [op.name for op in experiment.instructions]: logger.warning( 'No measurements in circuit "%s", ' 'classical register will remain all zeros.', name)
class StatevectorSimulator(QasmSimulator): """C++ statevector simulator""" DEFAULT_CONFIGURATION = { 'backend_name': 'statevector_simulator', 'backend_version': '1.0.0', 'n_qubits': int(log2(local_hardware_info()['memory'] * (1024**3)/16)), 'url': 'https://github.com/Qiskit/qiskit-terra/src/qasm-simulator-cpp', 'simulator': True, 'local': True, 'conditional': False, 'open_pulse': False, 'memory': False, 'max_shots': 65536, 'description': 'A single-shot C++ statevector simulator for the |0> state evolution', 'basis_gates': ['u1', 'u2', 'u3', 'cx', 'cz', 'id', 'x', 'y', 'z', 'h', 's', 'sdg', 't', 'tdg', 'rzz', 'load', 'save', 'snapshot'], 'gates': [ { 'name': 'u1', 'parameters': ['lambda'], 'qasm_def': 'gate u1(lambda) q { U(0,0,lambda) q; }' }, { 'name': 'u2', 'parameters': ['phi', 'lambda'], 'qasm_def': 'gate u2(phi,lambda) q { U(pi/2,phi,lambda) q; }' }, { 'name': 'u3', 'parameters': ['theta', 'phi', 'lambda'], 'qasm_def': 'gate u3(theta,phi,lambda) q { U(theta,phi,lambda) q; }' }, { 'name': 'cx', 'parameters': ['c', 't'], 'qasm_def': 'gate cx c,t { CX c,t; }' }, { 'name': 'cz', 'parameters': ['a', 'b'], 'qasm_def': 'gate cz a,b { h b; cx a,b; h b; }' }, { 'name': 'id', 'parameters': ['a'], 'qasm_def': 'gate id a { U(0,0,0) a; }' }, { 'name': 'x', 'parameters': ['a'], 'qasm_def': 'gate x a { u3(pi,0,pi) a; }' }, { 'name': 'y', 'parameters': ['a'], 'qasm_def': 'gate y a { u3(pi,pi/2,pi/2) a; }' }, { 'name': 'z', 'parameters': ['z'], 'qasm_def': 'gate z a { u1(pi) a; }' }, { 'name': 'h', 'parameters': ['a'], 'qasm_def': 'gate h a { u2(0,pi) a; }' }, { 'name': 's', 'parameters': ['a'], 'qasm_def': 'gate s a { u1(pi/2) a; }' }, { 'name': 'sdg', 'parameters': ['a'], 'qasm_def': 'gate sdg a { u1(-pi/2) a; }' }, { 'name': 't', 'parameters': ['a'], 'qasm_def': 'gate t a { u1(pi/4) a; }' }, { 'name': 'tdg', 'parameters': ['a'], 'qasm_def': 'gate tdg a { u1(-pi/4) a; }' }, { 'name': 'rzz', 'parameters': ['theta', 'a', 'b'], 'qasm_def': 'gate rzz(theta) a,b { cx a,b; u1(theta) b; cx a,b; }' }, { 'name': 'load', 'parameters': ['slot'], 'qasm_def': 'gate load(slot) q { TODO }' }, { 'name': 'save', 'parameters': ['slot'], 'qasm_def': 'gate save(slot) q { TODO }' }, { 'name': 'snapshot', 'parameters': ['slot'], 'qasm_def': 'gate snapshot(slot) q { TODO }' } ] } def __init__(self, configuration=None, provider=None): super().__init__(configuration=(configuration or BackendConfiguration.from_dict(self.DEFAULT_CONFIGURATION)), provider=provider) def run(self, qobj): """Run a qobj on the backend.""" job_id = str(uuid.uuid4()) job = SimulatorsJob(self, job_id, self._run_job, qobj) job.submit() return job def _run_job(self, job_id, qobj): """Run a Qobj on the backend.""" self._validate(qobj) final_state_key = 32767 # Internal key for final state snapshot # Add final snapshots to circuits for experiment in qobj.experiments: experiment.instructions.append( QobjInstruction(name='snapshot', params=[final_state_key], label='MISSING', type='MISSING') ) result = super()._run_job(job_id, qobj) # Remove added snapshot from qobj for experiment in qobj.experiments: del experiment.instructions[-1] # Extract final state snapshot and move to 'statevector' data field for experiment_result in result.results: snapshots = experiment_result.data.snapshots.to_dict() if str(final_state_key) in snapshots['statevector']: final_state_key = str(final_state_key) # Pop off final snapshot added above final_state = snapshots['statevector'].pop(final_state_key)[0] final_state = array([v[0] + 1j * v[1] for v in final_state], dtype=complex) # Add final state to results data experiment_result.data.statevector = final_state # Remove snapshot dict if empty if snapshots == {}: delattr(experiment_result.data, 'snapshots') return result def _validate(self, qobj): """Semantic validations of the qobj which cannot be done via schemas. Some of these may later move to backend schemas. 1. No shots 2. No measurements in the middle """ if qobj.config.shots != 1: logger.info("statevector simulator only supports 1 shot. " "Setting shots=1.") qobj.config.shots = 1 for experiment in qobj.experiments: if getattr(experiment.config, 'shots', 1) != 1: logger.info("statevector simulator only supports 1 shot. " "Setting shots=1 for circuit %s.", experiment.name) experiment.config.shots = 1 for op in experiment.instructions: if op.name in ['measure', 'reset']: raise SimulatorError( "In circuit {}: statevector simulator does not support " "measure or reset.".format(experiment.header.name))
class StatevectorSimulator(QasmSimulator): """C++ statevector simulator""" DEFAULT_CONFIGURATION = { 'backend_name': 'statevector_simulator', 'backend_version': '1.0.0', 'n_qubits': int(log2(local_hardware_info()['memory'] * (1024**3)/16)), 'url': 'https://github.com/Qiskit/qiskit-terra/src/qasm-simulator-cpp', 'simulator': True, 'local': True, 'conditional': False, 'open_pulse': False, 'memory': False, 'max_shots': 65536, 'description': 'A single-shot C++ statevector simulator for the |0> state evolution', 'basis_gates': ['u1', 'u2', 'u3', 'cx', 'cz', 'id', 'x', 'y', 'z', 'h', 's', 'sdg', 't', 'tdg', 'rzz', 'load', 'save', 'snapshot'], 'gates': [{'name': 'TODO', 'parameters': [], 'qasm_def': 'TODO'}] } def __init__(self, configuration=None, provider=None): super().__init__(configuration=(configuration or BackendConfiguration.from_dict(self.DEFAULT_CONFIGURATION)), provider=provider) def properties(self): """Return backend properties""" properties = { 'backend_name': self.name(), 'backend_version': self.configuration().backend_version, 'last_update_date': '2000-01-01 00:00:00Z', 'qubits': [[{'name': 'TODO', 'date': '2000-01-01 00:00:00Z', 'unit': 'TODO', 'value': 0}]], 'gates': [{'qubits': [0], 'gate': 'TODO', 'parameters': [{'name': 'TODO', 'date': '2000-01-01 00:00:00Z', 'unit': 'TODO', 'value': 0}]}], 'general': [] } return BackendProperties.from_dict(properties) def run(self, qobj): """Run a qobj on the the backend.""" job_id = str(uuid.uuid4()) aer_job = AerJob(self, job_id, self._run_job, qobj) aer_job.submit() return aer_job def _run_job(self, job_id, qobj): """Run a Qobj on the backend.""" self._validate(qobj) final_state_key = 32767 # Internal key for final state snapshot # Add final snapshots to circuits for experiment in qobj.experiments: experiment.instructions.append( QobjInstruction(name='snapshot', params=[final_state_key], label='MISSING', type='MISSING') ) result = super()._run_job(job_id, qobj) # Remove added snapshot from qobj for experiment in qobj.experiments: del experiment.instructions[-1] # Extract final state snapshot and move to 'statevector' data field for experiment_result in result.results: snapshots = experiment_result.data.snapshots.to_dict() if str(final_state_key) in snapshots: final_state_key = str(final_state_key) # Pop off final snapshot added above final_state = snapshots.pop(final_state_key, None) final_state = final_state['statevector'][0] # Add final state to results data experiment_result.data.statevector = final_state # Remove snapshot dict if empty if snapshots == {}: delattr(experiment_result.data, 'snapshots') return result def _validate(self, qobj): """Semantic validations of the qobj which cannot be done via schemas. Some of these may later move to backend schemas. 1. No shots 2. No measurements in the middle """ if qobj.config.shots != 1: logger.info("statevector simulator only supports 1 shot. " "Setting shots=1.") qobj.config.shots = 1 for experiment in qobj.experiments: if getattr(experiment.config, 'shots', 1) != 1: logger.info("statevector simulator only supports 1 shot. " "Setting shots=1 for circuit %s.", experiment.name) experiment.config.shots = 1 for op in experiment.instructions: if op.name in ['measure', 'reset']: raise SimulatorError( "In circuit {}: statevector simulator does not support " "measure or reset.".format(experiment.header.name))
class CliffordSimulator(BaseBackend): """"C++ Clifford circuit simulator with realistic noise.""" DEFAULT_CONFIGURATION = { 'backend_name': 'clifford_simulator', 'backend_version': '1.0.0', 'n_qubits': int(log2(local_hardware_info()['memory'] * (1024**3) / 16)), 'url': 'https://github.com/Qiskit/qiskit-terra/src/qasm-simulator-cpp', 'simulator': True, 'local': True, 'conditional': True, 'open_pulse': False, 'memory': False, 'max_shots': 65536, 'description': 'A C++ Clifford simulator with approximate noise', 'basis_gates': [ 'cx', 'id', 'x', 'y', 'z', 'h', 's', 'sdg', 'snapshot', 'wait', 'noise', 'save', 'load' ], 'gates': [{ 'name': 'TODO', 'parameters': [], 'qasm_def': 'TODO' }] } def __init__(self, configuration=None, provider=None): super().__init__(configuration=(configuration or BackendConfiguration.from_dict( self.DEFAULT_CONFIGURATION)), provider=provider) # Try to use the default executable if not specified. if 'exe' in self._configuration: paths = [self._configuration.exe] else: paths = DEFAULT_SIMULATOR_PATHS # Ensure that the executable is available. try: self._configuration.exe = next( path for path in paths if (os.path.exists(path) and os.path.getsize(path) > 100)) except StopIteration: raise FileNotFoundError( 'Simulator executable not found (using %s)' % getattr(self._configuration, 'exe', 'default locations')) def properties(self): """Return backend properties""" properties = { 'backend_name': self.name(), 'backend_version': self.configuration().backend_version, 'last_update_date': '2000-01-01 00:00:00Z', 'qubits': [[{ 'name': 'TODO', 'date': '2000-01-01 00:00:00Z', 'unit': 'TODO', 'value': 0 }]], 'gates': [{ 'qubits': [0], 'gate': 'TODO', 'parameters': [{ 'name': 'TODO', 'date': '2000-01-01 00:00:00Z', 'unit': 'TODO', 'value': 0 }] }], 'general': [] } return BackendProperties.from_dict(properties) def run(self, qobj): """Run a Qobj on the backend. Args: qobj (dict): job description Returns: AerJob: derived from BaseJob """ job_id = str(uuid.uuid4()) aer_job = AerJob(self, job_id, self._run_job, qobj) aer_job.submit() return aer_job def _run_job(self, job_id, qobj): if isinstance(qobj, Qobj): qobj_dict = qobj.as_dict() else: qobj_dict = qobj self._validate() # set backend to Clifford simulator if 'config' in qobj_dict: qobj_dict['config']['simulator'] = 'clifford' else: qobj_dict['config'] = {'simulator': 'clifford'} qobj = Qobj.from_dict(qobj_dict) result = run(qobj, self._configuration.exe) result['job_id'] = job_id return result_from_old_style_dict(result) def _validate(self): return